VMware {code} API How to Python

Read vCenter inventory using VI/JSON API

It has been a while since vCenter 8.0u1 shipped and with it the JSON based protocol for the Virtual Infrastructure APIs aka VI/JSON. In this article we will put the VI/JSON APIs to use in a practical and complete example. We will read vCenter inventory using VI/JSON API to obtain a list of the virtual machines and hosts in a vCenter system using the PropertyCollector API and Python aiohttp library. This includes:

  1. Negotiate API release identifier
  2. Call VI/JSON APIs with Python aiohttp library
  3. Implement Proxies to VI/JSON APIs
  4. Retrieve Properties from a Set of Managed Objects with a View

You could easily do the same with PyVMOMI library except PyVMOMI still supports Python 2.7 and thus lacks asynchronous option.

“Why would you need asynchronous code?” one may ask. The simple answer is – scale. If your environment features multiple vCenter systems it is much faster to send API requests to all of them in parallel and await the results then iterating the servers one by one. Asynchronous Python allows us this flexibility without need for complex multithreaded programming. Thus VI/JSON with asynchronous Python could be great addition to an app doing complex workflows with PyVMOMI in places where it needs to optimize speed. One such scenario is to read vCenter inventory using VI/JSON API.

In this example we focus on the use of the vCenter API from Python using aiohttp library. To keep it manageable length we will not look into orchestrating multiple connections. The later may be a fun exercise for the reader to develop further the current example.

The code in the example can be ported to other programming languages such as Javascript, Rust, PHP or Dart that lack SDK support for vSphere.

Configure the environment

This article and the code with it can be downloaded as Jupyter Notebook from github.

To start with this example put the Jupiter notebook file in an empty folder where you will run the experiment. You can delete the folder at the end to clean up your system. To run the example you will need Python 3.10 or later installed on the system.

I used Visual Studio code to view and edit the Notebook. I ran the example on WSL Ubuntu 22. The same instructions should work on Linux or MacOS.

I would recommend setting up Python environment first to isolate the experiment. You can skip this step if your Jupiter Notebook application already created Python environment or other isolation for your notebook. Visual Studio Code was happy to use Python virtual environment with my notebook.

The next step is to install the necessary Python packages – “aiohttp” and “python-dotenv”. We’ll use “pip” for this.

Set up the Basics

Now we can import the Python libraries we will need:

Next we set up the connection parameters for the vCenter system. Create a .env file in the folder where you run the notebook and define the connection settings in it as follows. We will use simple user name and password authentication here. More advanced authentication options are available and we will discuss in future articles.

Then we load the connection parameters in Python

Now to some less glorious details about our application like log level, an exception type we will use to convey errors, a function to convert from JSON identifier to URL path string, a utility to print shortened JSON, a “well known” reference to the root ServiceInstance object and the JSON MIME type string.

Negotiate API release identifier

vSphere API exposes a complex and evolving object structure. A client and server must negotiate mutually supported release identifier before APIs can be put to use.

Using a negotiated release identifier the vSphere server will coerce it’s responses to data types that the client understands. For example if the server supports new kind of ethernet adapter that the client does not know about the server will return a more generic adapter i.e. the server will upcast the network adapter to a parent class that the client understands.

Similarly clients should only rely on API features that exist in the negotiated release. Clients need to do further checks in the vSphere API as capabilities of individual objects may vary. For example depending on the virtual hardware version a virtual machine will support different use cases and configurations.

In PyVMOMI determining the release number is achieved by SmartConnect API. SmartConnect relies on internal PyVMOMI tables that combined with the vCenter server metadata allow PyVMOMI to compute a mutually supported API release identifier. To make this simpler for VI/JSON clients in vCenter 8.0.2 there is a new System::hello API that is release agnostic. Clients provide the vSphere release identifiers they have been tested with in order of preference and vCenter selects the best match.

We can now try this to see what release can be used to talk to vCenter.

Call VI/JSON APIs with Python aiohttp library

Looking a the VI/JSON Reference documentation we see a pattern. There are 2 types of HTTP requests made. GET requests retrieving properties of objects. POST requests that invoke methods with parameters. We will set up a vSphere connection class to encapsulate this concern. It will have 2 methods – fetch and invoke for the two classes of APIs and common logic to process responses as their structure is the same across both classes of APIs.

In addition we will add option to set a session key to our connection class. Session key in VI/JSON API is both moniker for the current user identity and a handle to server side state linked to the specific API client instance. A session key is obtained by successful call to one of the SessionManager::Login* APIs.

Let’s test our code by fetching the ServiceInstance::content property.

Implement Proxies to VI/JSON APIs

As we want to go a bit deeper than fetching miscellaneous detail about our server we would like to have objects similar to PyVMOMI library. The structure of the VI/JSON API is similar to Object Oriented programming libraries like the Java and .Net standard libraries. So objects with methods corresponding to the VI/JSON requests would come handy. We would like to recreate scenario similar to the one described in Retrieve Properties from a Set of Managed Objects with a View. We will create proxies to remote objects that expose Python friendly methods calling into the VSphereConnection we implemented above.

  1. First the SessionManager proxy will give us access to the Login and Logout methods. The Login method upon success will set the vmware-api-session-id header back in the connection for subsequent calls.
  2. ContainerView proxy to invoke the destroyView API to release system resources
  3. ViewManager proxy to create a container view.
  4. PropertyCollector to read data from the server in pages and handle errors.
  5. Lastly the ServiceInstance will give us access to the other proxies and the identifier of the root folder that we need to create a Container View.

This was a lot. Let’s do a simple test. We will Login and Logout to see how all this works.

Retrieve Properties from a Set of Managed Objects with a View

We can now retrieve inventory data from our vCenter server. We want to build a query to PropertyCollector that will fetch all VMs – VirtualMachine and ESXi hosts – HostSystem with their names and IP addresses for VMs. IP addresses of VMs are reported by the Guest OS Virtual Machine tools. Thus we need to navigate deeper into the virtual machine settings. We start from the summary property and then look into the guest field and last to the ipAddress within it. To ask this of the PropertyCollector we use dot delimited string summary.guest.ipAddress.

To build our code we will follow the instruction in the vSphere Web Service Programming Guide The basic steps are:

Build the request to RetrievePropertiesEx

I did this by first copying the example from the reference documentation. Then I copied the details from the vSphere Web Service Programming Guide. Lastly I had to look up the names of various structures I used and fill in the _typeName fields for all objects. It was not as easy as having autocompletion in PyVMOMI and also was not as bad as I feared.

Call the PropertyCollector

Thanks to the proxies we built above the actual code making calls to vCenter is straight forward.

It is worth highlighting the clean up code as the end. It is important to clean up and the vCenter API is stateful and the objects we interact with occupy memory and other resources on vCenter. Leaving resources behind may result in increased memory consumption and failures on vCenter side.

In our example there are two elements to cleanup. If iteration through the pages is not complete we should dismiss the server state associated with the token. When done with success or error we must release the ContainerView object that tracks the inventory for us.

We are now ready to read vCenter Inventory using VI/JSON API. Let’s put a short script to list the VMs and ESXi hosts in our vCenter system:

Final touch

To make sense of the data we will output data into tables for better readability and organization. It’s the final step in the data processing workflow. We will loop over the output split it in VMs and hosts and extract properties for each data type.

…and now the moment of truth.

Conclusion and next steps

In the above example we read vCenter Inventory using VI/JSON API and saw the basic mechanics of the VI/JSON API – negotiating a release identifier, using session, making basic requests to the API to fetch properties and call methods. We also built a set of proxy objects to simplify API use. Lastly we saw how to construct complex API requests by leveraging the reference documentation and API guides. The lessons from this example can be easily developed further to utilize other APIs and build powerful Asynchronous Python tools that operate scaled out vSphere environments. The example can also be ported to different language that lacks SDK support such as Javascript.