TextFsm is available as a plugin to various device interaction libraries like Netmiko but what if we need to use TEXTFSM as a standalone python package, it is also available. Conditions where you don’t have access to the devices but have outputs from the device in a text file. We have already seen how to use TextFSM with Netmiko here. Let’s see how to parse data with TextFSM as a standalone package. Assume we have below directory structure
╰─ tree ─╯
.
├── 01_textfsm.py
├── Input
│ ├── abc-aust-001-as01.txt
│ ├── abc-aust-001-cs01.txt
│ ├── abc-aust-006-as01.txt
│ ├── abc-aust-006-as02.txt
│ ├── abc-aust-006-as03.txt
│ ├── abc-aust-006-as04.txt
│ ├── abc-newz-001-as01.txt
│ ├── abc-newz-001-as02.txt
│ ├── abc-newz-001-as03.txt
│ ├── abc-newz-001-as04.txt
│ ├── abc-newz-001-as05.txt
│ ├── abc-newz-001-as07.txt
│ ├── abc-newz-001-as08.txt
│ ├── abc-newz-001-as09.txt
│ ├── abc-newz-001-as10.txt
│ ├── abc-newz-001-as11.txt
│ ├── abc-newz-001-cs01.txt
│ └── abc-newz-003-cs01.txt
├── Output
Script blueprint:-
- Read all files in the input directory
- For each command we want to parse, load the correct textfsm template from the templates folder
- After textfsm has parsed the data, convert data into tabular format.
- Write to excel using openpyxl or pandas
import textfsm
import os
import pandas as pd
from datetime import datetime
import ntc_templates
if __name__ == "__main__":
# Find the folder where ntc_templates are installed.
templates = os.path.dirname(os.path.dirname(ntc_templates.__file__)) + '/ntc_templates/templates'
#print(templates)
current_time = datetime.now()
#command list for each command that is in the input files you want to parse
commands = ['show_version', 'show_interfaces']
# Create a seperate df for each command thereby seperate sheet for each command for all devices.
df_list = []
for command in commands:
print(f"Parsing the output of {command}\n!")
template = _load_template(command, templates) # _load_template function will load correct template
#print(dir(template))
df_list.append([_parse_each_file(), command]) # _parse_each_file will prepare a dataframe containing parsed content of each file.
_write_to_excel(df_list) # this function will write the dataframes for each command into an excel file.
## Let's now see each function as it occurs in the above blueprint code.
- Load template
def _load_template(command, templates):
with open(f"{templates}/cisco_ios_{command}.textfsm") as f:
return textfsm.TextFSM(f)
2. Parse each file using TextFsm template
def _parse_each_file():
df = pd.DataFrame()
for file in sorted(os.listdir('Input')):
template.Reset() # otherwise entires from next loop item adds to the previous loop item,
with open('Input/' + file) as f:
text = f.read()
df_parsed = pd.DataFrame(template.ParseTextToDicts(text))
"""Calls ParseText and turns the result into list of dicts.
List items are dicts of rows, dict key is column header and value is column value. """
df_parsed.insert(0, 'DEVICE_NAME', file[:-4])
df = df.append(df_parsed)
return df
3. After all parsing is complete, we need to write the dataframes to output sheet.
def _write_to_excel(df_list):
writer = pd.ExcelWriter('Output/parsed_commands_' + current_time.strftime("%Y-%m-%d-%H-%M") + '.xlsx', engine='xlsxwriter')
for df, sheetname in df_list:
df.to_excel(writer, sheet_name=sheetname, index=False)
workbook = writer.book
worksheet = writer.sheets[sheetname]
cell_format = workbook.add_format()
cell_format.set_text_wrap()
cell_format.set_align('center')
cell_format.set_align('vcenter')
# worksheet.set_column('A:E', 20, cell_format)
writer.save()
Putting all the pieces together and executing the script it finally gets this output. Notice separate tabs for each command.
Within a fraction of seconds, you will be able to parse data of well-known command outputs into separate tabs for later reference without requiring any understanding of writing any parsers for parsing the text into fields you want.
I hope this was informative. If you have any suggestions/comments, please feel free to contact me below.