This is the original post which used Python 2.7. Please see the updated post using Python 3.
TextFSM is a powerful parsing tool (python module) developed by Google. There are some great examples out there to get you started. Here are two I urge you to read if this topic is of interest to you:
- TextFSM and Structured Data By Kirk Byers
- Programmatic Access to CLI Devices with TextFSM from Jason Edelmans blog
I can never get enough of examples so here is a very simple one to get you started or keep you practicing. I find that a quick example where I can see results of my own making really energizes my learning process.
For this example, you need python 2.7 and the textfsm module installed.
TextFSM Getting Started is an excellent resource which includes the installation process for textfsm (textfsm is a pip installable module).
In addition to the environment, you will need 2 things to get started.
- A TextFMS template
- Content to parse with the TextFMS template
The template may be the tricky part. This template defines the structure of the data you are trying to gather or parse out of your content. We are very fortunate that the Network to Code (NTC) team has given us a large library of templates from which to choose and so we don't have to worry too much about the template itself. We just need to know what Cisco IOS show command has the information we want.
Once you have your template, you need content to parse. You can get this in a variety of ways. You can query your devices real time via Ansible or via a python scirpt or you can act on file based data that you already have.
In this example we will keep it simple and assume we have a text file of show commands that we need to parse to get the device hardware information and software version.
To get hardware and software information, the "show version" output should have what we want. So looking at the existing templates in the NTC library, it looks like the cisco_ios_show_version.template has what we need, If we look at the template we can see that it has two variables, VERSION and HARDWARE (which will return a list). That looks just about right for the information we want to extract and luckily the file of show commands includes the output of the "show version" command.
So here are the two files we will work with in this example:
- the textFSM template file (downloadable from GitHub)
ntc-templates/templates/cisco_ios_show_version.template
- the content file with show commands including the output of the show version command (downloadable here)
lab-swtich-show-cmds
For simplicity I've put them both in a temp directory and I will launch the python interpreter from there so we can work real time. I also list the modules that I have in the virtual environment. The only one you need for this example is textfsm.
Working directory and files
(textfsm) Eugenias-PB:textfsm-example eugenia$ pwd /Users/eugenia/Downloads/textfsm-example (textfsm) Eugenias-PB:textfsm-example eugenia$ ls cisco_ios_show_version.template lab-swtich-show-cmds.txt (textfsm) Eugenias-PB:textfsm-example eugenia$ ls -al total 24 drwxr-xr-x 4 eugenia staff 136 May 11 05:06 . drwx------+ 118 eugenia staff 4012 May 11 05:04 .. -rw-r--r--@ 1 eugenia staff 680 May 11 05:06 cisco_ios_show_version.template -rw-r--r--@ 1 eugenia staff 7425 May 11 05:05 lab-swtich-show-cmds.txt
The environment
(textfsm) Eugenias-PB:~ eugenia$ python --version Python 2.7.15 (textfsm) Eugenias-PB:textfsm-example eugenia$ pip freeze et-xmlfile==1.0.1 graphviz==0.8.2 jdcal==1.3 netaddr==0.7.19 openpyxl==2.5.1 **textfsm==0.3.2** xlrd==1.1.0 (textfsm) Eugenias-PB:textfsm-example eugenia$
Lets get started...
Launch the interpreter and import the textfsm module. The ">>>" tells you that you are in the python command interpreter. (textfsm) Eugenias-PB:textfsm-example eugenia$ python Python 2.7.10 (default, Feb 7 2017, 00:08:15) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> >>> import textfsm
Open the template file into a file handle I've called template and pass that as the argument to the textfsm.TextFSM method creating a "parsing object" based on that template (the show version template in our case). >>> template = open('cisco_ios_show_version.template') >>> results_template = textfsm.TextFSM(template)
Look at some of the methods available in the results_template object by using dir() on the results_template object you just created. Make note of the 'ParseText' one as that is the one we will use shortly to parse our content. >>> dir(results_template) ['GetValuesByAttrib', 'MAX_NAME_LEN', 'ParseText', 'Reset', '_AppendRecord', '_AssignVar', '_CheckLine', '_CheckRule', '_ClearAllRecord', '_ClearRecord', '_DEFAULT_OPTIONS', '_GetHeader', '_GetValue', '_Operations', '_Parse', '_ParseFSMState', '_ParseFSMVariables', '_ValidateFSM', '_ValidateOptions', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cur_state', '_cur_state_name', '_line_num', '_options_cls', '_result', 'comment_regex', 'header', 'state_list', 'state_name_re', 'states', 'value_map', 'values'] >>> results_template.value_map {'UPTIME': '(?P<UPTIME>.+)', 'HOSTNAME': '(?P<HOSTNAME>\\S+)', 'RUNNING_IMAGE': '(?P<RUNNING_IMAGE>\\S+)', 'CONFIG_REGISTER': '(?P<CONFIG_REGISTER>\\S+)', 'HARDWARE': '(?P<HARDWARE>\\S+\\d\\S+)', 'VERSION': '(?P<VERSION>.+?)', 'SERIAL': '(?P<SERIAL>\\S+)', 'ROMMON': '(?P<ROMMON>\\S+)'} >>> results_template.values [<textfsm.TextFSMValue object at 0x107c0ac90>, <textfsm.TextFSMValue object at 0x107c181d0>, <textfsm.TextFSMValue object at 0x107c18450>, <textfsm.TextFSMValue object at 0x107cf1750>, <textfsm.TextFSMValue object at 0x107cf1790>, <textfsm.TextFSMValue object at 0x107cf17d0>, <textfsm.TextFSMValue object at 0x107cf18d0>, <textfsm.TextFSMValue object at 0x107cf1950>]
You don't need the last three commands. I just wanted to show you how to investigate the object a little which is quite easy when working in the interpreter.
This is the heart of what we are trying to do now that we've selected our parsing template and created an object that will parse our content and pull out the information we want.
First we open our text file of show commands. Below I create a file handle (basically a variable) to the show command file called content2parse and read the contents of that file into the variable content. You have to open the file first before you can read its contents. >>> content2parse = open('lab-swtich-show-cmds.txt') >>> content = content2parse.read() Now we parse out the data we want using our results_template object (and its ParseText) method against our content and store the results in the parsed_results variable. As you can see, this is a list of lists. If you ran this against show command files from 5 different devices you can begin to see the possibilities. You would have a list of 5 lists with the show version information for each device. >>> parsed_results = results_template.ParseText(content) >>> parsed_results [['15.2(2)E3', 'Bootstrap', 'artic-sw01', '4 days, 14 hours, 2 minutes', 'c2960s-universalk9-mz.152-2.E3.bin', ['WS-C2960S-24TS-S'], ['FOC1709W1DT'], '0xF']] Your first inclination might be to iterate over the results but remember that its a list of lists so iterating over the results would iterate over the only item in the "top level" list [0]. What you want is to iterate over each element of the results list which is also a list. I know that my top level list has 1 element, the [0] element, and that list has 8 elements. We only parsed one file which is why our list only has one element. If I iterate over the [0] element of the list I get each individual bit of information. Get the length of the top level list (should only have one element since we only parsed 1 file) >>> len(parsed_results) 1 Get the length of that first element >>> len(parsed_results[0]) 8 >>> for item in parsed_results: ... print(item) ... ['15.2(2)E3', 'Bootstrap', 'artic-sw01', '4 days, 14 hours, 2 minutes', 'c2960s-universalk9-mz.152-2.E3.bin', ['WS-C2960S-24TS-S'], ['FOC1709W1DT'], '0xF'] >>> index = 0 >>> for item in parsed_results[0]: ... print("Element # {}: {}".format(index,item)) ... index = index + 1 ... Element # 0: 15.2(2)E3 Element # 1: Bootstrap Element # 2: artic-sw01 Element # 3: 4 days, 14 hours, 2 minutes Element # 4: c2960s-universalk9-mz.152-2.E3.bin Element # 5: ['WS-C2960S-24TS-S'] Element # 6: ['FOC1709W1DT'] Element # 7: 0xF >>> I know I have 8 elements (0-7) because i checked the length and if you do a length on the value_map you can see that the template is designed to get 8 pieces of information so that makes sense. Get the length of the first list >>> len(parsed_results[0]) 8 Get the length of the value map >>> len(results_template.value_map) 8 Finally, after all of this I wanted to get the version and hardware information out of this file and here it is: >>> parsed_results[0][0] '15.2(2)E3' >>> parsed_results[0][5] ['WS-C2960S-24TS-S'] >>>
I hope these basics help you get started or keep practicing.
Here are all the main commands in sequence so you can see the flow without all of the interruptions. Also easier to copy and paste!
Python 2.7.10 (default, Feb 7 2017, 00:08:15) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import textfsm >>> template = open('cisco_ios_show_version.template') >>> results_template = textfsm.TextFSM(template) >>> content2parse = open('lab-swtich-show-cmds.txt') >>> content = content2parse.read() >>> parsed_results = results_template.ParseText(content) >>> parsed_results [['15.2(2)E3', 'Bootstrap', 'artic-sw01', '4 days, 14 hours, 2 minutes', 'c2960s-universalk9-mz.152-2.E3.bin', ['WS-C2960S-24TS-S'], ['FOC1709W1DT'], '0xF']]