vRealize Automation REST API With Python - Part 2 - Interacting With The Catalog · TheHumbleLab

vRealize Automation REST API With Python - Part 2 - Interacting With The Catalog

· Read in about 9 min · (1902 words) ·

On Our Last Adventure

This is a continuation on of my post on vRealize Automation API’s located here - Introduction to vRealize Automation REST API’s

Previously we looked at how to do some basic interaction with the vRealize Automaton API which mostly consisted of pulling data out from a reporting perspective and dropping it into a table to display back. This is cool, and can be extremely helpful - but now with that foundational knowledge we can start to push the ball further.

In this post we are going to focus on interacting with blueprints and the vRealize Automation Catalog. We will look at how to leverage the Python API to list out all of our catalog items and then ultimately the process for calling those catalog items into provisioning requests.

Getting Started…

We did a lot of work with authentication and basic requests in the previous part, so we can actually use some of this code to help get us started. But first it’ll be helpful for us to look at the overall process that we need to follow to call a blueprint

  • Authenticate to vRA and store a bearer token
  • Enumerate list of Catalog items and their reference ID’s
  • Call catalog item’s JSON and store it as a “template”
  • Modify the template to suit our requirements (sizing, number of instances, etc…)
  • Call catalog request API and supply reference ID as well as template

While these steps sound pretty involved; it’s actually pretty easy to accomplish when call this programmatically. What’s more interesting about this is that after we complete our function we could actually take it and load it into OpenFaaS (which I discussed in my previous blog post here…). This gives us a simple URL that we could call to deploy additional systems with. Slick stuff!

As a quick side note, as usual - all the API URL’s and details can be easily found on the VMware Code Communities API Explorer. It’s absolutely fantastic and worth digging into. Give them a look. What’s great is you can easily sort the API explorer to show only API’s pertaining to the version you’re working with. This post was done based on vRA 7.3.

VMware Code API Explorer

Let’s jump in!

Authentication

Grabbing our function we worked with last time for authentication…

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def vra_auth(vrafqdn,user,password,tenant):
    url = "https://{}/identity/api/tokens".format(vrafqdn)
    payload = '{{"username":"{}","password":"{}","tenant":"{}"}}'.format(user, password, tenant)
    headers = {
        'accept': "application/json",
        'content-type': "application/json",
        }
    response = requests.request("POST", url, data=payload, headers=headers, verify=False)
    j = response.json()['id']
    auth = "Bearer "+j
    return auth

Stepping through this function, its pretty straight forward. Our function requires a few inputs;

  • FQDN for our vRealize Automation Appliance
  • Username (blah@vpshere.local)
  • Password
  • Tenant (vsphere.local or humblelab.com)

Once we bring this function in via python, we’re able to call vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local' and our bearer token will be returned.

Now that we have this, we can start to build out our new function that will return all of our catalog items

Enumerate Our Catalog Items and Their ID’s

def get_catalog_items():
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    return catalogitems.json()

What we’ve done here is build a function that will return a JSON of all our entitled catalog items, or published blueprints. We return this list as a JSON that we can review. We could be done at this point; and easily just parse the data we need out of the JSON to move onto the next goal. Let’s make this a little more…pretty?

from prettytable import PrettyTable
def get_catalog_items():
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    catalogitemtable = PrettyTable(['Blueprint Name','ID'])
    for i in catalogitems.json()['content']:
        catalogitemtable.add_row((i['name'],i['catalogItemId']))
    print(catalogitemtable)
    return catalogitems.json()['content']

Excellent! We now have a simple table response that shows the data we want, as well as dumping the entire JSON out for us to work with as needed. Next, we need to focus on getting our template in place.

Getting Our Template

Things start to get a bit tricky here. In order for us to successfully get our template, we need to call a specific URL that’s referenced in the JSON we returned, for the specific catalog we want to work with. Let’s build another function that will return that for us.

def get_template_url(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == blueprint:
            return i['links'][0]['href']

When we look at the URL returned; we see that the only part that’s really “dynamic” is the string that is the same as the blueprints catalogItemId. This makes it very easy to programmatically build ourselves a function to save the template. What if we build a function that takes the blueprint name as an “input”, will then get the catalogItemId and drop it into the URL. We can then have that url called and return the template JSON! Another goal complete maybe?

def get_template(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == blueprint:
            refid = i['catalogItemId']
    templateurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItems/{}/requests/template'.format(refid)
    template = requests.get(templateurl, headers = vraheaders, verify=False)
    return template.json()['data']

So what we do here is very similar to all of our functions so far. We build authentication, setup our headers, and hit our catalog list. From there, we loop over the catalog list until we find our blueprint that we want to call. Once we find that - we assign its reference ID to a variable. We know the URL from our previous call that we need to work with - so we build the URL but include a format string to be able to feed that reference ID in. We call that URL as a variable within another rest all and assign the object that returns to a new variable; template. We then return our template, specifically the data key under it back to the user. This is the “shell” of our catalog request.

From here we can manipulate the data as we see fit. Pro tip, if you want to be able to read the JSON a bit easier, in your terminal you can do print(json.dumps(template['data], indent=4)) which will return the data in the standard JSON format, but a bit easier to read! Now, modifying it is simple…

Modification of the Template

If we want to manipulate this data from the command line, all we need to do is call the keys that we want to update and feed new values in. For example, the _number_of_instances variable. If we want to modify that, we can do template['data']['_number_of_instances'] = 4 which will set it to 4 instances to deploy. If we do another print(json.dumps(template['data], indent=4)) we will see that the existing key for _number_of_instances has been updated to reflect 4 instances to deploy!

We can step through our template and modify any of the values as we see fit. It’s important to understand “what you’re doing however, because if you enter a value that’s not valid - the build will fail. For example, if there was a restriction on total number of instances to deploy, and you requested outside that boundary - it would fail. This is especially something to watch out for with Datastores as well as different networks/network profiles you might want to deploy.

With all that said…we’re on to the final step! Deploying our new catalog request!

Calling The Catalog Request

We’ve come a long way - but now for the easy part. We have everything we need at this point to successfully deploy our catalog item, except for one thing - the catalog request URL. Earlier we built a simple function to return the template. Keen coders would see that we called the first item in the array. If you looked at the JSON that was originally returned - you would’ve seen 2 items in that array; the 2nd is our request URL!

We can perform a simple modification to our original function to return this URL for us to look at…

def get_template_request_url(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == blueprint:
            return i['links'][1]['href']

We tell the array to return the ‘1’ object, which is actually the 2nd in the array. This will return our request URL. Again; this is a standard URL with one dynamic field for our reference ID. We can go ahead and just build this as a variable in our code.

Now, the big function at the end of the rainbow - our catalog request function.

def request_catalog_item(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.get(catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == 'Windows':
            refid = i['catalogItemId']
    template = get_template(blueprint)
    requesturl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItems/{}/requests'.format(refid)
    catalogrequest = requests.post(requesturl, headers=vraheaders, data=template, verify=False)
    return catalogrequest

Now, if we request this - in its current state - it’s going to fail. Why is that? Because we’ve setup a lot of our functions so far to be more “readable” by sending them out to a JSON already. We need to back those changes out. Below; I will list the full series of functions corrected to make this deployment successful

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def vra_auth(vrafqdn,user,password,tenant):
    url = "https://{}/identity/api/tokens".format(vrafqdn)
    payload = '{{"username":"{}","password":"{}","tenant":"{}"}}'.format(user, password, tenant)
    headers = {
        'accept': "application/json",
        'content-type': "application/json",
        }
    response = requests.request("POST", url, data=payload, headers=headers, verify=False)
    j = response.json()['id']
    auth = "Bearer "+j
    return auth

def get_template(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.request("GET", catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == blueprint:
            refid = i['catalogItemId']
    templateurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItems/{}/requests/template'.format(refid)
    template = requests.get(templateurl, headers = vraheaders, verify=False)
    return template

def request_catalog_item(blueprint):
    auth = vra_auth('hlcloud.humblelab.com','codyde@vsphere.local','VMware123!','vsphere.local')
    catalogviewurl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItemViews'
    vraheaders = {
        'accept': "application/json",
        'authorization': auth
    }
    catalogitems = requests.get(catalogviewurl, headers=vraheaders, verify=False)
    for i in catalogitems.json()['content']:
        if i['name'] == 'Windows':
            refid = i['catalogItemId']
    template = get_template(blueprint)
    requesturl = 'https://hlcloud.humblelab.com/catalog-service/api/consumer/entitledCatalogItems/{}/requests'.format(refid)
    catalogrequest = requests.post(requesturl, headers=vraheaders, data=template, verify=False)
    return catalogrequest

Now, I’ll be the first to admit that there is some solid cleanup we could do here to reduce redundancies. We could clean up a lot of the .format strings and use more clever inputs for our functions to drastically reduce the amount of real estate our code uses. I’ll leave that for you to do as additional homework :)

Conclusion

With these 3 functions in place, we are able to successfully call a catalog item by name using the API. It’s important to note that it’s case and space sensitive, so make sure you’re requesting it EXACTLY as you’ve seen.

There’s a lot we could expand on. We could add some variables in to let us specify the number of instances to deploy. We could add network variables that would change the network based on string. The opportunities are endless.

In future articles, we’ll start diving into the other areas of the vRA API; things like endpoint creation, business group creation, and reservations.

My good buddy Grant Orchard did a great series covering these topics on his blog, https://grantorchard.com. He actually covers this very topic as well; so for a different perspective - give it a read here.

I hope this has been a help at demystifying the vRealize Automation API!