VCF Automation Blog

from Stefan Schnell

Aria Automation offers a Representational State Transfer (REST) Application Programming Interface (API) to call functions from outside. The Orchestrator offers functions to design, manage and run workflows, actions and policies. Here are two functions in the Action Service, whose call we take a closer look at in this blog post. On the one hand the function to create a new action and on the other hand to run an action. Of particular interest here are the calling conventions for the parameters.

REST API Calling Conventions
for Executing an Action


The existing documentation for defining parameters for the functions to create and run actions is very limited. Normally the same example is always given with string parameters. Further data types are now available as example here. They can be used directly with the POST requests /actions, to create a new action, and /actions/{actionId}/executions, to call an action.

vcf automation api documentation, orchestrator, action service, create and run an action
var parameters = [

  // string{...}
  { "name": "in_name", "type": "string", "value": {
    "string": { "value": "Stefan" }
  } },
  { "name": "in_city", "type": "string", "value": {
    "string": { "value": "Oberirsen" }
  } },

  // number{...}
  { "name": "in_age", "type": "number", "value": {
    "number": { "value": 42 }
  } },

  // boolean{...}
  { "name": "in_flag", "type": "boolean", "value": {
    "boolean": { "value": true }
  } },

  // date{...}
  { "name": "in_date", "type": "date", "value": {
    "date": { "value": 1667206800000 }
  } },

  // properties{...}
  { "name": "in_prop", "type": "properties", "value": {
      "properties": { "property": [
        { "key": "in_number", "value": { "number": { "value": 1234 } } },
        { "key": "in_string", "value": { "string": { "value": "yes" } } },
        { "key": "in_bool",   "value": { "boolean": { "value": false } } }
      ] }
    }
  },

  // Array with Value type elements{...} (here e.g. Array/string)
  { "name": "in_arr", "type": "Array/string", "value": {
      "array": { "elements": [
        { "string": { "value": "apple" } },
        { "string": { "value": "banana" } },
        { "string": { "value": "orange" } }
      ] } 
    }
  },

  // composite{...}
  {"name": "in_composite",
    "type":"CompositeType(str:string,num:number,bool:boolean):test", "value": {
      "composite": 
        { "type": "CompositeType(str:string,num:number,bool:boolean):test",
        "property": [
          { "id": "str", "value": { "string": { "value": "bla" } } },
          { "id": "bool", "value": { "boolean": { "value": true } } },
          { "id": "num", "value": { "number": { "value": 42 } } }
        ]
      }
    }
  },

  // sdk-object{...}
  {"name": "in_sdkobject", "type": "VC:VirtualMachine", "value": {
    "sdk-object": {
      "type": "VC:VirtualMachine",
      "id": "vcsa-01a.corp.vmbeans.com/vm-16003"
    }
  } }

  // attribute-reference{...}
  // encrypted-string{...}
  // mime-attachment{...}
  // regex{...}
  // secure-string{...}

];

The following example shows how an action can be created and executed using the REST API. The POST requests mentioned above are used. The parameters can be used directly as described above.

/**
 * Creates and executes an action via Aria Automation REST API.
 *
 * @function createAndExecuteAction
 *
 * @returns {Properties}
 *
 * @author Stefan Schnell <mail@stefan-schnell.de>
 * @license MIT
 * @version 0.1.0
 *
 * Set com.vmware.scripting.javascript.allow-native-object in the
 * system properties to true.
 *
 * Checked with Aria Automation 8.12.0 and 8.16.0
 */

var _actionActivities = function() {

  this._url = null;
  var fqdn = this.getFQDN();
  if (fqdn !== null) {
    this._url = "https://" + fqdn;
  } else {
    throw new Error("Error at FQDN detection");
  }

  this._httpRestHost = null;
  if (this._url !== null) {
    this._httpRestHost = RESTHostManager.createTransientHostFrom(
      RESTHostManager.createHost("dynamicRequest")
    );
    this._httpRestHost.operationTimeout = 60;
    this._httpRestHost.connectionTimeout = 30;
    this._httpRestHost.hostVerification = false;
    this._httpRestHost.url = this._url;
  } 

  this.bearerToken = null;

};

_actionActivities.prototype = {

  /**
   * Detects the Full Qualified Domain Name (FQDN)
   *
   * @function getFQDN
   *
   * @returns {string} FQDN
   */
  getFQDN : function() {
    var fqdn = "";
    var jvmOpts = java.lang.System.getenv("JVM_OPTS");
    if (jvmOpts !== null) {
      var options = jvmOpts.split(" ");
      options.forEach( function(option) {
        if (option.substring(0, 19) === "-Dvco.app.hostname=") {
          fqdn = option.substring(19, option.length);
        }
      });
    }
    if (fqdn !== "") {
      return fqdn;
    } else {
      return null;
    }
  },

  /**
   * Retrieves Bearer token
   *
   * @function retrieveBearerToken
   *
   * @param {string} username - Name of the user
   * @param {string} password - Password of the user
   */
   retrieveBearerToken : function(username, password) {

    if (this._url === null) {
      return;
    }

    var httpRestHost = this._httpRestHost.clone();

    var jsonLogin = {
      "username": username,
      "password": password
    };
    var login = JSON.stringify(jsonLogin);

    var request = httpRestHost.createRequest(
      "POST",
      "/csp/gateway/am/api/login?access_token",
      login
    );
    request.contentType = "application/json";

    var response = request.execute();
    if (response.statusCode === 200) {
      var oRefreshToken = JSON.parse(response.contentAsString);
      var refreshToken = "{\"refreshToken\":\"" +
        oRefreshToken.refresh_token + "\"}";
      request = httpRestHost.createRequest(
        "POST",
        "/iaas/api/login",
        refreshToken
      );
      request.contentType = "application/json";

      response = request.execute();
      if (response.statusCode === 200) {
        var oBearerToken = JSON.parse(response.contentAsString);
        this.bearerToken = oBearerToken.token;
      } else {
        System.error("Error at retrieving bearer token");
      }
    }

  },

  /**
   * Creates a new action
   *
   * @function createAction
   *
   * @param {string} moduleName - Module name that should contain the action
   * @param {string} actionName - Name of the action to be created
   * @param {string} code - Source code in the action
   * @param {Array.<{name:string, type:string, value:{objectType:{value:Any}}}>}
   *   parameters - Parameters as JSON
   * @param {string} outputType - Type of return value
   * @returns {string} actionId
   */
  createAction : function(
    moduleName,
    actionName,
    code,
    parameters,
    outputType
  ) {

    var _parameters = [];

    if (parameters instanceof Array) {
      _parameters = JSON.parse(JSON.stringify(parameters));
    }

    if (typeof outputType !== "string") {
      outputType = "string";
    }

    var httpRestHost = this._httpRestHost.clone();

    // Deletes value column from parameters array
    if (_parameters.length > 0) {
      _parameters.forEach( function(parameter) {
        delete parameter.value;
      });
    }

    var jsonScript = {
      "name": actionName,
      "module": moduleName,
      "version": "0.1.0",
      "description": "Test",
      "script": code,
      "input-parameters": _parameters,
      "output-type": outputType
    };
    var script = JSON.stringify(jsonScript);

    // API Documentation > Orchestrator > Actions Service
    var request = httpRestHost.createRequest(
      "POST",
      "/vco/api/actions?uniqueName=false",
      script
    );

    request.contentType = "application/json";
    request.setHeader("Accept", "application/json");
    request.setHeader("Authorization", "Bearer " + this.bearerToken);

    var response = request.execute();
    if (response.statusCode === 201) {
      var jsonResponse = JSON.parse(response.contentAsString);
      return jsonResponse.id;
    } else {
      System.error("Error creating action");
      throw new Error("Error creating action");
    }

  },

  /**
   * Runs an action
   *
   * @function executeAction
   *
   * @param {string} actionId
   * @param {Array.<{name:string, type:string, value:{objectType:{value:Any}}}>}
   *   parameters - Parameters as JSON
   * @returns {string} executionId
   */
  executeAction : function(actionId, parameters) {

    if (!Array.isArray(parameters)) {
      parameters = [];
    }

    var httpRestHost = this._httpRestHost.clone();

    var jsonBody = {
      "parameters": parameters,
      "async-execution": false
    };
    var body = JSON.stringify(jsonBody);

    // API Documentation > Orchestrator > Actions Service
    var request = httpRestHost.createRequest(
      "POST",
      "/vco/api/actions/" + actionId + "/executions",
      body
    );
    request.contentType = "application/json";
    request.setHeader("Accept", "application/json");
    request.setHeader("Authorization", "Bearer " + this.bearerToken);

    var response = request.execute();
    if (response.statusCode === 200) {
      var jsonResponse = JSON.parse(response.contentAsString);
      return jsonResponse;
    } else {
      System.log("Error at execution\n" + response.contentAsString);
    }

  },

  /**
   * Retrieves the definition of an action
   * Hint: In this case this call is used to check if the action has
   * been created.
   *
   * @function getAction
   *
   * @param {string} actionId
   * @returns {number}
   */
  getAction : function(actionId) {

    var httpRestHost = this._httpRestHost.clone();

    var request = httpRestHost.createRequest(
      "GET",
      "/vco/api/actions/" + actionId
    );
    request.setHeader("Accept", "application/json");
    request.setHeader("Authorization", "Bearer " + this.bearerToken);

    var response = request.execute();
    return response.statusCode;

  },

  /**
   * Gets the action run logs
   *
   * @function getActionLog
   *
   * @param {string} executionId
   * @returns {string} log
   */
  getActionLog : function(executionId) {

    var httpRestHost = this._httpRestHost.clone();

    var request = httpRestHost.createRequest(
      "GET",
      "/vco/api/actions/" + executionId + "/logs?maxResult=2147483647"
    );
    request.setHeader("Accept", "application/json");
    request.setHeader("Authorization", "Bearer " + this.bearerToken);

    var response = request.execute();
    if (response.statusCode === 200) {
      return response.contentAsString;
    } else {
      System.log("Error at get log");
    }

  }

};

function main(userName, password) {

  if (
    String(userName).trim() !== "" &&
    String(password).trim() !== ""
  ) {

    // Retrieve Bearer token
    var actionActivities = new _actionActivities();
    actionActivities.retrieveBearerToken(userName, password);
    if (actionActivities.bearerToken === null) {
      throw new Error("No Bearer token available");
    }

    // JavaScript code
    var code = "\n" +
    "function main(name, city, vm) {\n" +
    "  System.log(\"Hello \" + name + \" from \" + city +" +
    " \" is working on \" + vm.name);\n" +
    "  return \"Hello \" + name + \" from \" + city +" +
    " \" is working on \" + vm.name;\n" +
    "}\n" +
    "\n" +
    "return main(in_name, in_city, in_sdkobject)\n";

    // Inputs
    var parameters = [
      { "name": "in_name", "type": "string", "value": {
        "string": { "value": "Stefan" }
      } },
      { "name": "in_city", "type": "string", "value": {
        "string": { "value": "Oberirsen" }
      } },
      { "name": "in_sdkobject", "type": "VC:VirtualMachine", "value": {
        "sdk-object": {
          "type": "VC:VirtualMachine",
          "id": "vcsa-01a.corp.vmbeans.com/vm-16003"
        }
      } }
    ];

    // Return type
    var outputType = "string";

    // Create action with random name in temp module
    // Hint: The module temp must exist
    var tempActionName = "x" + System.nextUUID().replace(/-/g, "_");
    var actionId =
      actionActivities.createAction(
        "temp",
        tempActionName,
        code,
        parameters,
        outputType
      );

    // Wait until action exists
    while (actionActivities.getAction(actionId) !== 200) {
      System.sleep(1000);
    }

    // Execute action
    var response = actionActivities.executeAction(
      actionId,
      parameters
    );

    var executionId = response["execution-id"];

    System.sleep(2500);

    // Read action log
    var actionLog = actionActivities.getActionLog(executionId);

    return {
      "returnType": response["type"],
      "returnValue": JSON.stringify(response["value"]),
      "executionId": executionId,
      "actionLog": actionLog
    };

    // Alternatively a generated action can also be executed with eval.
/**
    var strParameters = "(\"Stefan\", \"Cuxhaven\")";
    var result = eval(
      "System.getModule(\"temp\")." + tempActionName + strParameters
    );
    System.log(result);
 */

  } else {
    throw new Error(
      "userName or password argument can not be null"
    );
  }

}

// Main
return main("holadmin@corp.vmbeans.com", "VMware1!");

via rest api created action with random nameVia REST API created action with random name.

result of executing the action created via rest apiResult of executing the action created via REST API