Python Archives - GMBYTES https://gmbytes.com/category/python/ Glenn McCallum Tech Bytes Tue, 07 Sep 2021 16:45:14 +0000 en-US hourly 1 https://gmbytes.com/wp-content/uploads/2021/08/cropped-android-chrome-512x512-1-32x32.png Python Archives - GMBYTES https://gmbytes.com/category/python/ 32 32 Tkinter python GUI package https://gmbytes.com/tkinter-python-gui-package/ https://gmbytes.com/tkinter-python-gui-package/#respond Tue, 07 Sep 2021 16:06:29 +0000 https://gmbytes.com/?p=287 I decided to teach myself Tkinter and build a little program to help quickly search and ping/connect to networking devices....

continue reading »

The post Tkinter python GUI package appeared first on GMBYTES.

]]>
I decided to teach myself Tkinter and build a little program to help quickly search and ping/connect to networking devices. The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.

End result we will make

What this post will end up making

So tkinter has some learning curve but ideally you just want to layout your program in boxes and then fill those items with buttons, text boxes and anything you want to build. You can see a more detailed documentation here.

This little program grew as I learnt more and more about layouts and positioning and was built to be packaged up and run on a Windows box as the buttons run commands on Windows OS. I have added a few devices into the CSV file as a placeholder but this helped our team search for devices across all the customers we managed.

I split up the program into 3 sections

  1. top-Frame (orange)
  2. Centre-Frame split into
    1. ctr-left (red)
    2. ctr-right (red)
  3. Bottom Frame (blue)

I will break down the code sections below and there is full code on my github https://github.com/glennmccallum/tkinter-nms-helper

Code

from tkinter import *
from tkinter import ttk
import pandas as pd
import os

This code is imported to run tkinter and pandas to read in the csv

#----main----#
host = ""
# Read in csv using pandas
devices = pd.read_csv("all_devices.csv")

# Get All hostnames in a sorted List
hostnames = devices['Hostname']
hostnames = list(hostnames)
hostnames = sorted(hostnames)
# Get All Companies in a sorted List
companies = devices['Company'].unique()
companies = list(companies)
companies = sorted(companies)

This is reading in the csv using pandas and then extracing and sorting the hostnames and customers in alphabetical order.

# Initalise Tkinter
root = Tk()
# Title on window
root.title('NMS HELPER 2.0')
# favicon for window
root.iconbitmap("favicon.ico")
# Size of window
root.geometry('{}x{}'.format(460, 375))

Setup the window using tkinter, naming the, adding a favicon and sizing the window

# create all of the main containers
top_frame = Frame(root, bg='#333333', width=450, height=50, pady=3)
center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='#333333', width=450, height=45, pady=3)

# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

# Set the way window can expand
top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=3, sticky="ew")

This sets up the top, centre and bottom frames of the window

# Create the widgets for the top frame
# Search Label and place in topframe
searchLabel = Label(top_frame, text="Filter Hostname:", bg='#333333', fg="grey")
# Entry textbox for user to filter results in topframe
entry = Entry(top_frame, bg='grey', font=(12))
# give curser focus 
entry.focus()
# when user adds charactrs to filter pass to on_keyrelease function
entry.bind('<KeyRelease>', on_keyrelease)
# Clear search Button assigned to top frame, if pressed pass to clearEntry function
clearSearch = Button(top_frame, text = "Clear Search", command = clearEntry)
# Company seach dropdown comboxbox in top frame
comboBoxLabel = Label(top_frame, text="Filter Company:", bg='#333333', fg="grey")
# Fill the combobox with companies
comboBox = ttk.Combobox(top_frame, values=companies)
# If user selects a company pass to callbackFunc to filter hostnames
comboBox.bind("<<ComboboxSelected>>", callbackFunc)

# layout the widgets in the top frame
searchLabel.grid(row=0, column=0)
entry.grid(row=0, column=1)
clearSearch.grid(row=0, column=2, padx=3, pady=3)
comboBoxLabel.grid(row=1, column=0)
comboBox.grid(row=1, column=1)

This is setting up widgets in Top Frame

# create the center widgets row and columns 
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)
center.grid_columnconfigure(2, weight=4)

# layout the widgets in center frame
ctr_left = Frame(center, bg='#333333')
ctr_mid = Frame(center, bg='#333333')
ctr_right = Frame(center, bg='#333333', padx=3, pady=3)
ctr_left.grid(row=0, column=0, sticky="nsew")
ctr_mid.grid(row=0, column=1, sticky="nsew")
ctr_right.grid(row=0, column=2, sticky="nsew")

# create the widgets in the ctr_left Frame
scrollbar = Scrollbar(ctr_left)
# Make listbox with a black background and grey text
listbox = Listbox(ctr_left, bd=0, background="black", fg="grey",selectbackground="grey", font = (12))

# layout the scrollbar and listbox and attach in the ctr_left Frame
scrollbar.pack(side=RIGHT, fill=Y)
listbox.pack(fill = BOTH, expand=True)
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
# For debugging to show selection was selected in terminal
listbox.bind('<<ListboxSelect>>', on_select)

This sets up the centre_left frame with listbox to list the devices and attach the scrollbar to listbox.

# Show all hostnames when first opening program
listbox_update(hostnames)

shows all the hostnames in the list box when starting the program, listbox_update function explained in a bit

# create the widgets in the centre Right Frame to display hostname details
hostnameLabel = Label(ctr_mid, text="HOSTNAME:", bg='#333333', fg="grey",font = (12))
hostname_entry = Entry(ctr_right, text="",bg='grey',font = (12))

# layout the widgets in the ctr_right Frame
hostnameLabel.pack(fill = X, expand=True)
hostname_entry.pack(fill = X, expand=True)

I wont list all of the text fields but this is a snapshot of how I created the labels and the fields to be dynamically populated

# create the buttons in the Bottom Frame to send to each function to action
ping = Button(btm_frame, text = "PING", command = pingDevice, font = (12))
ssh = Button(btm_frame, text = "SSH", command = sshDevice, font = (12))
telnet = Button(btm_frame, text = "TELNET", command = telnetDevice, font = (12))
http = Button(btm_frame, text = "HTTP", command = httpDevice, font = (12))
https = Button(btm_frame, text = "HTTPS", command = httpsDevice, font = (12))
vnc = Button(btm_frame, text = "VNC", command = vncDevice, font = (12))

# layout the widgets in the Bottom Frame
ping.grid(row=0, column=1, padx=3, pady=3)
ssh.grid(row=0, column=2, padx=3, pady=3)
telnet.grid(row=0, column=3, padx=3, pady=3)
http.grid(row=0, column=4, padx=3, pady=3)
https.grid(row=0, column=5, padx=3, pady=3)
vnc.grid(row=0, column=6, padx=3, pady=3)

Create the buttons in the bottom frame

# run the program
root.mainloop()

Run the program

Functions called from the above code

# Keep filtering listbox when user enters characters in textbox
def on_keyrelease(event):

    # Get value from Entrybox
    value = event.widget.get()
    # Strip whitespaces and convert to lower case
    value = value.strip().lower()

    # If nothing entered display all hostnames
    if value == '':
        data = hostnames
    # else serach hostnames and display any that match string
    else:
        data = []
        for item in hostnames:
            if value in item.lower():
                data.append(item)                

    # Update hostanmes in listbox
    listbox_update(data)

This function was binding to textbox when user typed in to filter the list of devices after each key is pressed it is filters the listbox

# Used to populate the listbox from data entered
def listbox_update(data):
    # delete previous data
    listbox.delete(0, 'end')

    # sorting data 
    data = sorted(data, key=str.lower)

    # put new data
    for item in data:
        listbox.insert('end', item)

listbox_update populates the listbox from the data entered in the text box

# For debugging purposes to show active seclection in terminal
def on_select(event):
    # display element selected on list
    print('(event) previous:', event.widget.get('active'))
    print('(event)  current:', event.widget.get(event.widget.curselection()))
    print('---')
    infoDevice()

Just a debug function to see what the user selected in the terminal

# Get IP of selection from CSV file
def getSelection():
    # Get Current Selection
    selected = listbox.curselection()
    # Get the value of selection
    for item in selected:
        host = listbox.get(item)
    # From selected hostname get the IP from CSV file
    ip = devices.loc[devices['Hostname'] == host, 'IP'].item()

    #return the IP of selected device
    return ip

Once user clicks on a hostname it them looks up the device in csv and returns the IP address

# Used for clear search button to clear all fields
def clearEntry():
    # clear search
    entry.delete(0, 'end')
    # clear data in textbox and update listbox
    value = ''
    data = hostnames
    listbox_update(data)
    # leave curser in focus in textbox
    entry.focus()

    # clear out all device infomration fields
    hostname_entry.delete(0,END)
    natIp_entry.delete(0,END)
    custIp_entry.delete(0,END)
    customer_entry.delete(0,END)
    model_entry.delete(0,END)
    serial_entry.delete(0,END)
    circuit_entry.delete(0,END)
    site_entry.delete(0,END)
    critical_entry.delete(0,END)
    sla_entry.delete(0,END)
    version_entry.delete(0,END)

    #Clear out dropdown cobo box
    comboBox.set('')

Used when you click on the clear button so it resets the filter and clears out all fields

# used for Ping Button
def pingDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and ping IP
    os.system("start cmd /k ping " + ip + " -t")

# used for SSH Button
def sshDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and SSH to IP using Putty
    os.system("start cmd /k putty.exe -ssh " + ip + " 22")

# used for TELNET Button
def telnetDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and telnet to IP using Putty
    os.system("start cmd /k putty.exe -telnet " + ip + " 23")

# used for HTTP Button
def httpDevice():
    # Get IP from selection
    ip = getSelection()
    # Start browser and http to IP
    url = "http://" + ip
    os.startfile(url)

# used for HTTPS Button
def httpsDevice():
    # Get IP from selection
    ip = getSelection()
    # Start browser and https to IP
    url = "https://" + ip
    os.startfile(url)

# used for VNC Button
def vncDevice():
    # Get IP from selection
    ip = getSelection()
    # Start cmd and telnet to IP using Putty
    os.system("start cmd /k vncviewer.exe " + ip)

The functions when you click on each of the buttons. This was built for Windows box so either runs a ping using command prompt or putty.exe for ssh and telnet, opens browser for http and https and uses vncviewer.exe to vnc to host.

# used to get info from CSV for selected device
def infoDevice():
    # Get Current Selection
    selected = listbox.curselection()

    # Get the value of selection
    for item in selected:
        host = listbox.get(item)

    # Get all fields required from CSV
    ip = devices.loc[devices['Hostname'] == host, 'IP'].item()
    cust_ip = devices.loc[devices['Hostname'] == host, 'Customer IP'].item()
    customer = devices.loc[devices['Hostname'] == host, 'Company'].item()
    models = devices.loc[devices['Hostname'] == host, 'Model'].item()
    sn = devices.loc[devices['Hostname'] == host, 'Serial number'].item()
    circuit = devices.loc[devices['Hostname'] == host, 'Circuits'].item()
    site = devices.loc[devices['Hostname'] == host, 'Site'].item()
    critical = devices.loc[devices['Hostname'] == host, 'Critical'].item()
    sla = devices.loc[devices['Hostname'] == host, 'SLA'].item()
    version = devices.loc[devices['Hostname'] == host, 'OS Version'].item()

    # Display results in text fields
    hostname_entry.delete(0,END)
    hostname_entry.insert(0,host)
    natIp_entry.delete(0,END)
    natIp_entry.insert(0,ip)
    custIp_entry.delete(0,END)
    custIp_entry.insert(0,cust_ip)
    customer_entry.delete(0,END)
    customer_entry.insert(0,customer)
    model_entry.delete(0,END)
    model_entry.insert(0,models)
    serial_entry.delete(0,END)
    serial_entry.insert(0,sn)
    circuit_entry.delete(0,END)
    circuit_entry.insert(0,circuit)
    site_entry.delete(0,END)
    site_entry.insert(0,site)
    critical_entry.delete(0,END)
    critical_entry.insert(0,critical)
    sla_entry.delete(0,END)
    sla_entry.insert(0,sla)
    version_entry.delete(0,END)
    version_entry.insert(0,version)

Used to populate the fields about the device once user selects it

# used for Dropdown when selecting a specific company
# display only hostnames in that company
def callbackFunc(event):
    company_Selected = comboBox.get()
    print (company_Selected)
    # locate all hostnames under that copany selected
    data = devices.loc[devices.Company == company_Selected, 'Hostname']
    # update data in listbox
    listbox_update(data)

Used to display only hosts that are related to the customer user picks from the dropdown list

This started out as a learning experience about tkinter and kept evolving so there might be a better way to set out the layout but this worked for me.

Please find the full code on my github https://github.com/glennmccallum/tkinter-nms-helper

The post Tkinter python GUI package appeared first on GMBYTES.

]]>
https://gmbytes.com/tkinter-python-gui-package/feed/ 0
TextFSM parse Cisco output https://gmbytes.com/textfsm-parse-cisco-output/ https://gmbytes.com/textfsm-parse-cisco-output/#respond Sat, 04 Sep 2021 15:59:27 +0000 https://gmbytes.com/?p=263 I recently got a file with all the show cdp neighbour detail from 200 routers in a network and was...

continue reading »

The post TextFSM parse Cisco output appeared first on GMBYTES.

]]>
I recently got a file with all the show cdp neighbour detail from 200 routers in a network and was required to produce a report to show all the devices and their direct connections.

I have cut the output to 3 routers for this example and simplified the output to Router1 to Router3

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Router1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
terminal length 0
Router1#show cdp neighbors detail
-------------------------
Device ID: Switch1
Entry address(es): 
  IP address: 1.1.1.1
Platform: cisco WS-C2960-48PST-L,  Capabilities: Switch IGMP 
Interface: GigabitEthernet0/1/0,  Port ID (outgoing port): FastEthernet0/48
Holdtime : 139 sec

Version :
Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(55)SE5, RELEASE SOFTWARE (fc1)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2012 by Cisco Systems, Inc.
Compiled Thu 09-Feb-12 19:11 by prod_rel_team

advertisement version: 2
Protocol Hello:  OUI=0x00000C, Protocol ID=0x0112; payload len=27, value=00000000FFFFFFFF010221FF00000000000034A84E680E80FF0000
VTP Management Domain: 'toxfree'
Native VLAN: 1
Duplex: full
Management address(es): 
  IP address: 1.1.1.1


Total cdp entries displayed : 1
Router1#

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Router2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
terminal length 0
Router2#show cdp neighbors detail
-------------------------
Device ID: Switch2
Entry address(es): 
  IP address: 1.1.1.2
Platform: Cisco C867VAE-K9,  Capabilities: Router Trans-Bridge Source-Route-Bridge Switch IGMP 
Interface: GigabitEthernet0/0/0,  Port ID (outgoing port): GigabitEthernet1
Holdtime : 175 sec

Version :
Cisco IOS Software, C860 Software (C860VAE2-ADVSECK9-M), Version 15.6(3)M3a, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2017 by Cisco Systems, Inc.
Compiled Tue 19-Sep-17 00:39 by prod_rel_team

advertisement version: 2
VTP Management Domain: ''
Native VLAN: 5
Duplex: full
Management address(es): 
  IP address: 1.1.1.2

-------------------------
Device ID: Switch3
Entry address(es): 
  IP address: 1.1.1.3
Platform: cisco WS-C2960X-24PS-L,  Capabilities: Switch IGMP 
Interface: GigabitEthernet0/1/0,  Port ID (outgoing port): GigabitEthernet1/0/24
Holdtime : 167 sec

Version :
Cisco IOS Software, C2960X Software (C2960X-UNIVERSALK9-M), Version 15.2(2)E4, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2016 by Cisco Systems, Inc.
Compiled Fri 12-Feb-16 22:57 by prod_rel_team

advertisement version: 2
Protocol Hello:  OUI=0x00000C, Protocol ID=0x0112; payload len=27, value=00000000FFFFFFFF010221FF0000000000005006ABCB6980FF0000
VTP Management Domain: ''
Native VLAN: 1
Duplex: full
Management address(es): 
  IP address: 1.1.1.3


Total cdp entries displayed : 2
Router2#

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Router3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
terminal length 0
Router3#show cdp neighbors detail
-------------------------
Device ID: Switch4
Entry address(es): 
  IP address: 1.1.1.4
Platform: cisco WS-C3560CG-8PC-S,  Capabilities: Switch IGMP 
Interface: GigabitEthernet0/1/0,  Port ID (outgoing port): GigabitEthernet0/9
Holdtime : 143 sec

Version :
Cisco IOS Software, C3560C Software (C3560c405ex-UNIVERSALK9-M), Version 15.2(2)E4, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2016 by Cisco Systems, Inc.
Compiled Sat 13-Feb-16 02:28 by prod_rel_team

advertisement version: 2
Protocol Hello:  OUI=0x00000C, Protocol ID=0x0112; payload len=27, value=00000000FFFFFFFF010221FF0000000000009C57AD04C500FF0000
VTP Management Domain: ''
Native VLAN: 1
Duplex: full
Management address(es): 
  IP address: 1.1.1.4


Total cdp entries displayed : 1
Router3#

Create a folder named textfsm and save the above output as show_cdp.txt

I am using Sublime Text, python 3.8 and TextFSM to complete this so you can follow along

I am going to assume you already have Python installed so lets install TextFSM, im using Ubuntu so

sudo apt install python3-textfsm

You can download all the textfsm templates from https://github.com/networktocode/ntc-templates but we are only going to use the template named cisco_ios_show_cdp_neighbors_detail.textfsm also shown below

Value Required DESTINATION_HOST (\S+)
Value MANAGEMENT_IP (\d+\.\d+\.\d+\.\d+|\w+\.\w+\.\w+)
Value PLATFORM (.*)
Value REMOTE_PORT (.*)
Value LOCAL_PORT (.*)
Value SOFTWARE_VERSION (.*$)
Value CAPABILITIES (.+?)

Start
  ^Device ID: ${DESTINATION_HOST}
  ^Entry address\(es\)\s*:\s* -> ParseIP
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}\s+$$
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}$$
  ^Interface: ${LOCAL_PORT},  Port ID \(outgoing port\): ${REMOTE_PORT}
  ^Version : -> GetVersion
  # Capture time-stamp if vty line has command time-stamping turned on
  ^Load\s+for\s+
  ^Time\s+source\s+is

ParseIP
  ^.*IP address: ${MANAGEMENT_IP} -> Start
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}\s+$$ -> Start
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}$$ -> Start
  ^.* -> Start

GetVersion
  ^${SOFTWARE_VERSION} -> Record Start

Create a folder named “templates” inside textfsm folder and save the above to a file named cisco_ios_show_cdp_neighbors_detail.textfsm

Below is how I started the script to test TextFSM parsing was working

import textfsm
import json
from pprint import pprint
import re

# Load cisco output into file_data
input_file = open("show_cdp.txt", encoding='utf-8')
file_data = input_file.read()
input_file.close()

# Use textfsm show cdp neighbour detail template
template = open("templates/cisco_ios_show_cdp_neighbors_detail.textfsm")
re_table = textfsm.TextFSM(template)
parsed_output = re_table.ParseText(file_data)

# Parsed output into Dict and print using pretty print
results = [dict(zip(re_table.header, textfsm)) for textfsm in parsed_output]
pprint(results)

Save the above as textfsm_parse_cdp_neighbour_detail.py

With Sublime text you can run the script within sublime by going to Tools->Build System -> Python and then you can just press Control+b to run the script and output will be generated below

You can see that the output in bottom section of window is showing the outputs using Pretty Print but i noticed it did not capture the actual Hosts (ie Router 1, Router2, Router3)

So I made an edit in bold to the TextFSM template below to capture the Hosts to put into the report. I am only using a simple regex to capture the string before the # in each line in show_cdp.txt like

Router1#show cdp neighbors detail 

Add the below in bold to your TextFSM template

Value HOST (.*)
Value Required DESTINATION_HOST (\S+)
Value MANAGEMENT_IP (\d+\.\d+\.\d+\.\d+|\w+\.\w+\.\w+)
Value PLATFORM (.*)
Value REMOTE_PORT (.*)
Value LOCAL_PORT (.*)
Value SOFTWARE_VERSION (.*$)
Value CAPABILITIES (.+?)


Start
  ^${HOST}#show\scdp\sneighbors\sdetail
  ^Device ID: ${DESTINATION_HOST}
  ^Entry address\(es\)\s*:\s* -> ParseIP
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}\s+$$
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}$$
  ^Interface: ${LOCAL_PORT},  Port ID \(outgoing port\): ${REMOTE_PORT}
  ^Version : -> GetVersion
  # Capture time-stamp if vty line has command time-stamping turned on
  ^Load\s+for\s+
  ^Time\s+source\s+is

ParseIP
  ^.*IP address: ${MANAGEMENT_IP} -> Start
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}\s+$$ -> Start
  ^Platform\s*:\s*${PLATFORM}\s*,\s*Capabilities\s*:\s*${CAPABILITIES}$$ -> Start
  ^.* -> Start

GetVersion
  ^${SOFTWARE_VERSION} -> Record Start

Run your script file again and you will now see HOST output

Now we want to output this to a CSV file so add the below at the bottom of your script

# open csv file for output
output = open("cdp_report.csv", "w+")
cdp_report = output

# Write the csv headers on first row
print(re_table.header)
for s in re_table.header:
    cdp_report.write("%s;" % s)
cdp_report.write("\n")

# Write each result to a new row in cdp report
counter = 0
for row in parsed_output:
    print(row)
    for s in row:
        cdp_report.write("%s;" % s)
    cdp_report.write("\n")
    counter += 1

# Print to screen how many rows printed
print("\nWrote %d devices" % counter)

Run your script again in Sublime with Control+b and this should now generate a csv file named cdp_report.csv and output should look like below but there is a small issue. Can you see it?

It is missing Router2 in Cell A4. Router2 has two cdp neighbours where Router1 and Router 3 has only one cdp neighbour each. To fix this we can use a feature on TextFSM called Filldown

Filldown - value that previously matched with a regex, remembered until the next processing line (if has not been explicitly cleared or matched again). This means that the last column value that matches regex is stored and used in the following strings if this column is not present.

So go back and edit your template file and add Filldown to the first line we added in

Value Filldown HOST (.*)

Rerun your script and reopen your csv file and this should be fixed

I hope this demonstrates the power of TextFSM in a practical way to process Cisco output.

I have the full code details on my github https://github.com/glennmccallum/textfsm-parse-cisco-output

Please leave any comments below or any questions

The post TextFSM parse Cisco output appeared first on GMBYTES.

]]>
https://gmbytes.com/textfsm-parse-cisco-output/feed/ 0