Starting with AOS v7, API v4 is GA (generally available) for a multitude of endpoints (888 in total so far which is almost the same number as all previous API versions endpoints cumulated).
One of the new feature of this API is that it comes with SDK (software development kits) for several programming languages, including Python.
Using SDKs means that you do not need to make direct HTTP requests to API endpoints. Instead you can use functions available in the API module directly which facilitate things like authentication or dealing with specific methods available to the API endpoint.
I’ll admit it, I’m lazy, which is why I work in IT.
I don’t like to read thru hundreds of pages of documentation and any shortcut I can take, I will. Just in case you are not like me and for reference, the Nutanix v4 API SDK is documented here: https://developers.nutanix.com/
All API things are documented here: https://nutanix.dev
To get started, the best read available at the moment is this one: https://www.nutanix.dev/nutanix-api-user-guide/
This page also contains important information about the v4 API in general: https://www.nutanix.dev/api-reference-v4/
Now that this is out of the way, let’s review what this specific article series will cover, keeping in mind that its intent is to go straight to the point and get you going with the Python SDK in record time:
- the basics of using the v4 API Python SDK (how to install and import modules, how to initiate the first connection, how to use the online documentation)
- how to deal with pagination in the API (we might as well get this out of the way quickly as this will be a recurring issue otherwise)
- a suggested method for dealing with credentials securely (not directly related to this API but generally useful for working with any API that uses basic authentication in Python)
- provide an example of a Prism Central entities reporting script
The intent of all code examples used in this article is to show code that is production ready and can be used in a live customer environment (as opposed to leave the burden of figuring that out on you).
The basics of using the v4 API Python SDK
Picking and installing the right modules
The first thing you will need to do (assuming you already have a functional Python setup) is to install the Python modules for the Nutanix v4 API. Note that this is “modules” (plural), not “module” (singular) because each domain has its own module.
At the time of writing, the available domains are the following (you can click the domain to access the specific documentation page):
domain | module | install command | import command |
---|---|---|---|
AIOps | ntnx_aiops_py_client | pip install ntnx-aiops-py-client | import ntnx_aiops_py_client |
Cluster Management | ntnx_clustermgmt_py_client | pip install ntnx-clustermgmt-py-client | import ntnx_clustermgmt_py_client |
Data Policies | ntnx_datapolicies_py_client | pip install ntnx-datapolicies-py-client | import |
Data Protection | ntnx_dataprotection_py_client | pip install ntnx-dataprotection-py-client | import ntnx_dataprotection_py_client |
Files | ntnx_files_py_client | pip install ntnx-files-py-client | import |
Identity and Access Management | ntnx_iam_py_client | pip install ntnx-iam-py-client | import ntnx_iam_py_client |
Licensing | ntnx_licensing_py_client | pip install ntnx-licensing-py-client | import ntnx_licensing_py_client |
Life Cycle Management | ntnx_lifecycle_py_client | pip install ntnx-lifecycle-py-client | import ntnx_lifecycle_py_client |
Flow Management | ntnx_microseg_py_client | pip install ntnx-microseg-py-client | import ntnx_microseg_py_client |
Monitoring | ntnx_monitoring_py_client | pip install ntnx-monitoring-py-client | import ntnx_monitoring_py_client |
Networking | ntnx_networking_py_client | pip install ntnx-networking-py-client | import ntnx_networking_py_client |
Object Storage Management | ntnx_objects_py_client | pip install ntnx-objects-py-client | import ntnx_objects_py_client |
NCM Operation Base Platform | ntnx_opsmgmt_py_client | pip install ntnx-opsmgmt-py-client | import ntnx_opsmgmt_py_client |
Prism | ntnx_prism_py_client | pip install ntnx-prism-py-client | import ntnx_prism_py_client |
Security1 | ntnx_security_py_client | pip install ntnx-security-py-client | import ntnx_security_py_client |
Virtual Machine Management | ntnx_vmm_py_client | pip install ntnx-vmm-py-client | import ntnx_vmm_py_client |
Volumes | ntnx_volumes_py_client | pip install ntnx-volumes-py-client | import ntnx_volumes_py_client |
So at this point you’re probably asking yourself a number of questions:
- “This is fine and dandy, but how the hell am I supposed to know what those domains mean and which I will need for my specific use case?“
- “Why the hell aren’t all those domains regrouped in a single module?“
- “And why the hell would you have a different naming convention for the module name between the pip install and import commands?” (notice how one is using dashes while the other is using underscores)
I’m guessing the answer to that last one has to do with some weird Python semantic but I honestly haven’t dug around to find out.
About using different modules for each domain, I agree this is a bit of a pain but at the same time I figure that this will enable different domains to update and evolve their APIs separately and will benefit us grumpy API users in the long term.
As for the first question, I have a couple of things to help you figure that out. The first one is this table that shows you the description for each domain (keeping only the essential parts of those description):
domain | description |
---|---|
AIOps | “… features such as Analysis, Note that this is also where you get your stats/metrics for entities. I also crossed out reporting because the reports apis are actually in another domain (NCM Operations Base Platform). |
Cluster Management | “… manage Hosts, Clusters and other Infrastructure.“ |
Data Policies | “… manage Policies for Disaster Recovery and Storage.“ |
Data Protection | “… business Continuity with full spectrum of Disaster Recovery and Backup solution. Spanning across Single PC, Cross AZ, MultiSite. Configuration of Recovery points, Protection policies, Recovery Plans. Execution and monitoring of back up and recovery orchestrations on OnPrem as well as Cloud.“ |
Files | “… manage virtual file servers, create and configure shares for client access, protect them using DR and sync policies, provision storage space and administer security controls.“ |
Identity and Access Management | “… for managing users, user-groups, directory services, identity providers, roles and authorization policies.“ Yes, this is where RBAC is. |
Licensing | No useful description in the documentation, but this helps you manage licenses (shocking). |
Life Cycle Management | “… manage Infrastructure, Software and Firmware Upgrades.” While that description is not tremendously useful, for those of you familiar with the Nutanix platform, this is the LCM API (LCM being the framework used to do firmware and software updates on the Nutanix platform). |
Flow Management | “… manage Network Security Policy configuration of Nutanix clusters.“ Translation: this is for Flow Network Security microsegmentation policies. |
Monitoring | “… manage Alerts, Alert policies, Events and Audits.“ |
Networking | “… manage networking configuration on Nutanix clusters, including AHV and advanced networking.“ Note that “advanced networking” means Flow Virtual Networking (VPCs and such). |
Object Storage Management | “… manage Petabytes of Unstructured and Machine-generated data using a software-defined Object Store Service.“ Translation: this is the Nutanix Objects API. |
NCM Operation Base Platform | “… provide functionalities that are common to APIs in namespaces aiops, devops, secops, finops.“ This has reports APIs (while aiops does not, despite its description). |
Prism | “… manage Tasks, Category Associations and Submit Batch Operations.“ Note that this also contains categories APIs. |
Security1 | “… manage security features, such as encryption, certificates, or platform hardening.“ No, this isn’t where RBAC is. You’ll need Identity and Access Management for RBAC (and use authorization policies). |
Virtual Machine Management | “… manage the life-cycle of virtual machines hosted on Nutanix.“ Translation: where all VM things are (almost). |
Volumes | “… configure volumes.“ Translation: Nutanix Volumes APIs (which are used to create and manage volume groups which are used to provide access to Nutanix storage using the iscsi protocol). |
The second is the following spreadsheet which is a complete inventory of all public Nutanix v4 API endpoints. You can search it to help you hopefully identify which domain/namespace has the endpoint you need for your use case:
You’re welcome.
So now that you know which domains and modules you need, that you know how to install them and how to import them in your script, how the heck do they actually work?
Understanding how modules work and establishing a first connection
The steps to use a Python SDK module for the Nutanix v4 API are:
- Import the module
- Create an API client configuration object and set key attributes
- Use the API client configuration to create an API client object
- Create an instance of the API client by connecting it to a specific submodule
- Use the API client instance to make a call to a specific API endpoint and get or post data
The step that was most confusing for me was step 4: why do I have to create yet another object since I already have a connected API client? This is because each SDK module has submodules (endpoints).
For example, ntnx_clustermgmt_py_client has six submodules/endpoints: cluster_profiles_api, clusters_api, disks_api, pcie_devices_api, storage_containers_api, and vcenter_extensions_api.
Each of those submodules/endpoints has specific methods. You can view all the submodules/endpoints in the spreadsheet where “namespace” is the domain/module and endpoint_name is the submodule/endpoint.
For convenience, here is a summary table of all modules and their submodules/endpoints (with the name you would use invoking them in Python):
module | submodules/endpoints |
---|---|
aiops | ScenariosApi, StatsApi |
clustermgmt | ClusterProfilesApi, ClustersApi, DisksApi, PcieDevicesApi, StorageContainersApi, VcenterExtensionsApi |
datapolicies | ProtectionPoliciesApi |
dataprotection | ProtectedResourcesApi, RecoveryPointsApi |
files | AnalyticsApi, AntivirusServerApi, DnsApi, FileServersApi, InfectedFilesApi, MountTargetsApi, NotificationPoliciesApi, PartnerServersApi, QuotaPoliciesApi, RansomwareConfigsApi, RecommendationsApi, ReplicationJobsApi, RepolicationPoliciesApi, SnapshoptChangedContentsApi, SnapshotSchedulesApi, SnapshotsApi, TierApi, UnifiedNamespacesApi, UserMappingsApi, VirusScanPoliciesApi |
iam | AuthorizationPoliciesApi, CertificateAuthenticationProvidersApi, ClientsApi, DirectoryServicesApi, EntitiesApi, OperationsApi, RolesApi, SAMLIdentityProvidersApi, UserGroupsApi, UsersApi |
licensing | EndUserLicenseAgreementApi, LicenseKeysApi, LicensesApi |
lifecycle | BundlesApi, ConfigApi, EntitiesApi, ImagesApi, InventoryApi, LcmSummariesApi, NotificationsApi, PrechecksApi, RecommendationsApi, StatusApi, UpgradesApi |
microseg | AdressGroupsApi, DirectoryServerConfigsApi, NetworkSecurityPoliciesApi, ServiceGroupsApi |
monitoring | AlertEmailConfigurationApi, AlertsApi, AuditsApi, ClusterLogsApi, EventsApi, ManageAlertsApi, SystemDefinedPoliciesApi, UserDefinedPoliciesApi |
networking | AwsSubnetsApi, AwsVpcsApi, BgpRoutesApi, BgpSessionsApi, BridgesApi, ClusterCapabilitiesApi, FloatingIpsApi, GatewaysApi, IPFIXExportersApi, Layer2StretchStatsApi, Layer2StretchesApi, LoadBalancerSessionStatsApi, LoadBalancerSessionsApi, MacAdressesApi, NetworkControllersApi, RemoteEntitiesApi, RouteTablesApi, RoutesApi, RoutingPoliciesApi, RoutingPolicyStatsApi, SubnetIPReservationApi, SubnetMigrationsApi, SubnetsApi, TrafficMirrorStatsApi, TrafficMirrorsApi, UplinkBondsApi, VirtualSwitchNodesInfoApi, VirtualSwitchesApi, VpcNsStatsApi, VpcVirtualSwitchMappingsApi, VpcsApi, VpnConnectionStatsApi, VpnConnectionsApi |
objects | ObjectStoresApi |
opsmgmt | GlobalReportSettingApi, ReportArtifactsApi, ReportConfigApi, ReportsApi |
prism | BatchesApi, CategoriesApi, DomainManagerApi, DomainManagerBackupsApi, TasksApi |
security | ApprovalPoliciesApi, STIGsApi |
vmm | EsxiStatsApi, EsxiVmApi, ImagePlacementPoliciesApi, ImageRateLimitPoliciesApi, ImagesApi, StatsApi, TemplatesApi, VmApi |
volumes | IscsiClientsApi, VolumeGroupsApi |
So, back to our 4 steps, this time with code examples to bring it all together (this example will get the first page of virtual machines):
# step 1 of 5: import the module
import ntnx_vmm_py_client
# step 2 of 5: create an API client configuration object and set key attributes
api_client_configuration = ntnx_vmm_py_client.Configuration()
api_client_configuration.host = api_server
api_client_configuration.username = username
api_client_configuration.password = secret
api_client_configuration.verify_ssl = False
# step 3 of 5: use the API client configuration to create an API client object
api_client = ntnx_vmm_py_client.ApiClient(configuration=api_client_configuration)
# step 4 of 5: create an instance of the API client by connecting it to a specific submodule
api_instance_vm = ntnx_vmm_py_client.api.VmApi(api_client=api_client)
# step 5 of 5: use the API client instance to make a call to a specific API endpoint and get or post data
vm_list = api_instance_vm.list_vms()
And now we have a dictionary (vm_list) which contains the list of vm entities available on the first page with all their attributes.
So how do you know what the response looks like? How do you know which functions (other than list_vms) are available for that submodule/endpoint? That’s what the documentation is for, and here is how you use that documentation:
- First, you connect to the documentation page: https://developers.nutanix.com/
- You select the domain/module under “SDK Reference” at the top of the page and the language (exp: Virtual Machine Management > Python)
- You then select “SDK Reference” and “api_package”
- You see all the functions available for each endpoint. Select the function you want (exp: VmApi.list_vms()) to use to see which parameters you can use with each function (for list_vms, you’ll see that we can specify the page number, filter, etc…).
- Under the “Returns:” section, click the class name (exp: ListVmsApiResponse). Pretty much all functions will have a data and a metadata section in their response.
- Under “data” click on the object type (exp: Vm) and you will see a list of all the properties which are part of that object. Note that sometimes, other objects are in the list of properties (exp: cluster in the vm object) and you will need to drill down to see what properties are part of that other object.
If you need to understand what other attributes can be configured in the API client configuration, click on the “Configuration” section for that module. The “Examples” section can also help you figure out how to use some of those functions.
Speaking of which, and back to our list vms example, I’m sure you noticed that I mentioned this is only returning the first page of entities, so you may be wondering how to get all entities. To do this, we have to deal with pagination which is our next topic.
Dealing with pagination
By default, the v4 API will return 50 entities in its data section. If you want to get more, you’ll have to either increase the limit of your initial request, or you’ll have to request the next page until the last page.
Increasing the limit of the initial request has its limitations (no pun intended) which may vary from endpoint to endpoint so it is not a reliable way to make sure you get all the entities you need. If you’re trying to reduce the amount of data and you already the name or some other attributes that can be filtered on, then filtering is the way to go which is well explained in this blog.
In case you need to increase the limit though, this is simply done in the request URL like so:
https://{{pc_ip}}:9440/api/vmm/v4.0/ahv/config/vms?$page=0&$limit=100
Note that we also specify the page number (which starts at 0) in the request URL so at this point you’re probably wondering “how do I know there is another page, and how do I know what’s the last page?“
The metadata section of a response contains an attribute called “totalAvailableResults” that helps you figure out how many entities are available in total. In addition, that medata section also contains a “links” section which is a list containing 3 objects: the href of the first page, the href of the current page, and the href of the last page of entities.
Working thru pagination is therefore a matter of starting at page 0, then moving on to the next page until the href of the current page is the same as the href of the last page.
Of course, doing it sequentially would be very inefficient, especially if you have hundreds of pages of data.
Using Python, we can use the concurrent
module to multi-thread the multiple pages retrieval process and cut down fetching times from minutes to seconds. In addition, we’ll use the tqdm
module to display a progress bar because if you are impatient like me you’ll want to know what the hell your script is doing.
The first step will be to figure out how many pages of entities there are. To do this, we start by setting up the api client and its instance, then make a call limited to a single entity (so it’s fast) and then we retrieve the total number of available entities from the metadata section of the response:
import math
client = ntnx_vmm_py_client.ApiClient(configuration=api_client_configuration)
entity_api = ntnx_vmm_py_client.VmApi(api_client=client)
LIMIT = 100
response = entity_api.list_vms(_page=0,_limit=1)
total_available_results=response.metadata.total_available_results
page_count = math.ceil(total_available_results/LIMIT)
Note that we are figuring out the page count by using the math.ceil
function on the total available results count divided by the limit.
Now that we know how many pages we have, we’ll use tqdm and concurrent to call a function to retrieve all those pages in parallel (with a controlled number of workers):
from concurrent.futures import ThreadPoolExecutor, as_completed
import tqdm
def fetch_entities(client,module,entity_api,function,page,limit):
entity_api_module = getattr(module, entity_api)
entity_api = entity_api_module(api_client=client)
list_function = getattr(entity_api, function)
response = list_function(_page=page,_limit=limit)
return response
entity_list=[]
LIMIT = 100
with tqdm.tqdm(total=page_count, desc="Fetching entity pages") as progress_bar:
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(
fetch_entities,
module=ntnx_vmm_py_client,
entity_api='VmApi',
client=client,
function='list_vms',
page=page_number,
limit=LIMIT
) for page_number in range(0, page_count, 1)]
for future in as_completed(futures):
try:
entities = future.result()
entity_list.extend(entities.data)
except Exception as e:
print(f"{(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M:%S')} [WARNING] Task failed: {e}")
finally:
progress_bar.update(1)
So, here we have a generic function to retrieve entities to which you can pass a module name, API instance, API client and function (so it works here for vms but could work for any type of entity).
We then have a loop that uses the page count to start multiple threads (up to 10 as defined in max_workers
) to retrieve different pages. The results of those queries are stored in a variable called future
, specifically in its result
function.
With each page retrieved, we extend a list variable called entity_list
with the retrieved data. We also update the progress bar for those impatient users (myself included).
Easy peazy and super speedy.
Remember that for an API instance object, you can figure out the retrieve function name from the documentation (and if you do not remember this, read the previous section of this blog again).
With this process, if we wanted to retrieve the list of all clusters entities we would use:
client = ntnx_clustermgmt_py_client.ApiClient(configuration=api_client_configuration)
entity_api = ntnx_clustermgmt_py_client.ClustersApi(api_client=client)
entity_list=[]
LIMIT = 100
response = entity_api.list_clusters(_page=0,_limit=1)
total_available_results=response.metadata.total_available_results<br>page_count = math.ceil(total_available_results/LIMIT)
with tqdm.tqdm(total=page_count, desc="Fetching entity pages") as progress_bar:
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(
fetch_entities,
module=ntnx_clustermgmt_py_client,
entity_api='ClustersApi',
client=client,
function='list_clusters',
page=page_number,
limit=LIMIT
) for page_number in range(0, page_count, 1)]
for future in as_completed(futures):
try:
entities = future.result()
entity_list.extend(entities.data)
except Exception as e:
print(f"{(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M:%S')} [WARNING] Task failed: {e}")
finally:
progress_bar.update(1)
Note that this is not using the same API instance object and that the function name has changed from list_vms
to list_clusters
.
In part 2 of this series, we will cover how to deal with arguments and credentials securely and examine a complete reporting scripts that uses the v4 API to produce an HTML inventory of a number of entity types available in Prism Central.
As a spoiler, here is a demo of the entire script running inside a terminal: