Monday 26 February 2018

Understanding The VMware REST API Interface


SOAP (Simple Object Access Protocol) is a protocol for accessing web services originally developed by Microsoft. It is based exclusively on XML and was developed because the existing messaging services that Microsoft used as DCOM (Distributed Component Object Model) and COBRA (Common Object Request Intermediary Architecture) did not work as well on the Internet. After developing SOAP, Microsoft transferred it to the IEFT, which standardized it, generating a series of other web service standards such as WS-Federation, WS-Addressing, WS-Policy, WS-Security, etc., which are defined using WSDL. (Web services description language). REST is basically a lightweight alternative to SOAP that uses HTTP 1.1 methods such as GET, POST, PUT and DELETE to make requests instead of XML, which uses SOAP. There are many web services based on REST that can generate data in simple formats such as CSV, JSON and even RSS. This makes REST much easier to use, for example, when JavaScript is used to develop a dynamic website or web application. VMware vSphere 6.5 introduces a number of new REST-based APIs in addition to the existing SOAP APIs on the platform. This may indicate that VMware considers REST as the future over SOAP. To help us understand the VMware REST API and how to use it, I asked Luc Dekens to share his experience with us. Luc is a vExpert and MVP and is interested in automating everything, and more specifically through PowerShell and PowerCLI. He is a co-author of the PowerCLI reference and a regular conference speaker. You can read Luc's blog here, or follow him on Twitter as @ LucD22.

The REST API in practice

In recent years, more and more providers are announcing that they are providing a RESTful interface for their products. On the risk of starting a flame war in the introductory section of this article, I tend to disagree with most of these vendors.

No, it is not providing a RESTful interface for your product; you are following some REST rules in your new API. But hey, what you are providing definitely makes my life as a consumer of your product much simpler and easier.

What is API REST?

So, why the statement in bold in the previous section? As in most cases, there is a difference between theory and practice, and more specifically between a RESTful interface and the basic REST rules. While the RESTful theory, as Roy T. Fielding originally described, in chapter 5 of his doctoral dissertation "Architectural Styles and the Design of Network-Based Software Architectures" and more specifically in his blog post, "REST APIs must be based on hypertext, "Highlights the importance of the ability to discover the actions that a client can perform on an object, most implementations of REST seem to deviate from this principle.

As a practical example, take a virtual machine, with the GET action we can request information about that virtual machine, but it will be the client who needs to interpret the PowerState returned from the Virtual Machine, "on" or "off", to determine if it can POST a "off" or "on" action.

In a REST API that offers a RESTful interface, it must comply with the HATEOAS restrictions (Hypermedia as the application state engine). More specifically, the object returned by the GET verb must contain the "links" to the actions that are available in the object in its current state.

Why use REST API?

Does the above mean that we should not use REST API that is not "pure", also known as RESTful? Of course, no. The REST API offers more than just the possibility of discovering actions for the consumer. And on the provider side (the provider), it offers the possibility to simplify its API. In addition, since most REST API implementations predominantly use HTTP (S) as a protocol, it also makes a REST-based architecture more manageable for network equipment. And finally, our preferred automation language, PowerShell, makes consuming REST API through HTTP quite easy with the Invoke-RestMethod and Invoke-WebRequest cmdlets.

VMware and REST API


There may be several reasons, and that is my personal interpretation, VMware will choose the REST API to replace its SOAP API. One of them would definitely be that it allows VMware to switch to Swagger to define its API in a much simpler way than what used to be the case with WADL (Web Application Design Language) for its SOAP API.

As an example, the Explorer API that is available from VMware vCenter 6.5 is a direct result of this change to Swagger. This API Explorer available in vCenter allows one to explore and "test" the available REST API, without code:


Call the REST API

We already mentioned that PowerShell has integrated cmdlets to work with the REST API, but there are some complexities that one needs to understand to make a valid call to a REST API:


In the VMware REST API that is available in vCenter 6.5, there is an API that allows one to retrieve (Get) all the available commands (/com/vmware/vapi/metadata/cli/command), and then through a second API to fetch (Post) more details on the parameters that are available (rest/com/vmware/vapi/metadata/cli/command?~action=get).

A simple script to use these two API could look like this.

$vCenterName = ‘vcsa.local.lab’
$auth = @{
‘vmware-api-session-id’ = ‘8d377b7ee718b9fdead33d8f3230019c’
}
$get_command = "https://$($vCenterName)/rest/com/vmware/vapi/metadata/cli/command"
$get_Command_detail = "https://$($vCenterName)/rest/com/vmware/vapi/metadata/cli/command?~action=get"
# Get all command identifiers
$result = Invoke-RestMethod -Uri $get_command -Headers $auth
$sRest = @{
Method = ‘Post’
Uri = $get_Command_detail
Body = ‘‘
Headers = $auth
ContentType = ‘application/json’
}
# Get more information about each command
$report = @()
foreach($command in $result.value){
$sRest.Body = @{‘identity’ = $command} | ConvertTo-Json
$detail = Invoke-RestMethod @sRest
$report += $detail.value.options |
Select-Object @{N=‘Path’;E={$command.path}},
@{N=‘Operation’;E={$command.name}},
@{N=‘Description’;E={$detail.value.description}},
@{N=‘Field’;E={$_.field_name}},
@{N=‘Type’;E={$_.type}},
@{N=‘Field Descriptoin’;E={$_.description}}
}
$report | Export-Csv -Path C:\Temp\rest-commands.csv -UseCulture

No matter the $ auth header for now, it is the way to refer to a session that we opened earlier. We'll come back to that later.

There are two calls Invoke-RestMethod in this fragment. The first uses the API to retrieve all commands, the second uses the API to get more details for each command. Note that in the second call to Invoke-RestMethod, the fragment uses "splatting" to pass the parameters to the cmdlet. This is my personal preference, since I think it makes the code more readable and organized.

Since we are using the Invoke-RestMethod cmdlet, we will have to convert the objects that we pass in the Body to the correct format. In this case, we convert to JSON. The resulting CSV file contains information about all the commands (330 at the time of writing this) and the parameters available for each command:


Note that this contains the same information that we also find in the API Explorer for this specific call:



Invoke-RestMethod vs. Invoke-WebRequest

In the previous fragment, we use the Invoke-RestMethod cmdlet to make the call to the REST API. Could we have done it through the Invoke-WebRequest cmdlet?

Of course, both cmdlets can be used to execute REST API calls, but there are some differences that you should keep in mind. I prefer to classify Invoke-WebRequest as "raw", while Invoke-RestMethod is "interpreted". A sample should make it clear. The following are two calls to the same REST API:

$vCenterName = ‘vcsa.local.lab’
$auth = @{
‘vmware-api-session-id’ = ‘10e8e9965cd56bdd5478951fbe52c33f’
}
$uri = "https://$($vCenterName)/rest/com/vmware/cis/session?~action=get"
$sRest = @{
Uri = $uri
Method = ‘Post’
Headers = $auth
}
$resultRest = Invoke-RestMethod @sRest
$resultWeb = Invoke-WebRequest @sRest
The Invoke-RestMethod returns "interpreted" data, as we can see when we expand the result:
value
-----
@{created_time=2017-12-04T12:38:11.232Z; last_accessed_time=2017-12-04T12:49:38.850Z; user=Administrator@VSPHERE.LOCAL}
The Invoke-WebRequest returns the "raw" data. Notice how the "Content" property contains the (only) data we see with the Invoke-RestMethod:
StatusCode : 200
StatusDescription : OK
Content : {"value":{"created_time":"2017-12-04T12:38:11.232Z","last_accessed_time":"2017-12-04T12:49:38.859Z","user":"Administrator@VSPHERE.LOCAL"}}
RawContent : HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Date: Mon, 04 Dec 2017 12:49:38 GMT
{"value":{"created_time":"2017-12-04T12:38:11.232Z","last_accessed_time":"2017-12-04...
Forms : {}
Headers : {[Transfer-Encoding, chunked], [Content-Type, application/json], [Date, Mon, 04 Dec 2017 12:49:38 GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 138

What happens is that the Invoke-RestMethod cmdlet does a lot of work for you. This includes translating StatusCode into an error message, when it is not 200. But it also converts JSON into a PowerShell object.

The cmdlet that I use is a matter of choice, but my personal preference is to use the Invoke-RestMethod cmdlet. Why would you recode part of the functionality that is available in the cmdlet in each and every one of your scripts?

Authentication

Since we want to make secure calls to SOAP and REST API, both have implemented an authentication mechanism. Our scripts using SOAP API calls use the same session that we established with the Connect-VIServer cmdlet.

For calls from the REST API to vSphere, we also need to authenticate. That is done, in what other way? Through a specific REST API call. We convert our credentials into a "basic" Base64 chain:

$user = ‘administrator@vsphere.local’
$pswd = ‘VMware1!’
$vCenterName = ‘vcsa.local.lab’
$encoded = [System.Text.Encoding]::UTF8.GetBytes(($user, $pswd -Join ‘:’))
$encodedPassword = [System.Convert]::ToBase64String($Encoded)
$authHeader = @{
Authorization = "Basic $($EncodedPassword)"
}
$sRest = @{
Method = ‘Post’
Uri = "https://$($vCenterName)/rest/com/vmware/cis/session"
Headers = $authHeader
}
$result = Invoke-RestMethod @sRest

Once we created a session this way, we can use the returned session token in future calls to the REST API. This authorization header can be created as follows. Note the nonstandard way (‘vmware-api-session-id’) VMware selected to name the session token header field:

$authHeader = @{
‘vmware-api-session-id’ = $result.value
}

We’ll follow this article with another soon that shows how to perform the same action twice, once via the SOAP API and once via the REST API. The differences between these two should make it clear where your gain, as the consumer/coder, is located.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.