예제 #1
0
def get_mac_table(session):
    send_cmd = "show mac address-table"
    peer_link = None   # Defining variable to hold peer link information if we have an NXOS output

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "IOS":
        template_file = "textfsm-templates/cisco_ios_show_mac_addr_table.template"
    else:
        template_file = "textfsm-templates/cisco_nxos_show_mac_addr_table.template"

    logger.debug("Using template: '{0}'".format(template_file))

    raw_mac = session.get_command_output(send_cmd)
    mac_table = utils.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(mac_table) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))

        raw_mac = session.get_command_output(send_cmd)

        mac_table = utils.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    # Check for vPCs on NXOS to account for "vPC Peer-Link" entries in MAC table of N9Ks
    elif session.os == "NXOS":
        send_cmd = "show vpc"
        vpc_template = "textfsm-templates/cisco_nxos_show_vpc.template"

        raw_show_vpc = session.get_command_output(send_cmd)
        vpc_table = utils.textfsm_parse_to_list(raw_show_vpc, vpc_template)

        if len(vpc_table) > 0:
            peer_link_record = vpc_table[0]
            peer_link = utils.long_int_name(peer_link_record[1])
        else:
            peer_link = None

    # Convert TextFSM output to a dictionary for lookups
    output = {}
    for entry in mac_table:
        vlan = entry[0]
        mac = entry[1]

        raw_intf = entry[2]
        if "vpc" in raw_intf.lower():
            intf = peer_link
        else:
            intf = utils.long_int_name(raw_intf)

        if intf in output:
            output[intf].append((mac, vlan))
        else:
            output[intf] = [(mac, vlan)]

    return output
예제 #2
0
def get_auth_list(session, to_cvs=False):
    """
    A function that captures the WLC AireOS auth-list and returns an output list

    :param session: The script object that represents this script being executed
    :type session: session.Session

    :return: A list of MAC auth-list
    :rtype: list of lists
    """
    send_cmd = "show auth-list"
    output_raw = session.get_command_output(send_cmd)

    # TextFSM template for parsing "show auth-list" output
    template_file = session.script.get_template(
        "cisco_aireos_show_auth_list.template")
    output = utilities.textfsm_parse_to_list(output_raw,
                                             template_file,
                                             add_header=True)

    if to_cvs:
        output_filename = session.create_output_filename("auth-list",
                                                         ext=".csv")
        utilities.list_of_lists_to_csv(output, output_filename)

    return output
예제 #3
0
def get_desc_table(session):
    """
    A function that creates a lookup dictionary that can be used to get the description of an interface.

    :param session: The script object that represents this script being executed
    :type session: sessions.Session

    :return: A dictionary that allows getting the description of an interface by using the interface as the key.
    :rtype: dict
    """
    send_cmd = "show interface description"

    if session.os == "IOS":
        int_template = session.script.get_template("cisco_ios_show_interfaces_description.template")
    else:
        int_template = session.script.get_template("cisco_nxos_show_interface_description.template")

    raw_int_desc = session.get_command_output(send_cmd)
    desc_list = utilities.textfsm_parse_to_list(raw_int_desc, int_template)

    desc_table = {}
    # Change interface names to long versions for better matching with other outputs
    for entry in desc_list:
        intf = utilities.long_int_name(entry[0])
        desc_table[intf] = entry[1]

    return desc_table
def get_int_status(session):
    """
    A function that captures the "show interface status" command and returns the processed output from TextFSM

    :param session: The script object that represents this script being executed
    :type session: sessions.Session

    :return: TextFSM output from processing the "show interface status" command
    :rtype: list of list
    """
    if session.os == "IOS":
        template_file = session.script.get_template(
            "cisco_ios_show_interfaces_status.template")
    else:
        template_file = session.script.get_template(
            "cisco_nxos_show_interface_status.template")

    raw_int_status = session.get_command_output("show interface status")
    fsm_results = utilities.textfsm_parse_to_list(raw_int_status,
                                                  template_file)

    for entry in fsm_results:
        entry[0] = utilities.long_int_name(entry[0])

    return fsm_results
def get_desc_table(session):
    """
    A function that creates a lookup dictionary that can be used to get the description of an interface.

    :param session: The script object that represents this script being executed
    :type session: sessions.Session

    :return: A dictionary that allows getting the description of an interface by using the interface as the key.
    :rtype: dict
    """
    send_cmd = "show interface description"

    if session.os == "IOS":
        int_template = session.script.get_template(
            "cisco_ios_show_interfaces_description.template")
    else:
        int_template = session.script.get_template(
            "cisco_nxos_show_interface_description.template")

    raw_int_desc = session.get_command_output(send_cmd)
    desc_list = utilities.textfsm_parse_to_list(raw_int_desc, int_template)

    desc_table = {}
    # Change interface names to long versions for better matching with other outputs
    for entry in desc_list:
        intf = utilities.long_int_name(entry[0])
        desc_table[intf] = entry[1]

    return desc_table
예제 #6
0
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.",
                            "Unsupported OS",
                            options=sessions.ICON_STOP)
        return

    send_cmd = "show interface"

    if session.os == "NXOS":
        template_file = "textfsm-templates/cisco_nxos_show_interface.template"
    else:
        template_file = "textfsm-templates/cisco_ios_show_interfaces.template"

    raw_intf = session.get_command_output(send_cmd)

    fsm_results = utils.textfsm_parse_to_list(raw_intf,
                                              template_file,
                                              add_header=True)

    output_filename = session.create_output_filename("int-stats", ext=".csv")
    utils.list_of_lists_to_csv(fsm_results, output_filename)

    # Clean up before closing session
    session.end()
예제 #7
0
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

    send_cmd = "show cdp neighbors detail"

    raw_cdp = session.get_command_output(send_cmd)

    template_file = "textfsm-templates/cisco_os_show_cdp_neigh_det.template"

    cdp_table = utils.textfsm_parse_to_list(raw_cdp, template_file)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in cdp_table:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utils.extract_system_name(
                entry[1], strip_list=local_settings['strip_domains'])

    num_created, num_skipped = create_sessions_from_cdp(
        session, cdp_table, local_settings)

    setting_msg = "{0} sessions created in the directory '{1}' under Sessions\n\n{2} sessions skipped (no IP or " \
                  "duplicate)".format(num_created, local_settings['session_path'], num_skipped)
    session.message_box(setting_msg, "Sessions Created", sessions.ICON_INFO)

    # Clean up before closing session
    session.end()
예제 #8
0
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

    if session.os == "IOS":
        send_cmd = "show vlan brief"
        template_file = "textfsm-templates/cisco_ios_show_vlan.template"
    else:
        send_cmd = "show vlan brief"
        template_file = "textfsm-templates/cisco_nxos_show_vlan.template"
    logger.debug("Using template file: {0}".format(template_file))

    raw_vlan = session.get_command_output(send_cmd)

    fsm_results = utils.textfsm_parse_to_list(raw_vlan,
                                              template_file,
                                              add_header=True)

    normalize_port_list(fsm_results)

    output_filename = session.create_output_filename("vlan", ext=".csv")
    utils.list_of_lists_to_csv(fsm_results, output_filename)

    # Clean up before closing session
    session.end()
예제 #9
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will capture the ARP table of the attached device and output the results as a CSV file.  While this
    script can be used to capture the ARP table, the primary purpose is to create the ARP associations that the
    "s_switchport_mapping.py" script can use to map which MAC and IP addresses are connected to each device.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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"])

    # Prompt for the VRF
    selected_vrf = script.prompt_window("Enter the VRF name.\n(Leave blank for default VRF)")
    if selected_vrf == "":
        selected_vrf = None
    logger.debug("Set VRF to '{0}'".format(selected_vrf))

    # Select template file based on network OS
    if session.os == "IOS":
        send_cmd = "show ip arp"
        template_file = script.get_template("cisco_ios_show_ip_arp.template")
    else:
        send_cmd = "show ip arp detail"
        template_file = script.get_template("cisco_nxos_show_ip_arp_detail.template")

    logger.debug("Command set to '{0}'".format(send_cmd))

    # If a VRF was specified, update the commands and outputs to reflect this.
    if selected_vrf:
        send_cmd = send_cmd + " vrf {0}".format(selected_vrf)
        session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf)
        logger.debug("Updated hostname to: '{0}'".format(session.hostname))

    # Get "show ip arp" data
    raw_arp = session.get_command_output(send_cmd)

    # Process with TextFSM
    logger.debug("Using template: '{0}'".format(template_file))
    fsm_results = utilities.textfsm_parse_to_list(raw_arp, template_file, add_header=True)

    # Generate filename and output data as CSV
    output_filename = session.create_output_filename("arp", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #10
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will capture the ARP table of the attached device and output the results as a CSV file.  While this
    script can be used to capture the ARP table, the primary purpose is to create the ARP associations that the
    "s_switchport_mapping.py" script can use to map which MAC and IP addresses are connected to each device.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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"])

    # Prompt for the VRF
    selected_vrf = script.prompt_window("Enter the VRF name.\n(Leave blank for default VRF)")
    if selected_vrf == "":
        selected_vrf = None
    logger.debug("Set VRF to '{0}'".format(selected_vrf))

    # Select template file based on network OS
    if session.os == "IOS":
        send_cmd = "show ip arp"
        template_file = script.get_template("cisco_ios_show_ip_arp.template")
    else:
        send_cmd = "show ip arp detail"
        template_file = script.get_template("cisco_nxos_show_ip_arp_detail.template")

    logger.debug("Command set to '{0}'".format(send_cmd))

    # If a VRF was specified, update the commands and outputs to reflect this.
    if selected_vrf:
        send_cmd = send_cmd + " vrf {0}".format(selected_vrf)
        session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf)
        logger.debug("Updated hostname to: '{0}'".format(session.hostname))

    # Get "show ip arp" data
    raw_arp = session.get_command_output(send_cmd)

    # Process with TextFSM
    logger.debug("Using template: '{0}'".format(template_file))
    fsm_results = utilities.textfsm_parse_to_list(raw_arp, template_file, add_header=True)

    # Generate filename and output data as CSV
    output_filename = session.create_output_filename("arp", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #11
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and export it to a CSV file
    containing the important information, such as Remote Device hostname, model and IP information, in addition to the
    local and remote interfaces that connect the devices.

    **Script Settings** (found in settings/settings.ini):

    * | **strip_domains** -  A list of domain names that will be stripped away if found in the CDP remote device name.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                    SecureCRTSession or DirectSession)
    :type session: sessions.Session
    """
    # 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"])

    # Define the command to send to the remote device
    send_cmd = "show cdp neighbors detail"
    logger.debug("Command set to '{0}'".format(send_cmd))

    # Get domain names to strip from device IDs from settings file
    strip_list = script.settings.getlist("cdp_to_csv", "strip_domains")

    # Get the output from our above command
    raw_cdp = session.get_command_output(send_cmd)

    # Choose the TextFSM template and process the data
    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")
    fsm_results = utilities.textfsm_parse_to_list(raw_cdp,
                                                  template_file,
                                                  add_header=True)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results[1:]:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utilities.extract_system_name(entry[1],
                                                     strip_list=strip_list)
        # Convert list of IPs into a comma-separated list of IPs
        entry[4] = ", ".join(entry[4])
        # Convert list of Mgmt IPs into a comma-separated list of IPs
        entry[7] = ", ".join(entry[7])

    output_filename = session.create_output_filename("cdp", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #12
0
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

    send_cmd = "show mac address-table"

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "NXOS":
        template_file = "textfsm-templates/cisco_nxos_show_mac_addr_table.template"
    else:
        template_file = "textfsm-templates/cisco_ios_show_mac_addr_table.template"

    logger.debug("Using template: '{0}'".format(template_file))

    raw_mac = session.get_command_output(send_cmd)
    fsm_results = utils.textfsm_parse_to_list(raw_mac,
                                              template_file,
                                              add_header=True)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(fsm_results) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))

        raw_mac = session.get_command_output(send_cmd)

        fsm_results = utils.textfsm_parse_to_list(raw_mac,
                                                  template_file,
                                                  add_header=True)

    output_filename = session.create_output_filename("mac-addr", ext=".csv")
    utils.list_of_lists_to_csv(fsm_results, output_filename)

    # Clean up before closing session
    session.end()
예제 #13
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the MAC address table from a Cisco IOS or NX-OS device and export it to a CSV file.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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"])

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "NXOS":
        template_file = script.get_template("cisco_nxos_show_mac_addr_table.template")
    else:
        template_file = script.get_template("cisco_ios_show_mac_addr_table.template")

    raw_mac = session.get_command_output("show mac address-table")
    fsm_results = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(fsm_results) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))
        raw_mac = session.get_command_output(send_cmd)
        fsm_results = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    output_filename = session.create_output_filename("mac-addr", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #14
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and export it to a CSV file
    containing the important information, such as Remote Device hostname, model and IP information, in addition to the
    local and remote interfaces that connect the devices.

    **Script Settings** (found in settings/settings.ini):

    * | **strip_domains** -  A list of domain names that will be stripped away if found in the CDP remote device name.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                    SecureCRTSession or DirectSession)
    :type session: sessions.Session
    """
    # 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"])

    # Define the command to send to the remote device
    send_cmd = "show cdp neighbors detail"
    logger.debug("Command set to '{0}'".format(send_cmd))

    # Get domain names to strip from device IDs from settings file
    strip_list = script.settings.getlist("cdp_to_csv", "strip_domains")

    # Get the output from our above command
    raw_cdp = session.get_command_output(send_cmd)

    # Choose the TextFSM template and process the data
    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")
    fsm_results = utilities.textfsm_parse_to_list(raw_cdp, template_file, add_header=True)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results[1:]:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utilities.extract_system_name(entry[1], strip_list=strip_list)
        # Convert list of IPs into a comma-separated list of IPs
        entry[4] = ", ".join(entry[4])
        # Convert list of Mgmt IPs into a comma-separated list of IPs
        entry[7] = ", ".join(entry[7])

    output_filename = session.create_output_filename("cdp", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #15
0
def get_int_status(session):
    send_cmd = "show interface status"

    if session.os == "IOS":
        int_template = "textfsm-templates/cisco_ios_show_interfaces_status.template"
    else:
        int_template = "textfsm-templates/cisco_nxos_show_interface_status.template"

    raw_int_status = session.get_command_output(send_cmd)
    fsm_results = utils.textfsm_parse_to_list(raw_int_status, int_template)

    for entry in fsm_results:
        entry[0] = utils.long_int_name(entry[0])

    return fsm_results
예제 #16
0
def get_desc_table(session):
    send_cmd = "show interface description"

    if session.os == "IOS":
        int_template = "textfsm-templates/cisco_ios_show_interfaces_description.template"
    else:
        int_template = "textfsm-templates/cisco_nxos_show_interface_description.template"

    raw_int_desc = session.get_command_output(send_cmd)
    desc_list = utils.textfsm_parse_to_list(raw_int_desc, int_template)

    desc_table = {}
    # Change interface names to long versions for better matching with other outputs
    for entry in desc_list:
        intf = utils.long_int_name(entry[0])
        desc_table[intf] = entry[1]

    return desc_table
예제 #17
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will scrape some stats (packets, rate, errors) from all the UP interfaces on the device and put it into
    a CSV file.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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"])

    # Get correct TextFSM template based on remote device OS
    if session.os == "NXOS":
        template_file = script.get_template(
            "cisco_nxos_show_interface.template")
    else:
        template_file = script.get_template(
            "cisco_ios_show_interfaces.template")

    # Get raw output from the device
    raw_intf = session.get_command_output("show interface")
    # Process the output with TextFSM
    fsm_results = utilities.textfsm_parse_to_list(raw_intf,
                                                  template_file,
                                                  add_header=True)
    # Create output filename
    output_filename = session.create_output_filename("int-stats", ext=".csv")
    # Write output to a CSV file
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #18
0
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

    selected_vrf = session.prompt_window(
        "Enter the VRF name.\n(Leave blank for default VRF)")
    if selected_vrf == "":
        selected_vrf = None
    logger.debug("Set VRF to '{0}'".format(selected_vrf))

    if session.os == "IOS":
        send_cmd = "show ip arp"
        template_file = "textfsm-templates/cisco_ios_show_ip_arp.template"
    else:
        send_cmd = "show ip arp detail"
        template_file = "textfsm-templates/cisco_nxos_show_ip_arp_detail.template"

    logger.debug("Command set to '{0}'".format(send_cmd))

    if selected_vrf:
        send_cmd = send_cmd + " vrf {0}".format(selected_vrf)
        session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf)
        logger.debug("Updated hostname to: '{0}'".format(session.hostname))

    raw_arp = session.get_command_output(send_cmd)

    logger.debug("Using template: '{0}'".format(template_file))

    fsm_results = utils.textfsm_parse_to_list(raw_arp,
                                              template_file,
                                              add_header=True)

    output_filename = session.create_output_filename("arp", ext=".csv")
    utils.list_of_lists_to_csv(fsm_results, output_filename)

    # Clean up before closing session
    session.end()
예제 #19
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will output the VLAN database to a CSV file.

    One possibly use of this script is to take the .CSV outputs from 2 or more devices, paste them
    into a single XLS file and use Excel to highlight duplicate values, so VLAN overlaps can be
    discovered prior to connecting switches together via direct link, OTV, etc.  This could also be used
    to find missing VLANs between 2 large tables that should have the same VLANs.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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 session.os == "IOS":
        template_file = script.get_template("cisco_ios_show_vlan.template")
    else:
        template_file = script.get_template("cisco_nxos_show_vlan.template")

    raw_vlan = session.get_command_output("show vlan brief")

    fsm_results = utilities.textfsm_parse_to_list(raw_vlan, template_file, add_header=True)

    normalize_port_list(fsm_results)

    output_filename = session.create_output_filename("vlan", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #20
0
def per_device_work(session, selected_vrf, add_header):
    """
    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.

    This function gathers the ARP table information and returns it (in list format) to the calling program.
    """
    script = session.script
    session.start_cisco_session()

    # Validate device is running a supported OS
    session.validate_os(["IOS", "NXOS"])

    # Select template file based on network OS
    if session.os == "IOS":
        send_cmd = "show ip arp"
        template_file = script.get_template("cisco_ios_show_ip_arp.template")
    else:
        send_cmd = "show ip arp detail"
        template_file = script.get_template(
            "cisco_nxos_show_ip_arp_detail.template")

    # If a VRF was specified, update the commands and outputs to reflect this.
    if selected_vrf:
        send_cmd = send_cmd + " vrf {0}".format(selected_vrf)
        script.hostname = script.hostname + "-VRF-{0}".format(selected_vrf)
        logger.debug("Updated hostname to: '{0}'".format(script.hostname))

    # Get "show ip arp" data
    raw_arp = session.get_command_output(send_cmd)

    # Process with TextFSM
    logger.debug("Using template: '{0}'".format(template_file))
    fsm_results = utilities.textfsm_parse_to_list(raw_arp,
                                                  template_file,
                                                  add_header=add_header)

    # Return terminal parameters to starting values
    session.end_cisco_session()

    return fsm_results
예제 #21
0
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will scrape some stats (packets, rate, errors) from all the UP interfaces on the device and put it into
    a CSV file.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                SecureCRTSession or DirectSession)
    :type session: sessions.Session

    """
    # 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"])

    # Get correct TextFSM template based on remote device OS
    if session.os == "NXOS":
        template_file = script.get_template("cisco_nxos_show_interface.template")
    else:
        template_file = script.get_template("cisco_ios_show_interfaces.template")

    # Get raw output from the device
    raw_intf = session.get_command_output("show interface")
    # Process the output with TextFSM
    fsm_results = utilities.textfsm_parse_to_list(raw_intf, template_file, add_header=True)
    # Create output filename
    output_filename = session.create_output_filename("int-stats", ext=".csv")
    # Write output to a CSV file
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #22
0
def get_ap_summ_table(session):
    """
    A function that captures the WLC AireOS ap summary 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 MAC information for AP summary
    :rtype: list
    """
    send_cmd = "show ap summary"

    # TextFSM template for parsing "show ap summary" output
    template_file = session.script.get_template(
        "cisco_aireos_show_ap_summary.template")

    raw_ap_summ = session.get_command_output(send_cmd)
    ap_summ_table = utilities.textfsm_parse_to_list(raw_ap_summ,
                                                    template_file,
                                                    add_header=True)

    return ap_summ_table
예제 #23
0
def get_int_status(session):
    """
    A function that captures the "show interface status" command and returns the processed output from TextFSM

    :param session: The script object that represents this script being executed
    :type session: sessions.Session

    :return: TextFSM output from processing the "show interface status" command
    :rtype: list of list
    """
    if session.os == "IOS":
        template_file = session.script.get_template("cisco_ios_show_interfaces_status.template")
    else:
        template_file = session.script.get_template("cisco_nxos_show_interface_status.template")

    raw_int_status = session.get_command_output("show interface status")
    fsm_results = utilities.textfsm_parse_to_list(raw_int_status, template_file)

    for entry in fsm_results:
        entry[0] = utilities.long_int_name(entry[0])

    return fsm_results
예제 #24
0
def per_device_work(session, selected_vrf, add_header):
    """
    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.

    This function gathers the ARP table information and returns it (in list format) to the calling program.
    """
    script = session.script
    session.start_cisco_session()

    # Validate device is running a supported OS
    session.validate_os(["IOS", "NXOS"])

    # Select template file based on network OS
    if session.os == "IOS":
        send_cmd = "show ip arp"
        template_file = script.get_template("cisco_ios_show_ip_arp.template")
    else:
        send_cmd = "show ip arp detail"
        template_file = script.get_template("cisco_nxos_show_ip_arp_detail.template")

    # If a VRF was specified, update the commands and outputs to reflect this.
    if selected_vrf:
        send_cmd = send_cmd + " vrf {0}".format(selected_vrf)
        script.hostname = script.hostname + "-VRF-{0}".format(selected_vrf)
        logger.debug("Updated hostname to: '{0}'".format(script.hostname))

    # Get "show ip arp" data
    raw_arp = session.get_command_output(send_cmd)

    # Process with TextFSM
    logger.debug("Using template: '{0}'".format(template_file))
    fsm_results = utilities.textfsm_parse_to_list(raw_arp, template_file, add_header=add_header)

    # Return terminal parameters to starting values
    session.end_cisco_session()

    return fsm_results
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
예제 #26
0
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

    send_cmd = "show cdp neighbors detail"

    logger.debug("Command set to '{0}'".format(send_cmd))

    raw_cdp = session.get_command_output(send_cmd)

    template_file = "textfsm-templates/cisco_os_show_cdp_neigh_det.template"
    logger.debug("Using template: '{0}'".format(template_file))

    fsm_results = utils.textfsm_parse_to_list(raw_cdp,
                                              template_file,
                                              add_header=True)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utils.extract_system_name(
                entry[1], strip_list=local_settings['strip_domains'])

    output_filename = session.create_output_filename("cdp", ext=".csv")
    utils.list_of_lists_to_csv(fsm_results, output_filename)

    # Clean up before closing session
    session.end()
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and create SecureCRT sessions
    based on the information.  By default all sessions will be created as SSH2, so you may have
    to manually change some sessions to make them work, depending on the device capabilities/configuration.

    Only devices that contain "Router" or "Switch" in their capabilities field of the CDP information will have sessions
    created for them. This skips phones, hosts like VMware or Server modules, and other devices that we don't usually
    log into directly).

    **NOTE ON DEFAULTS**: This script uses the SecureCRT Default Session settings as a base for any sessions that are
    created.  The folder where the sessions are saved is specified in the 'settings.ini' file, and the hostname and IP
    are extracted from the CDP information.  All other setting defaults are configured within SecureCRT.

    **Script Settings** (found in settings/settings.ini):

    * | **folder** - The path starting from the <SecureCRT Config>/Sessions/ directory where
      | the sessions will be created.
    * | **strip_domains** -  A list of domain names that will be stripped away if found in the CDP remote device name.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                    SecureCRTSession or DirectSession)
    :type session: sessions.Session
    """
    # 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"])

    raw_cdp = session.get_command_output("show cdp neighbors detail")

    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")

    cdp_table = utilities.textfsm_parse_to_list(raw_cdp, template_file)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    strip_list = script.settings.getlist("create_sessions_from_cdp", "strip_domains")
    for entry in cdp_table:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utilities.extract_system_name(entry[1], strip_list=strip_list)
        # Convert list of Mgmt IPs into a comma-separated list of IPs
        entry[7] = ", ".join(entry[7])

    session_list = create_session_list(cdp_table)

    # Get the destination directory from settings
    dest_folder = script.settings.get("create_sessions_from_cdp", "folder")

    for device in session_list:
        system_name = device[0]
        mgmt_ip = device[1]
        script.create_new_saved_session(system_name, mgmt_ip, folder=dest_folder)
        # Track the names of the hosts we've made already
        logger.debug("Created session for {0}.".format(system_name))

    # Calculate statistics
    num_created = len(session_list)
    num_skipped = len(cdp_table) - len(session_list)

    setting_msg = "{0} sessions created in the Sessions sub-directory '{1}'\n" \
                  "\n" \
                  "{0} sessions skipped (no IP, duplicate, or not Router/Switch)".format(num_created,
                                                                                         dest_folder, num_skipped)
    script.message_box(setting_msg, "Sessions Created", ICON_INFO)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
def script_main(session):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and create SecureCRT sessions
    based on the information.  By default all sessions will be created as SSH2, so you may have
    to manually change some sessions to make them work, depending on the device capabilities/configuration.

    Only devices that contain "Router" or "Switch" in their capabilities field of the CDP information will have sessions
    created for them. This skips phones, hosts like VMware or Server modules, and other devices that we don't usually
    log into directly).

    **NOTE ON DEFAULTS**: This script uses the SecureCRT Default Session settings as a base for any sessions that are
    created.  The folder where the sessions are saved is specified in the 'settings.ini' file, and the hostname and IP
    are extracted from the CDP information.  All other setting defaults are configured within SecureCRT.

    **Script Settings** (found in settings/settings.ini):

    * | **folder** - The path starting from the <SecureCRT Config>/Sessions/ directory where
      | the sessions will be created.
    * | **strip_domains** -  A list of domain names that will be stripped away if found in the CDP remote device name.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
                    SecureCRTSession or DirectSession)
    :type session: sessions.Session
    """
    # 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"])

    raw_cdp = session.get_command_output("show cdp neighbors detail")

    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")

    cdp_table = utilities.textfsm_parse_to_list(raw_cdp, template_file)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    strip_list = script.settings.getlist("create_sessions_from_cdp",
                                         "strip_domains")
    for entry in cdp_table:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utilities.extract_system_name(entry[1],
                                                     strip_list=strip_list)
        # Convert list of Mgmt IPs into a comma-separated list of IPs
        entry[7] = ", ".join(entry[7])

    session_list = create_session_list(cdp_table)

    # Get the destination directory from settings
    dest_folder = script.settings.get("create_sessions_from_cdp", "folder")

    for device in session_list:
        system_name = device[0]
        mgmt_ip = device[1]
        script.create_new_saved_session(system_name,
                                        mgmt_ip,
                                        folder=dest_folder)
        # Track the names of the hosts we've made already
        logger.debug("Created session for {0}.".format(system_name))

    # Calculate statistics
    num_created = len(session_list)
    num_skipped = len(cdp_table) - len(session_list)

    setting_msg = "{0} sessions created in the Sessions sub-directory '{1}'\n" \
                  "\n" \
                  "{0} sessions skipped (no IP, duplicate, or not Router/Switch)".format(num_created,
                                                                                         dest_folder, num_skipped)
    script.message_box(setting_msg, "Sessions Created", ICON_INFO)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
def get_mac_table(session):
    """
    A function that captures the mac address table and returns an output dictionary that can be used to look up the MAC
    address and VLAN associated with an interface.

    :param session: The script object that represents this script being executed
    :type session: session.Session

    :return: A dictionary that allows lookups of MAC and VLAN information for interfaces
    :rtype: dict
    """
    send_cmd = "show mac address-table"
    peer_link = None  # Defining variable to hold peer link information if we have an NXOS output

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "IOS":
        template_file = session.script.get_template(
            "cisco_ios_show_mac_addr_table.template")
    else:
        template_file = session.script.get_template(
            "cisco_nxos_show_mac_addr_table.template")

    raw_mac = session.get_command_output(send_cmd)
    mac_table = utilities.textfsm_parse_to_list(raw_mac,
                                                template_file,
                                                add_header=True)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(mac_table) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))

        raw_mac = session.get_command_output(send_cmd)

        mac_table = utilities.textfsm_parse_to_list(raw_mac,
                                                    template_file,
                                                    add_header=True)

    # Check for vPCs on NXOS to account for "vPC Peer-Link" entries in MAC table of N9Ks
    elif session.os == "NXOS":
        send_cmd = "show vpc"
        vpc_template = session.script.get_template(
            "cisco_nxos_show_vpc.template")

        raw_show_vpc = session.get_command_output(send_cmd)
        vpc_table = utilities.textfsm_parse_to_list(raw_show_vpc, vpc_template)

        if len(vpc_table) > 0:
            peer_link_record = vpc_table[0]
            peer_link = utilities.long_int_name(peer_link_record[1])
        else:
            peer_link = None

    # Convert TextFSM output to a dictionary for lookups
    output = {}
    for entry in mac_table:
        vlan = entry[0]
        mac = entry[1]

        raw_intf = entry[2]
        if "vpc" in raw_intf.lower():
            intf = peer_link
        else:
            intf = utilities.long_int_name(raw_intf)

        if intf in output:
            output[intf].append((mac, vlan))
        else:
            output[intf] = [(mac, vlan)]

    return output
예제 #30
0
def get_mac_table(session):
    """
    A function that captures the mac address table and returns an output dictionary that can be used to look up the MAC
    address and VLAN associated with an interface.

    :param session: The script object that represents this script being executed
    :type session: session.Session

    :return: A dictionary that allows lookups of MAC and VLAN information for interfaces
    :rtype: dict
    """
    send_cmd = "show mac address-table"
    peer_link = None   # Defining variable to hold peer link information if we have an NXOS output

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "IOS":
        template_file = session.script.get_template("cisco_ios_show_mac_addr_table.template")
    else:
        template_file = session.script.get_template("cisco_nxos_show_mac_addr_table.template")

    raw_mac = session.get_command_output(send_cmd)
    mac_table = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(mac_table) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))

        raw_mac = session.get_command_output(send_cmd)

        mac_table = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=True)

    # Check for vPCs on NXOS to account for "vPC Peer-Link" entries in MAC table of N9Ks
    elif session.os == "NXOS":
        send_cmd = "show vpc"
        vpc_template = session.script.get_template("cisco_nxos_show_vpc.template")

        raw_show_vpc = session.get_command_output(send_cmd)
        vpc_table = utilities.textfsm_parse_to_list(raw_show_vpc, vpc_template)

        if len(vpc_table) > 0:
            peer_link_record = vpc_table[0]
            peer_link = utilities.long_int_name(peer_link_record[1])
        else:
            peer_link = None

    # Convert TextFSM output to a dictionary for lookups
    output = {}
    for entry in mac_table:
        vlan = entry[0]
        mac = entry[1]

        raw_intf = entry[2]
        if "vpc" in raw_intf.lower():
            intf = peer_link
        else:
            intf = utilities.long_int_name(raw_intf)

        if intf in output:
            output[intf].append((mac, vlan))
        else:
            output[intf] = [(mac, vlan)]

    return output
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 table from a Cisco IOS or NXOS device and export it as a CSV file.

    :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)"
        )
        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"
                session.hostname = session.hostname + "-VRF-all".format(
                    selected_vrf)
            else:
                send_cmd = "show ip eigrp vrf {0} topology".format(
                    selected_vrf)
                session.hostname = session.hostname + "-VRF-{0}".format(
                    selected_vrf)
        else:
            send_cmd = "show ip eigrp topology vrf {0}".format(selected_vrf)
            session.hostname = session.hostname + "-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_list(raw_topo,
                                                  template_file,
                                                  add_header=True)

    output_filename = session.create_output_filename("eigrp-topo", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #32
0
def script_main(session):
    send_cmd = "show cdp neighbors detail"
    logger.debug("Command set to '{0}'".format(send_cmd))

    raw_cdp = session.get_command_output(send_cmd)

    template_file = "textfsm-templates/cisco_os_show_cdp_neigh_det.template"
    logger.debug("Using template: '{0}'".format(template_file))

    fsm_results = utils.textfsm_parse_to_list(raw_cdp,
                                              template_file,
                                              add_header=True)

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results:
        # entry[2] is system name, entry[1] is device ID
        if entry[2] == "":
            entry[2] = utils.extract_system_name(
                entry[1], strip_list=local_settings['strip_domains'])

    description_data = extract_cdp_data(fsm_results)

    # Capture port-channel output
    if session.os == "NX-OS":
        raw_pc_output = session.get_command_output("show port-channel summary")
        pc_template = "textfsm-templates/cisco_nxos_show_portchannel_summary.template"
        pc_table = utils.textfsm_parse_to_list(raw_pc_output,
                                               pc_template,
                                               add_header=True)
        add_port_channels(description_data, pc_table)
    elif session.os == "IOS":
        raw_pc_output = session.get_command_output("show etherchannel summary")
        pc_template = "textfsm-templates/cisco_ios_show_etherchannel_summary.template"
        pc_table = utils.textfsm_parse_to_list(raw_pc_output,
                                               pc_template,
                                               add_header=True)
        add_port_channels(description_data, pc_table)
    else:
        pass

    # This will contain our configuration commands as CDP neighbors are found.
    config_script = ""
    # Generate a string of config commands to apply interface descriptions
    intf_list = sorted(description_data.keys(), key=utils.human_sort_key)
    for interface in intf_list:
        config_script += "interface {}\n".format(interface)
        if "Po" in interface:
            neigh_list = description_data[interface]
            if len(neigh_list) == 1:
                config_script += "  description {}\n".format(neigh_list[0])
            if len(neigh_list) == 2:
                neigh_list = sorted(neigh_list, key=utils.human_sort_key)
                config_script += "  description vPC from {}, {}\n".format(
                    neigh_list[0], neigh_list[1])
        else:
            config_script += "  description {}, {}\n".format(
                description_data[interface][0],
                utils.short_int_name(description_data[interface][1]))

    output_filename = session.create_output_filename("intf-desc",
                                                     include_date=False)
    with open(output_filename, 'wb') as output_file:
        output_file.write(config_script)

    # Clean up before closing session
    session.end()
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 script_main(session, prompt_check_mode=True, check_mode=True, enable_pass=None):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and port-channel information and
    generate the commands to update interface descriptions.  The user will be prompted to run in "Check Mode" which will
    write the configuration changes to a file (for verification or later manual application).  If not, then the script
    will push the configuration commands to the device and save the configuration.

    Local Settings:
    "strip_domains" -  A list of domain names that will be stripped away if found in the CDP remote device name.
    "take_backups" - If set to True, the script will save a copy of the running config before and after making changes.
    "rollback_file" - If set to True, the script will generate a rollback configuration script and save it to a file.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
        SecureCRTSession or DirectSession)
    :type session: sessions.Session
    :param prompt_check_mode: A boolean that specifies if we should prompt the user to find out if we should run in
        "check mode".  We would make this False if we were using this function in a multi-device script, so that the
        process can run continually without prompting the user at each device.
    :type prompt_check_mode: bool
    :param check_mode: A boolean to specify whether we should run in "check mode" (Generate what the script would do
        only -- does not push config), or not (Pushes the changes to the device).   The default is True for safety
        reasons, and this option will be overwritten unless prompt_checkmode is False.
    :type check_mode: bool
    :param enable_pass: The enable password for the device.  Will be passed to start_cisco_session method if available.
    :type enable_pass: 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 prompt_check_mode:
        # Ask if this should be a test run (generate configs only) or full run (push updates to devices)
        check_mode_message = "Do you want to run this script in check mode? (Only generate configs)\n" \
                             "\n" \
                             "Yes = Connect to device and write change scripts to a file ONLY\n" \
                             "No = Connect to device and PUSH configuration changes"
        message_box_design = ICON_QUESTION | BUTTON_YESNOCANCEL
        logger.debug("Prompting the user to run in check mode.")
        result = script.message_box(check_mode_message, "Run in Check Mode?", message_box_design)
        if result == IDYES:
            check_mode = True
        elif result == IDNO:
            check_mode = False
        else:
            session.end_cisco_session()
            return

    # Get setting if we want to save before/after backups
    take_backups = script.settings.getboolean("update_interface_desc", "take_backups")

    if not check_mode and take_backups:
        # Save "show run" to file, plus read it back in for processing.
        before_filename = session.create_output_filename("1-show-run-BEFORE")
        session.write_output_to_file("show run", before_filename)
        # Read in contents of file for processing
        with open(before_filename, 'r') as show_run:
            show_run_before = show_run.read()
    else:
        # Just read in "show run" contents for processing
        show_run_before = session.get_command_output("show run")

    # Use TextFSM to extract interface/description pairs from the show run output
    desc_template = session.script.get_template("cisco_os_show_run_desc.template")
    desc_list = utilities.textfsm_parse_to_list(show_run_before, desc_template)

    # Turn the TextFSM list into a dictionary we can use to lookup by interface
    ex_desc_lookup = {}
    # Change interface names to long versions for better matching with other outputs
    for entry in desc_list:
        intf = utilities.long_int_name(entry[0])
        ex_desc_lookup[intf] = entry[1]

    # Get CDP Data
    raw_cdp = session.get_command_output("show cdp neighbors detail")

    # Process CDP Data with TextFSM
    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")
    fsm_results = utilities.textfsm_parse_to_list(raw_cdp, template_file, add_header=True)

    # Get domain names to strip from device IDs from settings file
    strip_list = script.settings.getlist("update_interface_desc", "strip_domains")

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results:
        # entry[2] is system name, entry[1] is device ID. Localhost is a corner case for ESX hosts, where DNS name is
        # in DeviceID, but localhost is in System Name
        if entry[2] == "" or entry[2] == "localhost":
            entry[2] = utilities.extract_system_name(entry[1], strip_list=strip_list)

    # Get Remote name, local and remote interface info to build descriptions.
    description_data = extract_cdp_data(fsm_results)

    # Capture port-channel output and add details to our description information
    if session.os == "NXOS":
        raw_pc_output = session.get_command_output("show port-channel summary")
        pc_template = script.get_template("cisco_nxos_show_portchannel_summary.template")
        pc_table = utilities.textfsm_parse_to_list(raw_pc_output, pc_template, add_header=False)
        add_port_channels(description_data, pc_table)
    else:
        raw_pc_output = session.get_command_output("show etherchannel summary")
        pc_template = script.get_template("cisco_ios_show_etherchannel_summary.template")
        pc_table = utilities.textfsm_parse_to_list(raw_pc_output, pc_template, add_header=False)
        add_port_channels(description_data, pc_table)

    # Create a list to append configuration commands and rollback commands
    config_commands = []
    rollback = []

    # Get an alphabetically sorted list of interfaces
    intf_list = sorted(description_data.keys(), key=utilities.human_sort_key)

    # Generate a list of configuration commands (and rollback if necessary)
    for interface in intf_list:
        # Get existing description
        try:
            existing_desc = ex_desc_lookup[interface]
        except KeyError:
            existing_desc = ""

        # If a port-channel only use hostname in description
        if "port-channel" in interface.lower():
            neigh_list = description_data[interface]
            # If there is only 1 neighbor, use that
            if len(neigh_list) == 1:
                new_desc = neigh_list[0]
            # If there are 2 neighbors, assume a vPC and label appropriately
            if len(neigh_list) == 2:
                neigh_list = sorted(neigh_list, key=utilities.human_sort_key)
                new_desc = "vPC: {0}, {1}".format(neigh_list[0], neigh_list[1])
            # Only update description if we will be making a change
            if new_desc != existing_desc:
                config_commands.append("interface {0}".format(interface))
                config_commands.append(" description {0}".format(new_desc))
                rollback.append("interface {0}".format(interface))
                if not existing_desc:
                    rollback.append(" no description")
                else:
                    rollback.append(" description {0}".format(existing_desc))

        # For other interfaces, use remote hostname and interface
        else:
            remote_host = description_data[interface][0]
            remote_intf = utilities.short_int_name(description_data[interface][1])
            new_desc = "{0} {1}".format(remote_host, remote_intf)
            # Only update description if we will be making a change
            if new_desc != existing_desc:
                config_commands.append("interface {0}".format(interface))
                config_commands.append(" description {0}".format(new_desc))
                rollback.append("interface {0}".format(interface))
                if not existing_desc:
                    rollback.append(" no description")
                else:
                    rollback.append(" description {0}".format(existing_desc))

    # If in check-mode, generate configuration and write it to a file, otherwise push the config to the device.
    if config_commands:
        if check_mode:
            output_filename = session.create_output_filename("intf-desc")
            with open(output_filename, 'wb') as output_file:
                for command in config_commands:
                    output_file.write("{0}\n".format(command))
            rollback_filename = session.create_output_filename("intf-rollback")
        else:
            # Check settings to see if we prefer to save backups before/after applying changes
            if take_backups:
                # Push configuration, capturing the configure terminal log
                output_filename = session.create_output_filename("2-CONFIG-RESULTS")
                session.send_config_commands(config_commands, output_filename)
                # Back up configuration after changes are applied
                after_filename = session.create_output_filename("3-show-run-AFTER")
                session.write_output_to_file("show run", after_filename)
                # Set Rollback filename, in case this option is used
                rollback_filename = session.create_output_filename("4-ROLLBACK")
            else:
                # Push configuration, capturing the configure terminal log
                output_filename = session.create_output_filename("CONFIG-RESULTS")
                session.send_config_commands(config_commands, output_filename)
                # Set Rollback filename, in case this option is used
                rollback_filename = session.create_output_filename("ROLLBACK")

            # Save configuration
            session.save()

        # Check our settings to see if we should create a rollback.
        create_rollback = script.settings.getboolean("update_interface_desc", "rollback_file")
        if create_rollback:
            with open(rollback_filename, 'wb') as output_file:
                for command in rollback:
                    output_file.write("{0}\n".format(command))

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #35
0
def update_helpers(session, check_mode, old_helpers, new_helpers,
                   remove_old_helpers):
    script = session.script
    # A list of supported OSes that this script is configured to handle.
    supported_os = ["IOS", "NXOS"]

    # Create data structure to record helper IPs that we find that aren't in our list that we are either looking for
    # or adding.
    unrecognized_helpers = [["Hostname", "Interface", "Helper IP"]]

    if session.os not in supported_os:
        logger.debug(
            "<UPDATE_HELPER> OS is {0}, which is not in supported OS list of {1}"
            .format(session.os, supported_os))
        raise sessions.UnsupportedOSError(
            "This device's OS is {0}, which is not a supported OS for this script which "
            "only supports: {1}).".format(session.os, supported_os))

    # Save our "Before" configuration.
    before_filename = session.create_output_filename("1-show-run-BEFORE")
    session.write_output_to_file("show run", before_filename)

    # Open the "Before" configuration and parse it with TextFSM to find helper-addresses
    with open(before_filename, 'r') as config_file:
        run_config = config_file.read()

    if session.os == "IOS":
        template_file = script.get_template(
            "cisco_ios_show_run_helper.template")
    else:
        template_file = script.get_template(
            "cisco_nxos_show_run_dhcp_relay.template")

    result = utilities.textfsm_parse_to_list(run_config, template_file)

    if check_mode:
        os.remove(before_filename)

    # Create a dictionary that will let us get a set of configured helpers under each interface.
    intfs_with_helpers = {}
    for entry in result:
        interface = entry[0]
        helper = entry[1]
        vrf = entry[2]
        if interface in intfs_with_helpers:
            intfs_with_helpers[interface]["helpers"].add(helper)
        else:
            intfs_with_helpers[interface] = {
                "vrf": "{}".format(vrf),
                "helpers": {helper}
            }

        # Check if helper is unrecognized and needs to be recorded
        if helper not in old_helpers and helper not in new_helpers:
            unknown_line = [session.hostname, interface, helper, vrf]
            unrecognized_helpers.append(unknown_line)
            logger.debug("<UPDATE_HELPER> Adding {} to unknown helpers".format(
                str(unknown_line)))

    logger.debug("<UPDATE_HELPER> Interfaces with helpers:\n{}".format(
        str(intfs_with_helpers)))

    # Figure out which interfaces need additional helpers
    need_to_update = []
    for interface in intfs_with_helpers:
        configured_helpers = intfs_with_helpers[interface]["helpers"]
        vrf = intfs_with_helpers[interface]["vrf"]
        helper_matches = configured_helpers.intersection(old_helpers)
        if helper_matches:
            needed_new_helpers = set(new_helpers).difference(
                configured_helpers)
            if remove_old_helpers:
                need_to_update.append(
                    (interface, vrf, needed_new_helpers, helper_matches))
            else:
                need_to_update.append((interface, vrf, needed_new_helpers, {}))

    logger.debug("<UPDATE_HELPER> Required Updates:\n{}".format(
        str(need_to_update)))

    # If we have anything we need to update, build out required config commands, depending on device OS.
    update_commands = []
    if session.os == "IOS":
        for entry in need_to_update:
            interface = entry[0]
            vrf = entry[1]
            helpers_to_add = entry[2]
            helpers_to_remove = entry[3]
            if helpers_to_add or helpers_to_remove:
                update_commands.append("interface {}".format(interface))
                for helper in helpers_to_add:
                    if vrf == "":
                        update_commands.append(
                            "ip helper-address {}".format(helper))
                    elif vrf == "global":
                        update_commands.append(
                            "ip helper-address global {}".format(helper))
                    else:
                        update_commands.append(
                            "ip helper-address vrf {} {}".format(vrf, helper))
                for helper in helpers_to_remove:
                    if vrf == "":
                        update_commands.append(
                            "no ip helper-address {}".format(helper))
                    elif vrf == "global":
                        update_commands.append(
                            "no ip helper-address global {}".format(helper))
                    else:
                        update_commands.append(
                            "no ip helper-address vrf {} {}".format(
                                vrf, helper))
    else:
        for entry in need_to_update:
            interface = entry[0]
            vrf = entry[1]
            helpers_to_add = entry[2]
            helpers_to_remove = entry[3]
            if helpers_to_add or helpers_to_remove:
                update_commands.append("interface {}".format(interface))
                for helper in helpers_to_add:
                    if vrf == "":
                        update_commands.append(
                            "ip dhcp relay address {}".format(helper))
                    else:
                        update_commands.append(
                            "ip dhcp relay address {} use-vrf {}".format(
                                helper, vrf))
                for helper in helpers_to_remove:
                    if vrf == "":
                        update_commands.append(
                            "no ip dhcp relay address {}".format(helper))
                    else:
                        update_commands.append(
                            "no ip dhcp relay address {} use-vrf {}".format(
                                helper, vrf))

    # Send config commands to the device and save the session.
    if update_commands:
        if check_mode:
            # If in Check Mode, only generate config updates and write to a file.
            logger.debug("<UPDATE_HELPER> CHECK MODE: Generating config")
            command_string = ""
            command_string += "configure terminal\n"
            for command in update_commands:
                command_string += "{}\n".format(command.strip())
            command_string += "end\n"

            config_filename = session.create_output_filename("PROPOSED_CONFIG")
            with open(config_filename, 'w') as output_file:
                output_file.write(command_string)
        else:
            config_filename = session.create_output_filename(
                "2-CONFIG_RESULTS")
            session.send_config_commands(update_commands,
                                         output_filename=config_filename)
            session.save()

            # Save our "After" configuration.
            after_filename = session.create_output_filename("3-show-run-AFTER")
            session.write_output_to_file("show run", after_filename)
예제 #36
0
def per_device_work(session, enable_pass, vlan_set):
    """
    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.
    """
    session.start_cisco_session()

    script = session.script
    hostname = session.hostname

    exclude_ports = {}
    # Find uplink port via spanning-tree root information, so we can exclude MAC addresses found on that port from
    # our analysis.
    if session.os == "IOS":
        template_file = script.get_template(
            "cisco_os_show_spanning-tree_root.template")
    else:
        template_file = script.get_template(
            "cisco_os_show_spanning-tree_root.template")

    raw_stp_root = session.get_command_output("show spanning-tree root")

    root_results = utilities.textfsm_parse_to_list(raw_stp_root,
                                                   template_file,
                                                   add_header=False)

    for entry in root_results:
        vlan_string = entry[0]
        vlan = int(vlan_string.split("N")[1])
        uplink = utilities.long_int_name(entry[6]).strip()
        if uplink:
            exclude_ports[vlan] = uplink

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "NXOS":
        template_file = script.get_template(
            "cisco_nxos_show_mac_addr_table.template")
    else:
        template_file = script.get_template(
            "cisco_ios_show_mac_addr_table.template")

    raw_mac = session.get_command_output("show mac address-table")
    fsm_results = utilities.textfsm_parse_to_list(raw_mac,
                                                  template_file,
                                                  add_header=False)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(fsm_results) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))
        raw_mac = session.get_command_output(send_cmd)
        fsm_results = utilities.textfsm_parse_to_list(raw_mac,
                                                      template_file,
                                                      add_header=False)

    # Find all MAC entries that are from the specific VLAN range and aren't learned from the uplink port for that VLAN
    results = []
    for entry in fsm_results:
        try:
            vlan = int(entry[0])
        except ValueError:
            continue

        if vlan in vlan_set:
            try:
                uplink = exclude_ports[vlan]
            except KeyError:
                uplink = ""

            mac_intf = utilities.long_int_name(entry[2]).lower()
            uplink_intf = uplink.strip().lower()
            if not mac_intf == uplink_intf:
                results.append(entry)

    # Return terminal parameters back to starting values
    session.end_cisco_session()

    return hostname, results
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 table from a Cisco IOS or NXOS device and export it as a CSV file.

    :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)")
        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"
                session.hostname = session.hostname + "-VRF-all".format(selected_vrf)
            else:
                send_cmd = "show ip eigrp vrf {0} topology".format(selected_vrf)
                session.hostname = session.hostname + "-VRF-{0}".format(selected_vrf)
        else:
            send_cmd = "show ip eigrp topology vrf {0}".format(selected_vrf)
            session.hostname = session.hostname + "-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_list(raw_topo, template_file, add_header=True)

    output_filename = session.create_output_filename("eigrp-topo", ext=".csv")
    utilities.list_of_lists_to_csv(fsm_results, output_filename)

    # Return terminal parameters back to the original state.
    session.end_cisco_session()
예제 #38
0
def per_device_work(session, enable_pass, vlan_set):
    """
    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.
    """
    session.start_cisco_session()

    script = session.script
    hostname = session.hostname

    exclude_ports = {}
    # Find uplink port via spanning-tree root information, so we can exclude MAC addresses found on that port from
    # our analysis.
    if session.os == "IOS":
        template_file = script.get_template("cisco_os_show_spanning-tree_root.template")
    else:
        template_file = script.get_template("cisco_os_show_spanning-tree_root.template")

    raw_stp_root = session.get_command_output("show spanning-tree root")

    root_results = utilities.textfsm_parse_to_list(raw_stp_root, template_file, add_header=False)

    for entry in root_results:
        vlan_string = entry[0]
        vlan = int(vlan_string.split("N")[1])
        uplink = utilities.long_int_name(entry[6]).strip()
        if uplink:
            exclude_ports[vlan] = uplink

    # TextFSM template for parsing "show mac address-table" output
    if session.os == "NXOS":
        template_file = script.get_template("cisco_nxos_show_mac_addr_table.template")
    else:
        template_file = script.get_template("cisco_ios_show_mac_addr_table.template")

    raw_mac = session.get_command_output("show mac address-table")
    fsm_results = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=False)

    # Check if IOS mac_table is empty -- if so, it is probably because the switch has an older IOS
    # that expects "show mac-address-table" instead of "show mac address-table".
    if session.os == "IOS" and len(fsm_results) == 1:
        send_cmd = "show mac-address-table dynamic"
        logger.debug("Retrying with command set to '{0}'".format(send_cmd))
        raw_mac = session.get_command_output(send_cmd)
        fsm_results = utilities.textfsm_parse_to_list(raw_mac, template_file, add_header=False)

    # Find all MAC entries that are from the specific VLAN range and aren't learned from the uplink port for that VLAN
    results = []
    for entry in fsm_results:
        try:
            vlan = int(entry[0])
        except ValueError:
            continue

        if vlan in vlan_set:
            try:
                uplink = exclude_ports[vlan]
            except KeyError:
                uplink = ""

            mac_intf = utilities.long_int_name(entry[2]).lower()
            uplink_intf = uplink.strip().lower()
            if not mac_intf == uplink_intf:
                results.append(entry)

    # Return terminal parameters back to starting values
    session.end_cisco_session()

    return hostname, results
def script_main(session,
                prompt_check_mode=True,
                check_mode=True,
                enable_pass=None):
    """
    | SINGLE device script
    | Author: Jamie Caesar
    | Email: [email protected]

    This script will grab the detailed CDP information from a Cisco IOS or NX-OS device and port-channel information and
    generate the commands to update interface descriptions.  The user will be prompted to run in "Check Mode" which will
    write the configuration changes to a file (for verification or later manual application).  If not, then the script
    will push the configuration commands to the device and save the configuration.

    **Script Settings** (found in settings/settings.ini):

    * | **strip_domains** -  A list of domain names that will be stripped away if found in
      | the CDP remote device name.
    * | **take_backups** - If True, the script will save a copy of the running config before
      | and after making changes.
    * | **rollback_file** - If True, the script will generate a rollback configuration script
      | and save it to a file.

    :param session: A subclass of the sessions.Session object that represents this particular script session (either
        SecureCRTSession or DirectSession)
    :type session: sessions.Session
    :param prompt_check_mode: A boolean that specifies if we should prompt the user to find out if we should run in
        "check mode".  We would make this False if we were using this function in a multi-device script, so that the
        process can run continually without prompting the user at each device.
    :type prompt_check_mode: bool
    :param check_mode: A boolean to specify whether we should run in "check mode" (Generate what the script would do
        only -- does not push config), or not (Pushes the changes to the device).   The default is True for safety
        reasons, and this option will be overwritten unless prompt_checkmode is False.
    :type check_mode: bool
    :param enable_pass: The enable password for the device.  Will be passed to start_cisco_session method if available.
    :type enable_pass: 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(enable_pass=enable_pass)

    # Validate device is running a supported OS
    session.validate_os(["IOS", "NXOS"])

    if prompt_check_mode:
        # Ask if this should be a test run (generate configs only) or full run (push updates to devices)
        check_mode_message = "Do you want to run this script in check mode? (Only generate configs)\n" \
                             "\n" \
                             "Yes = Connect to device and write change scripts to a file ONLY\n" \
                             "No = Connect to device and PUSH configuration changes"
        message_box_design = ICON_QUESTION | BUTTON_YESNOCANCEL
        logger.debug("Prompting the user to run in check mode.")
        result = script.message_box(check_mode_message, "Run in Check Mode?",
                                    message_box_design)
        if result == IDYES:
            check_mode = True
        elif result == IDNO:
            check_mode = False
        else:
            session.end_cisco_session()
            return

    # Get setting if we want to save before/after backups
    take_backups = script.settings.getboolean("update_interface_desc",
                                              "take_backups")

    if not check_mode and take_backups:
        # Save "show run" to file, plus read it back in for processing.
        before_filename = session.create_output_filename("1-show-run-BEFORE")
        session.write_output_to_file("show run", before_filename)
        # Read in contents of file for processing
        with open(before_filename, 'r') as show_run:
            show_run_before = show_run.read()
    else:
        # Just read in "show run" contents for processing
        show_run_before = session.get_command_output("show run")

    # Use TextFSM to extract interface/description pairs from the show run output
    desc_template = session.script.get_template(
        "cisco_os_show_run_desc.template")
    desc_list = utilities.textfsm_parse_to_list(show_run_before, desc_template)

    # Turn the TextFSM list into a dictionary we can use to lookup by interface
    ex_desc_lookup = {}
    # Change interface names to long versions for better matching with other outputs
    for entry in desc_list:
        intf = utilities.long_int_name(entry[0])
        ex_desc_lookup[intf] = entry[1]

    # Get CDP Data
    raw_cdp = session.get_command_output("show cdp neighbors detail")

    # Process CDP Data with TextFSM
    template_file = script.get_template("cisco_os_show_cdp_neigh_det.template")
    fsm_results = utilities.textfsm_parse_to_list(raw_cdp,
                                                  template_file,
                                                  add_header=True)

    # Get domain names to strip from device IDs from settings file
    strip_list = script.settings.getlist("update_interface_desc",
                                         "strip_domains")

    # Since "System Name" is a newer NXOS feature -- try to extract it from the device ID when its empty.
    for entry in fsm_results:
        # entry[2] is system name, entry[1] is device ID. Localhost is a corner case for ESX hosts, where DNS name is
        # in DeviceID, but localhost is in System Name
        if entry[2] == "" or entry[2] == "localhost":
            entry[2] = utilities.extract_system_name(entry[1],
                                                     strip_list=strip_list)

    # Get Remote name, local and remote interface info to build descriptions.
    description_data = extract_cdp_data(fsm_results)

    # Capture port-channel output and add details to our description information
    if session.os == "NXOS":
        raw_pc_output = session.get_command_output("show port-channel summary")
        pc_template = script.get_template(
            "cisco_nxos_show_portchannel_summary.template")
        pc_table = utilities.textfsm_parse_to_list(raw_pc_output,
                                                   pc_template,
                                                   add_header=False)
        add_port_channels(description_data, pc_table)
    else:
        raw_pc_output = session.get_command_output("show etherchannel summary")
        pc_template = script.get_template(
            "cisco_ios_show_etherchannel_summary.template")
        pc_table = utilities.textfsm_parse_to_list(raw_pc_output,
                                                   pc_template,
                                                   add_header=False)
        add_port_channels(description_data, pc_table)

    # Create a list to append configuration commands and rollback commands
    config_commands = []
    rollback = []

    # Get an alphabetically sorted list of interfaces
    intf_list = sorted(description_data.keys(), key=utilities.human_sort_key)

    # Generate a list of configuration commands (and rollback if necessary)
    for interface in intf_list:
        # Get existing description
        try:
            existing_desc = ex_desc_lookup[interface]
        except KeyError:
            existing_desc = ""

        # If a port-channel only use hostname in description
        if "port-channel" in interface.lower():
            neigh_list = description_data[interface]
            # If there is only 1 neighbor, use that
            if len(neigh_list) == 1:
                new_desc = neigh_list[0]
            # If there are 2 neighbors, assume a vPC and label appropriately
            if len(neigh_list) == 2:
                neigh_list = sorted(neigh_list, key=utilities.human_sort_key)
                new_desc = "vPC: {0}, {1}".format(neigh_list[0], neigh_list[1])
            # Only update description if we will be making a change
            if new_desc != existing_desc:
                config_commands.append("interface {0}".format(interface))
                config_commands.append(" description {0}".format(new_desc))
                rollback.append("interface {0}".format(interface))
                if not existing_desc:
                    rollback.append(" no description")
                else:
                    rollback.append(" description {0}".format(existing_desc))

        # For other interfaces, use remote hostname and interface
        else:
            remote_host = description_data[interface][0]
            remote_intf = utilities.short_int_name(
                description_data[interface][1])
            new_desc = "{0} {1}".format(remote_host, remote_intf)
            # Only update description if we will be making a change
            if new_desc != existing_desc:
                config_commands.append("interface {0}".format(interface))
                config_commands.append(" description {0}".format(new_desc))
                rollback.append("interface {0}".format(interface))
                if not existing_desc:
                    rollback.append(" no description")
                else:
                    rollback.append(" description {0}".format(existing_desc))

    # If in check-mode, generate configuration and write it to a file, otherwise push the config to the device.
    if config_commands:
        if check_mode:
            output_filename = session.create_output_filename("intf-desc")
            with open(output_filename, 'wb') as output_file:
                for command in config_commands:
                    output_file.write("{0}\n".format(command))
            rollback_filename = session.create_output_filename("intf-rollback")
        else:
            # Check settings to see if we prefer to save backups before/after applying changes
            if take_backups:
                # Push configuration, capturing the configure terminal log
                output_filename = session.create_output_filename(
                    "2-CONFIG-RESULTS")
                session.send_config_commands(config_commands, output_filename)
                # Back up configuration after changes are applied
                after_filename = session.create_output_filename(
                    "3-show-run-AFTER")
                session.write_output_to_file("show run", after_filename)
                # Set Rollback filename, in case this option is used
                rollback_filename = session.create_output_filename(
                    "4-ROLLBACK")
            else:
                # Push configuration, capturing the configure terminal log
                output_filename = session.create_output_filename(
                    "CONFIG-RESULTS")
                session.send_config_commands(config_commands, output_filename)
                # Set Rollback filename, in case this option is used
                rollback_filename = session.create_output_filename("ROLLBACK")

            # Save configuration
            session.save()

        # Check our settings to see if we should create a rollback.
        create_rollback = script.settings.getboolean("update_interface_desc",
                                                     "rollback_file")
        if create_rollback:
            with open(rollback_filename, 'wb') as output_file:
                for command in rollback:
                    output_file.write("{0}\n".format(command))

    # Return terminal parameters back to the original state.
    session.end_cisco_session()