I was asked recently to take a look at an API issue around uploading an image file from a desktop directory location into the Acropolis Image Service. The REST calls for this workflow are part of version 0.8 of the Nutanix Management API. As such they only work on an endpoint supplied by Prism Element.
REST Explorer

POST
We covered image upload briefly in a previous post were we touched upon uploading from an external URL. The mechanism for image upload discussed here is in two parts. The first requires a POST call that will populate the image entry in the repository with the various metadata. For example, name, image type and so on. The second phase is the actual “image binary data” upload and this is done via a PUT call. Once both parts are successfully returned, we have the fully completed image. Let’s start with the initial POST call to upload a small CirrOS cloud image, in which we specify a name for the image and its image type (imageType).
curl -k -u <apiuser>:<password> -X POST -H "Content-Type: application/json" -d '{"name":"CirrOS-0.4", "imageType": "disk_image"}' https://10.50.0.208:9440/api/nutanix/v0.8/images respsonse: {"taskUuid":"ec2537bc-9d87-454c-bfb4-ddccf93e9cc6"}
The resulting task UUID could be monitored for progress and status if required. However, the operation often completes before we get the chance. Next we review the returned JSON response from a REST call listing all current images. The imageState at this point is currently INACTIVE. It needs to be in this status for the subsequent update or PUT step to proceed.
curl -s --user <apiuser>:<password> -k -X GET --header "Content-Type: application/json" --header "Accept: application/json" "https://10.50.0.208:9440/api/nutanix/v0.8/images/"| jq '.entities[] | select (.name=="CirrOS-0.4")' response: { "uuid": "fe104c6d-5ced-4924-9b10-1d7e3dd5bed1", "name": "CirrOS-0.4", "deleted": false, "logicalTimestamp": 0, "imageType": "DISK_IMAGE", "imageState": "INACTIVE", "createdTimeInUsecs": 1579534578454887, "updatedTimeInUsecs": 1579534578454887 },
PUT
In order to load the image data to its previously created metadata, we need both the current image UUID and the UUID of the storage container we will upload the image to. The image UUID we already have from the /images call above. We can do similar to GET the storage container UUID
curl -s --user <apiuser>:<password> -k -X GET --header "Content-Type: application/json" --header "Accept: application/json" "https://10.50.0.208:9440/api/nutanix/v2.0/storage_containers/"| jq '.entities[] | {name: .name, uuid: .storage_container_uuid} | select (.name | . and contains("default"))' { "name": "default-container-28236049991398", "uuid": "90d044b3-93f4-4375-802d-770eada73fc9"
Then next step is the actual PUT call to load the image data
curl -s --user <apiuser>:<password> -k -T "/path/to/cirros-0.4.0-x86_64-disk.img" -X PUT --header "Content-Type: application/json" --header "Accept: application/json" -H "X-Nutanix-Destination-Container:90d044b3-93f4-4375-802d-770eada73fc9" "https://10.50.0.208:9440/api/nutanix/v0.8/images/fe104c6d-5ced-4924-9b10-1d7e3dd5bed1/upload" response: {"task_uuid": "b1902285-1f87-4500-8f46-83a959bb6a3e"}
The final image can be verified as before but this time notice the imageState is now ACTIVE.
curl -s --user <apiuser>:<password> -k -X GET --header "Content-Type: application/json" --header "Accept: application/json" "https://10.50.0.208:9440/api/nutanix/v0.8/images/"| jq '.entities[] | select (.name=="CirrOS-0.4")' response: { "uuid": "fe104c6d-5ced-4924-9b10-1d7e3dd5bed1", "name": "CirrOS-0.4", "deleted": false, "containerId": 11, "containerUuid": "90d044b3-93f4-4375-802d-770eada73fc9", "logicalTimestamp": 1, "imageType": "DISK_IMAGE", "vmDiskId": "10f2a257-f325-4a92-97f2-927553a7c8cb", "imageState": "ACTIVE", "createdTimeInUsecs": 1579534578454887, "updatedTimeInUsecs": 1579536194699953, "vmDiskSize": 46137344 }
The Nutanix API dev team are continually working on making improvements to our REST automation capabilities. If this is something that interests you then feel free to reach out with any suggestions. Maybe you want be able to take a first look at our latest API innovations or help drive those changes with feature requests? In either case, please reach out to us to get the ball rolling.
Note:
In this post I used api/nutanix/v0.8 and api/nutanix/v2.0 in my base/endpoint URLs. While the defined endpoints in older documentation are PrismGateway/services/rest/v[0.8,2.0]/, there has been a move (within REST Explorer) to standardise on a single endpoint syntax while still calling out the version of API albeit v0.8 or v2.0. Both base url formats are interchangeable, however I recommend you always stick to the latest format for sake of consistency.