vCenter 6 has introduced an important new feature for anyone interested in Automation and integration : a REST based endpoint providing access to some of the recent functionality such as the tagging service, the content library and transfer service : It is called  "VMware vCloud Suite SDK" and codenamed vAPI. In addition to the REST API it provides language bindings for Java, Python, .NET, Perl and Ruby. If you want to know more about vAPI you can read the blog article I wrote on the VMware CTO Ambassadors blog.
The following article is an introduction to using the vAPI REST Endpoint from vRealize Orchestrator REST plug-in. The concepts in this article may be useful even if you plan to consume the REST service with any REST client.

Background

A few months ago I quickly developed a tagging plug-in for vRealize Orchestrator leveraging the vAPI REST endpoint. While it is possible to look at the code I thought providing a tutorial would be more convenient for learning purposes.

Requirements

  • vCenter 6
  • vRealize Orchestrator (6 was used for the tutorial but 5.X may be used as well)
  • Have at least a tag category created in vCenter to make sure the tutorial output something

Setup

First, we need to add a new REST Host. Log in the vRO client, search and run the "Add a REST host" workflow.
Add a name and enter the URL of your vCenter 6 server in the format https://[vcenterServer]/rest (do not use http or do not forget the /rest).
The /rest points to the vAPI REST endpoint service. The other field can be left with default values. Click next.

b_600_0_16777215_01_images_phocagallery_tutorials_vAPI-REST-01.png


You can skip the proxy settings. For the Host authentication select Basic.

b_600_0_16777215_01_images_phocagallery_tutorials_vAPI-REST-02.png


For session mode you can either use "Per User Session" or "Shared Session".
This mainly depends if the username you used to authenticate in vRO has permissions on the target vCenter. If this is the case you can use per user session, if not use Shared session and enter credentials with permissions on the vCenter. You can submit the workflow.

b_600_0_16777215_01_images_phocagallery_tutorials_vAPI-REST-03.png

Demo workflow

Now we need to create our own workflow:
  • Create a new workflow called for example "vAPI REST demo".
  • Add an input parameter named restHost of type restHost
  • Add a scriptable task on the schema. Bind the restHost input parameter to this scriptable task input parameter.

Here is the JavaScript code to put in the scriptable task
createCisSession(restHost);

var cisSession = getCisSession(restHost);
System.log("Log in with user " + cisSession.user);

var categoryIds = getTagCategories(restHost);
for each(var categoryId in categoryIds) {
    var category = getTagCategory(restHost, categoryId);
    System.log("Found category " + category.name);
}

deleteCisSession(restHost);

// This function will set the Cookie header vmware-api-session-id=[CIS Session ID] based on the Basic AUthorization header set by vRO REST plug-in
function createCisSession(restHost) {
    var request = restHost.createRequest("POST", "/com/vmware/cis/session");
    // Alternate syntaxes
    //var request = restHost.createRequest("POST", "/com/vmware/cis/session?~action=create");
    //var request = restHost.createRequest("GET", "/com/vmware/cis/session?~method=post");
    // Setting an unset vmware-api-session-id Cookie header enforce using Basic Authorization instead of using a Cookie based authentication based on a previous sesion
    request.setHeader("Cookie", "vmware-api-session-id");
    var response = request.execute();
    if (response.statusCode != 200) throw "Status code " + statusCode + "\n" + response.contentAsString;
}

function getCisSession(restHost) {
    var request = restHost.createRequest("POST", "/com/vmware/cis/session?~action=get");
    var response = request.execute();
    if (response.statusCode != 200) throw "Status code " + statusCode + "\n" + response.contentAsString;
    // Returning the object
    return JSON.parse(response.contentAsString).value;
}

function getTagCategories(restHost) {
    var request = restHost.createRequest("GET", "/com/vmware/cis/tagging/category");
    var response = request.execute();
    if (response.statusCode != 200) throw "Status code " + statusCode + "\n" + response.contentAsString;
    // Returning an array of tag category ids
    return (JSON.parse(response.contentAsString)).value;
}

function getTagCategory(restHost, categoryId) {
    var request = restHost.createRequest("POST", "/com/vmware/cis/tagging/category?~action=get&category_id=" + categoryId);
    var response = request.execute();
    if (response.statusCode != 200) throw "Status code " + statusCode + "\n" + response.contentAsString;
    // Returning the object
    return JSON.parse(response.contentAsString).value;
}

function deleteCisSession(restHost) {
    var request = restHost.createRequest("DELETE", "/com/vmware/cis/session");
    var response = request.execute();
    if (response.statusCode != 200) throw "Status code " + statusCode + "\n" + response.contentAsString;
}

 


I have created 5 JavaScript functions.

  • CreateCisSession : Equivalent of login : Creates a REST request to create a new session using the credentials you selected during the REST host configuration. Creating a session will set a Cookie in vRO that will be used to authenticate any subsequent API call. The tricky part in this function was to set an empty session ID in the cookie to avoid authenticating with a previously generated cookie that may contain an older session ID. I have also commented out alternate vAPI request syntaxes. While using POST /com/vmware/cis/session is a common way to create a resource there is another syntax using POST with the "/com/vmware/cis/session?~action=create" URL : this allows to specify other operations with POST instead of limiting POST for the sole purpose of creating a resource. This syntax allows to define more operations than the limited number of HTTP verbs. The second alternative uses a GET verb but uses ~method=post to indicate this is a resource creation. Offering the choice to create resources using GET allows to interface the vAPI endpoint with HTTP client that do not have options besides GET.
  • GetCisSession : Get the created session. Note the use of the POST verb with the get action in the URL. This function output a JavaScript session object from the JSON returned by the request. This function will be called to identify the current user of the session.
  • getTagCategories : Returns an array of the tag categories. In this case vAPI returns an array of the ids of these categories.
  • getTagCategory : Returns the category object that we will use to list the category names. In this one I have used an interesting feature of vAPI allowing to pass the categoryId parameter in the URL. In this case this is more convenient to the other regular approach which is to provide a catogory ID attribute in a JSON content.
  • deleteCisSession : Equivalent of logout : Delete the actual session meaning that a new createCisSession will have to be performed before being able to perform other requests requiring authentications.

These functions are called in order. The categoryIds are returned in a variable which are iterated in a foreach loop getting each tagCategory object and displaying its name.

You can now run your workflow with selecting the vAPI REST endpoint you added.
b_600_0_16777215_01_images_phocagallery_tutorials_vAPI-REST-03a.png


And check the logs.

vAPI-REST-04.png


In my case vRealize Orchestrator is set to use vCenter 6 SSO. I authenticated as myself in vRO. The REST plug-in passed the credentials to the vAPI endpoint to create a session and the getSession function allowed to display my username.
One category was found and displayed.

Also a feature that is very conveneient is that the API is browsable so you can use a browser and enter the https://[vcenterServer]/rest URL to find out the resources and components available in the endpoint.
{

    "value":

{

    "resources":

{

    "method": "GET",
    "href": "https://192.168.1.98/rest/com/vmware/vapi/rest/navigation/resource",
    "metadata":
        {
            "method": "GET",
            "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.vapi.rest.navigation.resource/id:list"
        }
    },
    "components":

{

    "method": "GET",
    "href": "https://192.168.1.98/rest/com/vmware/vapi/rest/navigation/component",
    "metadata":

            {
                "method": "GET",
                "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.vapi.rest.navigation.component/id:list"
            }
        }
    }

}

 So if you follow the resource link and and then tagging one you get all URLs for tags including documentation and links to metadata describing the object and the POST content format.

{

    "value": 

[

{

    "name": "get",
    "documentation": "Fetches the tag information for the given tag identifier. To invoke this {@term operation}, you need the read privilege on the tag in order to view the tag info.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

{

    "method": "GET",
    "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}"

},

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=get"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:get"
    }

},
{

    "name": "update",
    "documentation": "Updates an existing tag. To invoke this {@term operation}, you need the edit privilege on the tag.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

{

    "method": "PATCH",
    "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}"

},

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=update"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:update"
    }

},
{

    "name": "detach",
    "documentation": "Detaches the tag from the given object. To invoke this {@term operation}, you need the attach tag privilege on the tag and the read privilege on the object.",
    "service": "com.vmware.cis.tagging.tag_association",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag-association/id:{tag_id}?~action=detach"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag_association/id:detach"
    }

},
{

    "name": "attach",
    "documentation": "Attaches the given tag to the input object. To invoke this {@term operation}, you need the attach tag privilege on the tag and the read privilege on the object.",
    "service": "com.vmware.cis.tagging.tag_association",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag-association/id:{tag_id}?~action=attach"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag_association/id:attach"
    }

},
{

    "name": "revoke_propagating_permissions",
    "documentation": "Revokes all propagating permissions on the given tag. You should then attach a direct permission with tagging privileges on the given tag. To invoke this {@term operation}, you need tag related privileges (direct or propagating) on the concerned tag.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=revoke-propagating-permissions"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:revoke_propagating_permissions"
    }

},
{

    "name": "remove_from_used_by",
    "documentation": "Removes the {@param.name usedByEntity} from the {@link TagModel#usedBy} subscribers set. If the {@param.name usedByEntity} is not using this tag, then this becomes a no-op. To invoke this {@term operation}, you need modify {@link TagModel#usedBy} privilege on the tag.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=remove-from-used-by"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:remove_from_used_by"
    }

},
{

    "name": "add_to_used_by",
    "documentation": "Adds the {@param.name usedByEntity} to the {@link TagModel#usedBy} subscribers {@term set}. If the {@param.name usedByEntity} is already in the {@term set}, then this becomes a no-op. To invoke this {@term operation}, you need the modify {@link TagModel#usedBy} privilege on the tag.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=add-to-used-by"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:add_to_used_by"
    }

},
{

    "name": "list_attached_objects",
    "documentation": "Fetches the {@term list} of attached objects for the given tag. To invoke this {@term operation}, you need the read privilege on the input tag. Only those objects for which you have read privileges will be returned.",
    "service": "com.vmware.cis.tagging.tag_association",
    "links": 

[

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag-association/id:{tag_id}?~action=list-attached-objects"
    }

],
"metadata": 

    {
        "method": "GET",
        "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag_association/id:list_attached_objects"
    }

},
{

    "name": "delete",
    "documentation": "Deletes an existing tag. To invoke this {@term operation}, you need the delete privilege on the tag.",
    "service": "com.vmware.cis.tagging.tag",
    "links": 

[

{

    "method": "DELETE",
    "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}"

},

    {
        "method": "POST",
        "href": "https://192.168.1.98/rest/com/vmware/cis/tagging/tag/id:{tag_id}?~action=delete"
    }

],
"metadata": 

            {
                "method": "GET",
                "href": "https://192.168.1.98/rest/com/vmware/vapi/metadata/metamodel/service/operation/id:com.vmware.cis.tagging.tag/id:delete"
            }
        }
    ]

}

 

 I hope this is a good introduction for leveraging vAPI. You can explore the current features documented in the VMware vCloud Suite SDK for REST and expect the APIs covered to grow over the next vAPI releases.