Skip to content

Commit e312efb

Browse files
committed
add Cisco NX-API example script
1 parent 99d4639 commit e312efb

2 files changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""
2+
interface description cleaner
3+
-----------------------------
4+
5+
This script will utilize the Cisco NX-API to update the interface descriptions on a Cisco Nexus switches
6+
based on CDP information.
7+
8+
"""
9+
import json
10+
import requests
11+
import re
12+
13+
# todo update the device information when testing the script
14+
# get CDP information from the following clients and update there configuration
15+
hosts = [
16+
"10.1.1.1",
17+
"10.1.1.2"
18+
]
19+
20+
# credentials to use on the Cisco NX-API
21+
dev_username = "setup"
22+
dev_password = "setup"
23+
24+
# HTTPs server port, which is used on every Switch to connect to the Cisco NX-API
25+
HTTPS_SERVER_PORT = "8181"
26+
27+
"""
28+
------------------------------------------------------------------------------------------------------------------------
29+
some helper functions to work with the Cisco NX-API
30+
"""
31+
# suppress the unverified request messages (when using self-signed certificates)
32+
requests.packages.urllib3.disable_warnings()
33+
34+
def nxapi_cli_conf(commands, hostname, username, password):
35+
"""
36+
executes configure commands on the given host using Cisco NX-API
37+
38+
:param username: username for authentication
39+
:param password: password for authentication
40+
:param hostname: the hostname, where the Cisco NX-API call must be executed
41+
:param commands: the configuration commands that should be executed on the switch using Cisco NX-API
42+
"""
43+
# convert the given configuration commands to a format which can be used within the Cisco NX-API and verify
44+
# that the configuration script does not end with the termination sign (lead to an error in the last command)
45+
commands = commands.replace("\n"," ; ")
46+
if commands.endswith(" ; "):
47+
commands = commands[:-3]
48+
49+
payload = {
50+
"ins_api": {
51+
"version": "1.2",
52+
"type": "cli_conf",
53+
"chunk": "0", # do not chunk results
54+
"sid": "1",
55+
"input": commands,
56+
"output_format": "json"
57+
}
58+
}
59+
return nxapi_call(hostname, payload, username, password, "json")
60+
61+
def nxapi_cli_show(show_command, hostname, username, password):
62+
"""
63+
execute show command on the given host using Cisco NX-API
64+
65+
:param username: username for authentication
66+
:param password: password for authentication
67+
:param hostname: the hostname, where the Cisco NX-API call must be executed
68+
:param show_command: the show command, that should be executed on the switch
69+
"""
70+
payload = [
71+
{
72+
"jsonrpc": "2.0",
73+
"method": "cli",
74+
"params": {
75+
"cmd": show_command,
76+
"version": 1.2
77+
},
78+
"id": 1
79+
}
80+
]
81+
return nxapi_call(hostname, payload, username, password, "json-rpc")
82+
83+
def nxapi_call(hostname, payload, username, password, content_type="json"):
84+
"""
85+
common NX-API call which includes some basic verification of the response
86+
87+
:param hostname: the hostname, where the Cisco NX-API call must be executed
88+
:param payload: the payload for the NX-API call
89+
:param username: username for authentication
90+
:param password: password for authentication
91+
:param content_type: the content type of the payload, defaults to "JSON"
92+
"""
93+
headers={'content-type':'application/%s' % content_type}
94+
response = requests.post("https://%s:%s/ins" % (hostname, HTTPS_SERVER_PORT),
95+
auth=(username, password),
96+
headers=headers,
97+
data=json.dumps(payload),
98+
verify=False, # disable SSH certificate verification
99+
timeout=4)
100+
if response.status_code == 200:
101+
# verify result if a cli_conf operation was performed
102+
if "ins_api" in payload:
103+
if "type" in payload['ins_api'].keys():
104+
if "cli_conf" in payload['ins_api']['type']:
105+
for result in response.json()['ins_api']['outputs']['output']:
106+
if result['code'] != "200":
107+
print("--> partial configuration failed, please verify your configuration!")
108+
break
109+
return response.json()
110+
else:
111+
msg = "call to %s failed, status code %d (%s)" % (target_host,
112+
response.status_code,
113+
response.content.decode("utf-8"))
114+
print(msg)
115+
raise Exception(msg)
116+
117+
def interface_shortener(interface_name):
118+
"""
119+
makes Cisco interface names shorter (e.g. 'Ethernet' to 'Eth')
120+
121+
:param interface_name:
122+
"""
123+
interface_short_string_map = [
124+
{ "from": "Ethernet", "to": "Eth"},
125+
{ "from": "GigabitEthernet", "to": "Gi"},
126+
{ "from": "FastEthernet", "to": "Fa"},
127+
{ "from": "TenGigabirEthernet", "to": "Te"},
128+
]
129+
result = interface_name
130+
for s in interface_short_string_map:
131+
if re.match(r"^%s" % s['from'], result) is not None:
132+
result = result.replace(s['from'], s['to'])
133+
break
134+
return result
135+
136+
"""
137+
------------------------------------------------------------------------------------------------------------------------
138+
the interface description cleaner script
139+
"""
140+
141+
if __name__ == "__main__":
142+
print("----------------------------------------")
143+
print("start the interface description cleaner ")
144+
print("----------------------------------------")
145+
host_results = dict()
146+
for target_host in hosts:
147+
print("request CDP information for switch: %s" % target_host)
148+
result = nxapi_cli_show("show cdp neighbor detail", target_host, dev_username, dev_password)
149+
150+
# dump neighbors
151+
neighbor_statements = result['result']['body']['TABLE_cdp_neighbor_detail_info']['ROW_cdp_neighbor_detail_info']
152+
153+
host_neighbors = list()
154+
155+
for neighbor in neighbor_statements:
156+
# remove SN and/or DNS prefix from hostname
157+
remote_host = neighbor['device_id'].split("(")[0]
158+
if "." in remote_host:
159+
remote_host = remote_host.split(".")[0]
160+
161+
# descriptions should not be that long...
162+
local_interface = interface_shortener(neighbor['intf_id'])
163+
remote_interface = interface_shortener(neighbor['port_id'])
164+
165+
entry = {
166+
"local_interface": local_interface,
167+
"remote_host": remote_host,
168+
"remote_interface": remote_interface,
169+
"remote_mgmt_ip": neighbor['v4mgmtaddr']
170+
}
171+
host_neighbors.append(entry)
172+
173+
host_results[target_host] = host_neighbors
174+
175+
# generate change script per device and push to it
176+
for host in host_results.keys():
177+
# create change script
178+
change_script = ""
179+
for entry in host_results[host]:
180+
change_script += "interface %s\n description *** %s, %s (%s)\n" % (entry['local_interface'],
181+
entry['remote_interface'],
182+
entry['remote_host'],
183+
entry['remote_mgmt_ip'])
184+
# verify that the output is correct
185+
print("apply change script: %s" % host)
186+
response = nxapi_cli_conf(change_script, host, dev_username, dev_password)
187+
188+
print("finished successful")

‎requirements.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Jinja2==2.8
22
MarkupSafe==0.23
33
textfsm==0.3.0
4+
requests

0 commit comments

Comments
 (0)