24 Apr 2017

Guide: From Start to Finish - Coding with the vRA Plugin in vRO Using Custom Properties Featured

Custom properties in VMware vRealize Automation (vRA) provide us with the ability to set data on vRA objects and to change configurations that affect the behaviour of objects in vRA. For example, when set on a vSphere Virtual Machine component contained in a composite blueprint in vRA 7, the property "VirtualMachine.Admin.ThinProvision" results in the virtual machine deployed with thin provisioned disks in vSphere. The "VirtualMachine.Admin.ThinProvision" property is a custom property that the out of the box vSphere provisioning workflow uses when set, and we do not have to do anything other than specifying a "true" value for the property to have an effect on the resulting virtual machine. VMware has developed the built-in workflows to make use of custom properties such as "VirtualMachine.Admin.ThinProvision" when they are specified on various components. These properties are documented in the "Custom Properties Reference" documentation provided with vRA 6 and 7.

Custom properties that ship out of the box with vRA, however, are only a small part of where the concept of custom properties can be used to extend the capabilities of your automation solution. Just as the built-in workflows make use of custom properties, so can your workflows take advantage of custom properties that you define using vRealize Orchestrator (vRO).

In this post, I demonstrate how to programmatically select a vRA catalog item, using a vRO workflow. That is by no means representative of a real-world implementation but provides a technical goal for this post to achieve. It is also a chance for me to show my methodology for coding against vRA in vRO. The vRA 7 plugin for vRO provides a lot of objects, properties and methods, and not knowing why they exist and how to use them is a primary source of frustration for those new to vRO.

During this course of this post, I walk you through vRA custom properties, how to define them in the property dictionary and how to assign then to a composite blueprint in vRA7.2. I then walk you through building a vRO workflow to select a catalog item based on the value of the custom property defined in vRA. We look at several vRO scripting classes that are provided by the vRA plugin, their properties, methods and method return types. We look at how to find sufficient information about these objects using the API Explorer in vRO, to achieve your goal of building a workflow from scratch to do just about anything you can dream up.

For this post, to keep things as simple as possible, we base our catalog item selection criteria on the operating system type of a single vSphere virtual machine component in a composite blueprint. This post was written against vRA7.2 and vRO7.2.

Deciding How to Determine the OS Type

Remember, a vSphere virtual machine component in a composite blueprint is a vRA object that defines the basic deployment parameters of a new virtual machine in vSphere. It, therefore, has properties that are used by vRA to, for instance, request a new VM clone from vSphere. None of the properties on the vSphere virtual machine component allows for an administrator to specify the guest operating system, nor does vRA interrogate the selected vSphere VM template for that information. Therefore, we need to attach this information manually somewhere on the blueprint or the component, so that we can use it later. There are a few ways to which we can attach this information. Our first option is to embed the guest OS type in the naming convention of the blueprint name itself. That is by far the simplest and most common method that I have seen out in the field. However, it is not flexible and not scalable to include many other "features" or "capabilities" that the blueprint might have on offer, within its name.

Our second option is to specify the guest OS type as a custom property on the vRA blueprint. Retrieving the custom property value for evaluation is more complicated than just obtaining blueprint name, but it is a cleaner and more robust solution to the problem. It also makes it possible to define any other custom properties that we might need to make a catalog item decision at request time in vRO.

Our third option is similar to option two, and also makes use of custom properties. However, instead of attaching the custom property to the blueprint, we can attach a custom property on the vSphere virtual machine blueprint component object directly. That, however, does add another layer of complexity to our JavaScript code in vRO, as we need to retrieve the vSphere virtual machine component after obtaining the blueprint, and then get all of its properties. 

To find a middle ground between simplicity and complexity for this post, I am going to select option two and define a new custom property in the vRA property dictionary, and then attach that property and its appropriate value, to the blueprint. I then demonstrate how to use vRO to:

  1. Get a list all published catalog items
  2. Get the associated blueprint for each catalog item
  3. Read the custom properties of the blueprint
  4. Select the correct blueprint based on the custom property values

For this post, we have the following blueprints published as catalog items:

  • Blueprint Name: Windows 2012 R2
  • Blueprint Name: CentOS 7 64-bit

Figure 1 below shows the catalog items configured in vRA7.2

Figure 1: vRA Catalog items configured in vRA7.2

 

Using the Property Dictionary

Although a custom property can be “made up” and specified directly on a vRA object without having to define it first, the property dictionary is where you should define custom properties “properly”, before using them. Using the property dictionary has several benefits. These include (but are not limited to):

  • Minimises typing mistakes when defining properties, as properties defined in the property dictionary can be selected from a drop-down list when attaching the property to a vRA object such as a blueprint
  • The ability to specify a custom label for the property which allows for user-friendly field names in request forms
  • The ability to specify an input data type, the input control type, input validation and even lists of data that can be selected by a user using a drop-down form control.

The custom property that we define for this post is only utilised with a static value in each blueprint, and will therefore not be displayed to the user at request time. We could therefore not have bothered using the property dictionary at all, and just defined the property on each blueprint directly using the “custom properties” tab. However, it is still a good idea to define the property in the property dictionary, as if it was to be displayed to the user at request time. Getting into a habit of always using the property dictionary when defining custom properties, helps you keep your properties consistent throughout the environment and reduces the risk of errors due to misspelt or inconsistent custom property names. It also means that all custom properties are labelled properly with user-friendly labels, which means that all of your custom properties are “safe” to be made visible on request forms displayed to users.

Defining our custom property

It was not my original intent to do a walk-through of the vRA user interface, as I assume in the post that you are at least familiar with the vRA user interface to some extent. However, to ensure that everyone knows how to define custom properties using the property dictionary, for this step, I walk through the vRA interface for defining custom properties using the property dictionary.

In the next steps, we use the property dictionary in vRA to define a new custom property called “vvcp.blueprint.guestOSType”

  1. In the vRA portal, click the “Administration” tab
  2. From the left-hand pane, select “Property Dictionary”, then select “Property Definitions.”
  3. Click New
  4. Fill in the form as below and click “OK.”

Figure 2 below shows the configuration for the property definition in vRA.

Figure 2: Property definition configuration settings for vvcp.blueprint.guestOSType

Ok, so we have a new property defined within the property dictionary called “vvcp.blueprint.guestOSType”. We could have named the property anything we wanted, as long as the property name is unique. The “vvcp” part of the property name is simply an abbreviation for VirtualvCP, and it aids as a visual clue for me as the administrator/developer that the property is a custom property defined in the VirtualvCP vRA tenant, rather than a built-in vRA custom property. It also prevents us from defining custom property names that might conflict with any built-in vRA custom properties.

Attaching our custom property to the blueprint

The “vvcp.blueprint.guestOSType” property is used on each blueprint to store the guest operating system type for the VM that the blueprint deploys. We, therefore, need to attach the custom property to each blueprint and provide an appropriate value for the custom property. We can attach the custom property by editing the blueprint and clicking on the silver cog icon as indicated in figure 3 below:

Figure 3: Click the silver cog to edit the blueprint properties in vRA

The “Blueprint Properties” window opens. Attach the custom property to the blueprint by completing the following steps:

  1. Click the “Properties” tab
  2. Click the “Custom Properties” tab
  3. Click “New.” A new blank line is displayed.
  4. Under the “Name” field, click the arrow to the right of the dropdown field, or simply start typing the custom property name. While typing, notice how vRA is suggesting to the property to select?
  5. Double click the “Value” cell, and enter the guest OS type. For the blueprint in this example, we have entered “windows”. Optional: You can set overridable to “No” for this custom property, as we do not need to override it with any other value once it has been set.
  6. Click “OK.”
  7. Click “Finish” to exit the blueprint editor.

Figure 4 below shows the steps required to attach a custom property to a blueprint and to provide a value for the custom property.

Figure 4: Attaching a custom property to the blueprint

Note: When we come to code in vRO, custom property names and values are pulled through to vRO in the same letter case as what is specified in vRA. vRO makes use of JavaScript as its scripting language, which is case sensitive. I therefore always use lowercase or camelCase in my custom property names and values for consistency. However, we cast everything to lowercase in vRO when testing values to ensure that a letter case mismatch does not provide us with incorrect logical operator results.

Repeat the process for the Linux blueprint, however, enter “centos7x64” as a custom property value as shown in figure 5 below.

Figure 5: Custom properties for blueprint CentOS 7 x64

Now that we have attached our custom property (“vvcp.blueprint.guestOSType”) to both our blueprints, we can look at how we can read the value of the custom property within a vRO workflow and can programmatically make a catalog item decision using vRO.

Using vRA custom properties in vRO

I acknowledge that the section above on vRA and custom properties could be considered mundane to some of the readers who are familiar with vRA. However, I did not want to make the assumption that everyone reading this knows what custom properties are and how to define them properly. It also provides a solid foundation for the next part of the blog post. 

With our two blueprints in place, each configured with a custom property to store the guest operating system type information, we are finally able to get to the real reason for this blog post, which is to look at some vRO code.

The next section of the post covers the basics of vRO, such as accessing the vRO client and creating a folder and a new workflow. If you are familiar with vRO, you can skip this section and go to Building the workflow.

Accessing the vRO Client

Head over to the vRO client and log in with an account that has administrative privileges. If you do not have a local copy of the vRO client, you can download it from https://<vro-appliance>:8281

Figure 6 below shows the login screen of the vRO client.

Figure 6: vRO Login screen

When creating a new tenant in vRA, I always create a new vRO instance, dedicated to that particular tenant. For the current vRA tenant, VirtualvCP, I have deployed a vRO appliance called vra7vrovvcp01.lab.virtualvcp.local. The port over which the client connects to the vRO server is TCP 8281. Therefore the “host name” field contains “vra7vrovvcp01.lab.virtualvcp.local:8281”. 

I could probably write a separate blog post about why I recommend a dedicated vRO instance for each tenant, and for that reason, that explanation is beyond the scope of this post. Let’s just say for now that it keeps things clean for authentication purposes and the execution of workflows triggered from vRA via the Event Broker Service (EBS), which is also outside the scope of this post.

Creating a New Folder Structure and Empty Workflow

For us to be able to do anything meaningful at this stage in vRO, we need to create a new workflow, and we also need a folder in which to place this new workflow. However, and although it is not technically required to be able to create new folders and workflows in vRO, I normally switch the vRO client from “Run” mode into “Design” mode. I tend to work in “Design” mode most of the time when writing workflows and vRO, as this gives you access to create and define actions and configuration elements/attributes, as well as import/browse resources.

To switch to design mode, click the “Run” dropdown list to the right of the “VMware vRealize Orchestrator” logo, and select “Design”, as shown in figure 7 below.

Figure 7: Switching between vRO client modes

To create a new folder:

  1. Ensure that the “Workflows” tab is selected (blue workflow icon)
  2. Right click on the vRO server name and click “Add folder”
  3. Specify a new name for the folder. I normally create a folder called “Sandpit” for workflows I intend to use to experiment

Figure 8 below shows the process of adding a new folder in the workflow tab of vRO.

Figure 8: Add a new folder in vRO for workflows

To create a new workflow in the “Sandpit” folder:

  1. Right-click the new “Sandpit” folder
  2. Click “New workflow”
  3. Enter “Select Catalog Item” as a name for the new workflow

Figure 9 below shows how to create a new, blank workflow in the “Sandpit” folder

Figure 9: Creating a new blank workflow

The workflow editor opens in full screen. The workflow editor can look quite daunting when you first start using vRO. Don’t be alarmed. I promise it is not as bad as it seems. For those new to vRO, have a look at figure 10 below where I explain key aspects of the workflow editor’s “General” tab.

Figure 10: Workflow Editor overview
  1. Right at the top of the workflow editor is the workflow name in bold. This might at first seem like redundant information as there is a “Name” field (marked as 3 in the image). However, this is useful when you work in other tabs, as it allows you to glance at the workflow name and its exact spelling if you need to add it somewhere in code (for example while logging).
  2. The “General” tab, which is currently selected.
  3. The workflow name. This field is editable and can be used to change the workflow name within the workflow editor if need required.
  4. The workflow ID. Although it is not relevant to this post, the ID is important as it identifies the workflow internally to vRO. You also need the ID to make calls directly to the workflow from the vRO REST API as REST API URL contains the ID. Note, that the workflow ID is generated when the workflow is first created and is unique to the workflow. Even when the workflow is exported and imported into another vRO instance, the ID remains the same.
  5. The “Version” field control is used to track the changes in the current version of the workflow in relation to previous versions. vRO contains a version control system which allows you to roll back to earlier versions of your workflow, and compare differences between versions. Versioning is also critical when it comes to importing exported workflow packages, as by default, vRO overwrites older versions of workflows already installed on the vRO library with newer versions contained in a package being imported, all based on the workflow version numbers. vRO also makes use of workflow version numbers when synchronising workflows between different vRO instances (for example, Dev -> Test -> QA -> Prod). As a general rule, always increment at least the minor version manually and entering notes about changes made in the current version, before saving changes in a workflow. If you do not manually increment the version number, vRO, by default, prompts for a version increment when saving a changed workflow. However, the automatic prompt simply increments the version without giving you the chance to enter notes about changes made in the current version.
  6. Use the description field to provide a brief summary of what the workflow does. You can also include details such as the author name, or anything else you would like to record.
  7. The attributes section of the “General” tab is where you can create attributes (think variables) that are accessible to all objects and elements within the workflow. More on attributes later.
  8. The inputs tab is where we define input parameters to the workflow.
  9. The outputs tab is where we define the output data that the workflow returns when it completes. These values are used as inputs to other processes (for example the vRA EBS) and workflows in vRO.
  10. The “Schema” tab is where we define workflow elements. We normally spend most of the time during workflow development in this tab.
  11. The “Presentation” tab is where we define the workflow input parameter presentation. The presentation is used in workflow forms when a workflow is run within the vRO client. This tab also enables us to set mandatory fields and define input data validation.

Building the Workflow

If you have opted to skip the above section, you will not have a new workflow to complete the steps in this post. Please open the vRO client and create a new workflow called “Select Catalog Item”

Now that everyone knows how to create a new empty workflow, we can get started on building out our workflow. Before starting a new workflow, or any other coding project such as a PowerCLI script, it is a good idea to write down what you would like to achieve and how you plan to go about creating the workflow or script.

At a high level, our workflow needs to programmatically select a vRA catalog item based on the value of a vRA custom property called “vvcp.blueprint.guestOSType”. Therefore the workflow needs to have the following components and tasks:

  1. An input parameter to request the desired OS from the user as a mandatory input
  2. A scriptable to write to the vRO system log that the workflow is starting and to confirm the input parameter value by logging this to the system log as well. The scriptable item then evaluates the input parameter and assigns its value to a local workflow attribute
  3. A scriptable item then gets a catalog item that matches the guest OS type based on the workflow input parameter. The scriptable item runs through the following steps to complete the catalog item selection process:
    1. Requests a list of available catalog items from vRA
    2. Returns the associated blueprint for each catalog item in turn
    3. Gets the blueprint properties and finds the “vvcp.blueprint.guestOSType” property
    4. Checks the value of the property and if a match to the input parameter is found, writes the calalog item to a workflow attribute via a scriptable items output parameter
  4. A scriptable item sets the workflow success state attribute to true, sets the workflow output parameter to the selected catalog item and logs to the system log that the workflow run is ending.

Setting Workflow Attributes

Our workflow requires local attributes to store information and data. A local attribute is like a variable that is available to all objects in the workflow to read and write data. An attribute has a name, type, value and a description. We need to define at least the following workflow attributes in the workflows “General” tab to get started:

Attribute Name Type Value Description
attWorkflowName  String  Select Catalog Item The name of the workflow. Used in vRO system logging messages
attErrorCode String  Not set The workflow stores exception messages in this attribute if an exception occurs
attSuccess Boolean False We set this attribute to true once we are sure that the workflow has completed all of its tasks successfully. Workflows that may call this workflow can evaluate its success state using this attribute value.
attvCACCafeHost vCACCAFE:VCACHost Set to your vRA CAFE Server vRO inventory object This object is used by the workflow scriptable item elements to request information from vRA
attCatalogItem vCACCAFE:CatalogItem Not set Once a catalog item is selected, the catalog item is stored in this attribute
attGuestOSType String Not set We assign a value to this attribute during the first scriptable item from the inGuestOSType input parameter

Figure 11 shows the attributes configured on the workflow.

Figure 11: Workflow attributes configured in vRO

Setting Workflow Input Parameters

For the workflow to be able to select a catalog item based on an operating system type, we need to have a way to tell the workflow which operating system we need. We use a workflow input parameter for this purpose. Input parameters are similar to workflow attributes. An input parameter has a Name, Type and Description. It does not have a value field as the value is set by workflow inputs when the workflow is run.

For this workflow, we only need one input parameter:

Name Type Description
inGuestOSType String Accepts windows, centos7x64, rhel7x64

Figure 12 shows the workflow input parameters configured on the workflow

Figure 12: Workflow input parameters configured

Setting Workflow Output Parameters

When the workflow completes, it needs to be able to pass information back to the system or workflow that called it. This information is passed back via output parameters. If another workflow has called our workflow, the calling workflow can use the values in the outputs of our workflow. An output parameter has a Name, Type and Description. It does not have a value field as the value is set by workflow elements during the workflow execution.

For this workflow, we need three output parameters:

Name Type Description
outCatalogItem vCACCAFE:CatalogItem The selected catalog item
outSuccess Boolean A true or false value to indicate whether our workflow completed successfully
outErrorCode String If an exception occurs in our workflow, the default exception handler populates this output parameter with the exception message. Calling workflows can read this information

Figure 13 shows the workflow output parameters configured on the workflow

Figure 13: Workflow output parameters configured

Building the Workflow Schema

We can now start to create the schema for the workflow. Click on the “Schema” tab to access the schema editor. The workflow schema is made up of elements that are dropped on the schema canvas into the process flow diagram. The elements that can be dropped onto the canvas are listed in the left pane of the editor. These elements are grouped into categories. From the general category, drag a “Scriptable task” onto the canvas between the green start marker and the grey end marker. Then, hover the mouse cursor over the new “Scriptable task” element, and click the yellow pencil to open the scriptable task editor, as shown in figure 14 below:

Figure 14: The workflow schema editor

The scriptable item editor opens. The editor has the following tabs:

  • Info: Provides several fields. Most of the fields on the Info tab are out outside the scope of this post. We are only interested in Name and Description. Figure 15 below shows the info tab configuration for the “Start workflow” item.
Figure 15: The info tab configuration for the “Start workflow” scriptable item
  • In: The IN tab is where we select and map workflow attributes and workflow inputs to new temporary local attributes that exist in the scriptable item. Follow the steps in the image below to select the “attWorkflowName” workflow attribute and the “inGuestOSType” workflow input parameter as inputs to the scriptable item. Figure 16 shows the IN tab parameters as well as the chooser dialog that is used to select available workflow parameters and attributes as inputs to this scriptable item.
Figure 16: The “in” tab configuration for the “Start workflow” scriptable item. Also displayed is the chooser dialog that is used to select available workflow parameters and attributes as inputs to this scriptable item

As shown in figure 16 above, the “IN” tab parameters have 4 fields. The local parameter field is only accessible within the scriptable item itself. The name of this parameter can be different than that of the mapped workflow source parameter. However, when mapping a local parameter to a workflow source parameter using the “Chooser” dialog, the name of the local parameter is set to match the source parameter name automatically, provided that the parameter name is not in use by any other local parameter.

  • Out: The “OUT” tab is where we select and map any local scriptable item parameters to workflow attributes and workflow outputs. As we would like to update the values of two of the existing workflow attributes (attSuccess and attGuestOSType) with the values of local scriptable item parameters, we map the local parameters of each to their respective workflow source attributes as shown in figure 17 below.
Figure 17: The “OUT” tab configuration for the “Start workflow” scriptable item
  • Exception: The “Exception” tab is simple, and I have seen many people ignore it. Do not fall into the habit of doing so. The exception tab allows us to bind a workflow attribute to the scriptable item. The workflow attribute is used as a storage location for any exception messages that may occur during the execution of the script in the scriptable item.

Click on the “Not set” link and select the “attErrorCode” attribute from the list in the “Chooser…” as displayed in figure 18 below.

Figure 18: the “Exception” tab configuration for the “Start workflow” scriptable item
  • Visual Binding: The “Visual Binding” tab is very helpful to quickly determine which local parameters are mapped to which workflow inputs, outputs and attributes. In the image below, we can see that the workflow input parameter “inGuestOSType” is mapped to an IN parameter, “inGuestOSType” of the “Start Workflow” scriptable item. We can also see that the workflow attribute “attWorkflowName” is mapped to an IN parameter, “attWorkflowName” of the “Start Workflow” scriptable item. 

The “Start workflow” scriptable item also the two local OUT parameters, “attSuccess” and “attGuestOSType” mapped to workflow attributes. Figure 19 shows the visual binding tab for our “Start workflow” scriptable item

Figure 19: The “Visual Binding” tab for the “Start workflow”
  • Scripting: The “Scripting” tab of the Scriptable Item element is where the real action happens as this is where we insert JavaScript code. Throughout this post, I attempt to explain all the code in detail. The scripting tab also gives us quick access to the API Explorer, a tool we frequently use to gain an understanding of what objects are available, what their properties are and what the return types are for methods. Figure 20 shows the Scripting tab with the API Explorer visible on the left-hand side.
Figure 20: The “Scripting” tab of the “Start workflow” scriptable item. The API Explorer is visible on the left

Coding the Start Workflow Scriptable Item

Let’s start with the code. Go ahead and paste this block of code into the scripting tab for our “Start workflow” scriptable item.

//Log that the workflow has now started
System.log("Starting workflow: " + attWorkflowName);

/*
The following System.log lines log input parameter values to the System log
for troubleshooting reasons
*/
System.log("Workflow input parameters received: ");
System.log("Input Parameter inGuestOSType: " + inGuestOSType);

//if inGuestOSType is NOT an empty string, then execute the following lines of code, else throw an exception
if (inGuestOSType != “”){
    //Set the attGuestOSType attribute value to the inGuestOSType workflow input param
    attGuestOSType = inGuestOSType;
} else throw "The workflow input parameter \"inGuestOSType\" cannot be empty"; 

//Set the workflow success state to false
attSuccess = false;

Code explained:

//These are comments and ignored by the JavaScript interpreter
/* These are multi-line comments and also
Ignored by the JavaScript interpreter */

System.log() accepts a string between the parentheses () and logs the input string to the vRO system log. For example, System.log(“Hello World!”) logs the text “Hello World!” to the system log.

if (inGuestOSType != “”){ 

The if statement checks to see if the inGuestOSType input parameter contains an empty string. If it is not empty, we continue by assigning the value of inGuestOSType to the workflow attribute attGuestOSType:

attGuestOSType = inGuestOSType;

 If it is empty, we throw an exception, and the default exception handler (which is implemented later in this post) is invoked.

else throw "The workflow input parameter \"inGuestOSType\" cannot be empty";

Figure 21 below shows the scripting tab filled out:

Figure 21: The “Scripting” tab for the “Start workflow” scriptable item completed

Click “Close” on the Scriptable Item editor.

Configuring and coding the “Get Catalog Item” Scriptable Item

Back on the workflow schema tab, drop another Scriptable item between the “Start workflow” element and the grey end element. Edit the scriptable item and ensure the following settings are applied, as shown in Table 4:

Table 4: “Get Catalog Item” Scriptable Item configuration settings

After the “Get Catalog Item” scriptable item has been configured, the workflow schema should match what is shown in figure 22.

Figure 22: Select Catalog Item Workflow Schema

Let’s walk through the process of writing the code. It would be easy for me just to paste the code and then talk through it, but I really would like to try and convey the thought processes I have when writing code, especially when it is code that interacts with vRA. When you look at the blocks of code that someone else wrote, it always seems that they knew what they were doing and that their code seems so good and complex. I always think that surely, the guy who wrote this code is a genius? However, I find that when I code it takes a lot of trial and error, but in the end, it always looks like polished code that was written line by line in 5 minutes. Let me assure you, it never takes 5 minutes and it never is written line by line without having to go back and make changes. I am sure that everyone thinks the same about code generated by others!

Ok, so where do we begin? We know that we must retrieve a list of available catalog items from vRA, so let’s start there.

The vRA plugin that ships with vRO 7.2 should have functions available to retrieve objects such as catalog items from vRA. So, let’s open the API explorer and search for the term “findCatalog”. Ensure that “Scripting class”, “Attributes & methods” and “Types & enumerations” are all checked. As shown in figure 23 below, the vRO API explorer has found a method that might just do the job. 

Figure 23: API Explorer Search Window

TIP: For an interactive online vRO API Explorer, I recommend you look at http://www.vroapi.com/Plugin/vCACCAFE/7.2.0 by @RuurdKeizer. It is an awesome resource!

One of the methods found is called findCatalogItems(), and it is part of the vCACCAFEEntitiesFinder scripting class. This method should do the job. Select it from the list and click the “Go to selection” button. Then click the “Close” button on the search dialog window.

Back in the script editor, notice how the API Explorer has selected the findCatalogItems method as shown in figure 24:

Figure 24: API Explorer view of the vCACCAFEEntitiesFinder.findCatalogItems method

The API explorer gives us all the information we need to work with this method. The signature (vCACCAFECatalogItem[] findCatalogItems(vCACCAFEHost host, String query) tells us that the method will return an array of vCACCAFECatalogItems (vCACCAFECatalogItem[]). We know it returns an array because the two [] (square brackets) following the vCACAFECatalogItem type indicates an array. We also know from the signature that the method name is findCatalofItems and that it accepts two parameters; a host object of type “vCACCAFEHost” and a query of type “String”. Finally, just to spell it out in plain English, the API Explorer tells us the “Return Type” is Array of vCACCAFECatalogItem.

Why does it matter that we know the return type, you may be asking? Well, methods return objects, and each object has a set of properties and methods, depending on its type. To know which properties are accessible for an object that returned from a method, we need to know the object type, so that we can inspect that object in the API explorer.

Before we start digging into the object type “vCACCAFECatalogItem”, let’s just write a bit of code first.

We need to define a new variable to contain the object that the findCatalogItems() method returns. We do this with the “var” keyword.

To start off with, we declare a new variable called catalogItemList, and at the same time, we initialize the variable to null. Although it is not necessary to initialize variables when you declare them, I want to make it syntactically clear that the variable is null to start with as we will be testing if the variable is null at a later stage, after attempting to assign it a value.

var catalogItemList = null;

Now that we have a variable that is null, we can go ahead and try to assign a value or an object to it calling the vCACCAFEEntitiesFinder.findCatalogItems() method, which should return an array of vCACCAFECatalogItem[] if successful.

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

After this code executes successfully, the catalogItemList object that we have created and initialised to null should now contain the result returned from the vCACCAFEEntitiesFinder.findCatalogItems() method.

We know that the findCatalogItems() method returns an array of objects. This array could contain one object, or it could contain many objects. At this point, we do not care what type of objects array might contain; we just need to test the array size first to see if the findCatalogItems() method returned a result.

When looking at API explorer, we find that there is a JavaScript scripting class called “Array”. When inspecting this class, we can see the properties/methods shown in figure 25.

Figure 25: Array properties and methods in the API Explorer

As shown in figure 25 above, we can get the size of any array by inspecting its length property. In our case, if the findCatalogItems() method returned a result of at least one catalog item in an array, then, that array is assigned to our variable called catalogItemsList. So, therefore we should be able to access a property called “catalogItemList.length”.

Let’s look at the code first before I explain what it does:

if (catalogItemList != null && catalogItemList.length > 0){
    System.log(“Number of catalog items found in vRA: “ + catalogItemList.length);
} else throw “No catalog items found in vRA”;

We use an if-statement to determine if the catalogItemList object passes two tests. The first test (evaluation) determines if the catalogItemList object is not null. The second evaluation determines if the length of the array is larger than 0. If the length property does not exist (the catalogItemList is probably not of type “Array” and therefore does not have the length property), then the evaluation returns “false”. 

The AND operator (&&) tells JavaScript that both conditions in the if-statement need to evaluate to “true” for the if-statement to return true. If either one of the evaluations returns “false”, the if statement also returns “false”. So, if both tests return “true”, JavaScript executes the code block between two curly brackets (or braces, depending on where you are from).

We use an “else” statement after the code blocks’ closing curly bracket if the result of the if-statement turns out to be false (catalogItemList.length is 0 or smaller), and we use the “throw” keyword to throw an exception as no catalog items were returned from vRA. The default exception handler in the workflow handles the exception, although we will implement the default exception handler later.

Running the workflow at this stage using the “Debug” button in the workflow editor, should provide the following in the system log (can be view in the Logs tab of the workflow editor)

[2017-04-11 21:41:06.744] [I] Starting workflow: Select Catalog Item
[2017-04-11 21:41:06.748] [I] Workflow input parameters received: 
[2017-04-11 21:41:06.753] [I] Input Parameter inGuestOSType: windows
[2017-04-11 21:41:07.440] [I] Number of catalog items found in vRA: 2

Ok, we are now ready to start digging into the array that came back from vRA. Fron now on, all code that we write is to exist within the if-statement curly brackets, as we know that we are dealing with an object that is not null and contains at least one element. Arrays are interesting things, and I find them rather difficult to explain to others. For those readers who are from a PowerShell / PowerCLI background, arrays should make much sense. For those new to scripting/programming, arrays might take a while to get your head around. I am not going to try and explain arrays here as there are many articles on the internet that do a much better job at explaining the concept of arrays than I could ever do. 

By nature, arrays could contain multiple elements(objects), and each object within the array has its set of properties and methods. Therefore we have to evaluate each object in turn. Many people would do this with a for loop, for example, something like this: 

for (var i = 0; i <= catalogItemList.length -1; i++){
    System.log(“Catalog Item Name: “ + catalogItemList[i].name);
}

The code above is perfectly legal JavaScript, and sometimes when you need to be able to return the index number of the current object you are processing with an array, the code might even be necessary. However, I like clean and simple code, and the code above might look geeky and clever, but it is not simple, and it is not clean. I recommend not to use code like that unless it is necessary. There is a much better way!

Consider this code instead:

for each (var catalogItem in catalogItemList){
    System.log(“Catalog Item Name: “ + catalogItem.name);
}

The for each statement above produces the same result as the for loop demonstrated before it, yet it is much easier to read and write. Keep your code simple; you will thank yourself when you come back to look at it again months later!

In the for each loop, we are declaring a new variable called catalogItem that only exists in memory as long as we stay in the for each loop. Every time the code loops round to the top, the next item in the CatalogItemList is assigned to the catalogItem object. The loop, therefore, runs as many times as the number of objects within the catalogItemList array. Each line of code within the curly brackets executes for each object in the array in turn.

You might be wondering what the catalogItem.name is all about. How did I know that would work? Again, we have to look at the API Explorer. We are aware from the signature of the findCatalogItems() method that it returns an array of type vCACCAFECatalogItem. Searching for vCACCAFECatalogItem, we see a scripting class for it as shown in figure 26 below:

Figure 26: API Explorer detail of vCACCAFECatalogItem

The API explorer reveals all of the properties and methods available for our object that lives within our for each loop’s catalogItem variable, which has one of the elements of the catalogItemList array assigned to it. All of the elements within the catalogItemList are of type vCACCAFECatelogItem, and that therefore also makes our catalogItem of type vCACCAFECatelogItem.

In the line of code that reads “System.log(“Catalog Item Name: “ + catalogItem.name);”, we simply access the “name” property of the catalogItem that the loop is currently evaluating. If you run the workflow in debug mode, you should see the following log output:

[2017-04-11 22:21:37.044] [I] Starting workflow: Select Catalog Item
[2017-04-11 22:21:37.054] [I] Workflow input parameters received: 
[2017-04-11 22:21:37.057] [I] Input Parameter inGuestOSType: windows
[2017-04-11 22:21:37.732] [I] Number of catalog items found in vRA: 2
[2017-04-11 22:21:37.739] [I] Catalog Item Name: CentOS 7 64-Bit
[2017-04-11 22:21:37.742] [I] Catalog Item Name: Windows2012R2

The log entries clearly show that we can read the name property for each of our vRA catalog items. That is a good start. However, the custom property “vvcp.blueprint.guestOSType” is attached to the backing blueprint of each catalog item and not the catalog item itself. Therefore we need to work on obtaining the blueprint for each catalog item in turn within out for each loop.

NOTE: Just a head’s up, we are going to end up with a few nested for each loop instances here!

Now, this is where things can get tricky with vRA and vRO. I would have been great if the vCACCAFECatalogItem object type had a method to get the vRA blueprint. Something like catalogItem.getBlueprint() would have worked a treat. Sadly, that is not the case. The API Explorer does not list a method that could help us, so we need to have a stab at this manually. Fortunately, vRO can help us out a little in our endeavours to find the blueprint.

Save the workflow and exit the editor. It might give you a validation failure but just ignore that. We need to browse the vRO inventory to look at the catalog item and composite blueprint vRO object representations of the actual vRA catalog items and composite blueprints. Our aim is to identify property values in the catalog item that correspond to property values in the blueprints.

To view the vRO inventory of the catalog items, ensure the vRO client is in “Design” mode (1), then click the inventory tab (2), expand vRealize Automation (3), expand the vRA server (4), expand Catalog (5), and select a catalog item. I have selected CentOS 7 64-Bit, as shown in figure 27 below.

Figure 27: vRO Inventory – Catalog Item Properties

From figure 27 above, we can see the providerBinding property has a value of “Binding id: virtualvcp!::!CentOS764Bit …” That looks like something we should be able to use to identify the blueprint. Make a note of the providerBinding property value before moving on.

Next, we need to browse the composite blueprint inventory object in vRO to determine if the same value exists in any property of the blueprint object. Under the server, expand “Administration”. Then expand “Composite Blueprints” and select the blueprint that matches the catalog item’s providerBinding id by name, in my case this was the CentOS 7 64-bit blueprint, as shown in figure 28 below.

Figure 28: vRO Inventory – Composite Blueprint Properties

From figure 28 above, we can see that the blueprint externalId matches the providerBinding Binding Id property value in the catalog item. We should be able to use these two properties to associate a catalog item with a blueprint in vRO.

Open the workflow in the editor again, and edit the “Get Catalog Item” scriptable item. In the API Explorer, browse to or find the vCACCAFECatalogItem Scripting Class. In the scripting class, we can see that VMware has provided us with a getProviderBinding() method, which returns an object of type vCACCAFEProviderBinding as seen in figure 29 below.

Figure 29: API Explorer – vCACCAFECatalogItem

Clicking on the vCACCAFEProviderBinding link in the API Explorer takes us directly to the vCACCAFEProviderBinding Scripting class in the explorer. Here we can see that it has a method called getBindingId() which returns a string, as seen in the image below. I think this is what we need, but we should test it using System.log before we know that we can trust it!

Figure 30 below shows vCACCAFEProviderBinding in the API Explorer.

Figure 30: API Explorer – vCACCAFEProviderBinding

Back in our script editor, we enter the following line of code, below the line that reads “System.log(“Catalog Item Name: “ + catalogItem.name);”:

var bindingId = catalogItem.getProviderBinding().getBindingId();
System.log("We are looking for a blueprint with an ID of: " + bindingId);

Running the workflow now provides the following output:

[2017-04-12 00:03:34.097] [I] Starting workflow: Select Catalog Item
[2017-04-12 00:03:34.100] [I] Workflow input parameters received: 
[2017-04-12 00:03:34.103] [I] Input Parameter inGuestOSType: windows
[2017-04-12 00:03:34.220] [I] Number of catalog items found in vRA: 2
[2017-04-12 00:03:34.227] [I] Catalog Item Name: CentOS 7 64-Bit
[2017-04-12 00:03:34.235] [I] We are looking for a blueprint with an ID of: virtualvcp!::!CentOS764Bit
[2017-04-12 00:03:34.242] [I] Catalog Item Name: Windows2012R2
[2017-04-12 00:03:34.250] [I] We are looking for a blueprint with an ID of: virtualvcp!::!Windows2012R2

Now that we have a way of obtaining the provider bindingId for our catalog item, we need to look at getting a blueprint that corresponds with the Id. We use the API Explorer to search for methods that return a composite blueprint, as seen in figure 31 below.

Figure 31: API Search – getCompositeBlueprint() 

The search turns up a method called vCACCAFEEntitiesFinder.getCompositeBlueprint(). Select that method and click “Go to selection”, then close the search window.

Back in the API Explorer, the getCompositeBlueprint() method signature tells us that the method expects two parameters, a vCACCAFEHost and a blueprintId, which is the Id of the composite blueprint. Figure 32 below shows the information for the getCompositeBlueprint() method.

Figure 32: API Explorer – vCACCAFEEntitiesFinder.getCompositeBlueprint()

Now, if we look at the inventory object in vRO of the composite blueprint, we notice that the composite blueprint ID is CentOS764Bit as shown in figure 33.

Figure 33: vRO Inventory - CompositeBlueprint Properties

However, the bindingId that we currently have is:

virtualvcp!::!CentOS764Bit

We need to pass an id as a parameter to the getCompositeBlueprint() method, however, passing the current bindingId will not work as it contains “virtualvcp!::!” in the string. Therefore, we need to get the composite blueprint Id which is CentOS764Bit from the bindingId string. Fortunately, it looks like there are characters in the bindingId that we can use to split the string. We can use the exclamation marks (!) as delimiters to form a new array object.

The “String” object in JavaScript has a cool method. It is called “split”. Since our bindingId object is of type “String”, we can use the split method on this string as well. The split method takes any string and splits it up into an array, based on a delimiter of your choosing. For example, the following line:

var testString = “This!Is!a!Test!String”

can be split like this:

var testStringArray = testString.split(“!”);

That will result in the following array elements:

testStringArray[0] == This
testStringArray[1] == Is
testStringArray[2] == a
testStringArray[3] == Test
testStringArray[4] == String

So, we can call the split method on the existing code that we have to obtain the bindingId like this:

var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2]; 

What the code above is doing, is telling JavaScript to take the “String” result from the getBindingId() method, and split the string on “!”. Then, return element index 2 (the 3rd element when counting from 0) and assign it to bindinId.

With the change made to our existing code, the current state of the “Get Catalog Item” scriptable item script is:

var catalogItemList = null;

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)
    for each(var catalogItem in catalogItemList){
        System.log("Catalog Item Name: " + catalogItem.name);
        var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
        System.log("We are looking for a blueprint with an ID of: " + bindingId);
    }
} else throw "No catalog items found in vRA";

Running the workflow now provides the following results:

[2017-04-12 00:39:34.852] [I] Starting workflow: Select Catalog Item
[2017-04-12 00:39:34.855] [I] Workflow input parameters received: 
[2017-04-12 00:39:34.859] [I] Input Parameter inGuestOSType: windows
[2017-04-12 00:39:34.969] [I] Number of catalog items found in vRA: 2
[2017-04-12 00:39:34.972] [I] Catalog Item Name: CentOS 7 64-Bit
[2017-04-12 00:39:34.975] [I] We are looking for a blueprint with an ID of: CentOS764Bit
[2017-04-12 00:39:34.978] [I] Catalog Item Name: Windows2012R2
[2017-04-12 00:39:34.981] [I] We are looking for a blueprint with an ID of: Windows2012R2

Now that we have the ID of the composite blueprint, we can call the vCACCAFEEntitiesFinder. getCompositeBlueprint() method. We pass in the vCACCafeHost and the bindingId objects as parameters. We assign the result of the method to a new variable called blueprint.

var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost, bindingId);

The getCompositeBlueprint() method returns an object of type “vCACCAFECompositeBlueprint”. Therefore, providing that a blueprint was found in vRA that matches the input ID, our blueprint variable is now of type vCACCAFECompositeBlueprint. Looking at the vCACCAFECompositeBlueprint in the API Explorer shows is a list of properties and methods that we can use as shown in figure 34.

Figure 34: API Explorer – vCACCAFECompositeBlueprint

Before we continue with getting the properties of the blueprint object, we need to check to see if the getCompositeBlueprint() method returned something. We can do this with a simple if-statement:

if (blueprint != null){
    System.log("Blueprint found: " + blueprint.name);
}

For logging purposes, we are interested in the object name property. Also available is a property called “properties”, which should contain the blueprint’s properties. However, we can also see that there is a method called getProperties(), which returns an object of type java.util.Map. Searching for: “java.util.Map” in the API Explorer yields no results, as it is undocumented in the API Explorer. However, we can find more details about this object and how it works on the internet. The following page explains the “java.util.Map” type better than I ever could:

http://tutorials.jenkov.com/java-collections/map.html

If you do not want to read the linked page, then here is a basic explanation of what java.util.Map is:

The java.util.Map is a mapping between a key-value pair and in simple terms could be described to look something like this:

[(“key1”, “element 1”), (“key2”, “element 2”)]

There are several implementations in Java of the Map interface, including a HashMap, HashTable, EnumMap, IdentityHashMap, LinkedHashMap, Properties, TreeMap, and WeakHashMap.

We can tell that the vRO vCACCAFECompositeBlueprint.getProperties() method returns a HashMap implementation with the following code:

blueprintProperties = blueprint.getProperties();
System.log("blueprint property object type: " + blueprintProperties);

The code above provides the following output:

blueprint property object type: HashMap:47996124 

Based on the log output, we know we are dealing with a HashMap in this instance. The HashMap is an array of key-value pairs. As it is an array, we need to iterate through the array to get each of the key names, so we need to set up another “for each” loop:

for each (var key in blueprintProperties.keys){
    System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
} 

The blueprintProperties have a method named get(), which accepts a key as a parameter. When called correctly, it returns the value of the key.

Now, you would think that the value of the key (property) is the same as the value provided in the vRA custom property, however, what is logged is the following:

[2017-04-20 20:05:32.831] [I] Key Name: vvcp.blueprint.guestOSType, Key Value: DynamicWrapper
(Instance) : [vCACCAFEComponentFieldValue]-[class
com.vmware.vcac.composition.rest.stubs.ComponentFieldValue] -- VALUE :
com.vmware.vcac.composition.rest.stubs.ComponentFieldValue@2d15795e

The text above includes the name of our vRA custom property (vvcp.blueprint.guestOSType). That is good and well, however, what does not make any sense is the value. It is not what you had probably expected to see. The truth is, we have yet another object, and we need to drill into this object to extract the actual value of the custom property. The reason for this is that in vRA, a custom property has a name, a value and then some other properties as well, such as the following boolean-valued properties: “Encrypted, Overridable, Show in Request”. All of these properties have to be accessible in vRO, so the vRA plugin in vRO presents these properties in the form of an object.

We can see from the logged output, that the key value is an instance of type “vCACCAFEComponentFieldValue” A quick search for this type in the API Explorer provides the following information as shown in figure 35.

Figure 35: API Explorer – vCACCAFEComponentFieldValue

The vCACCAFEComponentFiledValue provides a method called getFacets(), which has a return type of java.util.Map. So, what we have here is a blueprint with a set of custom properties, and each property has a name and value, and each value is another set of properties. We should, therefore, be able to use the same methodology to retrieve these properties as we did with the blueprint properties.

Just before we continue to build this out, let us just review what our code should look like at this moment:

var catalogItemList = null;

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)
    
    for each(var catalogItem in catalogItemList){
        System.log("Catalog Item Name: " + catalogItem.name);
        var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
        System.log("We are looking for a blueprint with an ID of: " + bindingId);
        var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost , bindingId);
        
        if (blueprint != null){
            System.log("Blueprint found: " + blueprint.name);
            //Get the properties for the blueprint
            blueprintProperties = blueprint.getProperties();
            System.log("blueprint property object type: " + blueprintProperties);
            
            for each (var key in blueprintProperties.keys){
                System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
            }
        }
    }
} else throw "No catalog items found in vRA";

To get the value field’s properties, we should add the following line of code within the “for each (var key in blueprintProperties.keys){}” loop:

var valueFieldProperties = blueprintProperties.get(key).getFacets();

We then need to set up another for each loop to iterate through each of the keys in the valueFieldProperties object:

for each (var valueFieldKey in valueFieldProperties.keys){
    System.log("Value Field Property Name: " + valueFieldKey);
} 

So our entire block of code should now look like this:

var catalogItemList = null;

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)
    
    for each(var catalogItem in catalogItemList){
        System.log("Catalog Item Name: " + catalogItem.name);
        var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
        System.log("We are looking for a blueprint with an ID of: " + bindingId);
        var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost , bindingId);
   
        if (blueprint != null){
            System.log("Blueprint found: " + blueprint.name);
            //Get the properties for the blueprint
            blueprintProperties = blueprint.getProperties();
            System.log("blueprint property object type: " + blueprintProperties);

            for each (var key in blueprintProperties.keys){
                System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
                var valueFieldProperties = blueprintProperties.get(key).getFacets();

                for each (var valueFieldKey in valueFieldProperties.keys){
                    System.log("Value Field Property Name: " + valueFieldKey);
                }
            }
        }
    }
} else throw "No catalog items found in vRA";

Running the workflow now will produce the following log output:

[2017-04-20 21:47:18.749] [I] Starting workflow: Select Catalog Item
[2017-04-20 21:47:18.752] [I] Workflow input parameters received: 
[2017-04-20 21:47:18.756] [I] Input Parameter inGuestOSType: windows
[2017-04-20 21:47:19.837] [I] Number of catalog items found in vRA: 2
[2017-04-20 21:47:19.841] [I] Catalog Item Name: CentOS 7 64-Bit
[2017-04-20 21:47:19.844] [I] We are looking for a blueprint with an ID of: CentOS764Bit
[2017-04-20 21:47:19.918] [I] Blueprint found: CentOS 7 64-Bit
[2017-04-20 21:47:19.923] [I] blueprint property object type: HashMap:1813938331
[2017-04-20 21:47:19.926] [I] Key Name: vvcp.blueprint.guestOSType, Key Value: DynamicWrapper (Instance) : [vCACCAFEComponentFieldValue]-[class com.vmware.vcac.composition.rest.stubs.ComponentFieldValue] -- VALUE : com.vmware.vcac.composition.rest.stubs.ComponentFieldValue@5dcee72e
[2017-04-20 21:47:19.931] [I] Value Field Property Name: visibility
[2017-04-20 21:47:19.933] [I] Value Field Property Name: fixedValue
[2017-04-20 21:47:19.936] [I] Value Field Property Name: encrypted
[2017-04-20 21:47:19.939] [I] Value Field Property Name: mandatory
[2017-04-20 21:47:19.942] [I] Catalog Item Name: Windows2012R2
[2017-04-20 21:47:19.945] [I] We are looking for a blueprint with an ID of: Windows2012R2
[2017-04-20 21:47:19.970] [I] Blueprint found: Windows2012R2
[2017-04-20 21:47:19.974] [I] blueprint property object type: HashMap:314283824
[2017-04-20 21:47:19.977] [I] Key Name: vvcp.blueprint.guestOSType, Key Value: DynamicWrapper (Instance) : [vCACCAFEComponentFieldValue]-[class com.vmware.vcac.composition.rest.stubs.ComponentFieldValue] -- VALUE : com.vmware.vcac.composition.rest.stubs.ComponentFieldValue@3806e1c8
[2017-04-20 21:47:19.979] [I] Value Field Property Name: visibility
[2017-04-20 21:47:19.982] [I] Value Field Property Name: fixedValue
[2017-04-20 21:47:19.984] [I] Value Field Property Name: encrypted
[2017-04-20 21:47:19.987] [I] Value Field Property Name: mandatory

The log output does now show the properties that we expect, and the one we are after is “fixedValue”. If we change the system log line from:

System.log("Value Field Property Name: " + valueFieldKey);

to:

System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey));

We get the following output:

[2017-04-20 21:53:36.729] [I] Value Field Property Name: visibility, Value: DynamicWrapper (Instance) : [vCACCAFEConstantValue]-[class com.vmware.vcac.platform.content.evaluators.ConstantValue] -- VALUE : ConstantValue{false}
[2017-04-20 21:53:36.732] [I] Value Field Property Name: fixedValue, Value: DynamicWrapper (Instance) : [vCACCAFEConstantValue]-[class com.vmware.vcac.platform.content.evaluators.ConstantValue] -- VALUE : ConstantValue{windows}
[2017-04-20 21:53:36.735] [I] Value Field Property Name: encrypted, Value: DynamicWrapper (Instance) : [vCACCAFEConstantValue]-[class com.vmware.vcac.platform.content.evaluators.ConstantValue] -- VALUE : ConstantValue{false}
[2017-04-20 21:53:36.737] [I] Value Field Property Name: mandatory, Value: DynamicWrapper (Instance) : [vCACCAFEConstantValue]-[class com.vmware.vcac.platform.content.evaluators.ConstantValue] -- VALUE : ConstantValue{false} 

At this point, you can be forgiven for feeling the need to give up, as it seems that we have yet again ended up with just an object rather than a value that we can use. However, just bear with it, we are almost there.

Each value has now returned a new object of type vCACCAFEConstantValue. The API Explorer describes this type as shown in figure 36.

Figure 36: API Explorer – vCACCAFEConstantValue

Ok, so the object does have a method, called getValue(). Let’s update out System.log() line to use this method:

System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue());

This change produces the following output:

[2017-04-20 22:04:02.717] [I] Value Field Property Name: visibility, Value: DynamicWrapper (Instance) : [vCACCAFEBooleanLiteral]-[class com.vmware.vcac.platform.content.literals.BooleanLiteral] -- VALUE : false
[2017-04-20 22:04:02.720] [I] Value Field Property Name: fixedValue, Value: DynamicWrapper (Instance) : [vCACCAFEStringLiteral]-[class com.vmware.vcac.platform.content.literals.StringLiteral] -- VALUE : windows
[2017-04-20 22:04:02.723] [I] Value Field Property Name: encrypted, Value: DynamicWrapper (Instance) : [vCACCAFEBooleanLiteral]-[class com.vmware.vcac.platform.content.literals.BooleanLiteral] -- VALUE : false
[2017-04-20 22:04:02.725] [I] Value Field Property Name: mandatory, Value: DynamicWrapper (Instance) : [vCACCAFEBooleanLiteral]-[class com.vmware.vcac.platform.content.literals.BooleanLiteral] -- VALUE : false

The output now shows yet another object, this time it is of the type that matches the property type in vRA. In this case, it is either of type vCACCAFEBooleanLiteral for boolean property types or vCACCAFEStringLiteral for String property types. However, we can see the values for each of these objects in the log as “ -- VALUE : xxxxx”. We should, therefore, be able to append the property name “.value” to our line of code, so that it reads:

System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);

This line now produces the following output:

[2017-04-20 22:12:14.498] [I] Value Field Property Name: visibility, Value: false
[2017-04-20 22:12:14.501] [I] Value Field Property Name: fixedValue, Value: windows
[2017-04-20 22:12:14.503] [I] Value Field Property Name: encrypted, Value: false
[2017-04-20 22:12:14.506] [I] Value Field Property Name: mandatory, Value: false 

Finally, we have a way of getting the custom property value. However, as we are only interested in the “fixedValue” property, we can skip the rest of the unwanted properties with an if-statement:

if (valueFieldKey.toLowerCase() == "fixedvalue"){
    System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);
}

The current state of our code is as follows:

var catalogItemList = null;

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)

    for each(var catalogItem in catalogItemList){
        System.log("Catalog Item Name: " + catalogItem.name);
        var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
        System.log("We are looking for a blueprint with an ID of: " + bindingId);
        var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost , bindingId);

        if (blueprint != null){
            System.log("Blueprint found: " + blueprint.name);
            //Get the properties for the blueprint
            blueprintProperties = blueprint.getProperties();
            System.log("blueprint property object type: " + blueprintProperties);

	        for each (var key in blueprintProperties.keys){
		        System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
			    var valueFieldProperties = blueprintProperties.get(key).getFacets();
			
			    for each (var valueFieldKey in valueFieldProperties.keys){
			    
				    if (valueFieldKey.toLowerCase() == "fixedvalue"){
				        System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);
				    }
			    }
		    }
	    }
    }
} else throw "No catalog items found in vRA";

We have almost completed our script for the “Get Catalog Item” scriptable item. However, we still need to make a few more changes to the code. First, we need to ensure that we are only requesting the fieldValueKeys for the “vvcp.blueprint.guestOSType” property. We can achieve this with an if-statement:

var catalogItemList = null; 

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)
	for each(var catalogItem in catalogItemList){
	    System.log("Catalog Item Name: " + catalogItem.name);
		var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
		System.log("We are looking for a blueprint with an ID of: " + bindingId);
		var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost , bindingId);
		
		if (blueprint != null){
		    System.log("Blueprint found: " + blueprint.name);
			//Get the properties for the blueprint
			blueprintProperties = blueprint.getProperties();
			System.log("blueprint property object type: " + blueprintProperties);
			
			for each (var key in blueprintProperties.keys){
			    System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
				
				if (key.toLowerCase() == "vvcp.blueprint.guestostype"){
				    var valueFieldProperties = blueprintProperties.get(key).getFacets();
					
					for each (var valueFieldKey in valueFieldProperties.keys){
					    if (valueFieldKey.toLowerCase() == "fixedvalue"){
						    System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);
							
							if (attGuestOSType == valueFieldProperties.get(valueFieldKey).getValue().value.toLowerCase()){
								//Some code here...
							}
						}
					}
				}
			}
		}
	}
} else throw "No catalog items found in vRA"; 

Now we need to match the value for the “vvcp.blueprint.GuestOSType” property of each blueprint and if that matches attGuestOSType workflow input parameter, select the catalog item by assigning the current catalog item being processed in the for each loop to the workflow attCatalogItem attribute.

for each (var valueFieldKey in valueFieldProperties.keys){
    if (valueFieldKey.toLowerCase() == "fixedvalue"){
	    System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);
        if (attGuestOSType == valueFieldProperties.get(valueFieldKey).getValue().value.toLowerCase()){
            System.log("Match found!");
		    //Set attCatalogItem output Parameter to the current catalogItem
		    attCatalogItem = catalogItem;
		}
	}
}
 

Once the script has found a matching catalog item, we would like to script to exit, as there is no need to continue iterating through all other catalog items. Once we find our catalog item, we need to break out of the “for each (var catalogItem in catalogItemList){…}” loop. As we are using nested “for each” loops, breaking out of a child loop using the “break” JavaScript keyword, won’t cause the parent loop to break, so we need to set up a variable to monitor the success of a child loop.

To begin, we define a variable at the top of the script called “catItemFound”, and we set that to “false”. Then at the beginning of the “for each (var catalogItem in catalogItemList){}” loop, we use a simple if statement to test the value of the catItemFound variable. If it returns true, then the break keyword stops the execution of the loop. If it returns false, the loop continues. We can then set the value of catItemFound to true anywhere in any of the nested loops. Obviously, we would only set the value to true once we are confident that we have found a result. Below is our completed script code:

var catalogItemList = null;
var catItemFound = false;

catalogItemList = vCACCAFEEntitiesFinder.findCatalogItems(attvCACCafeHost);

if (catalogItemList != null && catalogItemList.length > 0){
    System.log("Number of catalog items found in vRA: " + catalogItemList.length)
	
	for each(var catalogItem in catalogItemList){
	
        if (catItemFound) break;
	
        System.log("Catalog Item Name: " + catalogItem.name);
		var bindingId = catalogItem.getProviderBinding().getBindingId().split("!")[2];
		System.log("We are looking for a blueprint with an ID of: " + bindingId);
		var blueprint = vCACCAFEEntitiesFinder.getCompositeBlueprint(attvCACCafeHost , bindingId);
		
		if (blueprint != null){
		    System.log("Blueprint found: " + blueprint.name);
			//Get the properties for the blueprint
			blueprintProperties = blueprint.getProperties();
			System.log("blueprint property object type: " + blueprintProperties);
			
			for each (var key in blueprintProperties.keys){
			    System.log("Key Name: " + key + ", Key Value: " + blueprintProperties.get(key));
				
				if (key.toLowerCase() == "vvcp.blueprint.guestostype"){
				    var valueFieldProperties = blueprintProperties.get(key).getFacets();
					
					for each (var valueFieldKey in valueFieldProperties.keys){
					    if (valueFieldKey.toLowerCase() == "fixedvalue"){
					        System.log("Value Field Property Name: " + valueFieldKey + ", Value: " + valueFieldProperties.get(valueFieldKey).getValue().value);
						    
							if (attGuestOSType == valueFieldProperties.get(valueFieldKey).getValue().value.toLowerCase()){
							    System.log("Match found!");
								//Set attCatalogItem output Parameter to the current catalogItem
								attCatalogItem = catalogItem;
								//We have found what we were looking for, set the flag to break out of the for each (var catalogItem...) loop
								catItemFound = true;
							}
						}
					}
				}
			}
		}
	}
} else throw "No catalog items found in vRA";

With the attCatalogItem workflow attribute set, we are free to continue with the workflow. Drag two scriptable items onto the workflow schema and change their names name to “Set Success” and “End workflow”, as demonstrated in figure 37 below.

Figure 37: Workflow Schema

We can use the “Set success” scriptable item to set the workflow success state attribute (attSuccess) to “true”. The scriptable item has no IN attributes, and only have one OUT attribute, namely attSuccess. Figure 38 shows the visual binding for our “Set success” scriptable item.

Figure 38: “Set Success” Visual Binding

The script consists of  a single line of code:

attSuccess = true; 

With the workflow success state set to true, we can continue with the “End workflow” scriptable item. We can use the “End workflow” scriptable item to:

  • Set the output parameter values to their matching workflow local attributes
  • Set the workflow success state
  • Log that the workflow has ended

We need the following configuration for the “End workflow” scriptable item:

Figure 39 shows the configuration for the “IN” tab of the “End workflow” scriptable item.

Figure 39: “End Workflow” scriptable item “IN” tab configuration

Figure 40 shows the configuration for the “OUT” tab of the “End workflow” scriptable item.

Figure 40: “End Workflow” scriptable item “OUT” tab configuration

Figure 41 shows the configuration for the “Exception” tab of the “End workflow” scriptable item.

Figure 41: “End Workflow” scriptable item “Exception” tab configuration

Figure 42 shows the configuration for the “Visual Binding” tab of the “End workflow” scriptable item.

Figure 42: “End Workflow” scriptable item “Visual Binding” tab configuration

The script for the end workflow should be:

System.log("Setting workflow output parameter values...");
outSuccess = attSuccess;
outErrorCode = attErrorCode;
outCatalogItem = attCatalogItem;
System.log("Ending workflow " + attWorkflowName);

Running the workflow at this point, with an input value to inGuestOSType of “windows” results in the workflow completing with the following output parameters set as shown in figure 43.

Figure 43: Workflow run status

So far, so good. The workflow is working as expected. However, we have not done anything to catch exceptions. Fortunately, for a simple workflow such as this, the default exception handler provided by vRO should be more than sufficient. We can use the default exception handler to set the workflow success state to false and to log the exception.

As this workflow is intended to be called from other workflows, we have the option to either end the workflow on an exception or end it gracefully via the “End workflow” scriptable item. Ending the workflow on an exception by using the exception handlers’ default configuration causes the workflow run to be shown as “failed in vRO” (marked with a red X). Conversely, ending the workflow gracefully via the “End workflow” scriptable item still gives us the chance to log the exception, mark the workflow success state as false, and pass the exception information to the calling workflow (if called by another workflow). However, the workflow run in vRO is shown as having completed successfully. That might sound like an undesired outcome, but if your workflow is called by a master workflow, this behaviour might be beneficial. That is because the calling workflow can then decide what to do about the exception. There is no right or wrong way to go about handling exceptions, but I would go as far as to say that failing to handle exceptions is the wrong way. It does not matter how we handle the failure; it just matters that it is handled in some meaningful way.

In this post, we handle the exception by exiting the workflow gracefully via the “End workflow”  item. If the workflow you are working on is never going to be called by any other workflow, then I would suggest failing the workflow on an exception so that if an exception occurs, the workflow run in vRO is listed as failed. It all depends on the circumstances and goal which you are trying to achieve.

In the schema editor, browse the “Generic” category and drag a “Default error handler” object onto the canvas as shown in figure 44. 

Figure 44: Schema editor – Default error handler

The object has one property, and that is an error code on the Exception tab. If an attribute in the workflow is in use by other objects’ output exception binding property, that attribute should automatically be configured on the default error handler item. In our case, as shown in figure 45, it has selected attErrorCode, which is what we want.

Figure 45: Default error handler – Output exception binding

From the logging category on the left-hand side of the schema editor, drag a “System Error” item between the Default error handler item and the Exception item, as shown in figure 46 below.

Figure 46: Workflow Schema – “System error” item placement

The “System error” item is a scriptable item that is preconfigured to log an “error” message to the system log rather than an “Information” message that results from using “System.log”. We use the System.error() method to log whatever is in the attErrorCode attribute, which contains the exception message.

The “System error” item also gives us an opportunity to add other code. We add a line of code to set the attSuccess attribute to false since an exception was raised somewhere in the workflow.

Configure the “System error” item as follows:

Figure 47 shows the configuration for the “IN” tab of the “System error” item.

Figure 47: “System error” item “IN” tab configuration

Figure 48 shows the configuration for the “OUT” tab of the “System error” item.

Figure 48: “System error” item “OUT” tab configuration

Figure 49 shows the configuration for the “Exception” tab of the “System error” item.

Figure 49: “System error” item “Exception” tab configuration

Figure 50 shows the configuration for the “Scripting” tab of the “System error” item.

Figure 50: “System error” item “Scripting” tab configuration

If an exception occurs in the workflow’s current state, the workflow exits and is marked as “failed” in vRO. No output parameters are set, as output parameters are set in the “End workflow” item, which is bypassed by the exception handler. To make exception handler use the “End workflow” item, simply drag and drop the “Exception” item (Red exclamation mark) onto the “End workflow” item, as shown in figure 51.

Figure 51: Workflow Schema – Drag the Exception item onto the “End workflow” item

The workflow schema should now look similar to what is shown in figure 52.

Figure 52: Final Workflow Schema

Running the workflow with a blank input parameter to “inGuestOSType” results in the workflow throwing an exception, as that is what our line of code in the “Start workflow” items states:

if (inGuestOSType != ""){ //if inGuestOSType is NOT an empty string, then execute the following lines of code, else throw an exception
    //Set the attGuestOSType attribute value to the inGuestOSType workflow input param
    attGuestOSType = inGuestOSType;
} else throw "The workflow input parameter \"inGuestOSType\" cannot be empty"; 

However, now the exception does not cause the workflow to exit immediately and to be marked as failed in vRO. Instead, it is marked as successful by vRO. However, the attSuccess attribute is set to “No” or “False”, and the exception message is logged. Finally, the workflow output parameter outSuccess displays “No”, and the outErrorCode parameter contains the exception message. These parameters can be read by calling workflows. A workflow run where an exception has occurred is shown in figure 53 below.

Figure 53: Failed workflow run exits gracefully

As stated at the beginning, this post implemented option 2 or 3 options, where the custom property was applied to the blueprint, rather than the blueprint components. If you would like to know more about reading custom properties from individual blueprint components, please leave a comment below, and I will write a blog on how to achieve that.

Written by  0 comment
Last modified on Wednesday, 26 April 2017 14:06
Rate this item
(4 votes)

Comments (0)

There are no comments posted here yet

Leave your comments

Posting comment as a guest. Sign up or login to your account.
0 Characters
Attachments (0 / 3)
Share Your Location

RT @KingsGatePBO: WE'VE GOT SNOW! Due to the weather, we are not holding our 11:30am service at KingsGate Peterborough. You can watch a liv…
Follow Rynardt Spies on Twitter