In this post is a way described how to get detailed information about all actions. For this an action is used that collects all the desired information and saves it in XML format.
Get Detailed Information about all Actions
First we take a look at what information are provided and where it can be found in the UI of VCF Automation. Here a look at the General and Script tab of an Action.
General Tab
In the General Tab we can find information about the module, name and the unique ID of the script. Also we have a description and a version here.
Script Tab
In the Script tab we can find the script code, the return type of the script and the input parameters.
XML Mapping
In XML format the same information can be mapped as follows. The chosen tag or attribute names correspond exactly to the descriptions in the UI, to avoid confusion.
<?xml version="1.0"?>
<actions>
<action module="de.stschnell" name="testStefan">
<id>aa347d8e-1a8d-4a9b-8737-cf5dbbba7b23</id>
<description>This is a test</description>
<version>0.1.0</version>
<runtime/>
<inputs>
<input>
<name>in_HelloWho</name>
<type>string</type>
<description>Say Hello</description>
</input>
</inputs>
<returnType>string</returnType>
<script>
System.log("Hello World from " + in_HelloWho);
return "Hello Stefan";
</script>
</action>
</actions>
|
Action
To get these information an action which detects all actions is used. Then the detailed information are detected in a loop, with the help of the Action object and its methods. This information are stored in the XML structure presented above and viewed in the log. The XML data can then be copied from the log to the clipboard for further use.
JavaScript
/**
* This action creates an XML file with all details of all existing
* actions and its script code in the log.
*
* @name getActions
* @param {string} in_moduleFilter - Search for substring in module, optional
* @param {string} in_nameFilter - Search for substring in name, optional
*
* @author Stefan Schnell <mail@stefan-schnell.de>
* @license MIT
* @version 1.4.2
*
* Checked with release 8.12.0, 8.14.1, 8.16.2 and 9.0.0
*/
var _getActionsNS = {
/**
* _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, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
/*
.replace(/\u00e4/g, "ae")
.replace(/\u00c4/g, "Ae")
.replace(/\u00f6/g, "oe")
.replace(/\u00d6/g, "Oe")
.replace(/\u00fc/g, "ue")
.replace(/\u00dc/g, "Ue")
.replace(/\u00df/g, "ss");
*/
}
},
Main : function(userName, password, moduleFilter, nameFilter) {
var output = "<?xml version=\"1.0\"?>";
output += "<actions>";
try {
var actions =
System.getModule("com.vmware.library.action").getAllActions();
/* getAllActions
var actions = new Array();
var modules = System.getAllModules();
for (i in modules){
var actionDescriptions = modules[i].actionDescriptions;
for (j in actionDescriptions){
actions.push(actionDescriptions[j]);
}
}
return actions;
*/
// Search for substring in module
if (moduleFilter) {
// Reverse search in actions array to ...
for (var i = actions.length - 1; i >= 0; i--) {
if (actions[i].module.name.indexOf(moduleFilter) === -1) {
// delete element if moduleFilter not found in module name
actions.splice(i, 1);
}
}
}
// Search for substring in script name
if (nameFilter) {
// Reverse search in actions array to ...
for (var i = actions.length - 1; i >= 0; i--) {
if (actions[i].name.indexOf(nameFilter) === -1) {
// delete element if nameFilter not found in script name
actions.splice(i, 1);
}
}
}
actions.forEach( function(action) {
output += "<action module=\"" + action.module.name +
"\" name=\"" + _getActionsNS._escapeXML(action.name) + "\">";
// General
output += "<id>" + action.id + "</id>";
if (action.description) {
var actionDescription = action.description.replace(/[\r\n]/gm, '');
output += "<description>" +
_getActionsNS._escapeXML(actionDescription) + "</description>";
} else {
output += "<description/>";
}
output += "<version>" + action.version + "</version>";
// Runtime
output += "<runtime/>";
// Script > Properties > Inputs
var parameters = action.parameters;
output += "<inputs>";
parameters.forEach( function(parameter) {
output += "<input>";
output += "<name>" +
_getActionsNS._escapeXML(parameter.name) + "</name>";
output += "<type>" + parameter.type + "</type>";
if (parameter.description) {
output += "<description>" +
_getActionsNS._escapeXML(parameter.description) +
"</description>";
} else {
output += "<description/>";
}
output += "</input>";
});
output += "</inputs>";
// Script > Properties > Return type
output += "<returnType>" + action.returnType +
"</returnType>";
// Script > Code
output += "<script>";
if (action.script) {
output += _getActionsNS._escapeXML(action.script);
}
output += "</script>";
output += "</action>";
});
output += "</actions>";
} catch(e) {
System.log(e);
System.log(e.stack);
} finally {
System.log(output);
}
}
}
// Main
_getActionsNS.Main(
in_moduleFilter,
in_nameFilter
);
|
Python
"""
This action creates an XML file with all details of all existing actions
and its script code in the log.
@name getActions
@param {string} in_moduleFilter - Search for substring in module, optional
@param {string} in_nameFilter - Search for substring in name, optional
@returns {Properties}
@author Stefan Schnell <mail@stefan-schnell.de>
@license MIT
@version 2.0.0
@runtime python:3.10 and 3.11
@memoryLimit 256000000
Checked with release 8.17.0 and 9.0.0
"""
import json
import ssl
import urllib.request
def escapeXML(strXML):
"""
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
"""
if strXML:
returnValue = strXML.replace("&", "&")
returnValue = returnValue.replace("<", "<")
returnValue = returnValue.replace(">", ">")
returnValue = returnValue.replace("\"", """)
returnValue = returnValue.replace("'", "'")
return returnValue
else:
return ""
def getAllActions(vcoUrl, bearerToken):
"""
Get all actions.
@name getAllActions
@param {string} vcoUrl
@param {string} bearerToken
@returns {dictionary}
"""
returnValue = {}
try:
requestActions = urllib.request.Request(
url = vcoUrl + "/api/actions"
)
requestActions.add_header(
"Authorization", "Bearer " + bearerToken
)
requestActions.add_header(
"Content-Type", "application/json"
)
responseActions = urllib.request.urlopen(
requestActions,
context = ssl._create_unverified_context()
)
if responseActions.getcode() == 200:
returnValue = json.loads(responseActions.read())
except Exception as err:
raise Exception("An error occurred at detecting all actions") \
from err
return returnValue
def getActionDetails(actionHref, bearerToken):
"""
Gets the details of the given action.
@name getActionDetails
@param {string} actionHref
@param {string} bearerToken
@returns {dictionary}
"""
returnValue = {}
try:
requestAction = urllib.request.Request(
url = actionHref
)
requestAction.add_header(
"Authorization", "Bearer " + bearerToken
)
requestAction.add_header(
"Content-Type", "application/json"
)
responseAction = urllib.request.urlopen(
requestAction,
context = ssl._create_unverified_context()
)
if responseAction.getcode() == 200:
returnValue = json.loads(responseAction.read())
except Exception as err:
raise Exception("An error occurred at detecting action details") \
from err
return returnValue
def handler(context, inputs):
"""
Standard VCF Automation handler function.
"""
output = "<?xml version=\"1.0\"?>"
output += "<actions>"
try:
vcoUrl = context["vcoUrl"]
bearerToken = context['getToken']()
actions = getAllActions(
vcoUrl,
bearerToken
)
if not actions:
raise ValueError("No actions were detected")
# Sort actions by module and name
actionList = []
for action in actions["link"]:
fqn = ""
for attribute in action["attributes"]:
if attribute["name"] == "fqn":
fqn = attribute["value"]
if fqn != "":
actionList.append(
{"href": action["href"], "fqn": fqn}
)
actionList.sort(key = lambda x: x["fqn"])
# Filter for substring in module
if inputs["in_moduleFilter"]:
actionList = [
x for x in actionList \
if inputs["in_moduleFilter"].lower() in x["fqn"].lower()
]
# Filter for substring in script name
if inputs["in_nameFilter"]:
actionList = [
x for x in actionList \
if inputs["in_nameFilter"].lower() in x["fqn"].lower()
]
# Write action data
for action in actionList:
actionDetails = getActionDetails(
action["href"],
bearerToken
)
if not actionDetails:
raise Exception("No action details were detected")
output += "<action module=\""
output += actionDetails["module"]
output += "\" name=\""
output += escapeXML(actionDetails["name"])
output += "\">"
# General
output += "<id>"
output += actionDetails["id"]
output += "</id>"
if "description" in actionDetails:
actionDescription = actionDetails["description"]
actionDescription = actionDescription.replace("\r", "")
actionDescription = actionDescription.replace("\n", "")
output += "<description>"
output += escapeXML(actionDescription)
output += "</description>"
else:
output += "<description/>"
output += "<version>"
output += actionDetails["version"]
output += "</version>"
if "runtime" in actionDetails:
output += "<runtime>"
output += actionDetails["runtime"]
output += "</runtime>"
else:
output += "<runtime/>"
# Script > Properties > Inputs
if "input-parameters" in actionDetails:
output += "<inputs>"
for inputParameter in actionDetails["input-parameters"]:
output += "<input>"
output += "<name>"
output += inputParameter["name"]
output += "</name>"
output += "<type>"
output += inputParameter["type"]
output += "</type>"
output += "<description>"
output += escapeXML(inputParameter["description"])
output += "</description>"
output += "</input>"
output += "</inputs>"
else:
output += "<inputs/>"
# Script > Properties > Return type
output += "<returnType>"
output += actionDetails["output-type"]
output += "</returnType>"
# Script > Code
if "script" in actionDetails:
output += "<script>"
output += escapeXML(actionDetails["script"])
output += "</script>"
else:
output += "<script/>"
output += "</action>"
output += "</actions>"
print(repr(str(output)))
outputs = {
"status": "done",
"result": output
}
except Exception as err:
outputs = {
"status": "incomplete",
"error": repr(err),
"result": output
}
return outputs
|
Both codes are really easy to understand. First all actions are detected and possible selections are made over the inputs, by module or script names. Now, action by action, 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 output in the log with the desired information.
Conclusion
The generated XML information can be used for analysis purposes, e.g. via XPath. Here, for example, would be the finding of code duplicates or deviations from specifications. The advantage of fully local provision of these XML data brings independence and performance improvements.