vRealize Automation – Customizing IP Assignments with vRO

Overview

Today I thought I’d get back to vRealize Automation 8, in particular leveraging extensibility. Previously I have shown some examples of breaking out of the provisioning lifecycle to perform some external operations (CMDB updates etc.) however this time I want to look at how you can augment the provisioning process with information from external systems.

Note that much of this article is applicable to vRA Cloud (minor differences for authentication into vRA 8 etc.).

The Scenario

I’m going to use a very common scenario in this article, using an external system to retrieve some custom IP details that can be fed back into vRA (Cloud or 8) to override/supplement the information auto calculated by the vRA system.

It is important to note that there are other ways to achieve this goal that do not require vRO including:

  • ABX with IPAM plugin
  • Prompting for user info mapping directly to YAML blueprint constructs

However, what if you need the update to be done automatically without the user specifying IP information. Maybe the information the user provides can be combined to fetch an IP address from a 3rd party system (i.e. searching against a number of machine properties/capabilities that match your IPAM system). This is when ABX (without IPAM plugin) or vRO can be used.

In this scenario I am going to use vRO, purely because I can re-use some of my existing code.

Identifying The Correct Lifecycle State

In previous versions of vRA (7.x) there were a few ways of approaching this issue. One example was to front the whole request process via XaaS and custom workflows so that the IP information could be calculated way before vRA ever requested a machine to be provisioned. Event Broker Subscriptions could also be used.

With vRA 8/Cloud, using Event Broker Subscriptions is the go to method however things are not all as they used to be in 7.x. For a start the lifecycle event topics are different. In 8/Cloud there is a new topic called “Network Configure”. This is specifically for handling network configuration during the compute allocation process.

When Does This Event Fire?

Before you get to the point of coding workflows or ABX actions you need to understand when vRA generates the “Network Configure” event and what information the event contains in varying situations.

Here is a screenshot of an example blueprint I will use to illustrate things. It contains one existing vSphere network with 2 types of vSphere machines attached. The first type can have a quantity of one or more, the second type is fixed at a single VM.

When a user requests a deployment from the above blueprint my “Network Configure” event will fire multiple times, once for each machine type in the blueprint. It’s important to understand this, it is NOT once for each machine to be built!

The payload in the event for the first machine type will contain details of all the machines to be created for that type (1 or more) with the payload in the event for the second machine type only containing the details of the fixed one machine.

Planning The Workflow

Before you start writing code it’s always a good idea to create a conceptual view of how an orchestrated process will look. This gives you the opportunity to plan out code functions you may need to write, integration points as well as identify actions/workflows that already exist that you can re-use.

The key areas I have identified are:

  • Payload processing
  • Authentication
  • Fetching network data
  • Requesting IP addresses (from somewhere)
  • Returning info to vRA

Putting this into a flow allows me to fine tune this list and organize into my perceived correct processing order, also identifying anything that I can re-use from other work. In my case I have at least one action I can re-use from other extensibility work.

The next step is to examine what data vRA is going to provide me. I need to make sure I am going have all the information I need to request IP addresses from the right network and have other data I need such as hostnames used to register DNS records etc. The best place to start is the schema of the event topic. I have circled a number of items in the schema that might prove useful to my workflow.

Not all of the above data will be provided when an event occurs. Some of these properties will be things that I can return to vRA to influence configuration only. Unfortunately the schema doesn’t differentiate between input and output so some experimentation is required.

A good starting point is to log everything in a simple workflow that cycles through each payload property and logs all values. In this example my workflow is taking an input properties object called “inputProperties” which is populated when vRA triggers the workflow.

for each(key in inputProperties.keys)
{
System.log(key + ": " + inputProperties.get(key));
}

The output looks as follows. Now I know what information I have to use for my workflow.

The output doesn’t contain any network names but it does contain the network profile ID and the network selection ID. These are the internal vRA ID’s which I should be able to use in API calls to vRA to extract more information. The externalIds property is holding VM names so that will also come in useful.

Constructing The Workflow

Now that I have some useful information to base my workflow on I can start putting everything together.

I’m going to start with authentication. I need to extract more information out of vRA than the event payload provides so this means I will need to get vRO to authenticate into vRA. I have a previous vRO action I used for vRA Cloud to do this however this relied on exchanging an API token created in the vRA Cloud UI for a Bearer Token. With vRA 8 an API token cannot be created so I need another method. Thankfully the vRA 8 API provides an API call to login with a username/password combo and return the Bearer Token. Here’s my code:

if((username) && (password))
{
    if(cspRestHost)
    {
        try
        {
            var url = "/csp/gateway/am/api/login?access_token";
            var requestContent = '{ "username": "' + username + '", "password": "' + password + '"}';
            var request = cspRestHost.createRequest("POST", url, requestContent);
        
            request.setHeader("Content-Type", "application/json");
            request.setHeader("Cache-Control", "no-cache");
        
            var requestResponse = request.execute();

            if(requestResponse.statusCode != 200)
            {
                System.log("Bearer token request has returned an error status code: " + requestResponse.statusCode);
                throw "Error: " + requestResponse.statusCode;
            }
        
            var bearerToken = JSON.parse(requestResponse.contentAsString).access_token;
            System.log("Bearer Token: " + bearerToken);
            return bearerToken;
        }
        catch(e)
        {
            System.log(e);
            throw e;
        }
    }
    else
    {
        throw "No CSP REST host provided";
    }
}
else
{
    throw "No API token provided";
}

Next I need some code to extract data from the event payload. Remember I am following the order I laid out in my conceptual diagram.

The payload contains a multi-dimensional array containing the network selection IDs for all VMs covered by the payload. All of my machines have one network adapter and are attached to the same network so I have statically set this extraction to [0][0][0] rather than doing anything more complex. You may need to enhance this depending on your blueprint configuration.

//Extracting selected network ID from input payload
System.log("Extracting network selection IDs from payload");
networkSelectionIds = inputProperties.get("networkSelectionIds")[0][0][0];
System.log(networkSelectionIds);

Now that I have the network selection ID I can use this to fetch the details that relate to this ID. There are 2 types of network in vRA, an IaaS network and a fabric network. The IaaS networks are those that are discovered by vRA when a Cloud Account is added (and are displayed under “Resources > Networks”) where as a fabric network is an IaaS Network within a Network Profile. The network selection ID within the event payload relates to a fabric network.

if(bearerToken)
{
    if(caRestHost)
    {
        if(networkID)
        {
            try
            {
                var url = "/iaas/api/fabric-networks";
                var request = caRestHost.createRequest("GET", url + "/" + networkID);
        
                request.setHeader("Authorization", "Bearer " + bearerToken);
                request.setHeader("Content-Type", "application/json");
                request.setHeader("Cache-Control", "no-cache");
        
                var requestResponse = request.execute();

                if(requestResponse.statusCode != 200)
                {
                    System.log("Network request has returned an error status code: " + requestResponse.statusCode);
                    throw "Error: " + requestResponse.statusCode;
                }
        
                return requestResponse.contentAsString;
            }
            catch(e)
            {
                System.log("An un-handled exception was caught: " + e);
                throw e;
            }
        }
        else
        {
            throw "No network ID provided";
        }
    }
    else
    {
        throw "No Cloud Assembly REST host provided";
    }
}
else
{
    throw "No bearer token provided";
}

Once I have the network details as a string from the above action I can then extract the data that I need from it. In my case I have no actual 3rd party IPAM solution in my environment but I am assuming having the name of the network and it’s CIDR would give me all the info I need to locate the network in an IPAM system and request a IP address.

var jsonNetworkDetail = JSON.parse(networkDetails);
netName = jsonNetworkDetail.name;
netCidr = jsonNetworkDetail.cidr;

I’m not going to cover requesting an IP address from the IPAM system for obvious reasons so next I am going to construct the return object that will be fed by to vRA as the output of the workflow. A key difference between old vRA and vRA 8/Cloud here is that the return object is not a single complex properties object that you fill with all the schema updates you require. In the new world each schema property you wish to return must be an individual workflow output.

As I’m returning IP addresses I need to construct the “addresses” output object. Referring to the schema I can see that it’s a 2 dimensional string array. Here, I have initiated the array and pushed 2 string arrays into it. Each string array contains an IP address I wish to use.

addresses = new Array();
//hard coded address as I have no external IPAM system
addresses.push(["192.168.110.196"]); 
addresses.push(["192.168.110.197"]);

This is obviously hard-coded as an example and would only work if the user has requested 1 or 2 of a particular component in my blueprint. For something more dynamic you would process the externalIds in the input payload and fetch the corresponding number of IP addresses before assembling the addresses array with the correct data.

Note: The order that the addresses array should be populated in would be the same order as the machine names in the externalIds array are provided. If the input array contains VMs in the order “VM1, VM2, VM3” then the order you populate the addresses array would be “VM1, VM2, VM3”.

I hope this has given you an insight into performing some vRA extensibility that involves manipulating vRA.