Source code for eos_poller

"""eos_poller.py

Uses asyncio.to_thread and gather functions to poll multiple switches concurrently """

import asyncio
from concurrent.futures import ThreadPoolExecutor
import pyeapi


[docs] def invoker(switchlist_in: list, uname_in: str, passwd_in: str, runtype_in: str) -> tuple[list, list, list]: """Run synchronously; provide entry-point for module; manage the asyncio eventloop; invoke async/threaded functions to do the real work. Parameters --------- switchlist_in : list List of Arista switches to be interrogated uname_in : str The username credential for authenticating to the switch passwd_in : str The password credential for authenticating to the switch Returns ------- svsout : list Values of the switch that we're interested in lldpsout : list List of connections inferred from LLDP tables on the switches cfgsout : list List-of-lists holding all of the switch's configurations 'all_configs_out[0]' is the configuration of the first item in switchlist 'all_configs_out[0][0] is the 1st line of the 1st switch's configuration """ svsout, lldpsout, cfgsout = asyncio.run(main(switchlist_in, uname_in, passwd_in, runtype_in)) return svsout, lldpsout, cfgsout
[docs] async def main(switchlist_in2: list, uname_in2: str, passwd_in2: str, runtype_in2: str) -> tuple[list, list, list]: """Connect to switches and get the data that we want from them. Use asyncio to_thread function to enable concurrent processing of multiple switches. Parameters --------- switchlist_in2 : list List of Arista switches to be interrogated uname_in2 : str The username credential for authenticating to the switch passw_in2 : str The password credential for authenticating to the switch Returns ------- sw_vals_out2 : list Values of the switch that we're interested in lldpngbrs_out2 : list List of connections inferred from LLDP tables on the switches all_configs_out2 : list List-of-lists holding all of the switch's configurations 'all_configs_out2[0]' is the configuration of the first item in switchlist 'all_configs_out2[0][0] is the 1st line of the 1st switch's configuration """ # Set the maximum number of worker-threads we're willing to use loop = asyncio.get_running_loop() loop.set_default_executor(ThreadPoolExecutor(max_workers=20)) # Initialize the lists we will eventually return switchcount2 = len(switchlist_in2) sw_vals_out2 = [[]] * (switchcount2) allconfigs_out2 = [[]] * (switchcount2) lldpngbrs_out2 = [] tasks = [] print('Polling Arista switches via EOS API..') # Enumerate the switchlist, spawning a new asyncio thread/task to get data from each for sw_cntr2, value2 in enumerate(switchlist_in2): coro = asyncio.to_thread(get_sw_data, value2, uname_in2, passwd_in2, sw_cntr2) tasks.append(asyncio.create_task(coro)) # Gather the data from all the EAPI polling threads answers = [] * (switchcount2) answers = await asyncio.gather(*tasks) print('Finished polling switches.') print('') # Loop through the EAPI responses and split each response into the right buckets # Each item in "answers" is the data returned by get_sw_data for a single switch. # (A list switch properties; a list of LLDP neighbors, and a list of lines in the # startup config.) We don't want to three objects for every switch, we want to # return three objects, each of which has the information for *all* of the switches for val in answers: # Item [0] in each "answer" is the list of switch properties (switch_vals) sw_vals_out2[val[3]] = val[0] # Item [0] in each "answer" is the list of LLDP neighbors for the switch for i, entry in enumerate(val[1]): lldpngbrs_out2.append(entry) allconfigs_out2[val[3]] = val[2] return sw_vals_out2, lldpngbrs_out2, allconfigs_out2
[docs] def get_sw_data(switch3: str, uname_in3: str, passwd_in3: str, sw_cntr3_in: int ) -> tuple[list, list, list]: """Connect to a switch and gets the data that we want from it Parameters --------- switch3 : str The network switch to be interrogated uname_in3 : str The username credential for authenticating to the switch passwd_in3 : str The password credential for authenticating to the switch Returns ------- this_sw_vals : list Values of the switch that we're interested in this_sw_lldp_nbrs : list The switch's LLDP neighbors table (local/remote device-name and port-I) this_sw_cfg : list The switch's startup configuration """ # Clear any existing pyeapi.client.config pyeapi.client.config.clear() # Build the pyeapi.client.config object required for connecting to the switch pyeapi.client.config.add_connection(switch3, host=switch3, transport='https', username=uname_in3, password=passwd_in3) # Connect to the switch node = pyeapi.connect_to(switch3) # Get JSON-formatted results of several 'show...' commands eos_output = node.enable(("show version", "show lldp neighbors", "show lldp local-info"), format="json") # Pluck the specific bits out data we want from the "show" cmds' output eos_output_model = eos_output[0]["result"]["modelName"] eos_output_ver = eos_output[0]["result"]["version"] eos_output_mac = eos_output[0]["result"]["systemMacAddress"] eos_output_serial = eos_output[0]["result"]["serialNumber"] eos_output_lldpname = eos_output[2]["result"]["systemName"] # Create this_switch_data list to return (include empty indecesfor later) this_sw_vals = ([switch3, eos_output_model, eos_output_ver, eos_output_mac, eos_output_serial, eos_output_lldpname, '', '', '', '', '', '']) # Create this_sw_lldpnbrs list to return this_sw_lldpnbrs = [] for value in eos_output[1]["result"]["lldpNeighbors"]: this_sw_lldpnbrs.append([str(eos_output_lldpname), str(value["port"]), str(value["neighborDevice"]), str(value["neighborPort"])]) # Create this_sw_cfg list to return this_sw_cfg = [] this_sw_cfg = node.startup_config.splitlines() print("Finished polling: " + switch3) return this_sw_vals, this_sw_lldpnbrs, this_sw_cfg, int(sw_cntr3_in)