Intro

Textfsm is a text parsing library written in python to turn plain text into structured data. Originally created by Google, the project seemed largely abandoned until recently being added to github and receiving a small update.

This post will show how to extract interesting data from the output of the show interface command on a Cisco ASA using TextFSM. For reference the software versions used in this post are below.

  • TextFSM - 0.3.2
  • Python - 2.7.13
  • Ubuntu - 1604

Installation

Create a virtual environment and install textfsm .

cmd
virtualenv -p python2.7 ~/envs/textfsm-env
source ~/envs/textfsm-env/bin/activate

# inside virtual environment

pip install textfsm

Usage

The good folks over at network.toCode() maintain a library of templates and helpers for working with TextFSM. I will borrow one of the templates for this post and in a future post go into more depth around using the library.

Use curl to download the cisco_asa_show_interface.template template.

cmd
curl -O https://raw.githubusercontent.com/networktocode/ntc-templates/master/templates/cisco_asa_show_interface.template

Templates use a series of regular expressions to define the data to be extracted from plain text output. There is a pretty good explaination of the components of TextFSM here.

file
# textfsm template

Value Required INTERFACE (\S+)
Value INTERFACE_ZONE (.+?)
Value LINK_STATUS (\w+)
Value PROTOCOL_STATUS (.*)
Value HARDWARE_TYPE ([\w ]+)
Value BANDWIDTH (\d+\s+\w+)
Value DELAY (\d+\s+\w+)
Value DUPLEX (\w+\-\w+)
Value SPEED (\d+\w+\s\w+)
Value DESCRIPTION (.*)
Value ADDRESS ([a-zA-Z0-9]+.[a-zA-Z0-9]+.[a-zA-Z0-9]+)
Value MTU (\d+)
Value IP_ADDRESS (\d+\.\d+\.\d+\.\d+)
Value NET_MASK (\d+\.\d+\.\d+\.\d+)
Value ONEMIN_IN_PPS (\d+)
Value ONEMIN_IN_RATE (\d+)
Value ONEMIN_OUT_PPS (\d+)
Value ONEMIN_OUT_RATE (\d+)
Value ONEMIN_DROP_RATE (\d+)
Value FIVEMIN_IN_PPS (\d+)
Value FIVEMIN_IN_RATE (\d+)
Value FIVEMIN_OUT_PPS (\d+)
Value FIVEMIN_OUT_RATE (\d+)
Value FIVEMIN_DROP_RATE (\d+)

Start
  ^.*Interface ${INTERFACE} "${INTERFACE_ZONE}", is ${LINK_STATUS}.*protocol is ${PROTOCOL_STATUS}
  ^\s+Hardware is ${HARDWARE_TYPE} -> Continue
  ^.*BW ${BANDWIDTH}.*DLY ${DELAY}
  ^.*\(${DUPLEX}.*Auto-Speed\(${SPEED}\)
  ^.*Description: ${DESCRIPTION}
  ^.*MAC address ${ADDRESS}.*MTU ${MTU}
  ^.*IP address ${IP_ADDRESS}, .*subnet mask ${NET_MASK}
  ^.*1 minute input rate ${ONEMIN_IN_PPS} pkts/sec,\s+${ONEMIN_IN_RATE} bytes/sec
  ^.*1 minute output rate ${ONEMIN_OUT_PPS} pkts/sec,\s+${ONEMIN_OUT_RATE} bytes/sec
  ^.*1 minute drop rate, ${ONEMIN_DROP_RATE}
  ^.*5 minute input rate ${FIVEMIN_IN_PPS} pkts/sec,\s+${FIVEMIN_IN_RATE} bytes/sec
  ^.*5 minute output rate ${FIVEMIN_OUT_PPS} pkts/sec,\s+${FIVEMIN_OUT_RATE} bytes/sec
  ^.*5 minute drop rate, ${FIVEMIN_DROP_RATE} -> Record

Lets fire up a python interpreter and create an interfaces variable with the output of the show interface command from a Cisco ASA.

python
# output of show interface

interfaces = '''
Interface GigabitEthernet0/0 "inside", is up, line protocol is up
  Hardware is i82540EM rev02, BW 1000 Mbps, DLY 10 usec
    Auto-Duplex(Full-duplex), Auto-Speed(1000 Mbps)
    Input flow control is unsupported, output flow control is off
    MAC address 0800.2735.03c6, MTU 1500
    IP address 169.254.1.11, subnet mask 255.255.255.0
    0 packets input, 0 bytes, 0 no buffer
    Received 0 broadcasts, 0 runts, 0 giants
    0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort
    0 pause input, 0 resume input
    0 L2 decode drops
    1 packets output, 60 bytes, 0 underruns
    0 pause output, 0 resume output
    0 output errors, 0 collisions, 1 interface resets
    0 late collisions, 0 deferred
    0 input reset drops, 0 output reset drops
    input queue (blocks free curr/low): hardware (511/511)
    output queue (blocks free curr/low): hardware (511/510)
  Traffic Statistics for "inside":
    0 packets input, 0 bytes
    1 packets output, 28 bytes
    0 packets dropped
      1 minute input rate 0 pkts/sec,  0 bytes/sec
      1 minute output rate 0 pkts/sec,  0 bytes/sec
      1 minute drop rate, 0 pkts/sec
      5 minute input rate 0 pkts/sec,  0 bytes/sec
      5 minute output rate 0 pkts/sec,  0 bytes/sec
      5 minute drop rate, 0 pkts/sec
Interface Management0/0 "management", is up, line protocol is up
  Hardware is i82540EM rev02, BW 1000 Mbps, DLY 10 usec
    Auto-Duplex(Full-duplex), Auto-Speed(1000 Mbps)
    Input flow control is unsupported, output flow control is off
    MAC address 0800.277d.ea42, MTU 1500
    IP address 10.0.2.15, subnet mask 255.255.255.0
    1086 packets input, 83965 bytes, 0 no buffer
    Received 2 broadcasts, 0 runts, 0 giants
    0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort
    0 pause input, 0 resume input
    0 L2 decode drops
    1071 packets output, 130571 bytes, 0 underruns
    0 pause output, 0 resume output
    0 output errors, 0 collisions, 1 interface resets
    0 late collisions, 0 deferred
    0 input reset drops, 0 output reset drops
    input queue (blocks free curr/low): hardware (497/458)
    output queue (blocks free curr/low): hardware (510/506)
  Traffic Statistics for "management":
        1085 packets input, 62837 bytes
    1071 packets output, 114891 bytes
    2 packets dropped
      1 minute input rate 16 pkts/sec,  905 bytes/sec
      1 minute output rate 16 pkts/sec,  1762 bytes/sec
      1 minute drop rate, 0 pkts/sec
      5 minute input rate 0 pkts/sec,  0 bytes/sec
      5 minute output rate 0 pkts/sec,  0 bytes/sec
      5 minute drop rate, 0 pkts/sec
'''

Next up we will open the cisco_asa_show_interface.template file and run the interfaces data through the parser.

python
# import library

import textfsm

# open the template file

with open('cisco_asa_show_interface.template', 'r') as f:
    template = textfsm.TextFSM(f)

# run the interface data through the template parser

template.ParseText(interfaces)

# output

[['GigabitEthernet0/0',
  'inside',
  'up',
  'up',
  'i82540EM rev02',
  '1000 Mbps',
  '10 usec',
  'Full-duplex',
  '1000 Mbps',
  '',
  '0800.2735.03c6',
  '1500',
  '169.254.1.11',
  '255.255.255.0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0'],
 ['Management0/0',
  'management',
  'up',
  'up',
  'i82540EM rev02',
  '1000 Mbps',
  '10 usec',
  'Full-duplex',
  '1000 Mbps',
  '',
  '0800.277d.ea42',
  '1500',
  '10.0.2.15',
  '255.255.255.0',
  '16',
  '905',
  '16',
  '1762',
  '0',
  '0',
  '0',
  '0',
  '0',
  '0']]

As you can see above, the parser returns a list of lists where each list contains the values defined in the cisco_asa_show_interface.template .

Outro

TextFSM is a great tool for getting structured data out of devices that dont have an API. In a future post I will use TextFSM along with Netmiko and the ntc-templates libraries to programatically extract data from network devices without an API.