Nutanix v4 APIs: Making The First Requests (Python 3 API Client)

Nutanix v4 APIs Making The First Request (Python 3 API Client)

Table of Contents

This introductory article is the first part in a mini series covering how to get started with the Nutanix v4 REST APIs and SDKs. In this part, we’ll setup our first requests using a custom API client written with Python 3.

Part 2, coming soon, will cover making the same requests but using the new Nutanix v4 API SDKs. Please follow @NutanixNation on Twitter so you don’t miss the publication announcements!

Demo Environment

The demo environment used throughout this article is as follows.

  • Prism Central version: pc.2022.6 (Prism Central will be referred to as “PC”)
  • AOS version: 6.0.2.4
  • Postman version: 9.15.6
  • Python version: 3.10
  • Client details: Arch Linux (rolling release, although any OS capable of running Python >= 3.6 will be fine)

Requests

For this demo we’ll work with three of the currently available EA (Early Access) v4 APIs:

  • Images, from the “vmm” namespace
  • Tasks, from the “prism” namespace
  • Clusters, from the “clustermgmt” namespace

It is important to note these APIs are currently released as Early Access (EA) and are not yet considered GA.

Building the requests

Each of the three available APIs used in this article are published under v4.0.a1 of the Nutanix v4 REST APIs. This means all our requests will be sent to URLs constructed similar to the following:

http://{{pc_ip}}:9440/api/{{namespace}}/{{version}}/{{api}}

The first example we’ll look at is retrieving a list of AHV images. Images have many uses but are typically used as base disk images during VM deployments. The complete URL to retrieve a list of all images registered on a specific PC instance is as follows:

https://{{pc_ip}}:9440/api/vmm/v4.0.a1/images

Listing registered clusters and getting specific task details would be as follows (respectively):

https://{{pc_ip}}:9440/api/clusters/v4.0.a1/clusters
https://{{pc_ip}}:9440/api/prism/v4.0.a1/config/tasks/ZXJnb24=:{{task_extId}}

Important note: When requesting details about a specific task, the task extId must always be prefixed with ZXJnb24=:

The following details apply to all requests so far:

  • HTTP method: GET
  • Authentication: HTTP Basic auth (username & password)
  • Headers: Content-Type: application/json (this is a mandatory header)

The Script

With all required prerequisite information available, a usable demo Python script can be built fairly quickly. The demo script functions as follows:

  • The Prism Central IP address or FQDN and username are mandatory command-line parameters
  • The user’s password is accepted by the Python getpass function
  • Authentication headers are created using the Python b64encode function
  • Request headers are created as a Python dictionary
  • A request is sent to the specified Prism Central IP address, asking for a list of all visible images
  • If the request is successful, an image count is displayed
  • If the request fails, general exception information is displayed

The complete script is as follows:

"""
Use the Nutanix v4 REST APIs to request and parse a list of all available images
"""

import requests
import urllib3
import getpass
import argparse
from base64 import b64encode

"""
suppress warnings about insecure connections
please consider the security implications before doing this in a production environment
"""
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

"""
setup the command line parameters
for this example only two parameters are required
- the Prism Central IP address or FQDN
- the Prism Central username; the script will prompt for the user's password
  so that it never needs to be stored in plain text
"""
parser = argparse.ArgumentParser()
parser.add_argument("pc_ip", help="Prism Central IP address or FQDN")
parser.add_argument("username", help="Prism Central username")
args = parser.parse_args()

# get the cluster password
cluster_password = getpass.getpass(
    prompt="Please enter your Prism Central \
password: ",
    stream=None,
)

pc_ip = args.pc_ip
username = args.username

# make sure the user enters a password
if not cluster_password:
    while not cluster_password:
        print(
            "Password cannot be empty. Please enter a password or Ctrl-C/Ctrl-D to exit."
        )
        cluster_password = getpass.getpass(
            prompt="Please enter your Prism Central password: ", stream=None
        )

try:

    """
    setup the HTTP Basic Authorization header based on the
    supplied username and password
    """
    encoded_credentials = b64encode(
        bytes(f"{username}:{cluster_password}", encoding="ascii")
    ).decode("ascii")
    auth_header = f"Basic {encoded_credentials}"
    # setup the URL that will be used for the API request
    url = f"https://{pc_ip}:9440/api/vmm/v4.0.a1/images"

    """
    setup the request headers
    note the use of {auth_header} i.e. the Basic Authorization
    credentials we setup earlier
    """
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"{auth_header}",
        "cache-control": "no-cache",
    }
    # submit the request
    try:
        response = requests.request(
            "GET", url, headers=headers, verify=False, timeout=10
        )
        if response.ok:
            # show a total count of images found
            print(
                f'Total images found: {response.json()["metadata"]["totalAvailableResults"]}'
            )
        else:
            print(f"An error occurred while connecting to {pc_ip}.")
            """
            the following line can be uncommented to show
            detailed error information
            """
            print(response.text)
    except Exception as ex:
        print(
            f"An {type(ex).__name__} exception occurred while \
              connecting to {pc_ip}.\nArgument: {ex.args}."
        )

# catching all exceptions like this should be generally be avoided
except Exception as e:
    print(f"{e}")

Investigating the response

A request to list all images will return a response similar to what is shown below. This example shows 14 available images; to keep the response readable only the first image has been left in the list.

{
    "$dataItemDiscriminator": "List<vmm.v4.images.Image>",
    "data": [
        {
            "sizeBytes": 8589934592,
            "$sourceItemDiscriminator": "vmm.v4.images.UrlSource",
            "source": {
                "url": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2003.qcow2",
                "allowInsecure": false,
                "$reserved": {
                    "$fqObjectType": "vmm.v4.r0.a1.images.UrlSource"
                },
                "$objectType": "vmm.v4.images.UrlSource"
            },
            "$reserved": {
                "$fqObjectType": "vmm.v4.r0.a1.images.Image"
            },
            "$objectType": "vmm.v4.images.Image",
            "extId": "61c631c6-08d0-45c1-ac1a-d8bde49dd5c7",
            "name": "CentOS-7.8-2003",
            "type": "DISK_IMAGE"
        }
    ],
    "metadata": {
        "flags": [
            {
                "name": "hasError",
                "value": false,
                "$reserved": {
                    "$fqObjectType": "common.v1.r0.a3.config.Flag"
                },
                "$objectType": "common.v1.config.Flag"
            },
            {
                "name": "isPaginated",
                "value": true,
                "$reserved": {
                    "$fqObjectType": "common.v1.r0.a3.config.Flag"
                },
                "$objectType": "common.v1.config.Flag"
            }
        ],
        "links": [
            {
                "href": "https://10.42.250.40:9440/api/vmm/v4.0.a1/images?limit=3&$page=0&$limit=100",
                "rel": "last",
                "$reserved": {
                    "$fqObjectType": "common.v1.r0.a3.response.ApiLink"
                },
                "$objectType": "common.v1.response.ApiLink"
            },
            {
                "href": "https://10.42.250.40:9440/api/vmm/v4.0.a1/images?limit=3&$page=0&$limit=100",
                "rel": "self",
                "$reserved": {
                    "$fqObjectType": "common.v1.r0.a3.response.ApiLink"
                },
                "$objectType": "common.v1.response.ApiLink"
            },
            {
                "href": "https://10.42.250.40:9440/api/vmm/v4.0.a1/images?limit=3&$page=0&$limit=100",
                "rel": "first",
                "$reserved": {
                    "$fqObjectType": "common.v1.r0.a3.response.ApiLink"
                },
                "$objectType": "common.v1.response.ApiLink"
            }
        ],
        "totalAvailableResults": 14,
        "$reserved": {
            "$fqObjectType": "common.v1.r0.a3.response.ApiResponseMetadata"
        },
        "$objectType": "common.v1.response.ApiResponseMetadata"
    },
    "$reserved": {
        "$fqObjectType": "vmm.v4.r0.a1.images.ImageListApiResponse"
    },
    "$objectType": "vmm.v4.images.ImageListApiResponse"
}

The key data points/properties in this response are:

  • The data list includes details of all visible images
  • For each image within the data list, the following information is available:
    • Image size: sizeBytes
    • The type of image e.g. created from URL: $sourceItemDescriminator
    • Image source details, including the source URL: source
    • The image’s unique identifier: extId
    • Name and type: name and type, respectively
  • The metadata.flags object shows key request data, including:
    • Whether or not any errors were returned: hasError
    • If the request has been paginated i.e. multiple pages were returned: isPaginated
  • The total number of results: metadata.totalAvailableResults
    • If required, the total available results could also be extracted by examining the length of the data list

Wrapping Up

In this example the Nutanix v4 REST APIs are easily consumed by standard Python libraries and functions, although the same approach can be applied to any language capable of sending a REST API request. Similar to previous Nutanix REST API versions, the use of standard JSON-formatted responses allows easy parsing using any capable scripting or development language.

In the next article we’ll carry out similar actions but using the new Nutanix v4 SDKs.

Thanks for reading and have a great day! 🙂

© 2024 Nutanix, Inc. All rights reserved. Nutanix, the Nutanix logo and all Nutanix product, feature and service names mentioned herein are registered trademarks or trademarks of Nutanix, Inc. in the United States and other countries. Other brand names mentioned herein are for identification purposes only and may be the trademarks of their respective holder(s). This post may contain links to external websites that are not part of Nutanix.com. Nutanix does not control these sites and disclaims all responsibility for the content or accuracy of any external site. Our decision to link to an external site should not be considered an endorsement of any content on such a site. Certain information contained in this post may relate to or be based on studies, publications, surveys and other data obtained from third-party sources and our own internal estimates and research. While we believe these third-party studies, publications, surveys and other data are reliable as of the date of this post, they have not independently verified, and we make no representation as to the adequacy, fairness, accuracy, or completeness of any information obtained from third-party sources.