def collect_it(in_list_of_dict, in_send_cmd, in_send_cmd_key, in_template_filename, in_key_suffix=''): # start with an empty output this_output_raw = '' # step across each dict entry in the list for this_dict_entry in in_list_of_dict: # send the in_send_cmd and add to it per dict entry aspect defined by the in_send_cmd_key this_send_cmd = in_send_cmd + format(this_dict_entry[in_send_cmd_key]) # add this to the overall output to be processed this_output_raw += session.get_command_output(this_send_cmd) # use the template file provided & process the overall output processing_template_file = session.script.get_template(in_template_filename) processing_list_of_dict = utilities.textfsm_parse_to_dict(this_output_raw, processing_template_file) # start with an empty out_list_of_dict out_list_of_dict = [] for this_dict_entry in processing_list_of_dict: # start with an empty change_dict_entry change_dict_entry = {} # step across each key for this_key in this_dict_entry.keys(): # don't change the primary key name .. for the key:value entry if this_key == in_send_cmd_key: change_dict_entry[this_key] = this_dict_entry[this_key] # for the rest of the keys, add the suffix .. for the key:value entry else: change_dict_entry[this_key+in_key_suffix] = this_dict_entry[this_key] # now with key names changed, append the change_dict_entry out_list_of_dict.append(change_dict_entry) return out_list_of_dict
def script_main(session, ask_vrf=True, vrf=None): """ | SINGLE device script | Author: Jamie Caesar | Email: [email protected] This script will grab the route table information from a Cisco IOS or NXOS device and export details about each next-hop address (how many routes and from which protocol) into a CSV file. It will also list all connected networks and give a detailed breakdown of every route that goes to each next-hop. :param session: A subclass of the sessions.Session object that represents this particular script session (either SecureCRTSession or DirectSession) :type session: sessions.Session :param ask_vrf: A boolean that specifies if we should prompt for which VRF. The default is true, but when this module is called from other scripts, we may want avoid prompting and supply the VRF with the "vrf" input. :type ask_vrf: bool :param vrf: The VRF that we should get the route table from. This is used only when ask_vrf is False. :type vrf: str """ # Get script object that owns this session, so we can check settings, get textfsm templates, etc script = session.script # Start session with device, i.e. modify term parameters for better interaction (assuming already connected) session.start_cisco_session() # Validate device is running a supported OS session.validate_os(["IOS", "NXOS"]) # If we should prompt for a VRF, then do so. Otherwise use the VRF passed into the function (if any) if ask_vrf: selected_vrf = script.prompt_window("Enter the VRF name. (Leave blank for default VRF)") else: selected_vrf = vrf # If we have a VRF, modify our commands and hostname to reflect it. If not, pull the default route table. if selected_vrf: send_cmd = "show ip route vrf {0}".format(selected_vrf) session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf) logger.debug("Received VRF: {0}".format(selected_vrf)) else: send_cmd = "show ip route" raw_routes = session.get_command_output(send_cmd) if session.os == "IOS": template_file = script.get_template("cisco_ios_show_ip_route.template") else: template_file = script.get_template("cisco_nxos_show_ip_route.template") fsm_results = utilities.textfsm_parse_to_dict(raw_routes, template_file) route_list = parse_routes(fsm_results) output_filename = session.create_output_filename("nexthop-summary", ext=".csv") output = nexthop_summary(route_list) utilities.list_of_lists_to_csv(output, output_filename) # Return terminal parameters back to the original state. session.end_cisco_session()
def get_interface_detail(session, to_cvs=False): """ A function that captures the WLC AireOS interface detail table and returns an output list :param session: The script object that represents this script being executed :type session: session.Session :return: A list of interface details :rtype: list of lists """ send_cmd = "show interface summary" output_raw = session.get_command_output(send_cmd) # TextFSM template for parsing "show interface summary" output template_file = session.script.get_template( "cisco_aireos_show_interface_summary.template") interface_summ_dict = utilities.textfsm_parse_to_dict( output_raw, template_file) output_raw = '' for interface_entry in interface_summ_dict: send_cmd = "show interface detailed " + format( interface_entry["INT_Name"]) output_raw += session.get_command_output(send_cmd) # TextFSM template for parsing "show interface detailed <interface-name>" output template_file = session.script.get_template( "cisco_aireos_show_interface_detailed.template") output = utilities.textfsm_parse_to_list(output_raw, template_file, add_header=True) if to_cvs: output_filename = session.create_output_filename("interface-detail", ext=".csv") utilities.list_of_lists_to_csv(output, output_filename) return output
def script_main(session): supported_os = ["IOS", "NXOS"] if session.os not in supported_os: logger.debug("Unsupported OS: {0}. Exiting program.".format( session.os)) session.message_box( "{0} is not a supported OS for this script.".format(session.os), "Unsupported OS", options=sessions.ICON_STOP) return else: send_cmd = "show ip route" selected_vrf = session.prompt_window( "Enter the VRF name. (Leave blank for default VRF)") if selected_vrf != "": send_cmd = send_cmd + " vrf {0}".format(selected_vrf) session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf) logger.debug("Received VRF: {0}".format(selected_vrf)) raw_routes = session.get_command_output(send_cmd) if session.os == "IOS": template_file = "textfsm-templates/cisco_ios_show_ip_route.template" else: template_file = "textfsm-templates/cisco_nxos_show_ip_route.template" fsm_results = utils.textfsm_parse_to_dict(raw_routes, template_file) route_list = parse_routes(fsm_results) output_filename = session.create_output_filename("nexthop-summary", ext=".csv") output = nexthop_summary(route_list) utils.list_of_lists_to_csv(output, output_filename) # Clean up before closing session session.end()
def per_device_work(session, enable_pass): """ This function contains the code that should be executed on each device that this script connects to. It is called after establishing a connection to each device in the loop above. You can either put your own code here, or if there is a single-device version of a script that performs the correct task, it can be imported and called here, essentially making this script connect to all the devices in the chosen CSV file and then running a single-device script on each of them. """ script = session.script interesting_keys = ['HARDWARE', 'HOSTNAME', 'MODEL', 'VERSION', 'SERIAL', 'UPTIME', 'LAST_REBOOT_REASON', 'IMAGE'] # Validate device is of a proper OS supported_os = ['IOS', 'NXOS', 'ASA'] session.start_cisco_session(enable_pass=enable_pass) session.validate_os(supported_os) # Select the appropriate template to process show version data if session.os == 'IOS': ver_template_file = script.get_template('cisco_ios_show_version.template') elif session.os == 'NXOS': ver_template_file = script.get_template('cisco_nxos_show_version.template') elif session.os == 'ASA': ver_template_file = script.get_template('cisco_asa_show_version.template') else: raise sessions.UnsupportedOSError("{0} isn't a supported OS.".format(session.os)) # Process Show Version data raw_version = session.get_command_output('show version') fsm_output = utilities.textfsm_parse_to_dict(raw_version, ver_template_file) if len(fsm_output) > 1: raise sessions.InteractionError("Received multiple entries from a single device, which should not happen.") else: ver_info = fsm_output[0] # For NXOS get parse 'show inventory' for model and serial number if session.os == 'NXOS': ver_info['HOSTNAME'] = session.hostname logger.debug("<M_SCRIPT> NXOS device, getting 'show inventory'.") raw_inv = session.get_command_output('show inventory') inv_template_file = script.get_template('cisco_nxos_show_inventory.template') inv_info = utilities.textfsm_parse_to_dict(raw_inv, inv_template_file) for entry in inv_info: if entry['NAME'] == "Chassis": logger.debug("<M_SCRIPT> Adding {0} as model number".format(entry['PID'])) ver_info['MODEL'] = entry['PID'] logger.debug("<M_SCRIPT> Adding {0} as serial number".format(entry['SN'])) ver_info['SERIAL'] = entry['SN'] break elif session.os == 'ASA': logger.debug("<M_SCRIPT> ASA device, writing 'N/A' for last reboot reason.") # For ASA put a N/A reload reason since ASA doesn't have this output ver_info['LAST_REBOOT_REASON'] = "N/A" # If we don't have a model number in older 'show ver' extract it from the hardware column. if not ver_info['MODEL']: model = ver_info['HARDWARE'].split(',')[0] logger.debug("<M_SCRIPT> ASA device without model, using {0}".format(model)) ver_info['MODEL'] = model elif session.os == 'IOS': # Expand multiple serial numbers found in stacks, or just remove lists for serial and model if only 1 device logger.debug("<M_SCRIPT> IOS device, writing list of serials/models to separate entries") num_in_stack = len(ver_info['SERIAL']) if len(ver_info['MODEL']) != num_in_stack: # For older IOS, we may not get a model number, but we'll pick up the hardware. As long as this happens # when only 1 serial is detected (not a switch stack), then just use the HARDWARE for the model number. if len(ver_info['MODEL']) == 0 and num_in_stack == 1 and ver_info['HARDWARE']: ver_info['MODEL'] = [ver_info['HARDWARE']] else: logger.debug("<M_SCRIPT> List of Serials & Models aren't the same length. Likely TextFSM parsing problem.") raise sessions.InteractionError("Received {0} serial nums and only {1} model nums in output." .format(num_in_stack, len(ver_info['MODEL']))) new_output = [] for x in range(num_in_stack): stack_subset = dict((key, ver_info[key]) for key in interesting_keys) stack_subset['HOSTNAME'] = "{0}-{1}".format(ver_info['HOSTNAME'], x+1) stack_subset['SERIAL'] = ver_info['SERIAL'][x] stack_subset['MODEL'] = ver_info['MODEL'][x] new_output.append(stack_subset) logger.debug("Created an entry for {0}/{1}".format(stack_subset['MODEL'], stack_subset['SERIAL'])) fsm_output = new_output # Create output data structure with only the keys that we need. inv_data = [] logger.debug("Creating list of dictionaries to return, and adding manufacture dates.") for entry in fsm_output: subset = dict((key, entry[key]) for key in interesting_keys) subset['MANUFACTURE_DATE'] = get_manufacture_date(subset['SERIAL']) inv_data.append(subset) # End session on the Cisco device session.end_cisco_session() return inv_data
def script_main(session, ask_vrf=True, vrf=None): """ | SINGLE device script | Author: Jamie Caesar | Email: [email protected] This script will grab the route table information from a Cisco IOS or NXOS device and export details about each next-hop address (how many routes and from which protocol) into a CSV file. It will also list all connected networks and give a detailed breakdown of every route that goes to each next-hop. :param session: A subclass of the sessions.Session object that represents this particular script session (either SecureCRTSession or DirectSession) :type session: sessions.Session :param ask_vrf: A boolean that specifies if we should prompt for which VRF. The default is true, but when this module is called from other scripts, we may want avoid prompting and supply the VRF with the "vrf" input. :type ask_vrf: bool :param vrf: The VRF that we should get the route table from. This is used only when ask_vrf is False. :type vrf: str """ # Get script object that owns this session, so we can check settings, get textfsm templates, etc script = session.script # Start session with device, i.e. modify term parameters for better interaction (assuming already connected) session.start_cisco_session() # Validate device is running a supported OS session.validate_os(["IOS", "NXOS"]) # If we should prompt for a VRF, then do so. Otherwise use the VRF passed into the function (if any) if ask_vrf: selected_vrf = script.prompt_window( "Enter the VRF name. (Leave blank for default VRF)") else: selected_vrf = vrf # If we have a VRF, modify our commands and hostname to reflect it. If not, pull the default route table. if selected_vrf: send_cmd = "show ip route vrf {0}".format(selected_vrf) session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf) logger.debug("Received VRF: {0}".format(selected_vrf)) else: send_cmd = "show ip route" raw_routes = session.get_command_output(send_cmd) if session.os == "IOS": template_file = script.get_template("cisco_ios_show_ip_route.template") else: template_file = script.get_template( "cisco_nxos_show_ip_route.template") fsm_results = utilities.textfsm_parse_to_dict(raw_routes, template_file) route_list = parse_routes(fsm_results) output_filename = session.create_output_filename("nexthop-summary", ext=".csv") output = nexthop_summary(route_list) utilities.list_of_lists_to_csv(output, output_filename) # Return terminal parameters back to the original state. session.end_cisco_session()
def script_main(session, ask_vrf=True, vrf=None): """ | SINGLE device script | Author: Jamie Caesar | Email: [email protected] This script will grab the EIGRP topology from a Cisco IOS or NXOS device and export a summary of how many networks are learned from each successor/feasible successor and output it into a CSV file. It will also give a detailed breakdown of every network that was learned from a particular successor :param session: A subclass of the sessions.Session object that represents this particular script session (either SecureCRTSession or DirectSession) :type session: sessions.Session :param ask_vrf: A boolean that specifies if we should prompt for which VRF. The default is true, but when this module is called from other scripts, we may want avoid prompting and supply the VRF with the "vrf" input. :type ask_vrf: bool :param vrf: The VRF that we should get the route table from. This is used only when ask_vrf is False. :type vrf: str """ # Get script object that owns this session, so we can check settings, get textfsm templates, etc script = session.script # Start session with device, i.e. modify term parameters for better interaction (assuming already connected) session.start_cisco_session() # Validate device is running a supported OS session.validate_os(["IOS", "NXOS"]) # If we should prompt for a VRF, then do so. Otherwise use the VRF passed into the function (if any) if ask_vrf: selected_vrf = script.prompt_window("Enter the VRF name. (Leave blank for default VRF, 'all' for all VRFs)") selected_vrf = selected_vrf.strip() logger.debug("Input VRF: {0}".format(selected_vrf)) else: selected_vrf = vrf logger.debug("Received VRF: {0}".format(selected_vrf)) # If we have a VRF, modify our commands and hostname to reflect it. If not, pull the default route table. if selected_vrf: if session.os == "IOS": if selected_vrf == "all": send_cmd = "show ip eigrp vrf * topology" else: send_cmd = "show ip eigrp vrf {0} topology".format(selected_vrf) else: if selected_vrf == "*": send_cmd = "show ip eigrp topology vrf all" else: send_cmd = "show ip eigrp topology vrf {0}".format(selected_vrf) else: send_cmd = "show ip eigrp topology" logger.debug("Generated Command: {0}".format(send_cmd)) raw_topo = session.get_command_output(send_cmd) if session.os == "IOS": template_file = script.get_template("cisco_ios_show_ip_eigrp_topology.template") else: template_file = script.get_template("cisco_nxos_show_ip_eigrp_topology.template") fsm_results = utilities.textfsm_parse_to_dict(raw_topo, template_file) detailed_results = process_topology(fsm_results) for process, nexthops in detailed_results.iteritems(): nexthop_list = sorted(nexthops.keys(), key=utilities.human_sort_key) vrf = process[0] as_num = process[1] rid = process[2] output = [["Hostname:", session.hostname], ["VRF:", vrf], ["AS:", as_num], ["Router ID:", rid], ["", ""], ["Nexthop", "Routes Learned"]] # output.append(["EIGRP TOPOLOGY SUMMARY", ""]) for nexthop in nexthop_list: output.append([nexthop, len(nexthops[nexthop])]) output.append(["", ""]) # output.append(["DETAILED ROUTE LIST",""]) for nexthop in nexthop_list: output.append(["Nexthop", "Routes"]) sorted_networks = sorted(nexthops[nexthop], key=utilities.human_sort_key) for network in sorted_networks: output.append([nexthop, network]) output.append(["",""]) if vrf: output_filename = session.create_output_filename("{0}-eigrp-{1}-summary".format(vrf, as_num), ext=".csv") else: output_filename = session.create_output_filename("-eigrp-{0}-summary".format(as_num), ext=".csv") utilities.list_of_lists_to_csv(output, output_filename) # Return terminal parameters back to the original state. session.end_cisco_session()
def script_main(session, ask_vrf=True, vrf=None): """ | SINGLE device script | Author: Jamie Caesar | Email: [email protected] This script will grab the EIGRP topology from a Cisco IOS or NXOS device and export a summary of how many networks are learned from each successor/feasible successor and output it into a CSV file. It will also give a detailed breakdown of every network that was learned from a particular successor :param session: A subclass of the sessions.Session object that represents this particular script session (either SecureCRTSession or DirectSession) :type session: sessions.Session :param ask_vrf: A boolean that specifies if we should prompt for which VRF. The default is true, but when this module is called from other scripts, we may want avoid prompting and supply the VRF with the "vrf" input. :type ask_vrf: bool :param vrf: The VRF that we should get the route table from. This is used only when ask_vrf is False. :type vrf: str """ # Get script object that owns this session, so we can check settings, get textfsm templates, etc script = session.script # Start session with device, i.e. modify term parameters for better interaction (assuming already connected) session.start_cisco_session() # Validate device is running a supported OS session.validate_os(["IOS", "NXOS"]) # If we should prompt for a VRF, then do so. Otherwise use the VRF passed into the function (if any) if ask_vrf: selected_vrf = script.prompt_window( "Enter the VRF name. (Leave blank for default VRF, 'all' for all VRFs)" ) selected_vrf = selected_vrf.strip() logger.debug("Input VRF: {0}".format(selected_vrf)) else: selected_vrf = vrf logger.debug("Received VRF: {0}".format(selected_vrf)) # If we have a VRF, modify our commands and hostname to reflect it. If not, pull the default route table. if selected_vrf: if session.os == "IOS": if selected_vrf == "all": send_cmd = "show ip eigrp vrf * topology" else: send_cmd = "show ip eigrp vrf {0} topology".format( selected_vrf) else: if selected_vrf == "*": send_cmd = "show ip eigrp topology vrf all" else: send_cmd = "show ip eigrp topology vrf {0}".format( selected_vrf) else: send_cmd = "show ip eigrp topology" logger.debug("Generated Command: {0}".format(send_cmd)) raw_topo = session.get_command_output(send_cmd) if session.os == "IOS": template_file = script.get_template( "cisco_ios_show_ip_eigrp_topology.template") else: template_file = script.get_template( "cisco_nxos_show_ip_eigrp_topology.template") fsm_results = utilities.textfsm_parse_to_dict(raw_topo, template_file) detailed_results = process_topology(fsm_results) for process, nexthops in detailed_results.iteritems(): nexthop_list = sorted(nexthops.keys(), key=utilities.human_sort_key) vrf = process[0] as_num = process[1] rid = process[2] output = [["Hostname:", session.hostname], ["VRF:", vrf], ["AS:", as_num], ["Router ID:", rid], ["", ""], ["Nexthop", "Routes Learned"]] # output.append(["EIGRP TOPOLOGY SUMMARY", ""]) for nexthop in nexthop_list: output.append([nexthop, len(nexthops[nexthop])]) output.append(["", ""]) # output.append(["DETAILED ROUTE LIST",""]) for nexthop in nexthop_list: output.append(["Nexthop", "Routes"]) sorted_networks = sorted(nexthops[nexthop], key=utilities.human_sort_key) for network in sorted_networks: output.append([nexthop, network]) output.append(["", ""]) if vrf: output_filename = session.create_output_filename( "{0}-eigrp-{1}-summary".format(vrf, as_num), ext=".csv") else: output_filename = session.create_output_filename( "-eigrp-{0}-summary".format(as_num), ext=".csv") utilities.list_of_lists_to_csv(output, output_filename) # Return terminal parameters back to the original state. session.end_cisco_session()
def get_wlan_detail(session, to_cvs=False): """ A function that captures the WLC AireOS wlan & remote-lan & guest-lan details and returns an output list :param session: The script object that represents this script being executed :type session: session.Session :return: A list of wlan details :rtype: list of lists """ # Get the show wlan summary send_cmd = "show wlan summary" raw_wlan_summary = session.get_command_output(send_cmd) # Get the show remote-lan summary send_cmd = "show remote-lan summary" raw_rlan_summary = session.get_command_output(send_cmd) # Get the show guest-lan summary send_cmd = "show guest-lan summary" raw_glan_summary = session.get_command_output(send_cmd) template_file = session.script.get_template( "cisco_aireos_show_wlan_summary.template") wlan_summary_dict = utilities.textfsm_parse_to_dict( raw_wlan_summary, template_file) rlan_summary_dict = utilities.textfsm_parse_to_dict( raw_rlan_summary, template_file) glan_summary_dict = utilities.textfsm_parse_to_dict( raw_glan_summary, template_file) output_raw = '' output_list = [] for wlan_entry in wlan_summary_dict: send_cmd = "show wlan " + format(wlan_entry["WLAN_Identifier"]) output_list.append(session.get_command_output(send_cmd)) raw_rlan_detail = '' for wlan_entry in rlan_summary_dict: send_cmd = "show remote-lan " + format(wlan_entry["WLAN_Identifier"]) output_list.append(session.get_command_output(send_cmd)) raw_glan_detail = '' for wlan_entry in glan_summary_dict: send_cmd = "show guest-lan " + format(wlan_entry["WLAN_Identifier"]) output_list.append(session.get_command_output(send_cmd)) output = [] first = True for output_raw in output_list: # TextFSM template for parsing "show wlan <WLAN-ID>" output template_file = session.script.get_template( "cisco_aireos_show_wlan_detail.template") if first: output = utilities.textfsm_parse_to_list(output_raw, template_file, add_header=True) first = False else: output.append( utilities.textfsm_parse_to_list(output_raw, template_file, add_header=False)[0]) if to_cvs: output_filename = session.create_output_filename("wlan-detail", ext=".csv") utilities.list_of_lists_to_csv(output, output_filename) return output
def get_ap_detail(session, to_cvs=False): """ A function that captures the WLC AireOS ap details and returns an output list :param session: The script object that represents this script being executed :type session: session.Session :return: A list AP details :rtype: list of dicts """ AP_Name_Key = "AP_Name" AP_80211A_suffix = "_80211_A" AP_80211B_suffix = "_80211_B" AP_Slot1_suffix = "_Slot_1" AP_Slot0_suffix = "_Slot_0" AP_CDP_mid = "_CDP_" output_raw = '' send_cmd = "show ap summary" output_raw += session.get_command_output(send_cmd) template_file = session.script.get_template("cisco_aireos_show_ap_summary.template") ap_summ_list_of_dict = utilities.textfsm_parse_to_dict(output_raw, template_file) def collect_it(in_list_of_dict, in_send_cmd, in_send_cmd_key, in_template_filename, in_key_suffix=''): # start with an empty output this_output_raw = '' # step across each dict entry in the list for this_dict_entry in in_list_of_dict: # send the in_send_cmd and add to it per dict entry aspect defined by the in_send_cmd_key this_send_cmd = in_send_cmd + format(this_dict_entry[in_send_cmd_key]) # add this to the overall output to be processed this_output_raw += session.get_command_output(this_send_cmd) # use the template file provided & process the overall output processing_template_file = session.script.get_template(in_template_filename) processing_list_of_dict = utilities.textfsm_parse_to_dict(this_output_raw, processing_template_file) # start with an empty out_list_of_dict out_list_of_dict = [] for this_dict_entry in processing_list_of_dict: # start with an empty change_dict_entry change_dict_entry = {} # step across each key for this_key in this_dict_entry.keys(): # don't change the primary key name .. for the key:value entry if this_key == in_send_cmd_key: change_dict_entry[this_key] = this_dict_entry[this_key] # for the rest of the keys, add the suffix .. for the key:value entry else: change_dict_entry[this_key+in_key_suffix] = this_dict_entry[this_key] # now with key names changed, append the change_dict_entry out_list_of_dict.append(change_dict_entry) return out_list_of_dict def merge_it(in_base_list_of_dict, in_add_list_of_dict): # Now merge the various collected data out_list_of_dict = [] # step across the original in_base_list_of_dict for base_dict_entry in in_base_list_of_dict: # start with what came from the base dict entry process_dict_entry = base_dict_entry # now for each of the sub command lists, find the matching dict entry and update merge it for in_add_entry in in_add_list_of_dict: if in_add_entry[AP_Name_Key] == base_dict_entry[AP_Name_Key]: process_dict_entry.update(in_add_entry) out_list_of_dict.append(process_dict_entry) return(out_list_of_dict) dummy_dict_entry = {} dummy_dict_entry[AP_Name_Key] = "" dummy_list_of_dict = [dummy_dict_entry] ap_config_general_list_of_dict = collect_it(ap_summ_list_of_dict, "show ap config general ", AP_Name_Key, \ "cisco_aireos_show_ap_config_general.template" ) ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_config_general_list_of_dict) ap_config_slot_1_list_of_dict = collect_it(ap_summ_list_of_dict, "show ap config slot 1 ", AP_Name_Key, \ "cisco_aireos_show_ap_config_slot.template", AP_Slot1_suffix) ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_config_slot_1_list_of_dict) ap_config_slot_0_list_of_dict = collect_it(ap_summ_list_of_dict, "show ap config slot 0 ", AP_Name_Key, \ "cisco_aireos_show_ap_config_slot.template", AP_Slot0_suffix) ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_config_slot_0_list_of_dict) ap_TxPower_80211_A_list_of_dict = collect_it(dummy_list_of_dict, "show advanced 802.11a txpower", AP_Name_Key, \ "cisco_aireos_show_advanced_txpower.template", AP_80211A_suffix) ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_TxPower_80211_A_list_of_dict) ap_TxPower_80211_B_list_of_dict = collect_it(dummy_list_of_dict, "show advanced 802.11b txpower", AP_Name_Key, \ "cisco_aireos_show_advanced_txpower.template", AP_80211B_suffix) ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_TxPower_80211_B_list_of_dict) ap_cdp_list_of_dict = collect_it( dummy_list_of_dict, "show ap cdp neighbors detail all", AP_Name_Key, \ "cisco_aireos_show_ap_cdp_neighbors_detail_all.template") ap_list_of_dict = merge_it(ap_summ_list_of_dict, ap_cdp_list_of_dict) output = ap_list_of_dict if to_cvs: ap_list_of_dict.sort(key=itemgetter(AP_Name_Key)) # build an overall list of keys for using as header key_list = [] # first collect up the unique keys by stepping across each dict for ap_dict_entry in ap_list_of_dict: for this_key in ap_dict_entry.keys(): if this_key not in key_list: key_list.append(this_key) # next sort the resulting key list key_list.sort() # now resequence to group up the sorted sections key_list_new = [AP_Name_Key, "AP_Model", "AP_MAC_Enet", "AP_IP_Conf", "AP_IP_Addr"] key_list_slot_1 = [] key_list_slot_A = [] key_list_slot_0 = [] key_list_slot_B = [] key_list_cdp = [] key_list_misc = [] for this_key in key_list: if (this_key not in key_list_slot_1) and (AP_Slot1_suffix in this_key): key_list_slot_1.append(this_key) elif (this_key not in key_list_slot_A) and (AP_80211A_suffix in this_key): key_list_slot_A.append(this_key) elif (this_key not in key_list_slot_0) and (AP_Slot0_suffix in this_key): key_list_slot_0.append(this_key) elif (this_key not in key_list_slot_B) and (AP_80211B_suffix in this_key): key_list_slot_B.append(this_key) elif (this_key not in key_list_cdp) and (AP_CDP_mid in this_key): key_list_cdp.append(this_key) elif (this_key not in key_list_misc) and (this_key not in key_list_new): key_list_misc.append(this_key) key_list = key_list_new + key_list_misc \ + key_list_slot_1 + key_list_slot_A \ + key_list_slot_0 + key_list_slot_B \ + key_list_cdp output_filename = session.create_output_filename("ap-summ", ext=".csv") utilities.list_of_dicts_to_csv(output, output_filename, key_list, add_header=True) return output