pip install netmiko
doRouter()
function. Inside it, you connect to the router, send commands to the CLI, and receive and analyze responses. The input data for the function are: IP address of the router, login and password. If a problem occurs, the function will return the IP address of the router, we will write it in a separate file fail.txt. If everything went well, a message will simply be displayed on the screen.doRouter
procedure. I had problems with getpass working in PyCharm under Windows. The script worked correctly, only if you run it in Debug mode, not Run. On the command line, everything worked flawlessly. Also, the script was tested in OS X, there were no problems with PyCharm. user_name = input("Enter Username: ") pass_word = getpass()
try…except
construction will correctly handle the error of reading the file. At the output we get an array of data for connection connection_data
, containing the IP address, login and password. try: f = open('ip.txt') connection_data=[] filelines = f.read().splitlines() for line in filelines: if line == "": continue if line[0] == "#": continue conn_data = line.split(',') ipaddr=conn_data[0].strip() username=global_username password=global_password if len(conn_data) > 1 and conn_data[1].strip() != "": username = conn_data[1].strip() if len(conn_data) > 2 and conn_data[2].strip() != "": password = conn_data[2].strip() connection_data.append((ipaddr, username, password)) f.close() except: sys.exit("Couldn't open or read file ip.txt")
routers_with_issues
write what is returned to the doRouter
function. In our case, these are the IP addresses of the routers with which there were problems. multiprocessing.set_start_method("spawn") with multiprocessing.Pool(maxtasksperchild=10) as process_pool: routers_with_issues = process_pool.map(doRouter, connection_data, 1) process_pool.close() process_pool.join()
process_pool.join()
command is needed so that the script doRouter()
for the completion of all instances of the doRouter()
functions and only then continues to execute the main program. failed_file = open('fail.txt', 'w') for item in routers_with_issues: if item != None: failed_file.write("%s\n" % item) print(item)
doRouter()
. The first thing to do is to process the input. Using ReGex, we verify that the correct IP address was transferred to the function. ip_check = re.findall("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", ip_address) if ip_check == []: print(bcolors.FAIL + "Invalid IP - " + str(ip_address) + bcolors.ENDC) return ip_address
device = { 'device_type': 'cisco_ios', 'ip': ip_address.strip(), 'username': username, 'password': password, 'port': 22, } try: config_ok = True net_connect = ConnectHandler(**device)
cli_response
variable. In this example, we check the current settings. The result is displayed on the screen. This part needs to be changed for different tasks. In this script, we check the current configuration of the router. If it is correct, then we make changes. If the check config_ok
problems, then set the variable config_ok
to False
and do not apply the changes. cli_response = net_connect.send_command("sh dmvpn | i Interface") cli_response = cli_response.replace("Interface: ", "") cli_response = cli_response.replace(", IPv4 NHRP Details", "").strip() if cli_response != "Tunnel1": print(str(ip_address)+" - " + bcolors.WARNING + "WARNING - DMVPN not on Tunnel1. " + cli_response+ " " + bcolors.ENDC) config_ok=False
Operation | Description | Example |
---|---|---|
+ | String concatenation | s3 = s1 + s2 >>> print ('Happy New' + str (2017) + 'Year') Happy New 2017 Year |
len (s) | Determining the length of the string | |
[] | Substring selection (index starts from zero) | s [5] - the sixth character s [5: 7] - symbols from the sixth to the eighth s [-1] - the last character, the same as s [len (s) -1] |
s.split () s.join () | Split lines Combine strings | >>> 'Peter, Lesha, Kohl'.split (', ') ['Petya', 'Lesha', 'Kohl'] >>> ','. join ({'Peter', 'Lesha', 'Kohl'}) 'Lesha, Peter, Kohl' |
str (l) list (s) | Convert list to string Convert string to list | >>> str (['1', '2', '3']) "['1', '2', '3']" >>> list ('Test') ['T', 'e', ​​'s', 't'] |
% | Pattern Formatting | >>> s1, s2 = 'Mitya', 'Vasilisa' >>> '% s +% s = love'% (s1, s2) 'Mitya + Vasilisa = love' |
f | Variable substitution | >>> a = 'Maxim' >>> f'Name {a} ' 'Name Maxim' |
str.find (substr) | Search substr in string str Returns the position of the first substring found | >>> 'This is a text'.find (' a ') eight |
str.replace (old, new) | Replacing the substring old with the substring new in the string str | >>> newstr = 'This is a text'.replace (' is', 'is not') >>> print (newstr) This is not a text |
str.strip () str.rstrip () | Remove spaces and tabs at the beginning and end (or only at the end) | >>> 'This is a text \ t \ t \ t'.strip () 'This is a text' |
next-hop
. In my case, the easiest way is to look at the next-hop
address of existing static routes. cli_response2=net_connect.send_command("sh run | i ip route 8.8.8.8 255.255.255.255") if cli_response2.strip() == "": print(str(ip_address)+" — " + bcolors.FAIL + "WARNING — couldn't find static route to 8.8.8.8" + bcolors.ENDC) config_ok=False ip_next_hop = "" if cli_response2 != "": ip_next_hop = cli_response2.split(" ")[4] if ip_next_hop == "": print(str(ip_address)+" — " + bcolors.FAIL + "WARNING — couldn't find next-hop IP address " + bcolors.ENDC) config_ok=False
config_commands = ['ip route 1.1.1.1 255.255.255.255 '+ip_next_hop, 'ip route 2.2.2.2 255.255.255.255 '+ip_next_hop] net_connect.send_config_set(config_commands)
import sys from netmiko import ConnectHandler from getpass import getpass import time import multiprocessing import re start_time = time.time() class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' def doRouter(connection_data): ip_address = connection_data[0] username = connection_data[1] password = connection_data[2] ip_check = re.findall("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", ip_address) if ip_check == []: print(bcolors.FAIL + "Invalid IP - " + str(ip_address) + bcolors.ENDC) return ip_address device = { 'device_type': 'cisco_ios', 'ip': ip_address.strip(), 'username': username, 'password': password, 'port': 22, } try: config_ok = True net_connect = ConnectHandler(**device) cli_response = net_connect.send_command("sh dmvpn | i Interface") cli_response = cli_response.replace("Interface: ", "") cli_response = cli_response.replace(", IPv4 NHRP Details", "").strip() if cli_response != "Tunnel1": print(str(ip_address)+" - " + bcolors.WARNING + "WARNING - DMVPN not on Tunnel1. " + cli_response+ " " + bcolors.ENDC) config_ok=False cli_response2=net_connect.send_command("sh run | i ip route 1.1.1.1 255.255.255.255") if cli_response2.strip() == "": print(str(ip_address)+" - " + bcolors.WARNING + "WARNING - couldn't find static route to 8.8.8.8" + bcolors.ENDC) config_ok=False ip_next_hop = "" if cli_response2 != "": ip_next_hop = cli_response2.split(" ")[4] if ip_next_hop == "": print(str(ip_address)+" - " + bcolors.WARNING + "WARNING - couldn't find next-hop IP address " + bcolors.ENDC) config_ok=False if config_ok: config_commands = ['ip route 1.1.1.1 255.255.255.255 '+ip_next_hop, 'ip route 2.2.2.2 255.255.255.255 '+ip_next_hop] net_connect.send_config_set(config_commands) print(str(ip_address) + " - " + "Static routes added") else: print(str(ip_address) + " - " + bcolors.FAIL + "Routes weren't added because config is incorrect" + bcolors.ENDC) return ip_address if config_ok: net_connect.send_command_expect('write memory') print(str(ip_address) + " - " + "Config saved") net_connect.disconnect() except: print(str(ip_address)+" - "+bcolors.FAIL+"Cannot connect to this device."+bcolors.ENDC) return ip_address print(str(ip_address) + " - " + bcolors.OKGREEN + "Router configured sucessfully" + bcolors.ENDC) if __name__ == '__main__': # Enter valid username and password. Note password is blanked out using the getpass library global_username = input("Enter Username: ") global_password = getpass() try: f = open('ip.txt') connection_data=[] filelines = f.read().splitlines() for line in filelines: if line == "": continue if line[0] == "#": continue conn_data = line.split(',') ipaddr=conn_data[0].strip() username=global_username password=global_password if len(conn_data) > 1 and conn_data[1].strip() != "": username = conn_data[1].strip() if len(conn_data) > 2 and conn_data[2].strip() != "": password = conn_data[2].strip() connection_data.append((ipaddr, username, password)) f.close() except: sys.exit("Couldn't open or read file ip.txt") multiprocessing.set_start_method("spawn") with multiprocessing.Pool(maxtasksperchild=10) as process_pool: routers_with_issues = process_pool.map(doRouter, connection_data, 1) # doRouter - function, iplist - argument process_pool.close() process_pool.join() print("\n") print("#These routers weren't configured#") failed_file = open('fail.txt', 'w') for item in routers_with_issues: if item != None: failed_file.write("%s\n" % item) print(item) #Completing the script and print running time print("\n") print("#This script has now completed#") print("\n") print("--- %s seconds ---" % (time.time() - start_time))
python script.py
bash ~/PycharmProjects/p4ne $ python3 script.py Enter Username: cisco Password: Invalid IP - 10.1.1.256 127.0.0.1 - Cannot connect to this device. 1.1.1.1 - Cannot connect to this device. 10.10.100.227 - Static routes added 10.10.100.227 - Config saved 10.10.100.227 - Router configured sucessfully 10.10.31.170 - WARNING - couldn't find static route to 8.8.8.8 10.10.31.170 - WARNING - couldn't find next-hop IP address 10.10.31.170 - Routes weren't added because config is incorrect 2.2.2.2 - Cannot connect to this device. #These routers weren't configured# 10.1.1.256 127.0.0.1 217.112.31.170 1.1.1.1 2.2.2.2 #This script has now completed#
The authenticity of host '11.22.33.44 (11.22.33.44)' can't be established.
RSA key fingerprint is SHA256:C+BHaMBjuMIoEewAbjbQbRGdVkjs&840Ve3z4aJo.
Are you sure you want to continue connecting (yes/no)?
Source: https://habr.com/ru/post/336608/
All Articles