Showing results for 
Search instead for 
Did you mean: 

Any script ideas to save time? - Check out my fuzzy config searcher.


I'm wondering if there's anything, regarding script ideas, that can be written to save a network engineer time. If you post your idea I can try to write it in Python and share it here. For example I wrote a fuzzy configuration search parser to be ran against networking devices. For now I've added Cisco functionality to it. 

There are 3 escape characters used:

(())      is match any word no spaces
+         is match many words and spaces in one line
#         is match many words and spaces many lines


router ospf +
router-id +
network +
network 10.0.(()).(())


matches any router ospf configuration with at least 2 network statements, and at least one subnet of 10.0.x.x

A more generalized search is:

router ospf (())
network +

which returns the same result but also returns many lines  or zero line in between router and network command.

The script goes through the devices in the device list, and parses the fuzzy searching. You can include as many as templates as you want. Here's an example I used in my Github:

ip_address = [[, cisco, cisco123, cisco123], [, cisco, cisco123, cisco123],
[, cisco, cisco123, cisco123]]
router bgp 65001
neighbor 10.1.+
neighbor remote-as 65002
router ospf (())
router-id (())
network 10.0.(()).(()) area (())
router ospf +
router-id +
network +
network (()) (())
router bgp +
neighbor 10.1.(()).(())
neighbor + remote-as 65002
ip_address = [[, cisco, cisco123, cisco123]]
ssh version (())


in this case it searches for devices under ios_template for fuzzy searching of bgp and ospf,  then it looks for devices under asa templaye to return the ssh version on the device.

Here is the link to Github with a video of the code's execution. The documentation of how to use is in Github, can't seem to attach it here. I may add other stuff to Github as time progresses.


here's the code, some of the string parsing (towards the middle of the code)  may seem convoluted, I wrote it that way to give the user more flexibility when writing in fuzzy search text, and so there's less chance of errors. The first part is the main calling code, the second half of the code after the function is basically sting parsing and regular expression parsing.




import re
import netmiko
from netmiko.ssh_autodetect import SSHDetect
from netmiko.ssh_dispatcher import ConnectHandler
import time
import threading
from netmiko.ssh_autodetect import SSHDetect

# junos_command = "show configuration"
# ios_command = "show running-config"
# asa_command = "show running-config"

outfile = open("outfile.txt", "a+")

with open("device_template.txt", "r") as dev_template:
    dev_template =

dev_list = dev_template.split("$$$")

def connection_handler(template, address_list = [], configuration_block=[], dev_type="autodetect"):
    listx = []
    def threaded(address_variable):

            var = address_variable.split(",")
            ip_address, username, password, secret = str(var[0]).strip(), \
                str(var[1]).strip(), str(var[2]).strip(), str(var[3]).strip()

            connection = ConnectHandler(host=str(ip_address), username=username, password=password,
                                                                 secret=secret, device_type=dev_type)
            print("*" * 8)
            result = connection.send_command("show run")
            result = result.replace("\n ", "\n")

            for i in configuration_block:
                i = i.rstrip("\n")
                res =, result, re.DOTALL)

                if res:
                    print("found match")
                    outfile.write("Config IS found for   " + template + " " + ip_address + "\n" + "*" * 20 + "\n")
                    outfile.write( + "\n" * 2)
                if not res:
                    outfile.write("Configuration NOT found for   " + template + " " + ip_address + "\n" + "*" * 20 + "\n")
                    outfile.write(i + "\n" * 2)

    for i in address_list:
        tx = threading.Thread(target=threaded, args=(i,))
    for i in listx:


for i in dev_list:

    configuration_blocks = i.split("$$")
    # we get our configuration text to perform fuzzy searches later

    configuration_blocks = configuration_blocks[1:]
    #we remove the ip addres list header

    configuration_blocks = [sub.replace("\n", "",1) for sub in configuration_blocks]
    #replacing first occurence of \n in the split

    configuration_blocks = [sub.replace("+", "(.*)?").replace("(())", r"\S+" ).replace("#", ".*") \
			    for sub in configuration_blocks]
    #this is where we pass in fuzzy search characters for regex parsing
    # notice we made this a non-greedy quantifier with  .*?  : $ escape character is equal to 1 or 0 lines of any text
    #whereas the + escape character is equal to 1 or many lines of text

    template ="\w+_template$", i, re.MULTILINE)
    # we use this for device type/template later: multiline option used for surety in case someone mistypes

    template =
    #save string result to a variable to use later

    address_list ="ip_address.*]]", i, re.DOTALL)
    # here we get our netmiko conenction parameters

    updated_address ="\[\[.*]]",, re.DOTALL )
    # we need to use DOTALL to make '.*' match newline '\n', and so the user can format the textfile string on many or 1 line
    #this is so the user can type ip_address list in any format

    updated_address ="]]", "]")
    updated_address= re.sub(r'[\x00-\x1f]', '', updated_address)
    #here we remove control characters in textpad user may have put in when they type: removes tabs, spaces, blank lines

    updated_address=  updated_address.split("],")
    updated_address = [sub.replace("[[","").replace("]", "").replace("[", "") for sub in updated_address]
    #we now have a formatted list

    if template == "ios_template":
        ios_address_list, ios_template, ios_config  = updated_address, template, configuration_blocks
        connection_handler(ios_template, ios_address_list, ios_config, "cisco_ios")

    # if template == "junos_template": junos_address_list, junos_template, junos_config = updated_address, template, \
    if template == "asa_template":
        asa_address_list, asa_template, asa_config = updated_address, template, configuration_blocks
        connection_handler(asa_template, asa_address_list, asa_config, "cisco_asa")

#connection_handler( vendor_address_list, "autodetect")




There is a video link of the code working in a GNS3 environment.

Let me know of your scripting ideas that would help save time, and if you would be comfortable in me writing them, and sharing them here.



1 Reply 1


Hi i added a new program to my Github,


A diagnostics app that gets routing info and hardware info, stores it in a database and generates a report. After configurations it compares to older timestamped files which are stored and returns a report with differences: for example differences in next-hop, outgoing interface, or major hardware changes, or changes in arp table.

It also has a batch update and rollback functionality for devices, which batch updates compares outcomes and has option to batch rollback using scp feature on Cisco and rollback files saved on the device and directory; although i would be careful with this feature, imagine batch updating ospf authentication and causing connectivity loss (as opposed to manually, device by device, for example with redundant links). 


Finally it has a fuzzy search feature as well, for example inputiing [router ospf, redistribute]  will find all ospf with redistribution enabled, or typing in [ip access-list , permit] into the command line will find all acl's that permit default routes. i thought it was an interesting feature to add.


I may decide to make it a web hosted fully gui enabled app at some point, with an integrated config screen for the batch updating. Not sure about the practicality of a batch update feature, when someone could just do this with a script.


here is the link:


Star the repository, or follow my github to support me



Review Cisco Networking for a $25 gift card