VCF Automation Blog

from Stefan Schnell

This post describes a way how to get detailed information about all workflows. For this an action is used that collects all information and saves it in XML format.

Get Detailed Information about all Workflows


First we take a look what information are provided and where it can be found in the UI of VCF Automation and in the XML. Here a look at the different tabs of a Workflow and the corresponding XML sections.

General Tab and XML Mapping

In the General tab we can find information about the name and the unique ID. Also we have a description and a version here. In XML we have the folder too.

vcf automation orchestrator workflow general tab
<workflow folder="Library/vRealize Automation 8.x and Cloud Services/Sample Rest Operations" name="Put operation">
  <id>4fbef6d4-bf4f-4907-8e33-b446d5084aaf</id>
  <description>Generic rest client support for HTTP PUT operation.</description>
  <version>0.0.0</version>


Variables Tab and XML Mapping

In the Variables tab we can find the attributes of the workflow, its names, types, descriptions and value.

vcf automation orchestrator workflow variables tab
  <variables>
    <variable>
      <name>statusCodeAttribute</name>
      <type>number</type>
      <description></description>
    </variable>
    <variable>
      <name>errorCode</name>
      <type>string</type>
      <description></description>
    </variable>
  </variables>

Inputs/Outputs Tab and XML Mapping

In the Inputs/Outputs tab we can find the parameters of the workflow, its names, types and descriptions.

vcf automation orchestrator workflow inputs/outputs tab
  <inputs>
    <input>
      <name>pathUri</name>
      <type>string</type>
      <description>Request path uri</description>
    </input>
    ...
  </inputs>
  <outputs>
    <output>
      <name>headers</name>
      <type>Array/string</type>
      <description>Response headers</description>
    </output>
    ...
  </outputs>

Schema Tab and XML Mapping

In the Schema tab we can find workflow elements, which represents the procedure flow, with the details of each element.

vcf automation orchestrator workflow schema tab
vcf automation orchestrator workflow schema tab
  <numberOfItems>4</numberOfItems>
  <firstItem>Rest client - PUT operation</firstItem>
  <schema>
    <item>
      <name>Rest client - PUT operation</name>
      <description>Simple task with custom script capability.</description>
      <type>Task</type>
      <nextItem>Check status code</nextItem>
      <isActionCall>false</isActionCall>
      <isStartWorkflowCall>false</isStartWorkflowCall>
      <linkedWorkflow></linkedWorkflow>
      <usedActions>
      </usedActions>
      <script>
...
      </script>
    </item>
    <item>
      <name>End</name>
      <description></description>
      <type>End</type>
    </item>
    <item>
      <name>Check status code</name>
      <description>Custom decision based on a custom script.</description>
      <type>CustomCondition</type>
      <nextItem>End</nextItem>
      <nextItemTrue>End</nextItemTrue>
      <nextItemFalse>End</nextItemFalse>
      <script>
if ( statusCodeAttribute &gt;= 400 ) {
	throw &quot;HTTPError: status code: &quot; + statusCodeAttribute;
} else {
	return true;
}
      </script>
    </item>
    <item>
      <name>End</name>
      <description></description>
      <type>End</type>
    </item>
  </schema>
</workflow>

Action

To get all these information an action which detects all workflows is used, then the detailed information are collected in a loop, with the help of the Workflow object and its methods, and some variants of it. This information are stored in the XML structure presented above.

/**
 * This action creates an XML file with all details of all existing
 * workflows and its script code.
 *
 * @name getWorkflows
 * @param {string} in_folderFilter - Search for substring in folder, optional
 * @param {string} in_nameFilter - Search for substring in name, optional
 * @author Stefan Schnell
 * @version 1.3.3
 *
 * Checked with release 8.14.1
 */

var _getWorkflowsNS = {

  /**
   * _escapeXML
   *
   * Escapes characters in a string, which could be misinterpreted as
   * markup in XML.
   *
   * @name escapeXML
   * @param {string} strXML - XML which characters to convert
   * @returns {string} Converted XML
   */
  _escapeXML : function(strXML) {
    if (strXML) {
      return strXML.replace(/&/g, "&amp;")
                   .replace(/</g, "&lt;")
                   .replace(/>/g, "&gt;")
                   .replace(/"/g, "&quot;")
                   .replace(/'/g, "&apos;");
/*
                   .replace(/ä/g, "ae;")
                   .replace(/Ä/g, "Ae;")
                   .replace(/ö/g, "oe;")
                   .replace(/Ö/g, "Oe;")
                   .replace(/ü/g, "ue;")
                   .replace(/Ü/g, "Ue;")
                   .replace(/ß/g, "ss;");
*/
    }
  },

  // Main --------------------------------------------------------------
  Main : function() {

    try {

      var output = ("<?xml version=\"1.0\"?>");
      output += ("<workflows>");

      // Workflows
      var workflows =
        System.getModule("com.vmware.library.workflow").getAllWorkflows();
      /* getAllWorkflows
      var workflows = new Array();
      var categories = Server.getAllworkflowCategories();
      for (i in categories) {
        getworkflowsOfCategory(categories[i]);
      }
      return workflows;

      function getworkflowsOfCategory(cat) {
        workflows = workflows.concat(cat.workflows);
        var cats = cat.subCategories;
        if (cats != null) {
          for (index in cats) {
            if (cats[index] != null)
              getworkflowsOfCategory(cats[index]);
          }
        }
      }
      */

      // Search for substring in folder
      if (in_folderFilter) {
        // Reverse search in workflows array to ...
        for (var i = workflows.length - 1; i >= 0; i--) {
          if (
            workflows[i].workflowCategory.path.indexOf(in_folderFilter) === -1
          ) {
            // delete element if in_folderFilter not found in folder name
            workflows.splice(i, 1);
          }
        }
      }

      // Search for substring in workflow name
      if (in_nameFilter) {
        // Reverse search in workflows array to ...
        for (var i = workflows.length - 1; i >= 0; i--) {
          if (workflows[i].name.indexOf(in_nameFilter) === -1) {
            // delete element if in_nameFilter not found in workflow name
            workflows.splice(i, 1);
          }
        }
      }

      workflows.forEach( function(workflow) {

        // Workflow Summary
        output += "<workflow folder=\"" + workflow.workflowCategory.path +
          "\" name=\"" + _getWorkflowsNS._escapeXML(workflow.name) + "\">";
        output += "<id>" + workflow.id + "</id>";

        if (workflow.description) {
          var workflowDescription =
            workflow.description.replace(/[\r\n]/gm, '');
          output += "<description>" +
            _getWorkflowsNS._escapeXML(workflowDescription) +
            "</description>";
        } else {
          output += "<description/>";
        }
        output += "<version>" + workflow.version + "</version>";

        // Variables
        var attributes = workflow.attributes;
        output += "<variables>";
        attributes.forEach( function(attribute) {
          output += "<variable>";
          output += "<name>" +
            _getWorkflowsNS._escapeXML(attribute.name) + "</name>";
          output += "<type>" + attribute.type + "</type>";
          if (attribute.description) {
            output += "<description>" +
              _getWorkflowsNS._escapeXML(attribute.description) +
              "</description>";
          } else {
            output += "<description/>";
          }
          if (attribute.hasOwnProperty("value")) {
            if (attribute.value) {
              output += "<value>" + attribute.value + "</value>";
            } else {
              output += "<value/>";
            }
          }
          output += "</variable>";
        });
        output += "</variables>";

        // Inputs
        var inParameters = workflow.inParameters;
        output += "<inputs>";
        inParameters.forEach( function(parameter) {
          output += "<input>";
          output += "<name>" +
            _getWorkflowsNS._escapeXML(parameter.name) + "</name>";
          output += "<type>" + parameter.type + "</type>";
          if (parameter.description) {
            output += "<description>" +
              _getWorkflowsNS._escapeXML(parameter.description) +
              "</description>";
          } else {
            output += "<description/>";
          }
          output += "</input>";
        });
        output += "</inputs>";

        // Outputs
        var outParameters = workflow.outParameters;
        output += "<outputs>";
        outParameters.forEach( function(parameter) {
          output += "<output>";
          output += "<name>" +
            _getWorkflowsNS._escapeXML(parameter.name) + "</name>";
          output += "<type>" + parameter.type + "</type>";
          if (parameter.description) {
            output += "<description>" +
              _getWorkflowsNS._escapeXML(parameter.description) +
              "</description>";
          } else {
            output += "<description/>";
          }
          output += "</output>";
        });
        output += "</outputs>";

        // Schema
        output += "<numberOfItems>" + workflow.numberOfItem +
          "</numberOfItems>";
        output += "<firstItem>" +
          _getWorkflowsNS._escapeXML(workflow.firstItem.name) +
          "</firstItem>";
        var items = workflow.items;
        output += "<schema>";
        items.forEach( function(item) {
          output += "<item>";
          output +="<name>" +
            _getWorkflowsNS._escapeXML(item.name) + "</name>";
          if (item.description) {
            output += "<description>" +
              _getWorkflowsNS._escapeXML(item.description) +
              "</description>";
          } else {
            output += "<description/>";
          }

        var nextItem;
        if (item.nextItem) {
          nextItem = "<nextItem>" +
            _getWorkflowsNS._escapeXML(item.nextItem.name) + "</nextItem>";
        } else {
          nextItem = "<nextItem/>";
        }

          // Elements
          switch(item.constructor.name) {

            // WorkflowCustomConditionItem - CustomCondition
            case "WorkflowCustomConditionItem" :
              output += "<type>CustomCondition</type>";
              output += nextItem;
              output += "<nextItemTrue>" +
                _getWorkflowsNS._escapeXML(item.nextItemTrue.name) +
                "</nextItemTrue>";
              output += "<nextItemFalse>" +
                _getWorkflowsNS._escapeXML(item.nextItemFalse.name) +
                "</nextItemFalse>";
              output += "<script>";
              output += _getWorkflowsNS._escapeXML(item.script);
              output += "</script>";
              break;

            // WorkflowDecisionActivityItem - DecisionActivity
            // (Undocumented)
            case "WorkflowDecisionActivityItem" :
              output += "<type>DecisionActivity</type>";
              output += nextItem;
              output += "<nextItemTrue>" +
                _getWorkflowsNS._escapeXML(item.nextItemTrue.name) +
                "</nextItemTrue>";
              output += "<nextItemFalse>" +
                _getWorkflowsNS._escapeXML(item.nextItemFalse.name) +
                "</nextItemFalse>";
              output += "<script>" +
                _getWorkflowsNS._escapeXML(item.script) + "</script>";
              if (item.referencedWorkflow) {
                if (item.referencedWorkflow.name) {
                  output += "<referencedWorkflow>" +
                    _getWorkflowsNS._escapeXML(item.referencedWorkflow.name) +
                    "</referencedWorkflow>";
                }
              }
              if (item.referencedAction) {
                if (item.referencedAction.name) {
                  output += "<referencedAction>" +
                    _getWorkflowsNS._escapeXML(item.referencedAction.name) +
                    "</referencedAction>";
                }
              }
              break;

            // WorkflowForeachItem -Foreach (Undocumented)
            case "WorkflowForeachItem" :
              output += "<type>Foreach</type>";
              output += nextItem;
              output += "<loopInnerCatchBlock>" +
                _getWorkflowsNS._escapeXML(item.loopInnerCatchBlock) +
                "</loopInnerCatchBlock>";
              if (item.iterationElement === null) {
                output += "<iterationElement/>";
              } else {
                output += "<iterationElement>" +
                  _getWorkflowsNS._escapeXML(item.iterationElement.name) +
                  "</iterationElement>";
              }
              break;

            // WorkflowGenericConditionItem - GenericCondition
            case "WorkflowGenericConditionItem" :
              output += "<type>GenericCondition</type>";
              output += nextItem;
              output += "<nextItemTrue>" +
                _getWorkflowsNS._escapeXML(item.nextItemTrue.name) +
                "</nextItemTrue>";
              output += "<nextItemFalse>" +
                _getWorkflowsNS._escapeXML(item.nextItemFalse.name) +
                "</nextItemFalse>";
              output += "<script>";
              output += _getWorkflowsNS._escapeXML(item.script);
              output += "</script>";
              break;

            // WorkflowInputItem - Input
            case "WorkflowInputItem" :
              output += "<type>Input</type>";
              output += nextItem;
              break;

            // WorkflowItem
            case "WorkflowItem" :
              output += "<type/>";
              output += nextItem;
              break;

            // WorkflowItemEnd
            case "WorkflowItemEnd" :
              output += "<type>End</type>";
              break;

            // WorkflowItemWaitingEvent - WaitingEvent
            case "WorkflowItemWaitingEvent" :
              output += "<type>WaitingEvent</type>";
              output += nextItem;
              break;

            // WorkflowItemWaitingTimer - WaitingTimer
            case "WorkflowItemWaitingTimer" :
              output += "<type>WaitingTimer</type>";
              output += nextItem;
              break;

            // WorkflowLinkItem - Link
            case "WorkflowLinkItem" :
              output += "<type>Link</type>";
              output += nextItem;
              if (item.linkedWorkflow !== null) {
                output += "<linkedWorkflow>" +
                  _getWorkflowsNS._escapeXML(item.linkedWorkflow.name) +
                  "</linkedWorkflow>";
              }
              break;

            // WorkflowMultipleCallItem - MultipleCall
            case "WorkflowMultipleCallItem" :
              output += "<type>MultipleCall</type>";
              output += nextItem;
              output += "<linkedWorkflows>";
              item.linkedWorkflows.forEach( function(linkedWorkflow) {
                output += "<linkedWorkflow>" +
                  _getWorkflowsNS._escapeXML(linkedWorkflow.name) +
                  "</linkedWorkflow>";
              });
              output += "</linkedWorkflows>";
              break;

            // WorkflowSwitchItem - Switch (Undocumented)
            case "WorkflowSwitchItem" :
              output += "<type>Switch</type>";
              output += nextItem;
              output += "<outItems>";
              item.outItems.forEach( function(outItem) {
                output += "<outItem>";
                output += "<name>" +
                  _getWorkflowsNS._escapeXML(outItem.name) + "</name>";
                output += "</outItem>";
              });
              output += "</outItems>";
              output += "<conditions>";
              item.conditions.forEach( function(condition) {
                output += ("<condition>" + condition + "</condition>");
              });
              output += ("</conditions>");
              break;

            // WorkflowTaskItem - Task
            case "WorkflowTaskItem" :
              output += "<type>Task</type>";
              output += nextItem;
              output += "<isActionCall>" + item.isActionCall +
                "</isActionCall>";
              output += "<isStartWorkflowCall>" +
                item.isStartWorkflowCall + "</isStartWorkflowCall>";
              if (item.linkedWorkflow) {
                output += "<linkedWorkflow>" +
                  _getWorkflowsNS._escapeXML(item.linkedWorkflow.name) +
                  "</linkedWorkflow>";
              } else {
                output += "<linkedWorkflow/>";
              }
              output += "<usedActions>";
              item.usedActions.forEach( function(usedAction) {
                output += "<usedAction>" +
                  _getWorkflowsNS._escapeXML(usedAction.name) +
                  "</usedAction>";
              });
              output += "</usedActions>";
              output += "<script>";
              output += _getWorkflowsNS._escapeXML(item.script);
              output += "</script>";
              break;

          }

          output += "</item>";
        });
        output += "</schema>";

        output += "</workflow>";

      });

      output += "</workflows>";

    } catch(e) {
      System.log(e);
      System.log(e.stack);
    } finally {
      System.log(output);
    }

  }

}

// Main
_getWorkflowsNS.Main();

The code is really easy to understand. First, a file is created in which the XML data is stored. Then all workflows are detected and possible selections, by folder or workflow names, are made over the inputs. Now, workflow by workflow, the details are detected, with certain characters having to be converted to conform to XML conventions. If this action is now executed, we get an XML file with the desired information.

Conclusion

The generated XML information can be used for analysis purposes. All information are available in a very handy and compact well structured form.