01/15/2017 Leave a comment
*Note:Some basic introductory knowledge of Python will be assumed for these blogposts. If you’re new to Python, I suggest having a look at Al Sweigart’s Automate The Boring Stuff online book for a beginner’s guide. Also, I’ll come back and update this post with real working code samples that you can try it yourself for now, I’m using loose Python-like syntax to demonstrate the behavior rather than the individual lines of code.
PrefaceOver the last year, I’ve had a shift in focus (mostly thanks to $DAYJOB) in my daily responsibilities to focus inthe datacentre space of networking. The largest partof these newresponsibilities has been improving our deployments of network gear, reducingtime-to-market and eliminating as many common errors caused by these rollouts as possible. Without too many details, these implementations are accomplished via manual work of logging into devices, applying a configuration and verifying the change was completed successfully. This involves allcopy-pasta of CLI commands and has caused our department myriads of headaches (and late night phones calls).
Hardware & SoftwareWe needed to deploy a lot of new gear. This was brought up by the common enterprise IT culpritsfor putting in new kit, such as end-of-life concerns and adding capacity to the current network. I’m sure everyone has been in the same planning meetings, where you needed toreplace lots of old, aging and creaky equipment as well as addressing new business requirements.
The platform of our choice has been the Cisco Nexus 9300line of top-of-rack switches. After rolling the standard 3-tier network architecture for many years, we decided to switch gears and build a modern leaf-spine Clos fabric using Cisco’s VXLAN EVPN technology. This has given us great capacityfor 10G server access and scalability using 40G & 100G in the spine & core layers.
The best benefit of rolling with this technology&newer hardware has been the programmatic interfaces that Cisco has included. This comes to us in the form of NX-API.
NX-APICisco’s NX-API is an interface that allows an operator to interact with a Nexus devicevia standard HTTP calls. While this by itself doesn’t seem to be all that useful (and admittedly for most network engineers, adds complexity to their workflow) , the real benefitscomes in with the way in which NX-API interacts with the device’s state.
Takethis simple example of a manual CLI configuration:
switch1# conf tswitch1(config)# vlan 100
switch1(config-vlan)# name MyVlan100
switch1(config)# interface Vlan100
switch1(config-if)# ip address 10.0.100.1/24
switch1(config-if)# no ip redirects
switch1(config-if)# ip arp timeout 300
switch1(config-if)# int Ethernet1/4
switch1(config-if)# switchport mode trunk
switch1(config-if)# switchport trunk allowed vlan 100
How would you typically verify this config was applied?
show vlan id 100show interface Vlan100
show interface Ethernet1/4
All good so far. Now, imagine you had to verify this configuration was active on 5 devices? What about 50 devices? You can see how this starts to get unruly extremely quickly. Unfortunately, this has been the reality for most network engineers for decades as our vendors have not exposed interfaces for us to interact with that return this in a structured manner.
Let’s say you had 36 Cisco Nexus 9K’s that you wanted to verify this exact configuration. With NX-API, you use these same commands and get something back like this:
{ 'result':[
{ 'body':
[
{ 'TABLE_VLAN':
[
{ 'ROW_VLAN':
[
{ 'vlan_id': 100,
'vlan_name': 'MyVlan100',
'vlan_state': 'active' } ],
...
}
]
}
]
}
]
}
This particular example shows that same “show vlan id 100” command but formatted in JSON. NX-API takes most common CLI commands and puts wrappers on individual components of the data that is typically shown in the CLI output. What this allows you to do is, using a scripting language, to drill down to exactly the data you want and present it in a deterministic fashion. For example, the ‘vlan_id’ will always return an integer of 1-4095, ‘vlan_name’ will return a string of characters representing the name of the VLAN, etc. This means that you perform basic operations such as checking that the VLAN ID is greater than or less than a given number, that the VLAN name conforms to some pattern that you set for your naming conventions or that it’s even active at all. Here’s how you might use it in a script:
#!/usr/bin/env pythonimport requests # HTTP library
def send_nxapi_request(username, password, mgmt_ip_address, command):
'''
Example function to send HTTP GET to NX-API
'''
url = 'http://{}/ins'.format(mgmt_ip_address)
myrequestpayload = [
# JSON formatted payload to send to the switch
# Includes some control info such as API version, etc.
return requests.get('http://10.0.0.1/ins', auth=(username,password), payload=myrequestpayload))
result = send_nxapi_command('user123', 'Password1', '10.1.1.1', 'show vlan id 100')
for item in result['result']['body']['TABLE_VLAN']['ROW_VLAN']:
print("VLAN ID: %s" % item['vlan_id'])
print("VLAN Name: %s" % item['vlan_name'])
Another nice feature of this API is the Cisco NX-API Sandbox. So long as you’ve added “feature nxapi” to the device configuration, you can open up your web browser and log straight into this sandbox (by simply opening http://switch_ip_address/ ). The Sandbox will also give you example code snippets for building the proper payload and sending this HTTP request in Python so you can put in your scripts. Check out Cisco’s Programmability Guide for a quick guide on using the Sandbox .
Once you have a few working scripts that are using the API, you can even send the configuration commands using similar HTTP calls. I’ll cover this more on a later post.
Consistent DataWith an API such as this available, you can combine your CLI-fu with some basic knowledge of Python data types to interact with a device in interesting ways. For example, if you pulled the list of BGP peers via “show ip bgp summary” and the returned JSON data, you can loop through what is essentially a list of dictionaries to pull out the data you’re looking to extract. That is what you see in the for loop in the code sample above.
for each list item in result['key1']['key2']['key3']['list_of_peers']:# Whenever we loop over this particular list of dictionaries ('list_of_peers'),
# the dictionary I get back will almost always have the same set of keys that I can access
print(item['bgp_peer_ip'])
print(item['bgp_asn'])
print(item['bgp_prefixes_received']) Every time you send that command to any switch, you’re always going to receive the same nested list of key-value pairs, to which you can loop over quickly in Python and pull out only the data you c