{"id":3407,"date":"2015-08-01T23:16:32","date_gmt":"2015-08-02T05:16:32","guid":{"rendered":"http:\/\/benincosa.com\/?p=3407"},"modified":"2015-08-01T23:36:07","modified_gmt":"2015-08-02T05:36:07","slug":"go-with-nx-api","status":"publish","type":"post","link":"https:\/\/benincosa.com\/?p=3407","title":{"rendered":"Go with NX-API"},"content":{"rendered":"<p>I&#8217;ve been working on a <a href=\"https:\/\/github.com\/vallard\/stickypipe-agent\">project<\/a> to collect data from Cisco Nexus switches. \u00a0I first tackled making SNMP calls to collect counter statistics but then I thought, why not try it with the NX-API that came with the Nexus 9ks?<\/p>\n<p>The <a href=\"http:\/\/www.cisco.com\/c\/en\/us\/td\/docs\/switches\/datacenter\/nexus9000\/sw\/6-x\/programmability\/guide\/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide\/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Configuration_Guide_chapter_0101.pdf\">documentation for the APIs<\/a> I hoped would be better, but <a href=\"https:\/\/github.com\/datacenter\/nexus9000\/\">the samples on github<\/a> were enough to get anybody going&#8230; as long as you do it in Python. \u00a0But these days the cool systems programmers are moving to Go for several reasons:<\/p>\n<ol>\n<li>The concurrency capabilities will take your breath away. \u00a0Yes, I go crazy with all the <a href=\"https:\/\/gobyexample.com\/goroutines\">go routines<\/a> and channels. \u00a0Its a really cool feature in the language and great for when you want to run a lot of tasks in parallel. \u00a0(Like logging into a bunch of switches and capturing data maybe? )<\/li>\n<li>The binaries can be distributed without any hassle of dependencies. \u00a0Why does this matter? \u00a0Well, for example, if I want to run the python-novaclient commands on my machine, I have to first install python and its dependencies, then run pip to install the packages and those dependencies. \u00a0I&#8217;m always looking for more lube to make trying new things out easier. \u00a0Static binaries ease the friction.<\/li>\n<\/ol>\n<p>After playing around with the switch I finally got something working so I thought I&#8217;d share it. \u00a0The code I developed is on my <a href=\"https:\/\/github.com\/vallard\/stickypipe-agent\">sticky pipe project<\/a>. \u00a0For the TL;DR version of this post check out the function getNXAPIData for the working stuff. \u00a0The rest of this will walk through making a call to the switch.<\/p>\n<h3>1. \u00a0Get the parameters.<\/h3>\n<p>You&#8217;ll have to figure out a way to get the username and password from the user. \u00a0In my program I used environment variables, but you may also want to take command line variables. \u00a0There are lots of places on the internet you can find that so I&#8217;m not going into that with much detail other than something simple like:<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true\">func main() {\r\n  \/\/ some stuff above here...\r\n\r\n  \/\/ Get all the parameters the user entered.  os.Args[0] is the program\r\n  \/\/ name, so get everything from os.Args[1] to the end.  \r\n  argArray := os.Args[1:]\r\n\r\n  \/\/ make sure there are at least 3 arguments\r\n  if len(argArray) &lt; 3 { \r\n    fmt.Println(\"please enter the switch, username, and password\")\r\n    return\r\n  }\r\n  \/\/ more stuff\r\n}<\/pre>\n<h3>2. Create the message to send to the Nexus<\/h3>\n<p>The NX-API isn&#8217;t a\u00a0RESTful API. \u00a0Instead, you just enter Nexus commands like you would if you were on the command line. \u00a0The \u00a0NX-API then responds with output back in JSON notation. \u00a0You can also get XML, but why in the world would you do that to yourself? \u00a0XML was cool like 10 years ago, but let&#8217;s\u00a0move on people! \u00a0There&#8217;s a JSON RPC format, but I don&#8217;t get what this gives you that JSON doesn&#8217;t other than order by adding flags to order things. \u00a0Stick with JSON and your life will not suck.<\/p>\n<p>Here&#8217;s how we do that:<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true\">command := \"show interface brief\"\r\nvar jsonStr = []byte(`{\r\n  \"ins_api\": {\r\n    \"version\":       \"1.0\",\r\n    \"type\":          \"cli_show\",\r\n    \"chunk\":         \"0\",\r\n    \"sid\":           \"1\",\r\n    \"input\":         \"` + command + `\", \r\n    \"output_format\": \"json\",\r\n  }\r\n}`)<\/pre>\n<p>This format of a message seems to handle any JSON that we want to throw at the Nexus. \u00a0This is really all you need to send your go robots forth to manage your world.<\/p>\n<h3>3. \u00a0Connect to the Nexus Switch<\/h3>\n<p>I start by creating an http.NewRequest. \u00a0The parameters are<\/p>\n<ul>\n<li>POST &#8211; This is the type of HTTP request I&#8217;m sending<\/li>\n<li>The switch &#8211; This needs to be either http or https (I haven&#8217;t tried with https yet). \u00a0Then the switch IP address (or hostname) has to be terminated with the \/ins directory. \u00a0See the example below.<\/li>\n<li>The body of the POST request. \u00a0This is the bytes.NewBuffer(jsonStr) that we created in the previous step.<\/li>\n<\/ul>\n<pre class=\"theme:solarized-dark lang:go decode:true\">\/\/ Start formatting our HTTP POST request.\r\nreq, err := http.NewRequest(\"POST\", \"http:\/\/\"+server+\"\/ins\", bytes.NewBuffer(jsonStr))\r\nif err != nil {\r\n  log.Println(\"HTTP Post: \", err)\r\n}<\/pre>\n<p>After checking for errors, now we need to set some headers, including the credentials.<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true \">\/\/ The header has to be set to application\/json\r\nreq.Header.Set(\"content-type\", \"application\/json\")\r\n\r\n\/\/ add the username and password to the header.\r\nreq.SetBasicAuth(user, password)<\/pre>\n<p>This header also tells us that we are talking about JSON data.<\/p>\n<p>Finally, we make the request and check for errors, etc:<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true \">\/\/ create a new http client to execute the request.\r\nclient := &amp;http.Client{}\r\n\/\/ execute the request.\r\nresp, err := client.Do(req)\r\nif err != nil {\r\n  log.Fatal(\"response error: \", err)\r\n}\r\ndefer resp.Body.Close()<\/pre>\n<p>That last line with the defer statement closes the connection after we leave this function. \u00a0Launching this should actually get you the command executed that you are looking to do. \u00a0Closing is important cause if you&#8217;re banging that switch a lot, you don&#8217;t want zombie connections blocking your stack. Let him that readeth understand.<\/p>\n<h3>Step 4: Parse the Output<\/h3>\n<p>At this point, we should be able to get something to talk to the switch and have all kinds of stuff show up in the rest.Body. \u00a0You can see the raw output with something like the following:<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true\">body, err := ioutil.ReadAll(resp.Body)\r\nfmt.Println(\"responseBody:\", string(body))<\/pre>\n<p>But most likely you&#8217;ll want to get the information from the JSON output. \u00a0To do that I created a few structures that this command should respond back with nearly every time. \u00a0I put those in a separate class called nxapi. \u00a0Then I call those from my other functions as will be shown later. \u00a0Those structs are:<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true \">\/* These structures show the type of response we get back from\r\nThe NXAPI.  This could be quite big.  The Output is the same up until the\r\nbody.  That is where the outputs differ depending on which command is given.\r\n*\/\r\ntype NXAPI_Response struct {\r\n  Ins_api Ins_API\r\n}\r\n\r\ntype Ins_API struct {\r\n  Type    string\r\n  Version string\r\n  Sid     string\r\n  Outputs map[string]Output\r\n}\r\n\r\ntype Output struct {\r\n  Input string\r\n  Msg   string\r\n  Code  string\r\n  Body  map[string]interface{}\r\n}<\/pre>\n<p>These structs map with what the NXAPI usually always returns in JSON:<\/p>\n<pre class=\"theme:solarized-dark lang:js decode:true \">{\r\n  \"ins_api\": {\r\n    \"type\": \"cli_show\",\r\n    \"version\": \"1.0\",\r\n    \"sid\": \"eoc\",\r\n    \"outputs\": {\r\n      \"output\": {\r\n         ... output varies from here\r\n       }\r\n    }\r\n  }\r\n}<\/pre>\n<p>The outputs may also be an array if there are multiple commands entered. \u00a0(At least that&#8217;s what I saw via the NX-API developer sandbox. \u00a0 If there is an error then instead of Body for the output you&#8217;ll see &#8220;clierror&#8221;)<\/p>\n<p>So this should be mostly type safe. \u00a0It may be better to omit the Body from the type Output struct.<\/p>\n<p>Returning to our main program, we can get the JSON data and load it into a struct where we can parse through it.<\/p>\n<pre class=\"theme:solarized-dark lang:go decode:true\">\/\/ Create the structure to store the output in. \r\nvar rr nxapi.NXAPI_Response\r\n\/\/ unmarshall the JSON output into the variable. \r\nerr = json.Unmarshal(body, &amp;rr)\r\nif err != nil {\r\n  log.Fatal(\"Error unmarshalling: \", err)\r\n}\r\n\/\/ Now we can go through each outputs and get what we want\r\n\/\/ We find the output we expect by using the Input field:\r\n\r\nfor _, b := range rr.Ins_api.Outputs {\r\n  if b.Input == \"show version\" {\r\n    fmt.Println(b.Body)\r\n    fmt.Println(b.Body[\"host_name\"])\r\n  }\r\n}<\/pre>\n<p>In the above command, I&#8217;m looking to parse output from the &#8220;show version&#8221; command. \u00a0When I find that the input was the show version command, then I can use the keys from the body to get information from what was returned to us by the switch. \u00a0In this case the output will give us the hostname of the switch.<\/p>\n<h3>Conclusion<\/h3>\n<p>This brief tutorial left out all the go routines and other fanciness of Go in order to make it simple. \u00a0Once you have this part, you are ready to write a serious monitoring tool or configuration tool. \u00a0Armed with this, you can now make calls to the NX-API using Go. \u00a0Do me a favor and let me know <a href=\"https:\/\/twitter.com\/vallard\">on twitter<\/a> if this was useful to you! \u00a0Thanks!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working on a project to collect data from Cisco Nexus switches. \u00a0I first tackled making SNMP calls to collect counter statistics but then I thought, why not try it with the NX-API that came with the Nexus 9ks? The documentation for the APIs I hoped would be better, but the samples on github&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[990,166,778,148],"tags":[991,779,780,781],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3407"}],"collection":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3407"}],"version-history":[{"count":3,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3407\/revisions"}],"predecessor-version":[{"id":3435,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3407\/revisions\/3435"}],"wp:attachment":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3407"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3407"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}