Exploring the Nexus 1000 V REST API

Out with the old and in with the new and honestly I couldn’t be happier with the new that’s coming in. What is the new that I’m talking about? The Nexus 1000V REST API of course.

I just finished writing scripts to manage (create, modify, delete) vlans and port-profiles on a Nexus 1000V using expect. The scripts work fine, I’m using PowerShell as the main script and it calls out to expect and ssh running in a Cygwin environment, however it would be nice to use the REST API, and do everything from PowerShell or the language of your choice.

The customer I did the work for has multiple 1000V deployments and wanted to automate some aspects of the 1000V administration. Vlan provisioning and port-profile creation seemed to be obvious choices.

I wrote an expect script to check for the existence of one or more vlans and an expect script to check for the existence of a specific port-profile. Here’s the important part of that code;

1
2
3
4
5
6
7
8
9
10
11
12
13

set vlans [split $vlanIDs ","]
foreach vlan $vlans {
set chkVlanCmd "show vlan brief | egrep '^$vlan ' | count | sed 's/^/$vlan:/' | no-more"
send "$chkVlanCmd\r"
expect {
":0" {
puts "$vlan:0"
}
":1" {
puts "$vlan:1"
}
}
}

$vlanIDs is set from the command line it is a commas separated list of vlans to check for, the script loops through them one by one, using the “show vlan brief” command to display the vlan. Additionally the script uses the Nexus command line capabilities to run egrep, count and sed on the output of “show vlan brief”
If the input vlan list was “100,101,200,201” and vlan 100 and 201 exist then output of the script will be

100:1
101:0
200:0
201:1

Similarly the input to the expect script that checks for the existence of the port-profile will output the port-profile name with either a :0 or :1 appended to it, :0 for not found and :1 for found.

I call the expect scripts from a PowerShell and use the formatted output from expect to make logic decisions in the PowerShell script. With the new Nexus 1000V REST API I can run everything from PowerShell and get the results I need to make the correct decisions to either add the vlan or not, or to create the port-profile or just update it.

To retrieve Vlans via the Nexus 1000V REST API for the VMware version of the 1000V this code does the trick.

1
2
3
4

$n1kUser = "admin"
$n1kPass = ConvertTo-SecureString -String "admin" -AsPlainText -Force
$n1kCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $n1kUser, $n1kPass
$n1kVlans = Invoke-RestMethod -Uri http://172.21.67.89/api/vlan -Credential $n1kCred

The command will return either XML or JSON, by default XML is returned.  The retuned XML looks like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<?xml version="1.0" encoding="utf-8"?>
<set name="vlan_set">
<instance name="208" url="/api/vlan/208">
<properties>
<id>208</id>
<state>active</state>
<name>VLAN0208</name>
<shutdown>false</shutdown>
</properties>
</instance>
<instance name="610" url="/api/vlan/610">
<properties>
<id>610</id>
<state>active</state>
<name>VLAN0610</name>
<shutdown>false</shutdown>
</properties>
</instance>
</set>

Then I could do the following to list the returned Vlans

$n1kVlans.set.instance.properties.id
208
610

You could specify -Headers @{“Accept”=”application/json”} and get back JSON instead. Unfortunately there is a bug in PowerShell 3.0 that does not allow for the manipulation of the some web request headers, Accept being one of them. The bug has been reported as fixed for PowerShell 4.0. That does not mean that PowerShell 3.0 cannot be used to retrieve JSON output, it can, just not via the Invoke-RestMethod cmdlet.

This code will produce JSON output, using basic web authentication.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

function reqREST ()
{
param(
[string] $inUri = $null,
[string] $inPath = $null,
[string] $inCreds = $null,
[string] $inMethod = $null
);

$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($inCreds))
$webRequest = ""
$webRequest = [System.Net.WebRequest]::Create( "$inUri/$inPath")
$webRequest.PreAuthenticate = $true
$webRequest.Headers.Add('Authorization', "Basic "+$encoded)
$webRequest.Accept = 'application/json'
$webRequest.Method = $inMethod
$webRequest.GetResponse()
[Net.HttpWebResponse] $result = $webRequest.GetResponse()
[IO.Stream] $stream = $result.GetResponseStream()
[IO.StreamReader] $reader = New-Object IO.StreamReader($stream)
[string] $output = $reader.ReadToEnd()
$stream.Flush()
$stream.Close()
$output
}

reqREST "http://172.21.67.89" "api/vlan" "admin:admin" "GET"

Produces this output

1
2
3
4
5
6
7

{"208":{"url":"/api/vlan/208","properties":{"id":208,"state":"active","name":"VLAN0208","shutdown":false}},
"610":{"url":"/api/vlan/610","properties":{"id":610,"state":"active","name":"VLAN0610","shutdown":false}},
"613":{"url":"/api/vlan/613","properties":{"id":613,"state":"active","name":"VLAN0613","shutdown":false}},
"1":{"url":"/api/vlan/1","properties":{"id":1,"state":"active","name":"default","shutdown":false}},
"611":{"url":"/api/vlan/611","properties":{"id":611,"state":"active","name":"VLAN0611","shutdown":false}},
"33":{"url":"/api/vlan/33","properties":{"id":33,"state":"active","name":"VLAN0033","shutdown":false}},
"614":{"url":"/api/vlan/614","properties":{"id":614,"state":"active","name":"VLAN0614","shutdown":false}}}

A port-profile request and it’s output

1

reqREST "http://172.21.67.89" "api/port-profile" "admin:admin" "GET"

1
2
3
4

{ "Unused_Or_Quarantine_Uplink":{"url":"/api/port-profile/Unused_Or_Quarantine_Uplink","properties":{"minPorts":1,"description":"Port-group created for Nexus1000V internal usage. Do not use.","capability":"","state":false,"name":"Unused_Or_Quarantine_Uplink","shutdown":true,"portGroupName":"Unused_Or_Quarantine_Uplink","portBinding":"static","maxPorts":32,"type":"Ethernet"}},
"prd-clnt-uplink":{"url":"/api/port-profile/prd-clnt-uplink","properties":{"minPorts":1,"description":"","switchportMode":"trunk","capability":"","state":false,"type":"Ethernet","name":"prd-clnt-uplink","shutdown":false,"portGroupName":"prd-clnt-uplink","portBinding":"static","maxPorts":32,"switchportTrunkVLANs":[10]}},
"Unused_Or_Quarantine_Veth":{"url":"/api/port-profile/Unused_Or_Quarantine_Veth","properties":{"minPorts":1,"description":"Port-group created for Nexus1000V internalusage. Do not use.","capability":"","state":false,"name":"Unused_Or_Quarantine_Veth","shutdown":true,"portGroupName":"Unused_Or_Quarantine_Veth","portBinding":"static","maxPorts":32,"type":"Vethernet"}},
"profile-02":{"url":"/api/port-profile/profile-02","properties":{"minPorts":1,"description":"","switchportMode":"access","state":false,"name":"profile-02","capability":"","portGroupName":"","portBinding":"static","maxPorts":32,"type":"Vethernet"}}}

These two examples should show you how to eliminate the need for expect and ssh to query information on the Nexus 1000V. Additionally there is a CLI capability in the REST API. The Nexus 1000V REST API user guide, for the VMware version of the Nexus 1000V REST API has an example call to the CLI to save the running configuration.

curl -u username:password 10.10.10.2/api/cli -d ‘{“cmd”: “copy r s”}’ –i

This command will copy the running-config to the startup-config. The example uses curl but it easily translates to the Invoke-RESTMethod

Invoke-RestMethod -Uri http://172.21.67.89/api/cli -Credential $n1kCred -Method POST -Body ‘{“cmd”: “copy r s”}’

But it seems as if any cli command will work, e.g. “show vlan brief”

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/cli -Credential $n1kCred -Method POST -Body '{"cmd": "show vlan brief"}'

VLAN Name Status Ports
---- -------------------------------- --------- -------------------------------
1 default active
33 VLAN0033 active
208 VLAN0208 active
610 VLAN0610 active
611 VLAN0611 active
613 VLAN0613 active
614 VLAN0614 active

So you can still sort of have your expect, if you were thinking you would miss it. I showed how to query vlans and port-profiles and get back XML or JSON, but what about creating them? The process is outlined in the user guide using curl for a port-profile. Showing a creation, an update and a deletion.

curl -X POST -u admin:password 10.10.10.2/api/port-profile -d ‘{ “name” : “profile1”, “switchportMode” : “access”, “shutdown” : “false”}’

Successfully created “port-profile profile1”

curl -X POST -u admin:password 10.10.10.2/api/port-profile/profile1 -d ‘{ “switchportMode” : “trunk”, “shutdown” : “false”}’

Successfully modified “port-profile profile1”

curl -u admin:password -X DELETE 10.10.10.2/api/port-profile/profile1

Successfully deleted “port-profile profile1”

In PowerShell it is an easy translation.

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/port-profile -Credential $n1kCred -Method POST -Body ‘{ “name” : “profile1”, “switchportMode” : “access”, “shutdown” : “false”}’
Successfully created “port-profile profile1”

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/port-profile/profile1 -Credential $n1kCred -Method POST -Body ‘{ “switchportMode” : “trunk”, “shutdown” : “false”}’
Successfully modified “port-profile profile1″

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/port-profile/profile1 -Credential $n1kCred -Method DELETE”

Successfully deleted “port-profile profile1”

The same sequence for a VLAN, vlan 2020

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/vlan -Credential $n1kCred -Method POST -Body ‘{ “id” : “2020”, “name” : “vlan2020”, “state” : “active”, “shutdown” : “false”}’
Successfully created “vlan 2020”

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/vlan/2020 -Credential $n1kCred -Method POST -Body ‘{ “id” : “2020”, “name” : “vlan2020”, “state” :”active”, “shutdown” : “true”}’
Successfully modified “vlan 2020”

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/vlan/2020 -Credential $n1kCred -Method DELETE
Successfully deleted “vlan 2020”

When creating or modifying an entity, whether it is a VLAN or a port-profile there are available attributes that can be specified. To get those attributes access the metadata for the entity.

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/vlan?meta -Credential $n1kCred
Invoke-RestMethod : Querying metadata is not implemented for MIME type “application/xml”.

The error is because metadata comes back as JSON, let’s try specifying a Content Type

PS C:\Windows\system32> Invoke-RestMethod -Uri http://172.21.67.89/api/vlan?meta -Credential $n1kCred -ContentType “applicaiton/json”
Invoke-RestMethod : Querying metadata is not implemented for MIME type “application/xml”.

But that does not help because -Content-Type specifies the type of content that is being sent, it is the Accept header that allows for JSON to be returned and we will need to utilize the PowerShell work around from above for the Accept header.

PS C:\Windows\system32> reqREST “http://172.21.67.89” “api/vlan?meta” “admin:admin” “GET”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

{
"class": {
"operations": {
"get": "true",
"create": "true",
"enum": "true",
"query": "false",
"set": "true",
"delete": "true"
},
"key": "id",
"properties": {
"shutdown": {
"attrib": "rw",
"type": "boolean",
"doc": "Shut State of the vlan"
},
"name": {
"attrib": "rw",
"type": "string",
"doc": "Name of the Vlan (should be unique). Set null value to remove."
},
"state": {
"attrib": "rw",
"type": "string",
"doc": "set active or suspended state for the vlan. Set null value to remove."
},
"id": {
"attrib": "rw",
"type": "number",
"doc": "Vlan id <1-3967,4048-4093>"
}
},
"doc": "vlan document",
"classname": "vlan"
}
}

I formatted the output for easier reading, the metadata has a good amount of information in it. I would like to see it expanded in the future to include items like regular expressions and min and max length for strings. Still the Nexus 1000V REST API is significantly better than expect/ssh and brings down the number of moving parts for your scripting, automation, orchestration, etc…

As always I hope this helps in your scripting and automation efforts. Script formatting was provided by http://hilite.me/

follow @johnamcdonough