Many organization uses vRO for Host Provisioning. Various hardware vendors provide vRO Scripting APIs via plugins or REST APIs to manage and provision bare-metal servers. While doing so, there is always a possibility that post-provisioning, you would like to access your ESXi host from an account other than root for several reasons like security restrictions, limited access etc. In that case, the best way is to create a fresh new account using vRO with the kind of access mode or lets call it, role that suits the needs. In this post, we will see how to create an ESXi local user account using vRO Scripting API.
Classes & Methods
As shown below, we have used following classes and methods for retrieval of existing accounts, creation, updating & deletion of accounts as well as change access or Role of those accounts.
/**
*
* @version 0.0.0
*
* @param {VC:HostSystem} host
* @param {string} localUserName
* @param {SecureString} localUserPassword
* @param {string} accessMode
* @param {string} localUserDescription
*
* @outputType void
*
*/
function createEsxiLocalUser(host, localUserName, localUserPassword, accessMode, localUserDescription) {
if(!host) throw "host parameter not set";
if(!localUserName || !localUserPassword) throw "Either username or password parameter not set";
if(!localUserDescription) localUserDescription = "***Account created using vRO***";
if(localUserDescription.indexOf(localUserPassword) != -1) throw 'Weak Credentials! Avoid putting password string in description';
// Retrieve all system and custom user accounts
var arrExistingLocalusers = host.configManager.hostAccessManager.retrieveHostAccessControlEntries();
var accountSpecs = new VcHostAccountSpec(localUserName,localUserPassword,localUserDescription);
host.configManager.accountManager.createUser(accountSpecs);
switch(accessMode){
case 'Admin': //Full access rights
host.configManager.hostAccessManager.changeAccessMode(localUserName,false,VcHostAccessMode.accessAdmin);
break;
case 'ReadOnly': //See details of objects, but not make changes
host.configManager.hostAccessManager.changeAccessMode(localUserName,false,VcHostAccessMode.accessReadOnly);
break;
case 'NoAccess': //Used for restricting granted access
host.configManager.hostAccessManager.changeAccessMode(localUserName,false,VcHostAccessMode.accessNoAccess);
break;
default: //No access assigned. Note: Role assigned is accessNone
host.configManager.hostAccessManager.changeAccessMode(localUserName,false,VcHostAccessMode.accessNone);
}
System.warn(" >>> Local user "+localUserName+" created with accessMode "+accessMode+" on host "+host.name);
}
Demo Video
In this demo, we can see how the workflow is utilized to create a local account testuser1 through which we logged in to ESXi and check if it has required permissions.
vRO Package for CRUD operation
I have created a vRO Workflow to create and manage your ESXi local accounts directly from the input form itself. Please find the vRO package that contains the master workflow and associated actions.
vRealize Orchestrator a.k.a. vRO, which is a drag-and-drop automation tool, is quite an old tool developed and released as early as in 2007 by Dunes Technologies, Switzerland. After VMware acquired Dunes, there were 100’s of releases and updates came year after year. Lots and lots of improvements made over time in the UI, backend technologies, security, multi-language support, etc. However, one thing that remains the same is its JavaScript Engine. vRO uses Mozilla Rhino Engine 1.7R4 which was released in 2012.
In this post, My goal is to provide some insights on this Rhino Engine as it is almost extinct from the internet. However, I am still behind the JavaScript Engine which provides IntelliSense support to vRO 8.x. As you might have noticed and probably be wondering how CTRL+SPACE shows options only available to recent versions of JavaScript. I guess it’s for Node.js runtime.
What is Rhino Engine?
Rhino Engine converts JavaScript scripts into classes. It is intended to be used in desktop or server-side applications, hence there is no built-in support for the Web browser objects that are commonly associated with JavaScript which makes it very suitable for vRO. Rhino works in both compiled and interpreted mode. Rhino Engine got its name from the animal on the cover of the O’Reilly book about JavaScript published many years back.
The Rhino project was started at Netscape in the autumn of 1997. At the time, Netscape was planning to produce a version of Navigator written entirely in Java and so it needed an implementation of JavaScript written in Java. When Netscape stopped work on “Javagator,” as it was called, somehow Rhino escaped the axe (rumor had it that the executives “forgot” it existed). For a time, a couple of major companies (including Sun) licensed Rhino for use in their products and paid Netscape to do so, allowing work on Rhino to continue. Now Rhino is part of Mozilla’s open-source repository.
Released 10 years ago
Released in 2012-06-18, Rhino 1.7R4 is almost prehistoric for today’s standards and that’s been always a point of discussion in the vRO Community.
Release Notes of 1.7R4
Compatibility with JavaScript features
While trying to look deep into its compatibility matrix with ES, I found this Kangax’s Compat-table which gives an excellent and detailed view of all the possible features that Rhino 1.7R4 supports. Click the link to know more.
Rhino Compatibility Matrix with JavaScript
ECMAScript 5.1 Specifications
This document will give you a very in-depth knowledge of the ECMAScript 5.1 that vRO leverages to understand the language better. Learn more at https://262.ecma-international.org/5.1.
You can download this document and read about all the fine details by yourself.
Currently, on GitHub page of Mozilla, version 1.7R4 is not available. However, you may find some very old scripts that were written at the time of 1.7R4 as I can validate using web-achieve. You can explore their GitHub repo here.
When writing scripts for workflows, you must consider the following limitations of the Mozilla Rhino implementation in Orchestrator.
When a workflow runs, the objects that pass from one workflow element to another are not JavaScript objects. What is passed from one element to the next is the serialization of a Java object that has a JavaScript image. As a consequence, you cannot use the whole JavaScript language, but only the classes that are present in the API Explorer. You cannot pass function objects from one workflow element to another.
Orchestrator runs the code in scriptable task elements in a context that is not the Rhino root context. Orchestrator transparently wraps scriptable task elements and actions into JavaScript functions, which it then runs. A scriptable task element that contains System.log(this); does not display the global object this in the same way as a standard Rhino implementation does.
You can only call actions that return nonserializable objects from scripting, and not from workflows. To call an action that returns a nonserializable object, you must write a scriptable task element that calls the action by using the System.getModuleModuleName.action() method.
Workflow validation does not check whether a workflow attribute type is different from an input type of an action or subworkflow. If you change the type of a workflow input parameter, for example from VIM3:VirtualMachine to VC:VirtualMachine, but you do not update any scriptable tasks or actions that use the original input type, the workflow validates but does not run.
Access to additional Java Classes
By default, vRealize Orchestrator restricts JavaScript access to a limited set of Java classes. If you require JavaScript access to a wider range of Java classes, you must set an vRealize Orchestrator system property.
Allowing the JavaScript engine full access to the Java virtual machine (JVM) presents potential security issues. Malformed or malicious scripts might have access to all the system components to which the user who runs the vRealize Orchestrator server has access. Therefore, by default the vRealize Orchestrator JavaScript engine can access only the classes in the java.util.* package.
If you require JavaScript access to classes outside of the java.util.* package, you can list in a configuration file the Java packages to which to allow JavaScript access. You then set the com.vmware.scripting.rhino-class-shutter-file system property to point to this file.
Procedure
Create a text configuration file to store the list of Java packages to which to allow JavaScript access.For example, to allow JavaScript access to all the classes in the java.net package and to the java.lang.Object class, you add the following content to the file.java.net.* java.lang.Object
Enter a name for the configuration file.
Save the configuration file in a subdirectory of /data/vco/usr/lib/vco. The configuration file cannot be saved under another directory.
Log in to Control Center as root.
Click System Properties.
Click New.
In the Key text box, enter com.vmware.scripting.rhino-class-shutter-file.
In the Value text box, enter vco/usr/lib/vco/your_configuration_file_subdirectory.
In the Description text box, enter a description for the system property.
Click Add.
Click Save changes from the pop-up menu.A message indicates that you have saved successfully.
Wait for the vRealize Orchestrator server to restart.
See an implementation example of accessing external Java classes by BlueCat here. Here, the code implements new java.lang.Long(0)
.
.
.
var testConfig = BCNProteusAPI.createAPIEntity(new java.lang.Long(0),configName,"","Configuration" );
var args = new Array( new java.lang.Long(0), testConfig );
configId = new java.lang.Long( BCNProteusAPI.call( profileName,"addEntity",args ));
System.log( "New configuration was created, id=" + configId );
var addTFTPGroupArgs = new Array( configId, "tftpGroupName1", "" );
var tftpGroupId = new java.lang.Long( BCNProteusAPI.call(profileName,"addTFTPGroup", addTFTPGroupArgs ) );
System.log( "New TFTP Group was created, id=" + tftpGroupId );
.
.
.
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.
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.
Type
Result
Array
"Array"
Number
"number"
String
"string"
vRO Plugin Object Types (with or without new)
"null"
Date
"Date"
Composite Types
"Properties"
SecureString
"string"
undefined Variable
Reference 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.
Type
Result
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 Variable
Reference Error
null objects
Error: 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.
vRO JS code is generally plain and basic just enough to get the job done. But I was wondering, how to fancy it? So, I picked some slightly modern JS code (ES5.1+) and tried running it on my vRO 8.3. I found some interesting things which I would like to share in this article.
Snippets
Here are some JS concepts that you can use writing vRO JavaScript code to make it more compelling and beautiful.
External Modules
To utilize modern features, you can use modules like lodash.js for features such as map or filter etc. Other popular module is moment.js for complex Date and Time handling in vRO.
var _ = System.getModule("fr.numaneo.library").lodashLibrary();
var myarr = [1,2,3];
var myarr2 = [4,5,6];
var concatarr = _.concat(myarr, myarr2);
System.log(concatarr); // [1,2,3,4,5,6];
Find more information on how to leverage Lodash.js in vRO here.
First-class Functions
First-class functions are functions that are treated like any other variable. For example, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
// we send in the function as an argument to be
// executed from inside the calling function
function performOperation(a, b, cb) {
var c = a + b;
cb(c);
}
performOperation(2, 3, function(result) {
// prints out 5
System.log("The result of the operation is " + result);
})
Ways to add properties to Objects
There are 4 ways to add a property to an object in vRO.
// supported since ES3
// the dot notation
instance.key = "A key's value";
// the square brackets notation
instance["key"] = "A key's value";
// supported since ES5
// setting a single property using Object.defineProperty
Object.defineProperty(instance, "key", {
value: "A key's value",
writable: true,
enumerable: true,
configurable: true
});
// setting multiple properties using Object.defineProperties
Object.defineProperties(instance, {
"firstKey": {
value: "First key's value",
writable: true
},
"secondKey": {
value: "Second key's value",
writable: false
}
});
Custom Class
You can create your own custom classes in vRO using the function keyword and extend that function’s prototype.
// we define a constructor for Person objects
function Person(name, age, isDeveloper) {
this.name = name;
this.age = age;
this.isDeveloper = isDeveloper || false;
}
// we extend the function's prototype
Person.prototype.writesCode = function() {
System.log(this.isDeveloper? "This person does write code" : "This person does not write code");
}
// creates a Person instance with properties name: Bob, age: 38, isDeveloper: true and a method writesCode
var person1 = new Person("Bob", 38, true);
// creates a Person instance with properties name: Alice, age: 32, isDeveloper: false and a method writesCode
var person2 = new Person("Alice", 32);
// prints out: This person does write code
person1.writesCode();
// prints out: this person does not write code
person2.writesCode();
Both instances of the Person constructor can access a shared instance of the writesCode() method.
Private variable
A private variable is only visible to the current class. It is not accessible in the global scope or to any of its subclasses. For example, we can do this in Java (and most other programming languages) by using the private keyword when we declare a variable
// we used an immediately invoked function expression
// to create a private variable, counter
var counterIncrementer = (function() {
var counter = 0;
return function() {
return ++counter;
};
})();
// prints out 1
System.log(counterIncrementer());
// prints out 2
System.log(counterIncrementer());
// prints out 3
System.log(counterIncrementer());
Label
Labels can be used with break or continue statements. It is prefixing a statement with an identifier which you can refer to.
var str = '';
loop1:
for (var i = 0; i < 5; i++) {
if (i === 1) {
continue loop1;
}
str = str + i;
}
System.log(str);
// expected output: "0234"
with keyword
The with statement extends the scope chain for a statement. Check the example for better understanding.
var box = {"dimensions": {"width": 2, "height": 3, "length": 4}};
with(box.dimensions){
var volume = width * height * length;
}
System.log(volume); //24
// vs
var box = {"dimensions": {"width": 2, "height": 3, "length": 4}};
var boxDimensions = box.dimensions;
var volume2 = boxDimensions.width * boxDimensions.height * boxDimensions.length;
System.log(volume2); //24
Function binding
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
const module = {
x: 42,
getX: function() {
return this.x;
}
};
const unboundGetX = module.getX;
System.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
const boundGetX = unboundGetX.bind(module);
System.log(boundGetX());
// expected output: 42
Prototype Chaining
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
},
};
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype (we will explain
// what that means later).
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null
System.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
System.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
System.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
System.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and
// there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
This blogpost is simply about giving a consolidated view on all the official guides that VMware provides for vRealize Automation and vRealize Orchestrator. These guides can help Automation Engineers & Developers, Solution Architects, vRealize Admins, etc and can be used as a reference for developing vRO Code, vRA Templates and various other tasks. You can download them from the provided links for offline access.
Do you use a lot of Polyglot scripts in vRO? Are you tired of creating bundles every time you work on a Python, Node.js or PowerShell script which uses modules and libraries which are not provided out-of-the-box by vRealize Orchestrator? Probably vRO guys at VMware heard your prayers this time.
vRO 8.8 onwards, you can now add modules and libraries directly as a dependency in your vRO actions and scriptable tasks. How cool is that!
As we know, in earlier versions, you could only add dependencies by adding them as a ZIP package which is not only a tiring additional steps, but also, editing and understanding those scripts becomes a real nightmare. But not any more.
In this post, we will see a detailed procedure on how to setup an script environment in your vRO (>8.8). I am going with Node.js but almost similar process can be followed for other languages as well. We will use an advanced date & time library called MomentJS available at https://momentjs.com/ but you can use any other module or library of your choice for that matter.
Note Similarly to other vRealize Orchestrator objects such as workflows and actions, environments can be exported to other vRealize Orchestrator deployments as part of a package, which means they are also a part of version control.
Prerequisite
vRealize Orchestrator 8.8 or greater
Procedure
Log in to the vRealize Orchestrator Client.
Navigate to Assets > Environments, and click New Environment.
Under the General tab, enter a name for your environment.
(Optional) Enter a description, version number, tags, and group permissions for the action.
Under the Definition tab, Click on Add button under Dependencies.
You can also change the Memory Limit to 128, 512, 1024 etc. depending on the number and size of packages that you will be using. In my personal experience, using PowerShell modules will require more than default.
Provide the package name and version that you want to install. For Node.js, passing latest will get you the most recent package.
Tip The package name is same as what you would use with package managers like npm while installing that package.
Once you click Create button, you should see Environment successfully created.
Under the Download Logs tab, check if the libraries are already installed.
Here, I have installed two modules, moment and moment-timezone as you can see from the logs.
In the Environment variables, you can provide a variable that you want to use as a part of this environment.
Create an action or a workflow with scriptable item, Select Runtime Environment as the one that you have created. I have selected moment.
Play with your script. Don’t forget to call the modules and environment variables.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Did you know var π = Math.PI; is syntactically valid JavaScript code in vRealize Orchestrator? If the answer is “No” then probably this article maybe of some interest to you.
Let’s see what all Unicode glyphs are allowed in as JavaScript variable names, or identifiers as the ECMAScript specification calls them. This is more of a fun activity but can give you some insight on selecting the best (or may I say worst LOL! – will talk about this later) variable name in your vRO scripts.
An Identifier is an IdentifierName that is not a ReservedWord.
The spec describes four groups of reserved words: keywords, future reserved words, null literals and boolean literals.
Keywords are tokens that have special meaning in JavaScript:
break, case, catch, continue, debugger, default, delete, do, else, finally, for, function, if, in, instanceof, new, return, switch, this, throw, try, typeof, var, void, while, and with.
Future reserved words are tokens that may become keywords in a future revision of ECMAScript: class, const, enum, export, extends, import, and super. Some future reserved words only apply in strict mode: implements, interface, let, package, private, protected, public, static, and yield.
The null literal is, simply, null.
There are two boolean literals: true and false. None of the above are allowed as variable names.
Non-reserved words that act like reserved words
The NaN, Infinity, and undefined properties of the global object are immutable or read-only properties in ES5.1. So even though var NaN = 42; in the global scope wouldn’t throw an error, it wouldn’t actually do anything. To avoid confusion, I’d suggest avoiding the use of these variable names.
// In the global scope:
var NaN = 42;
console.log(NaN); // NaN//
…but elsewhere:
(function() {
var NaN = 42;
console.log(NaN); // 42
}());
In strict mode, eval and arguments are disallowed as variable names too. (They kind of act like keywords in that case.) The old ES3 spec defines some reserved words that aren’t reserved words in ES5 anymore:
It’s probably a good idea to avoid these as well, for optimal backwards compatibility.
Valid identifier names
As mentioned before, the spec differentiates between identifier names and identifiers. Identifiers form a subset of identifier names, since identifiers have the extra restriction that no reserved words are allowed. For example, var is a valid identifier name, but it’s an invalid identifier.
Unicode escape sequences are also permitted in an IdentifierName, where they contribute a single character. […] A UnicodeEscapeSequence cannot be used to put a character into an IdentifierName that would otherwise be illegal.
This means that you can use var \u0061 and var a interchangeably. Similarly, since var 1 is invalid, so is var \u0031.
Two IdentifierNames that are canonically equivalent according to the Unicode standard are not equal unless they are represented by the exact same sequence of code units.
The following are all examples of valid JavaScript variable names that will work in vRO
System.log(λ);
// How convenient!
var π = Math.PI;
System.log("Value of PI: "+π);
// Sometimes, you just have to use the Bad Parts of JavaScript:
var ಠ_ಠ = "Angry";
System.log(ಠ_ಠ);
// Code, Y U NO WORK?!
var ლ_ಠ益ಠ_ლ = 42;
System.log(ლ_ಠ益ಠ_ლ );
// Obfuscate boring variable names for great justice
var \u006C\u006F\u006C\u0077\u0061\u0074 = 'heh';
// Did you know about the [.] syntax?
var ᱹ = 1;
System.log([1, 2, 3][ᱹ] === 2);
// While perfectly valid, this doesn’t work in most browsers:
var foo\u200Cbar = 42;
// This is *not* a bitwise left shift (`<<`):
var 〱〱 = 2;
System.log(〱〱 << 〱〱); // This is though
// Fun with Roman numerals
var Ⅳ = 4;
var Ⅴ = 5;
System.log(Ⅳ + Ⅴ); // 9
// OK, it's gone too far now
var Hͫ̆̒̐ͣ̊̄ͯ͗͏̵̗̻̰̠̬͝ͅE̴̷̬͎̱̘͇͍̾ͦ͊͒͊̓̓̐_̫̠̱̩̭̤͈̑̎̋ͮͩ̒͑̾͋͘Ç̳͕̯̭̱̲̣̠̜͋̍O̴̦̗̯̹̼ͭ̐ͨ̊̈͘͠M̶̝̠̭̭̤̻͓͑̓̊ͣͤ̎͟͠E̢̞̮̹͍̞̳̣ͣͪ͐̈T̡̯̳̭̜̠͕͌̈́̽̿ͤ̿̅̑Ḧ̱̱̺̰̳̹̘̰́̏ͪ̂̽͂̀͠ = 'Zalgo';
System.log("Hͫ̆̒̐ͣ̊̄ͯ͗͏̵̗̻̰̠̬͝ͅE̴̷̬͎̱̘͇͍̾ͦ͊͒͊̓̓̐_̫̠̱̩̭̤͈̑̎̋ͮͩ̒͑̾͋͘Ç̳͕̯̭̱̲̣̠̜͋̍O̴̦̗̯̹̼ͭ̐ͨ̊̈͘͠M̶̝̠̭̭̤̻͓͑̓̊ͣͤ̎͟͠E̢̞̮̹͍̞̳̣ͣͪ͐̈T̡̯̳̭̜̠͕͌̈́̽̿ͤ̿̅̑Ḧ̱̱̺̰̳̹̘̰́̏ͪ̂̽͂̀͠ "+ Hͫ̆̒̐ͣ̊̄ͯ͗͏̵̗̻̰̠̬͝ͅE̴̷̬͎̱̘͇͍̾ͦ͊͒͊̓̓̐_̫̠̱̩̭̤͈̑̎̋ͮͩ̒͑̾͋͘Ç̳͕̯̭̱̲̣̠̜͋̍O̴̦̗̯̹̼ͭ̐ͨ̊̈͘͠M̶̝̠̭̭̤̻͓͑̓̊ͣͤ̎͟͠E̢̞̮̹͍̞̳̣ͣͪ͐̈T̡̯̳̭̜̠͕͌̈́̽̿ͤ̿̅̑Ḧ̱̱̺̰̳̹̘̰́̏ͪ̂̽͂̀͠);
Can you use them in between scripts?
Interestingly, we can also use this variable names not only in the scripts but also, out of them i.e. as inputs, outputs and attributes. See this action’s input:
As we see, vRO raises an alarm here saying that Parameter name can contain only letters, numbers, and the symbols "_" and "$". Parameter name cannot start or end with ".".
However, when I run this action, it runs perfectly normal.
I think it is quite amusing. If used dexterously, it could give another dimension to your code. 👽
JavaScript variable name validator
Even if you’d learn these rules by heart, it would be virtually impossible to memorize every character in the different Unicode categories that are allowed. If you were to summarize all these rules in a single ASCII-only regular expression for JavaScript, it would be 11,236 characters long.
For that reason, you can go to mothereff.in/js-variables, a webtool and check if a given string is a valid variable name in JavaScript or not.
That’s it in this post. If you want to know more about the JavaScript implementation in vRO, check out my other posts.
Manipulation of Date & Time is overwhelmingly difficult. Mostly because there are so many formats, standards and handling techniques. Being a vRO programmer, you can’t run away from this date manipulation, be it for Reports creation, REST Calls, Data fetching or Interacting with other servers. Ask any programmer about their experience handling dates and time zones and they will probably share some war stories. Handling date and time fields is certainly not rocket science but can often be tedious and error-prone.
In this article, we will explore some of the key concepts that are necessary for manipulating date and time values correctly, formats that are convenient for storing DateTime values and transferring them over APIs, and more. You can consider it as a complete guide to understand the DateTime concepts in vRealize Orchestrator using the system-provided classes.
If you are sending and receiving data through a REST API, you will eventually need to convert the date to a string and vice versa because JSON doesn’t have a native data structure to represent DateTime.
Advertisements
IntrinsicDate() Class
vRO provides a Date() class to satisfy almost all the date and time hunger. It has a constructor that takes variety of inputs to start off. There are various methods and functions take allows quick shape-shifting of date and time. However, It may lack some quirky features that you may look out for. Let’s start off by taking a look at this Date class in a more comprehensive way.
It starts here. You call the constructor and you will get the current date and time. BOOM!🚀
const currentDate = new Date();
If you don’t pass anything to the Date constructor, the date object returned contains the current date and time.
You can then format it to extract only the date part as follows:
const currentDate = new Date();
const currentDayOfMonth = currentDate.getDate();
const currentMonth = currentDate.getMonth(); // Be careful! January is 0, not 1
const currentYear = currentDate.getFullYear();
const dateString = currentDayOfMonth + "-" + (currentMonth + 1) + "-" + currentYear;
System.log(dateString)
//Output = "19-03-2022"
Caution It’s not getYear(), but getFullYear()
If you instead want to get the current time stamp, you can create a new Date object and use the getTime() method.
Tip In JavaScript, a time stamp is the number of milliseconds that have passed since January 1, 1970, which is also known as Unix/ECMAScript Epoch format.
You can also take an input of Type Date in workflow or action and handle it in your scripts just like any other Date object.
Parsing a Date
Converting a string to a JavaScript date object is done in different ways.
The Date object’s constructor accepts a wide variety of date formats:
const date1 = new Date("Wed, 27 July 2022 13:30:00");
const date2 = new Date("Wed, 27 July 2022 07:45:00 UTC");
const date3 = new Date("27 July 2022 13:30:00 UTC+05:30");
or we can also use Date.parse(), that will return timestamp as string
var ms = Date.parse("27 July 2022 13:30:00 UTC+05:30");
System.log(ms); // 1469605500000 (timestamp)
Note that you do not need to include the day of week because JS can determine the day of the week for any date.
You can also pass in the year, month, day, hours, minutes, and seconds as separate arguments:
const date = new Date(2022, 6, 27, 13, 30, 0);
or you can even pass the Unix Epoch number directly:
const date = new Date(1647678776796);
that means to get the zeroth timestamp i.e. Jan 1st of 1970 UTC+0, pass 0 as a parameter to Date()
const date = new Date(0);
or you can also use System.getDateFromFormat() to convert a string to Date object. Check more here.
d = System.getDateFromFormat("2019-01-01T01:45:00.100", "yyyy-MM-dd'T'HH:mm:ss.SSS");
System.log(d);
Advertisements
Working withISO 8601 Format (YYYY-MM-DDTHH:mm:ss.SSSZ)
Of course, you can always use this specific ISO date format:
const date = new Date("2022-07-27T07:45:00.000Z"); // Fri Sep 02 2022 07:45:00 GMT-0000 (GMT)
Get Current Date in ISO complete format
Many a times, we need Dates in a complete ISO format [YYYY-MM-DDTHH:mm:ss.SSSZ], for making REST calls etc. We can use the toISOString() or toJSON() methods of the Date object to convert the local DateTime to UTC.
Caution Not all variations of ISO 8601 format are supported by Date() constructor in vRO.
const date = new Date("2022-07-27T07:45:00Z"); // Invalid Date
If you are sure of the format you want to use, it is best to extract individual bits using the JavaScript functions we covered above and create a string yourself.
var currentDate = new Date();
var date = currentDate.getDate();
var month = currentDate.getMonth();
var year = currentDate.getFullYear();
We can get the date in MM/DD/YYYY format as
var monthDateYear = (month+1) + "/" + date + "/" + year;
The problem with this solution is that it can give an inconsistent length to the dates because some months and days of the month are single-digit and others double-digit. This can be problematic, for example, if you are displaying the date in a table column, because the dates don’t line up.
We can address this by using a “pad” function that adds a leading 0.
function pad(n) {
return n<10 ? '0'+n : n;
}
Now, we get the correct date in MM/DD/YYYY format using:
By now, you might understand how to get bits of information out of dates and how to pad them. Now, let’s create an ISO format from scratch like I have done here (contains +00:00 instead of Z as per my requirement).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Get the number of seconds since the Unix/ECMAScript Epoch
var seconds = Math.floor(Date.now() / 1000);
Working with past and future dates
The best way to work and calculate present and future dates is by using Unix Epoch format which is conveniently the number of milliseconds after midnight January 1, 1970 till the given date expressed as a string which is IETF format. Let’s see few examples.
Important It should be noted that the maximum Date is not of the same value as the maximum safe integer (Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991). Instead, it is defined in ECMA-262 that a maximum of ±100,000,000 (one hundred million) days relative to January 1, 1970 UTC (that is, April 20, 271821 BCE ~ September 13, 275760 CE) can be represented by the standard Date object (equivalent to ±8,640,000,000,000,000 milliseconds).
Get current time in milliseconds
// vRO System method
System.getCurrentTime() //1647861957381
//or
//Javascript method
Date.now() //1647861957381
//or
var date = new Date(); //allows any Date to be used
System.log(date.valueOf()); //1647861957381
Lets say you want to fetch the date 4 days later relative to today, you can convert the today’s date in Unix Epoch format and add 4 x 24 x 60 x 60 x 1000 milliseconds to it and you will get a date exactly 4 days ahead with same time of the day, that because you have not changed enough milliseconds to modify the time.
var date = new Date(); //Thu Mar 21 2022 11:42:06 GMT-0000 (GMT)
System.log(date.valueOf());
var frameOfTime = date.valueOf() + (4*24*60*60*1000);
var date = new Date(frameOfTime); //Thu Mar 25 2022 11:42:06 GMT-0000 (GMT)
Now, let’s say you want to go back in time 4 hours back, You will subtract 4 x 60 x 60 x 1000 milliseconds.
var date = new Date(); //Thu Mar 21 2022 11:42:06 GMT-0000 (GMT)
var frameOfTime = date.valueOf() - (4*60*60*1000);
var date = new Date(frameOfTime); //Thu Mar 25 2022 07:42:06 GMT-0000 (GMT)
Advertisements
Comparing Dates
First, we need to create date objects. Fortunately, <, >, <=, and >= all work. So comparing July 19, 2014 and July 18, 2014 is as easy as:
const date1 = new Date("July 19, 2022");
const date2 = new Date("July 28, 2022");
if(date1 > date2) {
System.log("First date is more recent");
} else {
System.log("Second date is more recent");
}
Checking for equality is trickier, since two date objects representing the same date are still two different date objects and will not be equal. Comparing date strings is a bad idea because, for example, “July 20, 2022” and “20 July 2022” represent the same date but have different string representations. The snippet below illustrates the first point:
This particular case can be fixed by comparing the integer equivalents of the dates (their time stamps) as follows:
date1.getTime() == date2.getTime();
Moreover, vRO is not very good with timezones. So, the best is that we should ignore the user’s time zone and use UTC while creating the date object. There are two ways to do it:
Create an ISO formatted date string from the user input date and use it to create a Date object. Using a valid ISO date format to create a Date object while making the intent of UTC vs local very clear.
const userEnteredDate = "12/20/1989";
const parts = userEnteredDate.split("/");
const userEnteredDateISO = parts[2] + "-" + parts[0] + "-" + parts[1];
const userEnteredDateObj = new Date(userEnteredDateISO + "T00:00:00.000Z");
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDateObj.getTime() == dateFromAPI.getTime(); // true
This also works if you don’t specify the time since that will default to midnight (i.e., 00:00:00Z):
const userEnteredDate = new Date("1989-12-20");
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true
Remember: If the date constructor is passed a string in correct ISO date format of YYYY-MM-DD, it assumes UTC automatically.
JavaScript provides a neat Date.UTC() function that you can use to get the UTC time stamp of a date. We extract the components from the date and pass them to the function.
const userEnteredDate = new Date("12/20/1989");
const userEnteredDateTimeStamp = Date.UTC(userEnteredDate.getFullYear(), userEnteredDate.getMonth(), userEnteredDate.getDate(), 0, 0, 0);
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDateTimeStamp == dateFromAPI.getTime();
System.log(result); //true
Finding the Difference Between Two Dates
A common scenario you will come across is to find the difference between two dates.
We discuss two use cases:
FINDING THE NUMBER OF DAYS BETWEEN TWO DATES
Convert both dates to UTC time stamp, find the difference in milliseconds and find the equivalent days.
const dateFromAPI = "2022-02-10T00:00:00.000Z";
const now = new Date();
const datefromAPITimeStamp = (new Date(dateFromAPI)).getTime();
const nowTimeStamp = now.getTime();
const microSecondsDiff = Math.abs(datefromAPITimeStamp - nowTimeStamp);
// Math.round is used instead of Math.floor to account for certain DST cases
// Number of milliseconds per day =
// 24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second
const daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60 * 24));
System.log(daysDiff); //2231
FINDING USER’S AGE FROM THEIR DATE OF BIRTH
Note: We have a non-standard format. Read the API doc to determine if this means 12 Oct or 10 Dec. Change to ISO format accordingly.
You can use these logics to get more purposeful result. You can calculate the execution time of a function you just created and may optimize it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
vRO does provide various methods to represent date and time in various formats out-of-the-box. Let’s have a look on their output.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters