Netmiko and TextFSM
This article was originally written in 2018 and subsequently updated in 2022.
Introduction
In Netmiko 2.0.0 (December 2017), I added support for direct TextFSM integration particularly for ntc-templates.
I have written about TextFSM previously here, but let's review a bit.
What does TextFSM and ntc-templates allow you to do?
Simply stated, it allows you to take unstructured data and convert it to structured data. Or worded differently, it takes a block of text and converts it to lists and dictionaries (or some combination thereof).
And even better...someone else might have already written the TextFSM template for you. In other words, you might be able to take some "show command" output and feed it into an existing TextFSM template and get structured data base directly.
Thus entirely avoiding needing to parse the output yourself.
For reference, see the ntc-templates index file which maps platform and show command to TextFSM templates. This will give you a sense of which existing TextFSM templates exist for which platforms in the ntc-templates library.
So to recap, TextFSM is a complex, regular expression state-machine that converts from blocks of text to structured data (lists and dictionaries) and someone might have already created the template for you.
Now this is all fine, but how do we use TextFSM and ntc-templates with Netmiko. First we have to setup our environment properly. Let's look at this process
Setting Up the Environment
Starting with a clean Python3 virtual environment:
$ python3.9 -m venv .venv
$ source .venv/bin/activate
$ pip install --upgrade pip
...
$ pip list
Package Version
---------- -------
pip 21.2.4
setuptools 56.0.0
Then install the latest version of Netmiko:
$ pip install git+https://github.com/ktbyers/netmiko.git@develop
$ pip list | grep netmiko
netmiko 4.0.0a4
At this point, you should be able to successfully import the Netmiko library.
$ python
Python 3.9.5 (default, Jun 22 2021, 14:08:52)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import netmiko
>>> netmiko.__version__
'4.0.0a4'
Installing Netmiko should also install the ntc-templates library:
$ pip list | grep ntc-templates
ntc-templates 2.3.2
Netmiko should automatically find the ntc-templates index file (in the pip-installed location).
Alternatively, you can tell Netmiko where the TextFSM templates directory is by specifying the following environment variable (note, there must be an index file in this directory):
export NET_TEXTFSM=/path/to/ntc-templates/templates/
How to use Netmiko with TextFSM?
Now that both Netmiko and ntc-templates have been installed, how do we tell Netmiko to use the TextFSM parsing capability.
First, look at the ntc-templates index file and verify a template exists for your network platform and command.
After you have done that, then add the 'use_textfsm=True' argument to either the send_command() method or to the send_command_timing() method. Here are a couple of examples:
$ pdbr
Python 3.9.5 (default, Jun 22 2021, 14:08:52)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.24.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from netmiko import ConnectHandler
In [2]: from getpass import getpass
In [3]: net_connect = ConnectHandler(host="cisco.domain.com", username="admin", password=get
...: pass(), device_type="cisco_ios")
Password:
In [4]: net_connect.find_prompt()
Out[4]: 'cisco3#'
In [5]: net_connect.send_command("show ip int brief", use_textfsm=True)
Out[5]:
[{'intf': 'GigabitEthernet0/0/0',
'ipaddr': '10.220.88.22',
'status': 'up',
'proto': 'up'},
{'intf': 'GigabitEthernet0/0/1',
'ipaddr': 'unassigned',
'status': 'administratively down',
'proto': 'down'},
{'intf': 'GigabitEthernet0/1/0',
'ipaddr': 'unassigned',
'status': 'down',
'proto': 'down'},
{'intf': 'GigabitEthernet0/1/1',
'ipaddr': 'unassigned',
'status': 'down',
'proto': 'down'},
{'intf': 'GigabitEthernet0/1/2',
'ipaddr': 'unassigned',
'status': 'down',
'proto': 'down'},
{'intf': 'GigabitEthernet0/1/3',
'ipaddr': 'unassigned',
'status': 'down',
'proto': 'down'},
{'intf': 'Vlan1', 'ipaddr': 'unassigned', 'status': 'up', 'proto': 'down'}]
Now is when you should smile. We have structured data and someone else did the heavy lifting for us!
Now what about the failure case?
In other words, what happens when the template lookup fails or ntc-templates is not installed properly.
In this situation you will just get back your original text (i.e. unstructured data).
In [12]: output = net_connect.send_command("show arp", use_textfsm=True)
In [13]: output
Out[13]: 'Protocol Address Age (min) Hardware Addr Type Interface\nInternet 10.220.88.1 11 0062.ec29.70fe ARPA GigabitEthernet0/0/0\nInternet 10.220.88.21 224 1c6a.7aaf.576c ARPA GigabitEthernet0/0/0\nInternet 10.220.88.22 - a093.5141.b780 ARPA GigabitEthernet0/0/0\nInternet 10.220.88.28 195 00aa.fc05.b513 ARPA GigabitEthernet0/0/0\nInternet 10.220.88.31 48 00ac.fc59.97f2 ARPA GigabitEthernet0/0/0\nInternet 10.220.88.37 70 0001.00ff.0001 ARPA GigabitEthernet0/0/0\nInternet 10.220.88.38 98 0002.00ff.0001 ARPA GigabitEthernet0/0/0'
In [14]: print(type(output))
<class 'str'>
Note, 'show ip arp' is supported in ntc-templates, but 'show arp' isn't (you can see this by looking at the ntc-templates index file).
How about one more working example:
In [15]: output = net_connect.send_command("show version", use_textfsm=True)
In [16]: output
Out[16]:
[{'version': '16.12.3',
'rommon': 'IOS-XE',
'hostname': 'cisco3',
'uptime': '6 weeks, 1 day, 23 hours, 52 minutes',
'uptime_years': '',
'uptime_weeks': '6',
'uptime_days': '1',
'uptime_hours': '23',
'uptime_minutes': '52',
'reload_reason': 'Reload Command',
'running_image': 'c1100-universalk9_ias.16.12.03.SPA.bin',
'hardware': ['C1111-4P'],
'serial': ['FGL222290LB'],
'config_register': '0x2102',
'mac': [],
'restarted': '14:33:46 PST Tue Nov 30 2021'}]
Kirk Byers
@kirkbyers