Netmiko and TextFSM

Author: Kirk Byers
Date: 2022-01-13

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

You might also be interested in: