I'm going to attempt to lay out a set of useful Python scripts that allow for easier management of mid-to-large networks. I'll be using Netmiko (the relative of Paramiko) which is an SSH session tool. In this case Netmiko actually does let you create and manage a SSH session -- something Paramiko doesn't really do in any practical sense.
Warning: If you're into just seeing how someone hacks together solutions for collecting data and automating commands you're in luck. Also, I'm not promising what I do here is always an optimal solution! I'm throwing this out for learning purposes -- even if I try to make the code as complete as possible in a reasonable amount of time. So disclaimer: This isn't a package solution for production! You can make it into one, though, and hopefully these tutorials will help.
There are lots of reasons to automate network tasks and the most important one is nobody can reasonably configure dozens or hundreds of switches without automation. Even so there are times when such horrible things must happen -- but let's keep them to a minimum, OK?
The other next most important reason is that analyzing information about your network requires you to not just look at one switch, router or firewall but to analyze the data across many (or all) of them. Automation allows you to gather data across all devices and create reports or even help you trigger actions.
Focus: The articles that I'm about to start producing will be focused entirely on Juniper switches. However, the fundamentals are easily ported to other device brands like Cisco. The analysis functions I will show, however, are structured around how Juniper reports information and how it's configuration process works.
Part I: The Essentials
The first thing we need is a quick framework to connect to switches and send/receive data. The following class we'll be using will be saved into it's own file named NetworkAutomation.py:
'''NetworkAutomation.py
'''
from netmiko import ConnectHandler
import base64
import pprint
class NetworkAutomation:
# Constructor
def __init__(self,p):
self.username = p["username"]
self.pwd_mask = p["pwd_mask"]
#Utility
def copyArray(self,array):
new_array = []
for item in array:
new_array.append(item.strip())
return new_array
def QueryHandler(self,p):
pp = pprint.PrettyPrinter(indent=4)
commands_type = p["cmd_type"]
show_output = p["show_output"]
switch = p["switch"]
switch_manifest = {
'device_type': p["switch_type"],
'ip': switch,
'username': self.username,
'password': str(str(base64.b64decode(self.pwd_mask))[2:-1]),
'verbose': False
}
command_type_output = {}
with ConnectHandler(**switch_manifest) as net_connect:
for cmd_type in commands_type.keys():
command = commands_type[cmd_type]
if (show_output):
print("%s\t%s" % (cmd_type.upper(),command))
results = net_connect.send_command(command)
results_split = self.copyArray( results.split('\n') )
command_type_output[cmd_type] = results_split
if (show_output):
pp.pprint(results_split)
return command_type_output
The above class gives us everything we need to get started for the framework. I've put some hooks in to let us print debug output. Well, it has almost everything... Here's a launcher subroutine that completes the template we will use to call this class. As we move forward this template will expand. This bit of code is in a file named automationtest.py:
from NetworkAutomation import NetworkAutomation
def StartProcess():
connect_p = {"username":"useraccess","pwd_mask":"Hgj3Keff9="}
bot = NetworkAutomation(connect_p)
command_types = {
'result_config' : "show configuration|display set",
'result_lldp_neighbors' : "show lldp neighbors",
'result_vlans' : "show vlans"
}
command_p = {
"cmd_type" : command_types,
"show_output" : False,
"switch" : "10.1.40.2",
"switch_type" : "juniper"
}
results = bot.QueryHandler(command_p)
We can see several things about the above use template that let us know how easy it is to use the class. First is we send just a username and password (which is encoded) to the class constructor (bot=NetworkAutomation(connect_p)). Later we can send a few additional flags for debugging and specific settings -- but really, this is the only constructor I really ever use in production.
The second is we create a dictionary of commands to send -- these are in the form of "command label" : "actual command". This lets us send a large number of commands relevant to whatever it is we want to do. The label bit allows us to parse our results so we know how to analyze the data and subsequently take action.
The third bit is the parameter wrapper that allows us to send the rest of the parameters to the class. It wraps the commands we want plus whether to show debug output, the switch (or other network device) we are connecting to and the type of switch that we're connecting to.
In the next post we'll take a look at parsing the actual results. I have a huge amount of actual production and development code to sanitize to make this all easy to understand.
No comments:
Post a Comment