Uncategorized

VREALIZE AUTOMATION & ORCHESTRATOR – DELETE A BUSINESS GROUP VIA VRO using the vRA API

I’ve just written a good example of how to use the restClient() method. The code snippet takes in 3 parameters, 2 inputs and 1 attribute.

Inputs are :

businessGroupToDelete – name of business group
tenant – name of tenant

Attribute:

vcacCafeHost (typeof vCACCAFE:VCACHost) with a value set to the vCAC CAFE host.

I’ve used the vCACCAFEEntitiesFinder to match on the business group name and this is case insensitive. So if the business group name is Corp, input could be CoRp and we get the right case to pass in the API filter, which quickens up the API search.

Then I use a filter in the API call using the BG name returned by the vCACCAFEEntitiesFinder.

I’m really liking using the method for the API and have done more and more coding like this. The vRealize 7 API is well documented and easy to use.


// Getting case of business group name, incase BG passed in is wrong case.
var entity = vCACCAFEEntitiesFinder.findBusinessGroups(vcacCafeHost , businessGroupToDelete)
if (!entity){throw "Could not find business group"};

for each (var bg in entity){

if (bg.tenantId.toLowerCase() == tenant.toLowerCase()){
System.log("Found the following business group: " + bg.name + " in " + bg.tenantId);
break;
}
}
if (!bg){throw "Could not find business group"};

// create rest endpoint for rest client
var endpoint = 'com.vmware.csp.core.cafe.identity.api';
var restClient = vcacCafeHost.createRestClient(endpoint);
var businessGroupUrl = "tenants/" + bg.tenantId + "/subtenants?filter=name eq '" + bg.name + "'";

// get business groups
var businessGroups = restClient.get(businessGroupUrl);
var res = businessGroups.getBodyAsString();

//clean json and parse
var json = JSON.parse(res.replace(/\\/g, ''));

for each (var v in json.content){

System.log("Business Group name: " + v.name + " and ID: " + v.id);

// match and delete business group by name
try{
if (v.name == bg.name) {

var businessGroupUrl = "tenants/" + bg.tenantId + "/subtenants/" + v.id;
System.log("Deleting business group: " + bg.name + " with the follow REST URL: " + businessGroupUrl);
restClient.delete(businessGroupUrl);
System.log("Deleted business group successfully");
break;
}
}
catch(e){
System.log(e);
break;
}
}

//System.debug(JSON.stringify(json));


Using createRestClient() in vRealize Orchestrator to create vRealize Automation REST calls.

There is a createRestClient() method available in the vRealize Automation 7.x plugin in vRealize Orchestrator that you can use to create REST calls. The difference with using this method over using a REST host created in vRealize Orchestrator is that you do not need to get a Bearer token as the createRestClient()  method uses the vCACCAFEHost object configured and therefore you use the authentication configured within the host object you have created.

Once you have set up a vCACCAFEHost object using the ‘Add a vRA host’ workflow, you can use REST calls very similarly to native REST but there are some slight nuances. However, understanding these nuances helps understand how to interact with the API in my opinion.

Here is a screen shot of the scripting class and the method I am referring to.

Screen Shot 2017-03-22 at 11.49.33

You can see the return type is a the vCACCAFERestClient object. Once you have created an instance of the vCACCAFERestClient object, you can start interacting with the API by using some of the methods shown below.

Screen Shot 2017-03-22 at 11.48.54

However, you need to add which API service you want to connect to when creating the clientRestHost() method as that method requires an input string.

To determine what endpoint you need to use, you can query the endpoints available in vRA using this URL against the vRA hostname  /  load balancer name and a GET method

/component-registry/endpoints?limit=500&orderby="url"&$filter endpointType/protocal eq "REST"

This will return a JSON response similar to this snippet.

{
    "url": "{vra-fqdn}/catalog-service/api",
    "endPointType": {
    "typeId": "com.vmware.csp.core.cafe.catalog.api",
    "protocol": "REST"
},

So for the catalog-service API, the endpoint you need to pass in to the clientRestHost() is

com.vmware.csp.core.cafe.catalog.api

Here is the Orchestrator code that you can use now:

var endpoint = 'com.vmware.csp.core.cafe.catalog.api';
var restClient = host.createRestClient(endpoint);
var catItemsUrl = "consumer/resources";

Notice that I am passing in an additional consumer/resources URL. This is because once I have connected to the vRA API endpoint, I can then chose what URI to use depending what I am trying to query form the vRA API Endpoint. The URI is apended to the base URL determined from the vRA REST endpoint. This information is available in the online docs.

The other thing to note is that the return type from the REST call returns a vCACCAFEServiceResponse object. Have a look at that object in the API browser to see available methods and attributes.

So now I have my full URL, I can then make a REST call as follows:

var catItems = restClient.get(catItemsUrl);
var res = catItems.getBodyAsString();

TIP: I noticed that if I parsed the string output to a JSON object, it would fail as the vRA Host reference looks to be escaped. Therefore, before converting to a JSON object, you need to strip out any ‘\’ (forwardslashes) before parsing the string.

json = JSON.parse(res.replace(/\\/g, ''));

This now turns the REST response into a JSON ojbect that you can iterate through.

However, what happens if you have many catalog resources? You may hit a page size limit or the info you are bring back may take a long time to return. This is where you can use ODATA filters and pagesize parameters in your URL.

For example:

var catItemsUrl = "consumer/resources?limit=10$filter=catalogItem/name eq 'vRA IaaS'";

This dramatically cuts down the response time and saves resources.

Check the /var/log/vcac/access_log.txt for more info and look here at a snippet

27.0.0.1 [ +0000][51 ms] "GET /catalog-service/api/consumer/resources?
$filter=catalogItem/name%20eq%20'vRA%20IaaS'

27.0.0.1 [ +0000][406 ms] "GET /catalog-service/api/consumer/resources'

So nearly 800% faster and I only have a hand ful or catalog resources available in my lab. If you have 000’s, make sure you use filters. For more info on ODATA filters for the catalog-service, see this URL https://{vra-fqdn}/component-registry/services/docs

How to get vRealize Automation Install Wizard values

The vRealize Automation installation wizards is one of the best new features in version 7 but I was recently asked how to get the install wizard values post installation for vRealize Automation. This is useful to just help understand what values were entered in case you need to troubleshoot the install or double check the config.

I found one way of doing this which may or may not be the best way.

You can pass in a parameter to the vcac-config utility to get page data specified during the install.

Each installation wizard page has an ID value. You can find these by running this command:

cat /opt/vmware/share/htdocs/service/wizard/wizard.xml
| grep -i '<wizardPage' -A1 | grep -i -w ' id\b'

This will list all the page IDs in the install wizard.

 id="welcome"
 id="eula"
 id="distribution.select"
 id="host.init"
 id="va.init"
 id="role.config"
 id="host.vra"
 id="identity.main"
 id="host.iaas"
 id="db.mssql.config"
 id="iaas.servers.web"
 id="iaas.servers.manager"
 id="iaas.servers.dem"
 id="iaas.servers.agents"
 id="va.certificate"
 id="web.certificate"
 id="ms.certificate"
 id="loadbalancers"
 id="validate"
 id="snapshot"
 id="install"
 id="licensing.main"
 id="telemetry"
 id="finish"

Now you have the wizard page IDs, you can get the parameters you have entered on the install wizard by running this command:

 /usr/sbin/vcac-config install-wizard --page iaas.servers.dem --params-get

This will output something similar to this:

---BEGIN---
 {"DemName":"DEM","instance":"iaas-01.vra.com\\DEM","ServiceUser":"vra\\svc_vra","DemRole":"dem-worker","ServiceUserPassword":"passw0000rd!","DemDescription":"DEM"}
 ---END---

You can run this command and enter the page ID to get any of the install wizard data you entered, just in case you aren’t sure what was configured.

vRealize Orchestrator – Working with XML and the NSX API

I’ve found that it’s a must to know how to work with XML in vRealize Orchestrator, especially when using the NSX API, and the good news is that it is very simple, once you know a few key commands.

First off, the XML plugin is based on E4X, so searching for help on this XML library is a good start.

Take this XML structure for example, which is for NSX load balance configuration that shows the configuration for pool members:

<loadBalancer>
  <pool>
    <poolId>pool-1</poolId>
    <name>pool-one</name>
    <algorithm>round-robin</algorithm>
    <transparent>false</transparent>
    <member>
      <memberId>member-1</memberId>
      <ipAddress>192.168.0.1</ipAddress>
      <weight>1</weight>
      <monitorPort>80</monitorPort>
      <port>80</port>
      <maxConn>0</maxConn>
      <minConn>0</minConn>
      <condition>enabled</condition>
      <name>member-one</name>
    </member>
    <member>
      <memberId>member-2</memberId>
      <ipAddress>192.168.0.2</ipAddress>
      <weight>1</weight>
      <monitorPort>80</monitorPort>
      <port>80</port>
      <maxConn>0</maxConn>
      <minConn>0</minConn>
      <condition>enabled</condition>
      <name>member-two</name>
    </member>
  </pool>
</loadBalancer>

Notice there are multiple member XML nodes, one for each pool member. That makes sense as you would have mor ethan one pool member in a NSX load balancer.

You can delete nodes by converting the XML into an object and using the delete command in Javascript. So something like this:

var xmlObj = new XML(xml);
delete xmlObj.pool.member[0];

So this would delete the first member node and all the child nodes but what happens if you need to delete a specific node, say the 192.168.0.2 pool member, or any member for that matter.

In order to do this, you need to find the index. Note that E4X is zero based, so the first member node is index 0 , the second member node is at index 1 etc..

So you need to find the index number for 192.168.0.2. Here’s a script to do this.

// Convert the XML to a XML document object
var document = XMLManager.fromString(xml);

//Search for member element by tag name
var members = document.getElementsByTagName("member");

// Set a marker so we can break in the loop
var memberFound = false;
// Now iterate using i and starting from 0 (remember E4X is zero based.
for (i = 0; i < members.length; i++){

// Now get all the child nodes for the first member.
var childNodes = members.item(i).getChildNodes();

// Iterate through the child node.
for (j = 0; j < childNodes.length; j++){

// Search for your IP address.
if (childNodes.item(j).textContent == "192.168.0.2"){

// Now take the value of the iterator for the member iteration, not the member nodes.
// In this case, it is 1.

System.log(childNodes.item(j).textContent + " found at index " + i);

// Set you flag as you've found the IP and do not want to iterate further.
memberFound = true;
break;
}
}
if (memberFound) {break;}
}

Now you can delete the member using the iterator value. Note that E4X is zero based, so the member-one node is at index 0, member-two is at index 1 etc..

// Create the xmlObj, although we did set this up above.
var xmlObj = new XML(xml);

// note, you need to have the value for i, which is 1 in this case
delete xmlObj.pool.member[i];

But what happens if you what to append an element with child nodes? Say you want to add this XML to the main xmlObj.

var newMemberXml = "<member>
      <memberId>member-3</memberId>
      <ipAddress>192.168.0.3</ipAddress>
      <weight>1</weight>
      <monitorPort>80</monitorPort>
      <port>80</port>
      <maxConn>0</maxConn>
      <minConn>0</minConn>
      <condition>enabled</condition>
      <name>member-three</name>
    </member>"

We can use the new XMLList() object type. An XML List is a linear sequence of XML nodes.

var xmlNewObj = new XMLList(newMemberXml);

// Create the xmlObj, although we did set this up above.
var xmlObj = new XML(xml);

// create the new xml object you wish to append
var xmlNewObj = new XMLList(newMemberXml);

// Append the xmlNeObj to the xmlObj.pool node
System.log(xmlObj.pool.appendChild(xmlNewObj));

This will add the new member node and its child elements to the main xmlObj.
So using delete and childApend(obj) can get you a long way.

Tip:

I use resource elements to hold XML structure in vRealize Orchestrator. Most structures, say to configure a load balancer or to add a node, have static element nodes.

So in order to change the resource element, you can set placeholders in your XML resource element. For example:

<monitor>
   <monitorId>monitor-1</monitorId>
   <type>http</type>
   <interval>10</interval>
   <timeout>15</timeout>
   <maxRetries>3</maxRetries>
   <method>GET</method>
   <url>{monitor_url}</url>
   <expected>{monitor_expected}</expected>
   <name>http_service_monitor</name>
   <send>{monitor_send}</send>
   <receive>{monitor_receive}</receive>
</monitor>

You can then use the following code to replace the placeholders in the curly brackets with values from vRO attributes or input parameters (addHTTPMonitor is the resource element)

var content = addHTTPMonitor.getContentAsMimeAttachment().content;

var monitorContent = content.replace("{monitor_url}", monitorUrl)
	.replace("{monitor_expected}", monitorExpected)
	.replace("{monitor_send}", monitorSend)
	.replace("{monitor_receive}", monitorReceive)

So once you have your XML structure from the resource element, append this using the childApend(obj) method to add the XML element and node to the main XML structure.

vRealize Cloud Client pagesize configuration

When running the vRealize Cloud Client CLI tool, the default page size is set to 25. This means that any command you run will return the 25 items on a page. Sometime you may have more than 25 items, for example for xaas-blueprints

Screen Shot 2016-06-27 at 09.25.14

You can include this parameter to set the pagesize

vra catalog list --pageSize 50

This will return 50 items per page.

However, you can set the default page size in your cloudclient configuration by configuring the cloudclient.config file on the virtual machine you are running cloud client on. For example:

vi ~/.cloudclient/cloudclient.config

And here is an example configuration

Screen Shot 2016-06-27 at 09.29.31

default.page.size can be set to any number you want.

 

 

 

Updating vRA ASD Service Blueprint workflows

There isn’t an easy way to find out what vRealize Orchestrator workflow is associated to a vRealize Automation Service Blueprint. You can query the vPostgres database to find out the workflow ID as follows:

1 – Log in to vPostgres and connect to the vcac database

2 – Run the following SQL command

vcac=# \x on;
Expanded display is on.
vcac=# select * from asd_serviceblueprint;

This should output something similar to this:

-[ RECORD 1 ]------------------------------------------------
id | 850b9e17-024e-44f7-9831-a5651a5d6e0f
description | This is a workflow description.
name | My XAAS workflow
status | 1
tenant | vsphere.local
workflowid | 1eea8b02-15e8-41b8-992e-3df1cd8e4b99
baseform |
outputparameter | ea001bf4-d43f-4c74-a010-b6027c7ecbdf
catalogrequestinfohidden | t
version | 1.0.0
-----------------------------------------------------------

You can change the vRealize Orchestrator workflow that gets called by a vRealize Automation service blueprint pretty easily, by changing the workflow ID value in the vPostgres database

vcac=# update asd_serviceblueprint 
set workflowid='ad9e2cc2-2efa-44c1-a574-6a02bec2f998'
where id = '9d6510ad-24a6-4f0c-adc8-736cc3e99d77';

This will change what workflow gets executed without redeploying the ASD form. It’s handy when you want to clone the ASD service blueprint and actually change the underlying workflow to test something new, without having to rebuild the ASD form.

You can also run a vRealize Orchestrator workflow using the below javascript to find out what the name is using the workflow ID

wfId = '9d6510ad-24a6-4f0c-adc8-736cc3e99d77'
System.log("Workflow name: " + Server.getWorkflowWithId(wfId).name);

 

All of this can be managed by creating your own service blueprint and publishing as a catalog item..