import sys import textfsm from tabulate import tabulate template = sys.argv[1] output_file = sys.argv[2] with open(template) as f, open(output_file) as output: re_table = textfsm.TextFSM(f) header = re_table.header result = re_table.ParseText(output.read()) # print(result) print(tabulate(result, headers=header)) ## Print the result ## ## $ python output/Cisco_IOS_Parsing.py output/cisco_ios_show_interfaces.template output/show_int ## ## $ python output/Cisco_IOS_Parsing.py output/cisco_ios_show_interfaces.template output/show_int > parse.csv ## ## https://pyneng.readthedocs.io/en/latest/book/21_textfsm/textfsm_examples.html ##
secret = sys.argv[4] try: print("collect CDP information from device %s..." % target_ip) cdp_det_result = get_cdp_neighbor_details(ip=target_ip, username=username, password=password, enable_secret=secret) found_hosts = [] nodes = [] edges = [] # parse the show cdp details command using TextFSM print("parse results...") re_table = textfsm.TextFSM(open("show_cdp_neighbor_detail.textfsm")) fsm_results = re_table.ParseText(cdp_det_result) local_hostname = "not discovered" counter = 1 for e in fsm_results: if len(nodes) == 0: # add local node (always ID 1) node = {"id": counter, "label": e[0], "group": "root_device"} counter += 1 nodes.append(node) # add new node remote_node = e[1] if remote_node not in found_hosts:
import textfsm traceroute = """ r2#traceroute 90.0.0.9 source 33.0.0.2 traceroute 90.0.0.9 source 33.0.0.2 Type escape sequence to abort. Tracing the route to 90.0.0.9 VRF info: (vrf in name/id, vrf out name/id) 1 10.0.12.1 1 msec 0 msec 0 msec 2 15.0.0.5 0 msec 5 msec 4 msec 3 57.0.0.7 4 msec 1 msec 4 msec 4 79.0.0.9 4 msec * 1 msec """ with open("traceroute.template") as f: fsm = textfsm.TextFSM(f) result = fsm.ParseText(traceroute) print(fsm.header) print(result)
{'DUPLEX': 'auto', 'PORT_NAME': 'Gi0/1/3', 'PORT_TYPE': '10/100/1000BaseTX', 'SPEED': 'auto', 'STATUS': 'notconnect', 'VLAN': '1'}]''' import textfsm from pprint import pprint template = open("Ex2_sh_int_status.textfsm") with open("Ex1_sh_int_status.txt") as f: raw_data = f.read() re_table = textfsm.TextFSM(template) sh_int_status_op = re_table.ParseText(raw_data) template.close() sh_int_status_list = [] for intf_entry in sh_int_status_op: intf_dict = {} port_name = intf_entry[0] intf_dict["PORT_NAME"] = port_name status = intf_entry[1] intf_dict["STATUS"] = status vlan = intf_entry[2] intf_dict["VLAN"] = vlan duplex = intf_entry[3] intf_dict["DUPLEX"] = duplex
def parse_output(tmpl, showcmd): with open(tmpl) as f: re_table = textfsm.TextFSM(f) header = re_table.header result = re_table.ParseText(showcmd) return [header, result]
import textfsm traceroute = """ r2#traceroute 90.0.0.9 source 33.0.0.2 traceroute 90.0.0.9 source 33.0.0.2 Type escape sequence to abort. Tracing the route to 90.0.0.9 VRF info: (vrf in name/id, vrf out name/id) 1 10.0.12.1 1 msec 0 msec 0 msec 2 15.0.0.5 0 msec 5 msec 4 msec 3 57.0.0.7 4 msec 1 msec 4 msec 4 79.0.0.9 4 msec * 1 msec """ with open("traceroute.template") as template: fsm = textfsm.TextFSM(template) result = fsm.ParseText(traceroute) print(fsm.header) print(result) """ Example: ['ID', 'Hop'] [['1', '10.0.12.1'], ['2', '15.0.0.5'], ['3', '57.0.0.7'], ['4', '79.0.0.9']] """
import telnetlib import time username = raw_input('Enter username for device login:'******'Enter the corresponding password:'******'device.txt', 'r') f2 = open('ciscocommand.txt', 'r') # Creates list based on f1 devices = f1.readlines() commands = f2.readlines() template_file = sys.argv[1] fsm = textfsm.TextFSM(open(template_file)) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) data = [] for device in devices: column = device.split() data.append([column[0]]) data[-1].append(column[1]) print column[0] def connectviatelnet(): try: fsm = textfsm.TextFSM(open(template_file)) output = "Connecting using Telnet" print output
def extract(template_path, raw_text=None, raw_text_file=None, saltenv="base"): r""" Extracts the data entities from the unstructured raw text sent as input and returns the data mapping, processing using the TextFSM template. template_path The path to the TextFSM template. This can be specified using the absolute path to the file, or using one of the following URL schemes: - ``salt://``, to fetch the template from the Salt fileserver. - ``http://`` or ``https://`` - ``ftp://`` - ``s3://`` - ``swift://`` raw_text: ``None`` The unstructured text to be parsed. raw_text_file: ``None`` Text file to read, having the raw text to be parsed using the TextFSM template. Supports the same URL schemes as the ``template_path`` argument. saltenv: ``base`` Salt fileserver envrionment from which to retrieve the file. Ignored if ``template_path`` is not a ``salt://`` URL. CLI Example: .. code-block:: bash salt '*' textfsm.extract salt://textfsm/juniper_version_template raw_text_file=s3://junos_ver.txt salt '*' textfsm.extract http://some-server/textfsm/juniper_version_template raw_text='Hostname: router.abc ... snip ...' Jinja template example: .. code-block:: jinja {%- set raw_text = 'Hostname: router.abc ... snip ...' -%} {%- set textfsm_extract = salt.textfsm.extract('https://some-server/textfsm/juniper_version_template', raw_text) -%} Raw text example: .. code-block:: text Hostname: router.abc Model: mx960 JUNOS Base OS boot [9.1S3.5] JUNOS Base OS Software Suite [9.1S3.5] JUNOS Kernel Software Suite [9.1S3.5] JUNOS Crypto Software Suite [9.1S3.5] JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5] JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5] JUNOS Online Documentation [9.1S3.5] JUNOS Routing Software Suite [9.1S3.5] TextFSM Example: .. code-block:: text Value Chassis (\S+) Value Required Model (\S+) Value Boot (.*) Value Base (.*) Value Kernel (.*) Value Crypto (.*) Value Documentation (.*) Value Routing (.*) Start # Support multiple chassis systems. ^\S+:$$ -> Continue.Record ^${Chassis}:$$ ^Model: ${Model} ^JUNOS Base OS boot \[${Boot}\] ^JUNOS Software Release \[${Base}\] ^JUNOS Base OS Software Suite \[${Base}\] ^JUNOS Kernel Software Suite \[${Kernel}\] ^JUNOS Crypto Software Suite \[${Crypto}\] ^JUNOS Online Documentation \[${Documentation}\] ^JUNOS Routing Software Suite \[${Routing}\] Output example: .. code-block:: json { "comment": "", "result": true, "out": [ { "kernel": "9.1S3.5", "documentation": "9.1S3.5", "boot": "9.1S3.5", "crypto": "9.1S3.5", "chassis": "", "routing": "9.1S3.5", "base": "9.1S3.5", "model": "mx960" } ] } """ ret = {"result": False, "comment": "", "out": None} log.debug("Using the saltenv: {}".format(saltenv)) log.debug("Caching {} using the Salt fileserver".format(template_path)) tpl_cached_path = __salt__["cp.cache_file"](template_path, saltenv=saltenv) if tpl_cached_path is False: ret["comment"] = "Unable to read the TextFSM template from {}".format( template_path ) log.error(ret["comment"]) return ret try: log.debug( "Reading TextFSM template from cache path: {}".format(tpl_cached_path) ) # Disabling pylint W8470 to nto complain about fopen. # Unfortunately textFSM needs the file handle rather than the content... # pylint: disable=W8470 tpl_file_handle = fopen(tpl_cached_path, "r") # pylint: disable=W8470 log.debug(tpl_file_handle.read()) tpl_file_handle.seek(0) # move the object position back at the top of the file fsm_handler = textfsm.TextFSM(tpl_file_handle) except textfsm.TextFSMTemplateError as tfte: log.error("Unable to parse the TextFSM template", exc_info=True) ret[ "comment" ] = "Unable to parse the TextFSM template from {}: {}. Please check the logs.".format( template_path, tfte ) return ret if not raw_text and raw_text_file: log.debug("Trying to read the raw input from {}".format(raw_text_file)) raw_text = __salt__["cp.get_file_str"](raw_text_file, saltenv=saltenv) if raw_text is False: ret[ "comment" ] = "Unable to read from {}. Please specify a valid input file or text.".format( raw_text_file ) log.error(ret["comment"]) return ret if not raw_text: ret["comment"] = "Please specify a valid input file or text." log.error(ret["comment"]) return ret log.debug("Processing the raw text:") log.debug(raw_text) objects = fsm_handler.ParseText(raw_text) ret["out"] = _clitable_to_dict(objects, fsm_handler) ret["result"] = True return ret
def build_iproute_template(): """ This is the information for the show ip route template. It comes directly from: https://github.com/networktocode/ntc-templates/blob/master/templates/cisco_ios_show_ip_route.template This script will make this file. """ fileContents = [ 'Value Filldown PROTOCOL (\w)\n', 'Value Filldown TYPE (\w{0,2})\n', 'Value Required,Filldown NETWORK (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\n', 'Value Filldown MASK (\d{1,2})\n', 'Value DISTANCE (\d+)\n', 'Value METRIC (\d+)\n', 'Value NEXTHOP_IP (\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\n', 'Value NEXTHOP_IF ([A-Z][\w\-\.:/]+)\n', 'Value UPTIME (\d[\w:\.]+)\n', '\n', 'Start\n', ' ^Gateway.* -> Routes\n', '\n', 'Routes\n', ' # For "is (variably )subnetted" line, capture mask, clear all values.\n', ' ^\s+\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\/${MASK}\sis -> Clear\n', ' #\n', ' # Match directly connected route with explicit mask\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\/${MASK}\sis\sdirectly\sconnected,\s${NEXTHOP_IF} -> Record\n', ' #\n', ' # Match directly connected route (mask is inherited from "is subnetted")\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\sis\sdirectly\sconnected,\s${NEXTHOP_IF} -> Record\n', ' #\n', ' # Match regular routes, with mask, where all data in same line\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\/${MASK}\s\[${DISTANCE}/${METRIC}\]\svia\s${NEXTHOP_IP}(,\s${UPTIME})?(,\s${NEXTHOP_IF})? -> Record\n', ' #\n', ' # Match regular route, all one line, where mask is learned from "is subnetted" line\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\s\[${DISTANCE}\/${METRIC}\]\svia\s${NEXTHOP_IP}(,\s${UPTIME})?(,\s${NEXTHOP_IF})? -> Record\n', ' #\n', ' # Match route with no via statement (Null via protocol)\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\/${MASK}\s\[${DISTANCE}/${METRIC}\],\s${UPTIME},\s${NEXTHOP_IF} -> Record\n', ' #\n', ' # Match "is a summary" routes (often Null0)\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\/${MASK}\sis\sa\ssummary,\s${UPTIME},\s${NEXTHOP_IF} -> Record\n', ' #\n', ' # Match regular routes where the network/mask is on the line above the rest of the route\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK}\/${MASK} -> Next\n', ' #\n', ' # Match regular routes where the network only (mask from subnetted line) is on the line above the rest of the route\n', ' ^${PROTOCOL}(\s|\*)${TYPE}\s+${NETWORK} -> Next\n', ' #\n', ' # Match the rest of the route information on line below network (and possibly mask)\n', ' ^\s+\[${DISTANCE}\/${METRIC}\]\svia\s${NEXTHOP_IP}(,\s${UPTIME})?(,\s${NEXTHOP_IF})? -> Record\n', ' #\n', ' # Match load-balanced routes\n', ' ^\s+\[${DISTANCE}\/${METRIC}\]\svia\s${NEXTHOP_IP} -> Record\n', ' #\n', ' # Clear all variables on empty lines\n', ' ^\s* -> Clearall\n', '\n', 'EOF\n', ] with tempfile.TemporaryFile('w+t') as curTemplate: # build the tempfile with the template information curTemplate.writelines(fileContents) # go to the beginning of the file curTemplate.seek(0) # build the parser parser = textfsm.TextFSM(curTemplate) return parser
import textfsm traceroute = ''' r2#traceroute 90.0.0.9 source 33.0.0.2 traceroute 90.0.0.9 source 33.0.0.2 Type escape sequence to abort. Tracing the route to 90.0.0.9 VRF info: (vrf in name/id, vrf out name/id) 1 10.0.12.1 1 msec 0 msec 0 msec 2 15.0.0.5 0 msec 5 msec 4 msec 3 57.0.0.7 4 msec 1 msec 4 msec 4 79.0.0.9 4 msec * 1 msec ''' template = open('traceroute.textfsm') # содержимое файла с шаблоном TextFSM считывается в переменную template fsm = textfsm.TextFSM(template) # класс, который обрабатывает шаблон и создает из него объект в TextFSM result = fsm.ParseText(traceroute) # метод, который обрабатывает переданный вывод согласно шаблону # и возвращает список списков, в котором каждый элемент - это обработанная строка print(fsm.header) # я заголовок: print(fsm.header) , который содержит имена переменных и результат обработки print(result) Результат выполнения скрипта: $ python parse_traceroute.py ['ID', 'Hop'] [['1', '10.0.12.1'], ['2', '15.0.0.5'], ['3', '57.0.0.7'], ['4', '79.0.0.9']] Строки, которые совпали с описанным шаблоном, возвращаются в виде списка списков. Каждый элемент - это список, который состоит из двух элементов: номера хопа и IP-адреса.
def read_template(template_name: str) -> textfsm.TextFSM: with open_asset(template_name) as fp: return textfsm.TextFSM(fp)
import textfsm template = "templates/juniper_junos_show_lldp_neighbors_interface.textfsm" raw_text_data = "tests/alcatel_sros/show_lldp_neighbor-match/alcatel_sros_show_lldp_neighbor-match.raw" re_table = textfsm.TextFSM(open(template)) data = re_table.ParseText("""LLDP Neighbor Information: Local Information: Index: 1 Time to live: 120 Time mark: Thu Nov 26 06:41:24 2015 Age: 1 secs Local Interface : ge-0/0/8 Parent Interface : - Local Port ID : 518 Ageout Count : 0 Neighbour Information: Chassis type : Mac address Chassis ID : 88:e0:f3:1f:14:e0 Port type : Locally assigned Port ID : 880 Port description : ge-0/0/8 System name : bng-nw6moj.juniper.net System Description : Juniper Networks, Inc. ex4300-24p Ethernet Switch, kernel JUNOS 14.1I20151125_0548_rajjs, Build date: 2015-11-25 06:06:58 UTC Copyright (c) 1996-2015 Juniper Networks, Inc. System capabilities Supported: Bridge Router Enabled : Bridge Router Management address Address Type : IPv4(1) Address : 10.204.39.232 Interface Number : 33 Interface Subtype : ifIndex(2)
Begin ^${Port}\s+${Vlans}$$ -> Record ^Port\s+Vlans allowed and active in management domain$$ -> End """) #------------Allowed Vlan Parser TextFSM for int_col_num in range(2, len(short_interfaces_dictionary) + 2): interface = short_interfaces_dictionary[int_col_num] print(interface) allowed_vlan_output = connect.send_command("""show interface """ + interface + """ trunk""") fsm = textfsm.TextFSM(allowed_vlan_parser_template) result = fsm.ParseText(allowed_vlan_output) row_id=get_key(interface,short_interfaces_dictionary) try: print(result[0][1]) worksheet.cell(row_id, 8).value = result[0][1] worksheet.cell(row_id, 9).value = "Trunk" except: worksheet.cell(row_id, 9).value = "Access" print("Have no result") worksheet.cell(row_id, 8).value = "No Result" workbook.save("helper.xlsx")