Although this is similar to the previous post of Scrapli Async and Multi threading. The difference is here is that it is a working example of what will be used in the Scrapli & Flask Project.
In the code below I will be using Scrapli to connect to 8 of my devices in the GNS3 lab to get the version. The number of devices is doubled to 16, reusing the first 8, so checking the version twice.
I have an update to this below that is how I have incorporated it into Flask.
Lab
Code Output
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
************************************************************ device: R2 ---> Version: 12.4(13b) device: SW2 ---> Version: 15.0(20121031:162848) device: SW3 ---> Version: 15.1(20130726:213425) device: R1 ---> Version: 15.2(4)S5 device: R3 ---> Version: 16.12.3 device: SW1 ---> Version: 12.2(20100802:165548) device: SW2 ---> Version: 15.0(20121031:162848) device: SW1 ---> Version: 12.2(20100802:165548) device: DC_SW1 ---> Version: 7.3(0)D1(1) device: DC_SW2 ---> Version: 7.3(0)D1(1) device: SW3 ---> Version: 15.1(20130726:213425) device: R1 ---> Version: 15.2(4)S5 device: R3 ---> Version: 16.12.3 device: R2 ---> Version: 12.4(13b) device: DC_SW3 ---> Version: 9.3(1) device: DC_SW4 ---> Version: 9.3(1) device: DC_SW2 ---> Version: 7.3(0)D1(1) device: DC_SW1 ---> Version: 7.3(0)D1(1) device: DC_SW3 ---> Version: 9.3(1) device: DC_SW4 ---> Version: 9.3(1) ************************************************************ Execution time in seconds: 8.420679807662964 |
Code
The code below is the actual file from the Scrapli & Flask project, I have used this to test with a device list and what will likely happen in the working version of the Scrapli and Flask Project. Right now this is a working example of Scrapli using multithreading for a range of Cisco devices when only entering an IP address. There are still some things I need address with this script such as what happens if I have three device types. I will make an update once I have the Arista switches working to test with them.
This code took me a while to get working because of the try/except for the Scrapli driver version. The Nexus switches were connecting but then taking a long time to time out. I think 15 seconds is the default timeout for connection, prompt and to get a return once a command is sent.
I used the three timeouts above to modify the behaviour. Before the timeout changes to get the version of 8 devices it was taking 30+ seconds. Now for 16 devices it is down to 8 seconds.
More Scrapli timeout reading can be found here
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
from scrapli.driver.core import IOSXEDriver, NXOSDriver import time import queue from threading import Thread class CiscoCommands: def __init__(self): self.username = 'admin' self.password = 'Stefan2020' def conn(self, ip): switch = { "host": ip, "auth_username":self.username, "auth_password":self.password, "auth_strict_key":False, # "auth_bypass": True, "transport": "paramiko", "timeout_ops": 2, "timeout_socket": 1, "timeout_transport": 1 } try: host = IOSXEDriver(**switch) host.open() os = "ios" except: host = NXOSDriver(**switch) host.open() os = "nxos" return host, os def show_version(self, ip, genie_list, error_ips): while True: ip = q.get() host, os = CiscoCommands.conn(self, ip) output = host.send_command("show version") # Genie Parsed Response genie_parsed_response = output.genie_parse_output() if os == "ios": hostname = genie_parsed_response['version']['hostname'] ip = ip sw_version = genie_parsed_response['version']['version'] try: model = genie_parsed_response['version']['chassis'] except: model = "Unknown" os_type = genie_parsed_response['version']['os'] chassis_sn = genie_parsed_response['version']['chassis_sn'] uptime = genie_parsed_response['version']['uptime'] genie_dict = {"Hostname":hostname, "IP":ip, "Version":sw_version, "Model":model, "OS":os_type, "Chassis":chassis_sn, "Uptime":uptime } genie_list.append(genie_dict) elif os == "nxos": hostname = genie_parsed_response['platform']['hardware']['device_name'] ip = ip sw_version = genie_parsed_response['platform']['software']['system_version'] try: model = genie_parsed_response['platform']['hardware']['model'] except: model = "Unknown" os_type = genie_parsed_response['platform']['os'] chassis_sn = genie_parsed_response['platform']['hardware']['processor_board_id'] uptime = genie_parsed_response['platform']['kernel_uptime']['days'] genie_dict = {"Hostname":hostname, "IP":ip, "Version":sw_version, "Model":model, "OS":os_type, "Chassis":chassis_sn, "Uptime":uptime } genie_list.append(genie_dict) q.task_done() # return hostname,ip,sw_version,model,os_type,chassis_sn,uptime if __name__ == '__main__': switches = ["172.16.1.101", "172.16.1.102", "172.16.1.103", "172.16.1.104", "172.16.1.105", "172.16.1.106", "172.16.1.111", "172.16.1.112", "172.16.1.113", "172.16.1.114", "172.16.1.101", "172.16.1.102", "172.16.1.103", "172.16.1.104", "172.16.1.105", "172.16.1.106", "172.16.1.111", "172.16.1.112", "172.16.1.113", "172.16.1.114" ] cmd = CiscoCommands() startTime = time.time() q = queue.Queue() genie_list = [] error_ips = [] for thread_no in range(8): worker = Thread(target=cmd.show_version, args=(q, genie_list, error_ips, ), daemon=True) worker.start() for switch in switches: q.put(switch) q.join() print("\n") print("*"*60) # Print Successes for device in genie_list: print(f"device: {device['Hostname']} ---> Version: {device['Version']}") # print("\n") # Print IPs with errors print("\n") if error_ips != []: for ip in error_ips: print(f"Error connecting to {ip['IP Address']}") # with open ("Scrapli_Connection_Error_IPs.txt", "a") as f: # f.write(ip + "\n") print("*"*60) executionTime = (time.time() - startTime) print('Execution time in seconds: ' + str(executionTime)) |
Update! Adding to Flask
I have made numerous changes to integrate the code above into flask. The main two were; the creation of the threads into its own function and error checking.
Below is the form that the IPS are input. And below that is the output to the console when IPs are submited through the Flask form.
I have used a list of all 8 IP addresses in my lab and four IP addresses that Scrapli will fail to connect to.
Right now there is no DB update, so Flask just redirects to the Device Inventory once the Scapli script has run.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# IPs input to form 2.2.2.2 172.16.1.101 172.16.1.102 172.16.1.103 172.16.1.104 172.16.1.105 1.1.1.1 172.16.1.106 4.4.4.4 172.16.1.111 172.16.1.112 172.16.1.113 172.16.1.114 3.3.3.3 |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
['2.2.2.2', '172.16.1.101', '172.16.1.102', '172.16.1.103', '172.16.1.104', '172.16.1.105', '1.1.1.1', '172.16.1.106', '4.4.4.4', '172.16.1.111', '172.16.1.112', '172.16.1.113', '172.16.1.114', '3.3.3.3'] ERROR!!!!! ERROR!!!!! ERROR!!!!! ERROR!!!!! [{'Hostname': 'SW1', 'IP': '172.16.1.101', 'Version': '12.2(20100802:165548)', 'Model': 'Unknown', 'OS': 'IOS', 'Chassis': '2048002', 'Uptime': '3 days, 1 hour, 29 minutes'}, {'Hostname': 'SW2', 'IP': '172.16.1.102', 'Version': '15.0(20121031:162848)', 'Model': 'Unknown', 'OS': 'IOS', 'Chassis': '2048003', 'Uptime': '3 days, 1 hour, 29 minutes'}, {'Hostname': 'SW3', 'IP': '172.16.1.103', 'Version': '15.1(20130726:213425)', 'Model': 'Unknown', 'OS': 'IOS', 'Chassis': '2048004', 'Uptime': '3 days, 1 hour, 29 minutes'}, {'Hostname': 'R1', 'IP': '172.16.1.104', 'Version': '15.2(4)S5', 'Model': '7206VXR', 'OS': 'IOS', 'Chassis': '4279256517', 'Uptime': '3 days, 1 hour, 29 minutes'}, {'Hostname': 'R3', 'IP': '172.16.1.106', 'Version': '16.12.3', 'Model': 'CSR1000V', 'OS': 'IOS-XE', 'Chassis': '9202RL75OHO', 'Uptime': '5 days, 10 hours, 35 minutes'}, {'Hostname': 'R2', 'IP': '172.16.1.105', 'Version': '12.4(13b)', 'Model': '2691', 'OS': 'IOS', 'Chassis': 'XXXXXXXXXXX', 'Uptime': '1 day, 8 hours, 30 minutes'}, {'Hostname': 'DC_SW2', 'IP': '172.16.1.112', 'Version': '7.3(0)D1(1)', 'Model': 'NX-OSv', 'OS': 'NX-OS', 'Chassis': 'TM8C709500B', 'Uptime': 5}, {'Hostname': 'DC_SW1', 'IP': '172.16.1.111', 'Version': '7.3(0)D1(1)', 'Model': 'NX-OSv', 'OS': 'NX-OS', 'Chassis': 'TM8C583900B', 'Uptime': 5}, {'Hostname': 'DC_SW3', 'IP': '172.16.1.113', 'Version': '9.3(1)', 'Model': 'Nexus9000 9000v', 'OS': 'NX-OS', 'Chassis': '9P9V3GQPHCF', 'Uptime': 5}, {'Hostname': 'DC_SW4', 'IP': '172.16.1.114', 'Version': '9.3(1)', 'Model': 'Nexus9000 9000v', 'OS': 'NX-OS', 'Chassis': '9P9V3GQPHCF', 'Uptime': 5}] ************************************************************ device: SW1 ---> Version: 12.2(20100802:165548) device: SW2 ---> Version: 15.0(20121031:162848) device: SW3 ---> Version: 15.1(20130726:213425) device: R1 ---> Version: 15.2(4)S5 device: R3 ---> Version: 16.12.3 device: R2 ---> Version: 12.4(13b) device: DC_SW2 ---> Version: 7.3(0)D1(1) device: DC_SW1 ---> Version: 7.3(0)D1(1) device: DC_SW3 ---> Version: 9.3(1) device: DC_SW4 ---> Version: 9.3(1) Error connecting to 1.1.1.1 Error connecting to 2.2.2.2 Error connecting to 4.4.4.4 Error connecting to 3.3.3.3 ************************************************************ Execution time in seconds: 5.309101104736328 |