We've all seen that vCenter Orchestrator (vCO) is quite powerful in automating many aspects of your Infrastructure through the variety of specific and generic plug-ins. While there are hundreds of workflows and actions for vCenter included in a clean install of vCO, they simply don't cover every single thing you may want to do. One such example has come up a few times in the VMware Communities: Managing Services. The following article will walk you through creating a simple workflow that allows you to manage services on a specified ESXi (VC:HostSystem) server.

As referenced in the "Onyx Output Conversion" thread linked at the end of this article, one way to get started creating new workflows is to use Onyx to capture actions you perform in your vCenter client. This will help you identify the necessary objects and values for the various inputs of the methods you execute against those objects. Be sure to spend some time figuring out this tool and getting to know the API Explorer in vCO.

In order to get things started, create a new workflow named something like "Update HostSystem Service" or whatever you like. 

Once you've created the workflow, place a single Scriptable task named "Apply Action" in there and make all your connections.

Now, let's identify what we'll need for this workflow:

  • ESXi server (VC:HostSystem) <- very important input 
  • Service Name we wish to take action against
  • Service action - what we want to do to the service
  • Policy action - how we want the service to act when the ESXi server starts

Let's get started with editing the Apply Action Scriptable Task!

NOTE:
If using vCO 5.5.1, you will need to disable use of the Inventory service by the vCO Server. This will actually resolve a number of issues and is safe to do and recommended many times over in the Orchestrator Community. To disable vCO's use of the Inventory Service, add the following line to your vmo.properties file, save, then restart the vCO Server Service:

com.vmware.o11n.vim.useInventoryService=false

INPUT PARAMETER: First of all, add a new input to your scriptable task named "host". The type should be "VC:HostSystem". This will be the ESXi server that we will manage the service of.

Now that we have our HostSystem object, let's start coding!

// Get the hostServiceSystem object from the host:
var hostServiceSystem = host.configManager.serviceSystem;

// refresh services info to make sure all properties are fresh:
hostServiceSystem.refreshServices();

We now have our hostServiceSystem object. This object is the one that has the methods that allow us to start/stop/restart the services so we're off to a good start. Next up, we need to make sure we get the service object that we wish to manage.

INPUT PARAMETER: Add another input to your scriptable task named "serviceName" - this should be a String.

// Get Service:
var serviceObj = null;
var services = hostServiceSystem.serviceInfo.service; // Retrieve a list of available services on this host
for each (svc in services){ // now try to match the service key with the service name we're passing in
     //System.log("Checking "+svc.key+" / "+serviceName); // Optionally uncomment the beginning of this line for additional logging
    if(svc.key == serviceName){
        System.log("Service Found! "+svc.label);
 serviceObj = svc;
        break;
    }
}
if (serviceObj == null){ // Make sure we got the service object we are trying to manipulate - if null, throw exception
    throw "unable to locate service: "+serviceName+" on host: "+host.name;
}

Okay, great. We found the service. Now let's do something with it.

Use the API Explorer to look at the "VcHostServiceSystem" object. This is the object we obtained in the first script snippet above. Take a look at the methods available (indicated by solid block dots).

VcHostServiceSystem properties and methods

Based on the API, it looks like we can: restart, start, stop, or uninstall a service. We can also update the Service Policy.

We'll need to account for each of these possible actions. As part of this, we'll need an input to prompt what we want to do. Additionally, we should pre-define our values so they cannot be mis-typed.

INPUT PARAMETER: Add another input to your scriptable task named "serviceAction" - this should be a String

ATTRIBUTE: Add a new attribute named "serviceActions" - this should be an Array of Strings with the following values:

  • start
  • stop
  • restart
  • uninstall

We'll use this attribute in our presentation to present a dropdown list of service actions. This allows us to prevent a user from typing an invalid value.

Now paste the following code at the bottom of the scriptable task.

switch(serviceAction){ 
    case "start": // Only run the startService method if the service is not running
        // before trying to start, make sure running is not true 
        if (serviceObj.running != true){ 
            hostServiceSystem.startService(serviceObj.key); 
        } 
        break;

    case "stop": // Only run the stopService method if the service is running
        if (serviceObj.running == true){ 
            hostServiceSystem.stopService(serviceObj.key); 
        } 
        break;

    case "restart": 
        hostServiceSystem.restartService(serviceObj.key); 
        break;

    case "uninstall": // Not all services can be uninstalled so we use a try/catch here
        try{ 
            hostServiceSystem.uninstallService(serviceObj.key); 
        }catch(err){ 
            System.error("Error uninstalling service "+serviceObj.key+" ("+err+")"); 
            Server.error("Error uninstalling service "+serviceObj.key,serviceObj.key); 
        } 
        break;

    default: // We should never get here so Provide some warning logging and optionally throw an exception if appropriate for your environment
        System.warn("Invalid service action selected"); 
        Server.warn("Invalid service action selected",serviceAction);
}

Finally, we need to provide the ability to specify how the service starts up. 
INPUT PARAMETER: Add a new input to your Scriptable Task named "policyAction" - this should be a String

The updateServicePolicy takes two parameters - the id of our service object and the policy. It took a little playing with Onyx for me to find that the possible values for the policy are on, off, or automatic. We don't always want to take action on a policy so we'll be adding an extra value to our choice: No Change.

ATTRIBUTE: Add a new attribute named "policyActions" - this should be an Array of Strings with the following values:

  • No Change
  • on
  • off
  • automatic

We'll use this attribute in our presentation to present a dropdown list of policy actions. This allows us to prevent a user from typing an invalid value.

if(policyAction != null && policyAction != "" && policyAction != "No Change"){ 
    // only valid policy actions are: on, off, automatic 
    try{ 
        hostServiceSystem.updateServicePolicy(serviceObj.key,policyAction); 
    }catch(err){ 
        System.error("Error updating service policy "+serviceObj.key+" ("+err+")"); 
        Server.error("Error updating service policy "+serviceObj.key+" ("+err+")",serviceObj.key); 
    }
}

At this point, the workflow has 1 object input and 3 string inputs. All of which are passed in to the Scriptable Task.

Go ahead and try running your workflow right now...

See a problem? Right, you could mis-type the service name and may not even know which actions are available. Earlier in the article, we created a pair of Attributes that we store an array of strings containing a static set of values. We don't want to do this for the services since the services available on one host may not be available on other hosts and we want this workflow to be very reusable. So, we'll create an Action that retrieves an array of Service Names running on the given HostSystem.

Create a new Action named "getHostSystemServiceKeys". I will create this in a module named com.vmware.pso

The action should have an input named "host" of type "VC:HostSystem". I have also added a "logging" input of type "boolean" to allow for some System.log entries to take place if I need them.

For the Return Type, set that to "Array/string" since we'll be returning the names of all the services available to the HostSystem.

Now, for the script:

var hostServiceSystem = host.configManager.serviceSystem;
hostServiceSystem.refreshServices();
var services = hostServiceSystem.serviceInfo.service; // Return the array of service objects
var serviceKeys = new Array();
for each (svc in services){ 
    if(logging == true){ 
        System.log("Key: "+svc.key+" -- Label: "+svc.label+" -- Policy: "+svc.policy); 
    } 
    serviceKeys.push(svc.key); // Since this object type cannot be passed as an output/input, we'll just pass the names into the array
}
return serviceKeys;

Okay, all of our code is in place. The only thing left is to cleanup our presentation so that our inputs are pre-populated with appropriate content after we have chosen our HostSystem.

Click on the Presentation tab and arrange as shown in the following screenshot:

Screenshot of Presentation Tab

Make the following changes on the properties tab of each input:

  • (VC:HostSystem) host
    • Mandatory Input: Yes
  • (string) serviceName (On General tab, under description, set value to "Service Name")
    • Predefined list of elements (Click the action icon, choose "getHostSystemServiceKeys". For first parameter, enter "#host". For second parameter, enter false)
  • (string) serviceAction (On General tab, under description, set value to "Service Action")
    • Predefined list of elements (Enter #serviceActions for the value)
  • (string) policyAction (On General tab, under description, set value to "Policy")
    • Predefined list of elements (Enter #policyNames for the value)
    • Default value (Set to "No Change")

Save and Close! Now try it out!

Screenshot of user Input for workflow run

Solution/Package:

file icon package Solution Package File - Download Here

Reference/Inspiration:

Onyx Output Converstion

Turning on SSH Service and Using SSH plug-in