Quantcast
Channel: VMware – The Practical Administrator
Viewing all articles
Browse latest Browse all 12

Executing WFA workflows from vCenter Orchestrator using REST

$
0
0

In the previous post I showed how to execute a NetApp OnCommand Workflow Automation (WFA) workflow using the REST cmdlets available in Powershell version 4. However, any language or platform can be used for execution via REST, including VMware’s vCenter Orchestrator (vCO).

For the most simple execution we can simply add the WFA host as a REST host to vCO. When the REST plug-in is added to your vCO instance, it adds some helper workflows for managing the connected hosts. Let’s start by executing the “Add a REST host” vCO workflow (located at Library->HTTP-REST->Configuration).

The first screen prompts for a friendly name this host is known by and the URL. Be sure to add the /rest to the end of the hostname/IP address when you provide the URL.

wfa_vco_rest_1

If you need to configure proxy settings, do so on the next screen. For WFA, we will want to use “Basic” authentication, on the third screen select this option, which will enable a fourth screen to provide credentials.

wfa_vco_rest_2

We will want to use the “Shared Session” mode (which means that the same set of credentials will always be used), then provide your WFA username and password. When finished, click “Submit”.

wfa_vco_rest_3

Assuming everything works, you should see the completed execution of the workflow, and browsing to the Inventory tab, then expanding the HTTP-REST option will show the newly created host.

wfa_vco_rest_4

wfa_vco_rest_5

What can we do with this now? Well, we can use the REST host object inside of a vCO script to replicate what we did using Powershell. Because the REST host properties are stored in the vCO object, the password does not have to be exposed, and we can now programatically access it when needed.

Let’s get started by creating a new vCO workflow. For convenience, I’ve chosen to name it the same as the WFA workflow.

wfa_vco_rest_6

Once it’s created, add a single parameter. The type should be REST:RESTHost and the value will be the REST host created above (which I have called “WFA”).

wfa_vco_rest_10

Now we need to add inputs to the vCO workflow. These will be completed by the user and passed to the WFA workflow. I chose to name the vCO inputs the same as the WfA inputs, but that is optional at this stage. For this example, I have three string inputs (ClusterName, VserverName, and VolumeName) and one number input (VolumeSizeInGB).

wfa_vco_rest_7

Change to the schema view, where we only need to add a single element…a scriptable task.

wfa_vco_rest_8

Edit the scriptable task, select the “Visual Binding” tab. Drag-and-drop each of the four inputs and the single attribute so that they are inputs to the scriptable task. It should look something like the screenshot below.

wfa_vco_rest_9

Switch to the script tab for the task, and copy/paste the script below:

// This is a vCO custom scriptable task which executes a WFA REST operation.
// By modifying the input parameters and workflow name, it is possible to execute any WFA
// workflow using this method.
 
System.debug("Starting execution of manual WFA REST operation");
 
// the name of the workflow to execute...this could be an attribute as well.  This is case sensitive.
var workflowName = "Create a Clustered Data ONTAP Volume";
 
// add the parameters to a properties object for safe keeping
var workflowParams = new Properties();
workflowParams.put("ClusterName", ClusterName);
workflowParams.put("VserverName", VserverName);
workflowParams.put("VolumeName", VolumeName);
workflowParams.put("VolumeSizeInGB", VolumeSizeInGB);
 
 
// ------------- end input section ---------------
 
// execute the request to get the specific workflow we want.  note that we are replacing
// spaces with the http safe "%20".  the data we are after is the UUID
var workflowUuid = null;
var workflowResponse = restHost.createRequest("GET", "/workflows?name=" + workflowName.replace(/ /g, '%20'), null).execute();
 
if (workflowResponse.statusCode == 200) {
    // the response was good from the host
    var workflowDocument = XMLManager.fromString(workflowResponse.contentAsString);
    var workflowItem = workflowDocument.getElementsByTagName("workflow");
 
    // when searching by name we should only get back one item
    if (workflowItem.length > 1) {
        throw "An error has occurred, more than one workflow was returned";
    } else {
        workflowUuid = workflowItem.item(0).getAttribute("uuid");
        System.debug("Found workflow UUID: " + workflowUuid);
    }
} else {
    throw "A " + workflowResponse.statusCode + " error occured while attempting to query WFA for the workflow: " + workflowResponse.contentAsString;
}
 
if (workflowUuid == null) {
    throw "Unable to determine the UUID of the workflow"
}
 
// set the execution URL of the workflow
var workflowExecutionUrl = "/workflows/" + workflowUuid + "/jobs";
 
// now that we have the UUID (and the url), we can execute the workflow
var executeRequest = restHost.createRequest("POST", workflowExecutionUrl, generateWfaInputXml( workflowParams ));
 
// ensure the content type is correct
executeRequest.contentType = "application/xml";
 
var executeResponse = executeRequest.execute();
var jobId = null;
 
// get the jobId from the execution response
if (executeResponse.statusCode == 201) {
    var executeDocument = XMLManager.fromString(executeResponse.contentAsString);
    var job = executeDocument.getElementsByTagName("job");
 
    if (job.length == 1) {
        var jobId = job.item(0).getAttribute("jobId");
        System.debug("Execution job id: " + jobId);
    } else {
        throw "an error occurred while retrieving the job id";
    }
} else {
    throw "A " + executeResponse.statusCode + " error occured while attempting to execute the workflow: " + executeResponse.contentAsString;
}
 
if (jobId == null) {
    throw "Unable to determine job id!"
}
 
var jobStatusUrl = workflowExecutionUrl + "/" + jobId;
 
for (var x = 0; x < 20; x++) {
    var complete = false;
    var statusResponse = restHost.createRequest("GET", jobStatusUrl, null).execute();
 
    if (statusResponse.statusCode != 200) {
        throw "An error occurred checking on job status: " + statusResponse.ContentAsString;
    }
 
    var statusDocument = XMLManager.fromString(statusResponse.contentAsString);
    var jobStatusElements = statusDocument.getElementsByTagName("jobStatus");
 
    // we are cheating here...there should be two elements with the name jobStatus...the second is a 
    // child of the first and will contain just the status, whereas the first will have many more
    // details
    var currentStatus = jobStatusElements.item(1).textContent;
    System.debug("Current execution status: " + currentStatus);
 
    switch (currentStatus) {
        case "RUNNING":
        case "EXECUTING":
        case "PENDING":
        case "SCHEDULED":
        case "PLANNING":
            System.debug("Job is not complete.  Sleeping.");
            System.sleep(15000);
            break;
        case "COMPLETED":
            complete = true;
            break;
        default:
            throw "An unknown status has been encountered!";
    }
 
    // stop checking the status if we're done
    if (complete == true) {
        break;
    }
}
 
// ---- helper functions ----
function generateWfaInputXml( inputs ) {
    var xml = XMLManager.newDocument();
 
    // create the element to hold the inputs
    var inputsElement = xml.createElement("userInputValues")
 
    for each (var inputName in inputs.keys) {
        // the input with it's key=value attributes
        var input = xml.createElement("userInputEntry");
        input.setAttribute("key", inputName);
        input.setAttribute("value", inputs.get(inputName));
        inputsElement.appendChild(input);
    }
 
    // add the root element
    var workflowElement = xml.createElement("workflowInput");
    xml.appendChild(workflowElement);
 
    // add the inputs to the root
    workflowElement.appendChild(inputsElement);
 
    // return the xml
    return XMLManager.getDocumentContent(xml);
 
}

Note that the content of the javascript here is very similar to the Powershell from the last post. We are, at the core, performing three REST operations against WFA:

  1. Query WFA for the workflow UUID using a GET operation
  2. Execute the workflow, passing the parameters as XML, by issuing a POST command to the workflow specific URL
  3. Monitor the status of the execution, by issuing a REST GET operation, for failure or completion

The final step is to edit the presentation to pretty it up a bit. This is also where we would do validation of the inputs if desired. For the three string inputs (ClusterName, VserverName, and VolumeName), I have simply made them mandatory. For the number input (VolumeSizeInGB), I added a minimum and maximum value which should represent sane values for your environment.

wfa_vco_rest_11

wfa_vco_rest_12

When we’re all done, let’s test by switching to the schema view and clicking the “Debug” button. We are using the debug to test because in the scriptable task’s code we used the command System.debug to send log messages. These are only visible to us using the debug execution.

Here is what the screen looks like:

wfa_vco_rest_13

After hitting submit, assuming the values provided are valid for the WFA workflow, you should see something like the following:

wfa_vco_rest_14

We see on the right side the messages from the script which indicate that it was able to execute the workflow and about how long it took to complete. I have chosen not to log where the volume was created (node and aggregate), as I did in the Powershell script, because it will not be seen. However, if desired, you could modify the the workflow to return those values. This would be particularly helpful if you are wanting to use this workflow as a part of a larger chain, for example, one which performs operations for deploying an entire application requiring a dedicated volume.

Questions and comments are always welcome below. If you have any suggestions for, or need help with, NetApp/VMware integration, automation, and solutions, please let me know using the comments!

The post Executing WFA workflows from vCenter Orchestrator using REST appeared first on The Practical Administrator.


Viewing all articles
Browse latest Browse all 12

Trending Articles