In the previous article on Cisco SD-WAN, I’ve shown you how to interact with the vManage using Postman software. Now it’s time to do some coding. We will interact with the SD-WAN using the Python requests library.
For this article, I’ve used the Cisco SD-WAN 20.4 Always On sandbox.
The code presented here is available in the GitLab repository.
Fasten your seatbelts, and let’s get straight to the code!
Devices
First of all, let’s grab a complete list of network devices from the vManage.
import requests
import json
vManage = 'https://sandbox-sdwan-2.cisco.com'
body = {
'j_username': 'devnetuser',
'j_password': 'RG!_Yw919_83'
}
session = requests.session()
auth_response = session.post(f'{vManage}/j_security_check', data=body, verify=None)
devices_response = session.get(f'{vManage}/dataservice/device', verify=None)
devices_dict = json.loads(devices_response.text)
topology = {
'vmanage': 0,
'vsmart': 0,
'vbond': 0,
'vedge': 0
}
print('--------- List of devices ---------')
for device in devices_dict['data']:
hostname = device['host-name']
device_id = device['deviceId']
status = device['status']
device_type = device['device-type']
model = device['device-model']
print(f'{hostname}({device_id}) is a {model}, and has {status} status')
topology[device_type] += 1
print('\n--------- Topology summary ---------')
for device_type in topology.keys():
print(f'We have {topology[device_type]} {device_type} in our topology')
As you could remember from the previous article, the first step is to get the cookie from the vManage. To accomplish that, we’re creating the session object and sending the first POST request containing credentials. The session object will be used for all further calls. After obtaining the cookie, we’re ready to send the first GET request.
The call result is stored in the devices_response variable. The JSON response content is stored in the devices_response.text.
To work with the response, we need to parse it to the Python data structures first. To accomplish that, we can use the built-in JSON module, which is very easy to use. Function loads take a string with JSON content and parse it to the Python data structures.
Here’s how the parsed response looks like.
We’re interested just in the data section because it contains the device list. That’s why in the for loop, we’re iterating just over the data list. The device variable in the for loop is a dictionary representing a specific device.
The script output is presented below.
--------- List of devices ---------
vmanage(10.10.1.1) is a vmanage, and has normal status
vsmart(10.10.1.5) is a vsmart, and has normal status
vbond(10.10.1.3) is a vedge-cloud, and has normal status
dc-cedge01(10.10.1.11) is a vedge-CSR-1000v, and has normal status
site1-cedge01(10.10.1.13) is a vedge-CSR-1000v, and has normal status
site2-cedge01(10.10.1.15) is a vedge-CSR-1000v, and has normal status
site3-vedge01(10.10.1.17) is a vedge-cloud, and has normal status
--------- Topology summary ---------
We have 1 vmanage in our topology
We have 1 vsmart in our topology
We have 1 vbond in our topology
We have 4 vedge in our topology
Let’s move to the running-configurations of the devices.
Configs
The situation with gathering configurations is slightly different than before. To get the device config, we need to provide a device id to the vManage in the GET request. Let’s jump to the code.
import requests
import json
vManage = 'https://sandbox-sdwan-2.cisco.com'
body = {
'j_username': 'devnetuser',
'j_password': 'RG!_Yw919_83'
}
session = requests.session()
auth_response = session.post(f'{vManage}/j_security_check', data=body, verify=None)
devices_response = session.get(f'{vManage}/dataservice/device', verify=None)
devices_dict = json.loads(devices_response.text)
configs = {}
for device in devices_dict['data']:
hostname = device['host-name']
device_id = device['deviceId']
config_response = session.get(f'{vManage}/dataservice/device/config', params={'deviceId': device_id}, verify=None)
try:
config_response.raise_for_status()
config = config_response.text
configs[hostname] = config
except requests.HTTPError as e:
print(f'Could not get config for {hostname}')
for hostname, config in configs.items():
print(f'{hostname} config:\n{config}')
The first half of the code is the same as before. Before the loop, there is a configs dictionary declared. It will store all the device configurations.
In the loop, we’re iterating through all the devices, for each device, we need just two pieces of information, the hostname, and device id.
The hostname will serve as a key in the configs dictionary. The device id is used for the further GET request, that gathers the running-config. It is placed in the params section of the request.
There’s another difference here, the try block. Each request could potentially fail due to various circumstances. After receiving the response, we can use the raise_for_status method, which will check if the call was successful. If it’s not, it will raise the HTTPError, which is handled in the code. In this case, we will receive the information that the request for a particular device failed.
But assuming that the request succeeds, we can look at its content.
Again we have the text section, which is a string without configuration. This time however it’s not a JSON. It’s just a raw string with the configuration commands, so we don’t need to parse it further. The last action puts the gathered configuration into the previously created configs dictionary.
After iteration through all the devices, here’s what the configs dict looks like.
And the script output is as follows.
vmanage config:
system
host-name vmanage
system-ip 10.10.1.1
site-id 101
admin-tech-on-failure
no vrrp-advt-with-phymac
sp-organization-name "DevNet Sandbox"
organization-name "DevNet Sandbox"
vbond 10.10.20.80
[...]
vsmart config:
system
host-name vsmart
system-ip 10.10.1.5
site-id 101
admin-tech-on-failure
no vrrp-advt-with-phymac
sp-organization-name "DevNet Sandbox"
organization-name "DevNet Sandbox"
upgrade-confirm 15
vbond 10.10.20.80
[...]
For the last section, we will grab network interfaces from each device.
Interfaces
Let’s jump straight to the code.
import requests
import json
vManage = 'https://sandbox-sdwan-2.cisco.com'
body = {
'j_username': 'devnetuser',
'j_password': 'RG!_Yw919_83'
}
session = requests.session()
auth_response = session.post(f'{vManage}/j_security_check', data=body, verify=None)
devices_response = session.get(f'{vManage}/dataservice/device', verify=None)
devices_dict = json.loads(devices_response.text)
for device in devices_dict['data']:
hostname = device['host-name']
device_id = device['deviceId']
interfaces_response = session.get(f'{vManage}/dataservice/device/interface', params={'deviceId': device_id},
verify=None)
try:
interfaces_response.raise_for_status()
interfaces = json.loads(interfaces_response.text)['data']
print(f'--------- Interfaces of {hostname} ---------')
for interface in interfaces:
print(
f' {interface["ifname"]} - Admin status: {interface["if-admin-status"]}, '
f'Operational status: {interface["if-oper-status"]}')
print('')
except requests.HTTPError as e:
print(f'Could not get interfaces for {hostname}')
This time is pretty similar to the previous example. This time, however, we need to parse the response, because it’s encoded in the JSON.
After parsing, we have an interfaces list filled with dictionaries, each representing each interface.
Let’s assume that we just want to print all interfaces with their admin and operational statuses. We just need to refer to the specific key in the dictionary.
The script output is following.
--------- Interfaces of vmanage ---------
eth0 - Admin status: Up, Operational status: Up
eth0 - Admin status: Up, Operational status: Up
eth1 - Admin status: Down, Operational status: Down
eth1 - Admin status: Down, Operational status: Down
eth2 - Admin status: Down, Operational status: Down
eth2 - Admin status: Down, Operational status: Down
system - Admin status: Up, Operational status: Up
system - Admin status: Up, Operational status: Up
docker0 - Admin status: Down, Operational status: Down
docker0 - Admin status: Down, Operational status: Down
br-4e7253334e08 - Admin status: Down, Operational status: Up
br-4e7253334e08 - Admin status: Down, Operational status: Up
--------- Interfaces of vsmart ---------
eth0 - Admin status: Up, Operational status: Up
eth0 - Admin status: Up, Operational status: Up
eth1 - Admin status: Down, Operational status: Down
eth1 - Admin status: Down, Operational status: Down
system - Admin status: Up, Operational status: Up
system - Admin status: Up, Operational status: Up
--------- Interfaces of vbond ---------
ge0/0 - Admin status: Up, Operational status: Up
ge0/0 - Admin status: Up, Operational status: Up
ge0/1 - Admin status: Down, Operational status: Down
ge0/1 - Admin status: Down, Operational status: Down
ge0/2 - Admin status: Down, Operational status: Down
ge0/2 - Admin status: Down, Operational status: Down
system - Admin status: Up, Operational status: Up
system - Admin status: Up, Operational status: Up
eth0 - Admin status: Up, Operational status: Up
loopback65528 - Admin status: Up, Operational status: Up
--------- Interfaces of dc-cedge01 ---------
Control Plane - Admin status: if-state-up, Operational status: if-oper-state-ready
GigabitEthernet1 - Admin status: if-state-up, Operational status: if-oper-state-ready
GigabitEthernet2 - Admin status: if-state-up, Operational status: if-oper-state-ready
GigabitEthernet3 - Admin status: if-state-up, Operational status: if-oper-state-ready
GigabitEthernet4 - Admin status: if-state-up, Operational status: if-oper-state-ready
Tunnel2 - Admin status: if-state-up, Operational status: if-oper-state-ready
[...]