Using TextFSM to Parse Cli Output

by: George El., July 2019, Reading time: 4 minutes

Many times we connect to a router or switch usually with netmiko and we want to parse the output of a command. TextFSM is a Python module for parsing semi-structured text into python tables. It uses templates that use regular expressions in order to parse the output. The good thing is that there are already hundreds of templates for many devices and commands.

Take a look at networktocode. Literally there are hundreds of templates for almost every command and every device you are going to use. But even if there are not, it is easy to write your own. All you need to know is regular expressions. In the following lines I will show you the show ip interface template for cisco devices

sh ip interface template for cisco ios

Value INTF (\S+)
Value IPADDR (\S+)
Value STATUS (up|down|administratively down)
Value PROTO (up|down)

Start
  ^${INTF}\s+${IPADDR}\s+\w+\s+\w+\s+${STATUS}\s+${PROTO} -> Record

The structure is not difficult to understand. It defines some variables, for instance INTF as \S+ which means anything but space one or more times. then it shows how a line should be, it should start with INTF, then have space (\s+), then a word (\w+), then again space, word, status, space and proto. Lets say I have this output from show ip int brief:

Interface                  IP-Address      OK? Method Status                Protocol
Embedded-Service-Engine0/0 unassigned      YES NVRAM  administratively down down    
GigabitEthernet0/0         unassigned      YES NVRAM  up                    up      
GigabitEthernet0/0.1       11.77.5.6       YES NVRAM  up                    up      
GigabitEthernet0/0.2       11.77.7.6       YES NVRAM  up                    up      
GigabitEthernet0/0.3       11.77.7.17      YES NVRAM  up                    up      
GigabitEthernet0/0.4       11.77.7.33      YES NVRAM  up                    up      
GigabitEthernet0/0.10      11.77.6.1       YES NVRAM  up                    up      
GigabitEthernet0/1         192.16.35.70    YES NVRAM  up                    up      
GigabitEthernet0/2         unassigned      YES NVRAM  administratively down down    

it will match the first, second, fifth and sixth column. lets see how to use it with python

import textfsm

input_file = open("sh_ip_int_brief.txt", encoding='utf-8')
cli_data = input_file.read()
input_file.close()

with open("sh_ip_int_template") as f:
    template = textfsm.TextFSM(f)
fsm_results = template.ParseText(cli_data)

for row in fsm_results:
    print(row)

the outcome is usually a list of lists that’s why I used a for loop to display it a bit better. the outcome is:

['Embedded-Service-Engine0/0', 'unassigned', 'administratively down', 'down']
['GigabitEthernet0/0', 'unassigned', 'up', 'up']
['GigabitEthernet0/0.1', '192.67.5.6', 'up', 'up']
['GigabitEthernet0/0.2', '192.67.7.6', 'up', 'up']
['GigabitEthernet0/0.3', '192.67.7.17', 'up', 'up']
['GigabitEthernet0/0.4', '192.67.7.33', 'up', 'up']
['GigabitEthernet0/0.10', '192.67.6.1', 'up', 'up']
['GigabitEthernet0/1', '172.16.35.70', 'up', 'up']
['GigabitEthernet0/2', 'unassigned', 'administratively down', 'down']

I can use textfsm with netmiko like this:

net_connect.send_command("show ip int brief", use_textfsm=True)

and make sure I have downloaded the templates and set the variable NET_TEXTFSM as follows

export NET_TEXTFSM=/path/to/ntc-templates/templates/  

Netmiko converts the output in a list of dictionaries and it is like that: (I use pprint to display it nicely)

[{'intf': 'Embedded-Service-Engine0/0',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'},
 {'intf': 'GigabitEthernet0/0',
  'ipaddr': 'unassigned',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/0.1',
  'ipaddr': '192.67.5.6',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/0.2',
  'ipaddr': '192.67.7.6',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/0.3',
  'ipaddr': '192.67.7.17',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/0.4',
  'ipaddr': '192.67.7.33',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/0.10',
  'ipaddr': '192.67.6.1',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/1',
  'ipaddr': '172.76.35.70',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet0/2',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'}]

since this is a list, I can use a list comprehension to perform some filtering like that:

[intf for intf in output if intf['proto']=='down']

this will give me

[{'intf': 'Embedded-Service-Engine0/0',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'},
 {'intf': 'GigabitEthernet0/2',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'}]

the output of “sh ver” using netmiko and testfsm is as follows

[{'config_register': '0x2102',
  'hardware': ['CISCO2911/K9'],
  'hostname': 'rtrXXXX',
  'mac': [],
  'reload_reason': 'Reload Command',
  'rommon': 'System',
  'running_image': 'c2900-universalk9-mz.SPA.157-3.M4a.bin',
  'serial': ['FGLxxxxx22D'],
  'uptime': '8 weeks, 6 days, 7 hours, 9 minutes',
  'version': '15.7(3)M4a'}]

I can use the above information to write the data to a csv file and create an inventory report. this is too trivial and I will not describe it here.

Another note is that you should always test that it works with your device. These are usually third party templates and they are not thoroughly tested with all routers.

comments powered by Disqus