How to create a custom JS class in vRO [CB10110]

  1. What is a class?
  2. But…wait! vRO doesn’t support ES6
  3. How to create a local class in vRO?
  4. Create a globally callable class in vRO
    1. Steps
    2. It’s time to test it
  5. Advantages
  6. Limitations
  7. References

What is a class?

A class is a template for creating objects. It encapsulates data and functions that manipulate data, grouping data and functionality together.

Classes are based on object-oriented programming concepts. As JavaScript is not an object-oriented programming language at its core, JavaScript classes are considered syntactic sugar.

What this means is classes are there as a visual aid in Javascript. They were introduced in EcmaScript2015 (ES6) to allow developers to follow object-oriented programming patterns in a cleaner way. It’s just one of the many reasons that ES6 is so great.

class Pen {
    constructor(name, color, price){
        this.name = name;
        this.color = color; 
        this.price = price;
    }
    
    showPrice(){
        console.log(`Price of ${this.name} is ${this.price}`);
    }
}
const pen1 = new Pen("Marker", "Blue", "$3");
pen1.showPrice();

But…wait! vRO doesn’t support ES6

Aria Automation Orchestrator (or vRO) doesn’t support ES6. It uses ES5.1 and in ES5.1, there is no such keyword as class. However, we can still use function construct to create a psuedo-class in vRO because a class is technically a function with prototype-based inheritance.

Before classes existed, prototypes were used to emulate classes in JavaScript. A prototype is a default object attached to every JavaScript function and object. Additional properties can be attached to a prototype, which helps us emulate JavaScript classes. We can see that when we transpile a ES6 class to ES5 syntax on Babel.

Transpiling ES6 Class to ES5-style syntax using Babel (loose mode ON)

How to create a local class in vRO?

Let’s see how to create to a class in vRO using similar syntax structure. We can create the same Pen class using function as

function Pen(name, color, price) {
    this.name = name;
    this.color = color;
    this.price = price;
}
Pen.prototype.showPrice = function(){
    System.log("Price of "+ this.name+" is "+this.price);
}
//Calling Pen class locally
const pen1 = new Pen("Marker", "Blue", "$3");
pen1.showPrice();

This will create a class that can be used locally inside a workflow scriptable task or an action. But what if you want to use a class globally in vRO, across actions and workflows, just like any other vRO SDK class. Let’s see how we can create that.

Create a globally callable class in vRO

To create a global class in vRO, we can use the action construct that is easily callable both in Workflows and Actions.

Steps

  • Create a new action module to store your custom classes. eg. in.co.cloudblogger.customclasses
  • Create a new action with class name. eg. Pen
  • Create a function which is basically the pseudo-class very similar to what we did for a local class. But here, we have to return that function. Lets see the example of Pen.
/**
 * @function Pen
 * @param {string} name 
 * @param {string} color 
 * @param {string} price 
 *
 * @return {*} A new Pen instance 
 */
function Pen(name, color, price) {
    this.name = name;
    this.color = color;
    this.price = price;
}

Pen.prototype.showPrice = function(){
    System.log("Price of "+ this.name+" is "+this.price);
}

Pen.prototype.showColor = function(){
    System.log("Color of "+ this.name+" is "+this.color);;
}

return Pen;
  • This class has 3 inputs, so add them as Inputs in action.
  • Set Return type to Any. Or you can also create a Composite Type and set it in Return type.

OR

It’s time to test it

  • Go to a scriptable task or an action where you want to call this Pen class.
  • Call the class action here in the first line.
var Pen = System.getModule("in.co.cloudblogger.customclasses").Pen();
  • Now, instantiate a pen object using the new keyword.
const pen1 = new Pen("Marker", "Blue", "₹30");
  • That’s it. You can call the method/attributes inside the Pen class like any other class.
var Pen = System.getModule("in.co.cloudblogger.customclasses").Pen();

// Pen 1
const pen1 = new Pen("Marker", "Blue", "₹30");
pen1.showPrice();
pen1.showColor();

//Pen 2
const pen2 = new Pen("Roller", "Black", "₹200");
pen2.showPrice();
pen2.showColor();

Advantages

One big advantage of using these global classes is that you can create beautiful and well-organized code for vRO. Other one being the ability to create multiple methods in your class and use them using one object. Ex.

var MathFunctions = {
    sum: function(a, b) {
        return a + b;
    },
    sub: function(a, b) {
        return a - b;
    },
    div: function(a, b) {
        return a / b;
    }

};
return MathFunctions;

var MathFunctions = System.getModule("in.co.cloudblogger.customclasses").MathFunctions();
var sum = MathFunctions.sum(1, 2);
var diff = MathFunctions.sub(10, 2);

Limitations

  • Custom class/Composite type is detected by vRO as a object or Properties. Learn more here.
  • No option to extend classes. I even used Babel to generate a transpiled version of extends
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    Object.defineProperty(subClass, "prototype", {
        writable: false
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}

But this throws error InternalError: Changing attributes not supported for Function prototype property

That’s in this post. I hope you like it.

References

  • Babel Transpiler compatible with vRO: You can use to convert any ES6 or TypeScript code to ES5. Don’t forget to change console.log() to System.log().

Trick here is to use loose and forceAllTransforms options in Babel which generates ES5-style code.

https://babeljs.io/docs/babel-preset-env#forcealltransforms

Advertisement

Workflow to run a Workflow over RESTAPI Dynamically [CB10107]

  1. Approach
    1. 1. Trigger WF in same vRO
    2. 2. Trigger WF in Master-Slave setup using Multi-Node Plugin
    3. 3. Trigger WF using Transient REST Host mechanism
  2. Steps
    1. Part 1 Get the Input parameters JSON of the WF that you want to trigger
    2. Part 1 Manually run the WF(to be triggered) just once
    3. Part 2 Time to trigger it remotely

In this post, the idea is to trigger a workflow from a vRO to another vRO over the REST API dynamically (no need to add the remote vRO as Inventory REST Host as we will be using transient hosts, you must have guessed). I have created a WF that you can use to get that functionality. Link to the package here.

Approach

There are multiple scenarios where we have to trigger a vRO workflow from another workflow. And for that, depending on the scope and requirement, We have 3 basic approaches to this.

1. Trigger WF in same vRO

This one is most commonly used through the Workflow editor. What you have to do is to simply add a WF item from the Generic Tab in the WF editor inside another WF. You can then will be able to run the WF synchronously (means the WF will wait for the sub-WF to execute completely and return its status) or asynchronously (that means the WF will run in a different thread and will not return the execution result).

If you want to run a workflow through action, you can also do that using this script in a asynchronous manner. Learn more about it in a post by Umit Demirtas here.

//Auto generated script, cannot be modified !
var workflowToLaunch = Server.getWorkflowWithId("d7c058fb-3f95-4d92-ad17-5d6a856343d4");
if (workflowToLaunch == null) {
	throw "Workflow not found";
}
var workflowParameters = new Properties();
workflowParameters.put("input1",input1Value);
workflowParameters.put("input2",input2Value);
//Add all the inputs needed..//
wfToken = workflowToLaunch.execute(workflowParameters); 

2. Trigger WF in Master-Slave setup using Multi-Node Plugin

The Multi-Node plug-in creates a primary-secondary relation between vRealize Orchestrator servers, which extends in the areas of package management and workflow execution. This plugin comes with vRO out-of-the-box. Learn how to setup Multi-Node plug-in here .

A typical script to trigger a WF in a proxy vRO Server synchronously.

// Id can be viewed in the scripting of the proxy workflows generated for a server
var workflowId = "BD808080808080808080808080808080F0C280800122528313869552e41805bb1";
var tokens = [];
for(var i = 0; i < servers.length; i++) {
	var parameters = new Properties();
	parameters.put("vm", vms[i].id);
	parameters.put("newName", newNames[i]);
	var tmpResult = VCOProxyWorkflowManager.executeSynchronousProxy(servers[i].connectionId, workflowId, parameters);
	tokens.push(tmpResult.get("remoteToken"));
}

Other way would be to trigger them asynchronously. Make sure the proxy workflows exists in local vRO server. Learn more about that here.

VCOProxyWorkflowManager.executeAsynchronousProxies(workflowId, params);

3. Trigger WF using Transient REST Host mechanism

This one is what I would like to share in today’s post. I have created a custom WF through which you can use this approach. Basically, the idea is to utilize the vRO REST API to trigger workflows asynchronously (workflows/{{workflowId}}/executions) and that too using the transient REST host which will not require any configuration to be done earlier. You just need the HTTP-REST plug-in that comes out-of-the-box.

Steps

Part 1 Get the Input parameters JSON of the WF that you want to trigger

Here, in this demo WF, there is one single input input1 with type=string, probably the simplest one. However, head to William Lam’s post on how create a complex input JSON with SDK objects here.

Part 1 Manually run the WF(to be triggered) just once

As you can see above, I have modified the sequence of steps. I have actually created a new action that will prepare the input parameter JSON body by itself. You will find that action (in.co.cloudblogger.customclasses/getWorkflowLastExecutionBody()) in the package itself.

Just 1 thing is needed to be done which is to run the WF to be triggered at least once manually. So that a JSON structure will prepared for the input set of the triggered WF and the action I have created will fetch the last run and automatically prepopulate the “Start WF Execution using REST” WF with the input set of last run.

Part 2 Time to trigger it remotely

  • Download the vRO package from here.
  • Once the package is imported, Go to the Workflow “Start WF Execution using REST” which will be considered as “master wf” here.
  • Provide inputs to this workflow
    • vRO FQDN – Name of the remote vRO where you want to trigger the WF.
    • Username – Username to access remote vRO (User should have permission to Run)
    • Password – Password for the user provided
    • Workflow ID – ID of the Workflow that you want to trigger (find it in your WF URL)
    • Parameter JSON – A structured JSON object with input parameters required to run the remote WF. This will be auto-populated using the new action.
  • Click Run

Critical Values of Certain Datatypes like SecureString or EncryptedString will be hidden in the Payload body. You will have to manually add that missing JSON. For eg. if you see here, the payload has no value field for SecureString.

{
   "parameters":[
      {
         "value":{
            "string":{
               "value":"mayank.goyal"
            }
         },
         "type":"string",
         "name":"username",
         "scope":"local"
      },
      {
         "type":"SecureString",
         "name":"password",
         "scope":"local"
      }
   ]
}

To fix this, you can add something like,

"value":{
            "string":{
               "value":"pa$$w0rd!"
            }
         },

to make the final payload complete to be sent over REST.

{
   "parameters":[
      {
         "value":{
            "string":{
               "value":"mayank.goyal"
            }
         },
         "type":"string",
         "name":"username",
         "scope":"local"
      },
      {
	"value":{
            "string":{
               "value":"Mar#2023"
            }
         },
         "type":"SecureString",
         "name":"password",
         "scope":"local"
      }
   ]
}

  • Once the master WF executed successfully, Login to remote vRO.
  • Go to the WF that was triggered by master WF, click ALL RUNS, you should see the executed run there.
  • In our test run, we passed the Input value as “Triggered over REST by Mayank Goyal” which can be validated here.

You can use this WF inside a Wrapper WF to prepare the inputs or probably inside another WF where you are performing other tasks and that way, you can use it in your automations easily.

That’s it in this post. I hope it will be of some help for you. Thanks for reading. See you on other post. Please subscribe to my newsletter and get latest posts directly in your inbox.

Edit text-based Resource Elements On-The-Go [CB10103]


TL:DR Idea is to update Resource elements from vRO itself as no such functionality exists in UI yet. Use the package and workflow to update resource elements directly from vRO quickly and conveniently. Link to vRO package here.


  1. Introduction
  2. Prerequisites
  3. Procedure
  4. Scope of Improvement
  5. References

Introduction

We all use Resource Elements inside vRealize Orchestrator for various reasons. Resource Elements are external independent objects that once imported, can be used in vRO Workflows and scripts.

Resource elements include image files, scripts, XML templates, HTML files, and so on. However,  I have one concern regarding updating them. It is such an overhead. Though, on official vRO Docs, it is clearly mentioned that you can import, export, restore, update, and delete a resource element, in reality, you have to update that object using a external editor that means a text editor for text based files, image editor for images etc.

Apart from images, the most common type of resource elements are text based files for example, .sh, .ps1, .json, .xml, .html, .yaml etc. In this post, to ease the process of updating resource elements, I have created a WF using which you really won’t have to follow that long boring method of exporting a resource element, edit it in the Notepad++, import it back. Just run that WF and select your resource element and it will give you a text editing area where you can update your resource element on-the-go.

Prerequisites

  • Download the package from here and import into your vRO.
  • Make sure you have the resource element you want to update.

Procedure

  • Run the Workflow Edit Resource Element On-The-Go and select the resource element. Here, I’ve selected template.yaml which I already imported in vRO earlier.
  • By default, vRO picks up a MIME type for your file. However, for text based objects, you can set it to text/{fileExtension}. Here, I will set it to text/yaml so that I can see its content directly in vRO.
  • Go to the next section of the workflow and you can see the current content of your resource element. Edit it the way you like. Here, this file was empty, so I added this YAML code.
  • Click Run and wait for workflow to complete.
  • Go back to the resource element to check if the content is there.
  • Now, you want to change the flavor from large to small. Rerun the workflow, edit the flavor value. Click Run.
  • Your changes should be visible now.

Scope of Improvement

I am looking for a way to give the version as an input, so that we can update the version of the resource element as we update its content. Seems like the resourceElement.version attributes is not working at least in vRO 8.x. Suggestions would be appreciated.

References

https://kb.vmware.com/s/article/81575

Find Object Types in vRealize Orchestrator

Find Object Types in vRealize Orchestrator [CB10100]

Last updated: 16-03-2023

Sometimes, we want to know exactly what type of vRO object we are working on. It could be something that is returning from an action of type Any or a method returning various types of objects or simply about switch cases. In this quick post, we will see what are the options that vRO provides and where to use them.

  1. typeof
    1. Code Examples
    2. Using new operator
    3. use of Parenthesis
  2. System.getObjectType()
    1. Code Examples
  3. System.getObjectClassName()
    1. Code Examples
  4. instanceof
    1. Syntax
    2. Code Examples

typeof

The typeof operator returns a string indicating the type of the operand’s value where the operand is an object or of primitive type.

TypeResult
Undefined"undefined"
Null"object" (reason)
Boolean"boolean"
Number"number"
String"string"
Function"function"
Array"object"
Date"object"
vRO Object types"object"
Composite Types"object"
vRO Object types with new operator"function"

Code Examples

var var1 = new VcCustomizationSpec(); 
System.debug(typeof var1); //function
var var2 = new Object();
System.debug(typeof var2); //object
var var3 = "a";
System.debug(typeof var3); //string
var var4 = 2;
System.debug(typeof var4); //number
var var4 = new Array(1, 2, 3);
System.debug(typeof var4); //object
System.debug(typeof []); //object
System.debug(typeof function () {}); //function
System.debug(typeof /regex/); //object
System.debug(typeof new Date()); //object
System.debug(typeof null); //object
System.debug(typeof undefinedVarible); //undefined

Using new operator

In this example, typeof operator is showing different results when used with new operator for class VC:CustomizationSpecManager. That’s because the new operator is used for creating a user-defined object type instance of one of the built-in object types that has a constructor function. So basically it calls the constructor function of that object type, hence typeof prints function. However, something to note here is that when new operator is used with primitive object type Number, typeof recognizes that as an object.

var num1 = 2;
System.debug(typeof num1); //number

var num2 = Number("123");;
System.debug(typeof (1 + num2)); //number

var num3 = new Number("123");;
System.debug(typeof (num3)); //object

var num4 = new Number("123");;
System.debug(typeof (1 + num4)); //number

use of Parenthesis

// Parentheses can be used for determining the data type of expressions.
const someData = 99;
typeof someData + "cloudblogger"; // "number cloudblogger"
typeof (someData + " cloudblogger"); // "string"

System.getObjectType()

The System.getObjectType() method returns the VS-O ‘type’ for the given operand. This method is more advanced than typeof and is able to detect more complex yet intrinsic object types like Date, Array etc. But, it still cannot figure out the plugin object types like VC:SDKConnection, etc.

TypeResult
Array"Array"
Number"number"
String"string"
vRO Plugin Object Types (with or without new)"null"
Date"Date"
Composite Types"Properties"
SecureString"string"
undefined VariableReference Error

Code Examples


var var1 = new VcCustomizationSpec(); 
System.debug(System.getObjectType(var1)); //null

var var2 = new Object();
System.debug(System.getObjectType(var2)); //Properties

var var3 = "a";
System.debug(System.getObjectType(var3)); //string

var var4 = 2;
System.debug(System.getObjectType(var4)); //number

var var4 = new Array(1, 2, 3);
System.debug(System.getObjectType(var4)); //Array

System.debug(System.getObjectType([])); //Array

System.debug(System.getObjectType(function () {})); //null

System.debug(System.getObjectType(new Date())); //Date

System.debug(System.getObjectType(undefinedVarible)); //FAIL ReferenceError: "undefinedVarible" is not defined.

System.getObjectClassName()

The System.getObjectClassName() method returns the class name of any vRO scripting object that typeof(obj) returns “object”. This works the best with complex vRO object types and surpasses System.getObjectType() in terms of its capability to identify object types.

TypeResult
Array"Array"
Number"Number"
String"String"
vRO Plugin Object Types (eg: VC:SdkConnection)Class Name (eg: VcSdkConnection)
Date"Date"
Composite Types"Properties"
SecureString"String"
undefined VariableReference Error
null objectsError: Cannot get class name from null object

Code Examples

System.debug(System.getObjectClassName(input));  //String

var var1 = new VcCustomizationSpec(); 
System.debug(System.getObjectClassName(var1)); //VcCustomizationSpec

var var2 = new Object();
System.debug(System.getObjectClassName(var2)); //Object

var var3 = "a";
System.debug(System.getObjectClassName(var3)); //String

var var4 = 2;
System.debug(System.getObjectClassName(var4)); //Double

var var4 = new Array(1, 2, 3);
System.debug(System.getObjectClassName(var4)); //Array

System.debug(System.getObjectClassName([])); //Array

System.debug(System.getObjectClassName(function () {})); //Function

System.debug(System.getObjectClassName(new Date())); //Date

instanceof

The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value. This means that instanceof checks if RHS matches the constructor of a class. That’s why it doesn’t work with primitive types like number, string, etc. However, works with variety of complex types available in vRO.

Syntax

object instanceof constructor

Code Examples

var var1 = new VcCustomizationSpec(); 
System.debug(var1 instanceof VcCustomizationSpec); //true

var var1 = new VcCustomizationSpec(); 
System.debug(var1 instanceof Object); //true

var var2 = new Object();
System.debug(var2 instanceof Object); //true

var var3 = "a";
System.debug(var3 instanceof String); //false

var var3 = new String("a");
System.debug(var3 instanceof String); //true

var var3 = "a";
System.debug(var3 instanceof String); //false

var var4 = 2;
System.debug(var4 instanceof Number); //false

var var4 = new Array(1, 2, 3);
System.debug(var4 instanceof Array); //true

System.debug([] instanceof Array); //true

System.debug(function () {} instanceof Function); //true

System.debug(new Date() instanceof Date); //true

System.debug({} instanceof Object); //true

That’s all in this port. I hope you will have a better understanding on how to check vRO Object types. Let me know in the comment if you have any doubt or question. Feel free to share this article. Thank you.