Batch Brewing – Multiple Requests with the Nutanix APIs

Batch Brewing with the Nutanix APIs

Table of Contents

In an article published last week, Dwayne Lessner, Principal Technical Marketing Engineer @ Nutanix, and I discussed using APIs to automate some DR migration procedures. The script would most likely need to send API requests to update multiple entities i.e. combine multiple requests into a single request. The use case there is quite straightforward. Multiple VMs need to have the same configuration change applied, but we don’t want to send multiple requests from our client across the network.

Today we’ll look at how this works.

Important note: Nutanix Calm APIs are not supported by the batch API methods outlined below.

A Simple Request

Before looking at the main subject of today’s article, I’ll first look at a simple Prism v3 API request. This request creates a virtual machine with basic spec and carries out that request using the v3 APIs published via Prism Central. In my demo environment, Prism Central is configured with a virtual IP address of 10.133.16.228, although you’ll need to alter that to work in your environment. Disclaimers, huh? 🙂

  • Prism Central IP address: 10.133.16.228
  • Prism v3 API used: vms
  • Request URL: https://10.133.16.228:9440/api/nutanix/v3/vms
  • Request method: POST
  • Request payload:
{
    "spec": {
        "name": "vm_from_v3_api",
        "resources": {}
    },
    "metadata": {
        "kind": "vm"
    }
}

The response from that request is as we’d expect – a JSON response containing information about what happened during the request and what the results of that request were. The status is “PENDING”, the UUID of the create VM request etc.

{
    "status": {
        "state": "PENDING",
        "execution_context": {
            "task_uuid": "4aac8208-5b76-4c0d-8265-c6e599bca73a"
        }
    },
    "spec": {
        "name": "vm_from_v3_api",
        "resources": {}
    },
    "api_version": "3.1",
    "metadata": {
        "use_categories_mapping": false,
        "kind": "vm",
        "uuid": "c35f41bc-3a2b-4363-b700-6a79019dbfc3",
        "project_reference": {
            "kind": "project",
            "name": "default",
            "uuid": "902f52d7-0f23-4090-af50-cc0d7b4d885f"
        },
        "spec_version": 0,
        "owner_reference": {
            "kind": "user",
            "uuid": "00000000-0000-0000-0000-000000000000",
            "name": "admin"
        },
        "categories": {}
    }
}

In this article, the contents of the response aren’t overly important but please be aware of what the responses look like.

The Requirements

In the Slide on Over To Nutanix Leap article, the requirement is get a list of existing VMs but only those VMs that belong to a specific Protection Domain. The list of VMs is then used as basis for a batch API request that modifies each VM and makes sure it is assigned to a specific category. The script used there is available for download on the NutanixDev GitHub account.

Now, it is possible to send an API request for each VM’s category assignment but that would involve sending a potentially large number of requests. What if we need to modify 1000 VMs? In that case, it may make more sense to package those into smaller “chunks”.

In this article we’re not going to send a bunch of the same type of request as a batch, though. The simple reason is that’s boring and doesn’t demonstrate the fact that the requests don’t all have to be the same type. Let’s do this for real, now.

Which API?

As per the Nutanix.dev Prism v3 API reference, we’ll be using the BATCH API to complete these requests. Please be aware of the following important points:

  • Batch request size should not exceed 60
  • Prism v3 API is supported on Prism Central only. All requests must be submitted to a Prism Central virtual IP (single node or scale-out).

The Individual Requests

To demonstrate the batch API, we’ll submit 3 requests at the same time.

  • Request 1 – Get information about a specific VM with UUID 237131d5-ba07-42d9-b4cf-f05baf329075.
  • Request 2 – Create a shell VM with very basic spec.
  • Request 3 – Create an AHV image based on the publicly-available CentOS 7 Cloud image.
  • The action to take if something goes wrong will be CONTINUE, i.e. continue even if one of the requests encounters an issue. The alternative setting is ABORT, at which time a failed request will stop the entire BATCH request from continuing.
  • Requests will be SEQUENTIAL, i.e. run one after the other. The other option is PARALLEL.

Building The Request

The parent batch API request is always a POST request. This means the parameters of the individual requests will be sent as the batch API request’s POST body or payload. The payload of a basic batch request looks like this. Note that the example below is perfectly valid – it just won’t do anything and will return an empty api_response_list array.

{
     "action_on_failure": "CONTINUE",
     "execution_order": "SEQUENTIAL",
     "api_request_list": [
     ],
     "api_version": "3.0"
}

Referring back to our settings, we can see the CONTINUE action is what to do if something goes wrong and the SEQUENTIAL order is how the requests should be executed.

In addition, we can see a JSON array called api_request_list. As the name suggests, this is where the list of individual requests will go. Let’s look at the entire payload.

{
     "action_on_failure": "CONTINUE",
     "execution_order": "SEQUENTIAL",
     "api_request_list": [
         {
             "operation": "GET",
             "path_and_params": "/api/nutanix/v3/vms/237131d5-ba07-42d9-b4cf-f05baf329075"
         },
         {
             "operation": "POST",
             "path_and_params": "/api/nutanix/v3/vms",
             "body": {
                 "spec": {
                     "name": "vm_from_batch",
                     "resources": {}
                 },
                 "metadata": {
                     "kind": "vm"
                 }
             }
         },
         {
             "operation": "POST",
             "path_and_params": "/api/nutanix/v3/images",
             "body": {
                 "spec": {
                     "name": "image_from_batch",
                     "resources": {
                         "image_type": "DISK_IMAGE",
                         "source_uri": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1905.qcow2"
                     },
                     "description": "Image created via v3 API batch request"
                 },
                 "api_version": "3.1.0",
                 "metadata": {
                     "kind": "image",
                     "categories": {},
                     "name": "image_from_batch"
                 }
             }
         }
     ],
     "api_version": "3.0"
 }

Break It Down

Looking at the api_request_list we can see the details of what each individual request will do.

  • A GET request to return information about a VM with UUID 237131d5-ba07-42d9-b4cf-f05baf329075.
  • A POST request to create a basic VM named vm_from_batch.
  • A POST request to create an image named image_from_batch.

Those three requests execute sequentially and will continue even if something doesn’t go as expected.

See If It Works

After sending a POST request to https://10.133.16.228:9440/api/nutanix/v3/batch with the JSON payload as defined above, a JSON response will be returned, as expected. The response itself is quite long as it contains a ton of information about what just happened.

The most important point, however, is that you’re aware of the api_response_list part of the response. Each of our individual requests are described there in detail, with a status property describing the HTTP response code and an api_response object containing all relevant details.

For the sake of completeness, here’s what the entire JSON response looks like (feel free to scroll past if you’re not interested in the specifics).

{
    "api_response_list": [
        {
            "status": "200",
            "api_response": {
                "status": {
                    "state": "COMPLETE",
                    "name": "DC05",
                    "resources": {
                        "num_threads_per_core": 1,
                        "vnuma_config": {
                            "num_vnuma_nodes": 0
                        },
                        "host_reference": {
                            "kind": "host",
                            "uuid": "1506ee4c-0457-453f-8d91-5bde03220bb7",
                            "name": "10.133.16.34"
                        },
                        "serial_port_list": [],
                        "nic_list": [
                            {
                                "nic_type": "NORMAL_NIC",
                                "uuid": "2ec98809-053c-4e19-accc-a4ce91a01a7b",
                                "ip_endpoint_list": [
                                    {
                                        "ip": "10.133.16.115",
                                        "type": "ASSIGNED"
                                    },
                                    {
                                        "ip": "10.133.16.115",
                                        "type": "LEARNED"
                                    }
                                ],
                                "vlan_mode": "ACCESS",
                                "mac_address": "50:6b:8d:d9:c8:1c",
                                "subnet_reference": {
                                    "kind": "subnet",
                                    "name": "vlan.0",
                                    "uuid": "f5606fed-2c33-45e8-9ccf-303db6eb93d8"
                                },
                                "is_connected": true,
                                "trunked_vlan_list": []
                            }
                        ],
                        "hypervisor_type": "AHV",
                        "num_vcpus_per_socket": 1,
                        "num_sockets": 4,
                        "gpu_list": [],
                        "is_agent_vm": false,
                        "memory_size_mib": 16384,
                        "power_state": "ON",
                        "hardware_clock_timezone": "Australia/Melbourne",
                        "power_state_mechanism": {
                            "guest_transition_config": {}
                        },
                        "machine_type": "PC",
                        "vga_console_enabled": true,
                        "disk_list": [
                            {
                                "data_source_reference": {
                                    "kind": "image",
                                    "uuid": "2a9ffbbc-bfb2-4972-8ddc-239a9ac7d961"
                                },
                                "device_properties": {
                                    "device_type": "DISK",
                                    "disk_address": {
                                        "device_index": 0,
                                        "adapter_type": "SCSI"
                                    }
                                },
                                "uuid": "b53fda74-2522-4077-ab81-df8f2f143134",
                                "disk_size_bytes": 42949672960,
                                "disk_size_mib": 40960
                            },
                            {
                                "device_properties": {
                                    "device_type": "CDROM",
                                    "disk_address": {
                                        "device_index": 0,
                                        "adapter_type": "IDE"
                                    }
                                },
                                "uuid": "be9314a6-2c95-44d6-81ac-b817c7f74080"
                            }
                        ]
                    },
                    "cluster_reference": {
                        "kind": "cluster",
                        "name": "NTNXDemo",
                        "uuid": "000577ea-d914-f7b2-1dac-002590ad0f00"
                    }
                },
                "spec": {
                    "name": "DC05",
                    "resources": {
                        "num_threads_per_core": 1,
                        "vnuma_config": {
                            "num_vnuma_nodes": 0
                        },
                        "serial_port_list": [],
                        "nic_list": [
                            {
                                "nic_type": "NORMAL_NIC",
                                "uuid": "2ec98809-053c-4e19-accc-a4ce91a01a7b",
                                "ip_endpoint_list": [
                                    {
                                        "ip": "10.133.16.115",
                                        "type": "ASSIGNED"
                                    }
                                ],
                                "vlan_mode": "ACCESS",
                                "mac_address": "50:6b:8d:d9:c8:1c",
                                "subnet_reference": {
                                    "kind": "subnet",
                                    "name": "vlan.0",
                                    "uuid": "f5606fed-2c33-45e8-9ccf-303db6eb93d8"
                                },
                                "is_connected": true,
                                "trunked_vlan_list": []
                            }
                        ],
                        "num_vcpus_per_socket": 1,
                        "num_sockets": 4,
                        "gpu_list": [],
                        "is_agent_vm": false,
                        "memory_size_mib": 16384,
                        "power_state": "ON",
                        "hardware_clock_timezone": "Australia/Melbourne",
                        "power_state_mechanism": {},
                        "vga_console_enabled": true,
                        "disk_list": [
                            {
                                "data_source_reference": {
                                    "kind": "image",
                                    "uuid": "2a9ffbbc-bfb2-4972-8ddc-239a9ac7d961"
                                },
                                "device_properties": {
                                    "device_type": "DISK",
                                    "disk_address": {
                                        "device_index": 0,
                                        "adapter_type": "SCSI"
                                    }
                                },
                                "uuid": "b53fda74-2522-4077-ab81-df8f2f143134",
                                "disk_size_bytes": 42949672960,
                                "disk_size_mib": 40960
                            },
                            {
                                "device_properties": {
                                    "device_type": "CDROM",
                                    "disk_address": {
                                        "device_index": 0,
                                        "adapter_type": "IDE"
                                    }
                                },
                                "uuid": "be9314a6-2c95-44d6-81ac-b817c7f74080"
                            }
                        ]
                    },
                    "cluster_reference": {
                        "kind": "cluster",
                        "name": "NTNXDemo",
                        "uuid": "000577ea-d914-f7b2-1dac-002590ad0f00"
                    }
                },
                "api_version": "3.1",
                "metadata": {
                    "last_update_time": "2019-05-24T05:32:55Z",
                    "kind": "vm",
                    "uuid": "237131d5-ba07-42d9-b4cf-f05baf329075",
                    "spec_version": 1,
                    "creation_time": "2019-05-24T05:32:55Z",
                    "categories_mapping": {},
                    "categories": {}
                }
            },
            "path_and_params": "/api/nutanix/v3/vms/237131d5-ba07-42d9-b4cf-f05baf329075"
        },
        {
            "status": "202",
            "api_response": {
                "status": {
                    "state": "PENDING",
                    "execution_context": {
                        "task_uuid": "b9f24571-ba5f-4c62-b90f-b4dba69c8582"
                    }
                },
                "spec": {
                    "name": "vm_from_batch",
                    "resources": {}
                },
                "api_version": "3.1",
                "metadata": {
                    "use_categories_mapping": false,
                    "kind": "vm",
                    "uuid": "9d68aa45-62cc-477c-87af-88af370c346e",
                    "project_reference": {
                        "kind": "project",
                        "name": "default",
                        "uuid": "902f52d7-0f23-4090-af50-cc0d7b4d885f"
                    },
                    "spec_version": 0,
                    "owner_reference": {
                        "kind": "user",
                        "uuid": "00000000-0000-0000-0000-000000000000",
                        "name": "admin"
                    },
                    "categories": {}
                }
            },
            "path_and_params": "/api/nutanix/v3/vms"
        },
        {
            "status": "202",
            "api_response": {
                "status": {
                    "state": "PENDING",
                    "execution_context": {
                        "task_uuid": "9851c461-e252-45f4-941c-7c1c38eb5380"
                    }
                },
                "spec": {
                    "name": "image_from_batch_04",
                    "resources": {
                        "image_type": "DISK_IMAGE",
                        "source_uri": "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1905.qcow2"
                    },
                    "description": "Image 04 created via v3 API batch request"
                },
                "api_version": "3.1",
                "metadata": {
                    "use_categories_mapping": false,
                    "kind": "image",
                    "name": "image_from_batch_04",
                    "spec_version": 0,
                    "owner_reference": {
                        "kind": "user",
                        "uuid": "00000000-0000-0000-0000-000000000000",
                        "name": "admin"
                    },
                    "categories": {},
                    "uuid": "6e6d8906-493c-448c-b2d8-7252f9583d50"
                }
            },
            "path_and_params": "/api/nutanix/v3/images"
        }
    ]
}

Wrapping Up

Using the batch API like this is a great way of packaging many individual requests and sending them all in one go. With the use of action_on_failure and execution_order it would be a relatively simple exercise to use the batch API to create an intricate and predictable API-based configuration workflow. Phew!

For more information, please don’t go past the other resources at your disposal.

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.