Finally, we've arrived at an exciting part of our journey, working directly with Network Devices. In this section, we'll dive into Netmiko, the most popular library for interacting with network devices via Python. Netmiko simplifies the process of connecting to, managing, and automating network devices from various manufacturers.
Throughout this part of the course, we will explore what Netmiko is, how to install it, and how to use it. You'll learn through practical examples how to leverage Netmiko to send commands to your devices, retrieve outputs, and automate routine network tasks. This will not only boost your efficiency but also open up new possibilities for network management and automation.
Installation and Basic Use of Netmiko
In the previous section, we discussed installing external modules so, installing netmiko is as simple as running pip install netmiko
. Once installed, you're ready to start scripting and interacting with your network devices. Below is a basic example that uses Netmiko to SSH into a Cisco router and execute a single show command.
from netmiko import ConnectHandler
connection = ConnectHandler(host='10.10.20.17', port='22', username='cisco', password='cisco123', device_type='cisco_ios')
output = connection.send_command('show ip interface brief')
print(output)
print('Closing Connection')
connection.disconnect()
- ConnectHandler - This is a part of Netmiko and acts as a bridge between your script and the network device. It takes several parameters to establish the connection.
host
- The IP address of the device you want to connect to.port
- The port number for the SSH connection (22 is the default for SSH).username
&password
- The credentials required to access the device.device_type
- Specifies the type of device (e.g.,cisco_ios
for Cisco IOS devices). Netmiko uses this to understand how to interact with the device correctly.
- send_command - Once the connection is established,
send_command
method is used to send a command to the device. Here, we're sending'show ip interface brief'
- Closing the Connection - It's important to close the connection after you're done with it.
connection.disconnect()
ensures the session is properly terminated.
Here is the output when you run the script
C:\Users\suresh\Projects\>python netmiko_01.py
Interface IP-Address OK? Method Status Protocol
GigabitEthernet0/0 10.10.20.17 YES manual up up
GigabitEthernet0/1 unassigned YES unset administratively down down
GigabitEthernet0/2 unassigned YES unset administratively down down
GigabitEthernet0/3 unassigned YES unset administratively down down
Closing Connection
What Exactly is ConnectHandler?
Though classes and object-oriented programming (OOP) are beyond this section's scope, it's helpful to know that ConnectHandler
is a class. In simple terms, a class in programming defines a blueprint for creating objects. An object, such as connection
in our script, is an instance of a class and can perform the actions (methods) defined in the class, like send_command
or disconnect
. Think of the class as a template and objects as specific instances that follow that template, each with its own data and behaviour.
Connecting Using a Dictionary
If you recall, I emphasized how crucial lists and dictionaries are as data types in Python. Now, we're going to see dictionaries in action and understand their value in our scripts.
In this second example with Netmiko, we've used a dictionary, cisco_01
, to store connection details for a Cisco device. This method offers a clear advantage, simplicity and organization. Instead of passing connection parameters directly into the ConnectHandler
method call, we group them in a dictionary.
from netmiko import ConnectHandler
cisco_01 = {
"device_type": "cisco_ios",
"host": "10.10.20.17",
"username": "cisco",
"password": "cisco123"
}
connection = ConnectHandler(**cisco_01) # unpacking the dictionary
output = connection.send_command('show interface desc')
print(output)
print('Closing Connection')
connection.disconnect()
Using the dictionary and unpacking it with **cisco_01
when calling ConnectHandler
is an elegant way to pass multiple keyword arguments.
Sending Multiple Commands
In this section, we'll explore how to send multiple commands to a network device using Netmiko. This process highlights the practical use of lists and for loops, powerful tools we've learnt earlier.
from netmiko import ConnectHandler
cisco_01 = {
"device_type": "cisco_ios",
"host": "10.10.20.17",
"username": "cisco",
"password": "cisco123",
"secret": "cisco123" # Enable password
}
connection = ConnectHandler(**cisco_01)
connection.enable() # Go to Priv EXEC mode
show_commands = ['show ip route', 'show interface desc', 'show clock'] # List of commands to send
for command in show_commands: # for loop
print(f'\n *** Sending { command} *** \n')
output = connection.send_command(command)
print(output)
connection.disconnect()
- We start by importing
ConnectHandler
from Netmiko and defining a dictionary (cisco_01
) with the necessary details to connect to our Cisco device. - Using the
ConnectHandler
and the unpackedcisco_01
dictionary, we establish a connection to the device. - We then enter privileged EXEC mode using
connection.enable()
. - The commands we intend to send are stored in a list named
show_commands
. This is where lists come in handy, allowing us to organize multiple commands easily. - A for loop iterates through each command in the
show_commands
list. For each command,- Print a message indicating which command is being sent.
- Use
connection.send_command(command)
to send the command to the device and store the output. - Print the output of the command.
- Finally, we ensure to properly disconnect from the device using
connection.disconnect()
.
#output
*** Sending show ip route ***
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
a - application route
+ - replicated route, % - next hop override
Gateway of last resort is not set
10.0.0.0/8 is variably subnetted, 2 subnets, 2 masks
C 10.10.0.0/16 is directly connected, GigabitEthernet0/0
L 10.10.20.17/32 is directly connected, GigabitEthernet0/0
*** Sending show interface desc ***
Interface Status Protocol Description
Gi0/0 up up MANAGEMENT
Gi0/1 admin down down
Gi0/2 admin down down
Gi0/3 admin down down
*** Sending show clock ***
*09:48:45.139 UTC Wed Apr 13 2022
Securing Passwords with Getpass
So far in our examples, we've stored passwords directly in our scripts as plain text, which is not a secure practice, especially in production environments. To enhance security, Python provides a helpful module named getpass
, which allows for more secure password handling by prompting the user to enter their password at runtime without displaying it on the screen.
getpass
works by presenting a password prompt to the user and then reading the input without echoing it back to the console. This means that passwords are not visible to bystanders, nor are they stored in the script.
from netmiko import ConnectHandler
import getpass
passwd = getpass.getpass('Please enter the password: ') # Reads the output from the user and save it as a string
cisco_01 = {
"device_type": "cisco_ios",
"host": "10.10.20.17",
"username": "cisco",
"password": passwd, # Log in password from getpass
"secret": passwd # Enable password from getpass
}
connection = ConnectHandler(**cisco_01)
connection.enable() # Go to Priv EXEC mode
output = connection.send_command('show interface desc')
print(output)
connection.disconnect()
In this example, when the script is run, getpass.getpass('Please enter the password: ')
displays the prompt "Please enter the password: ". The user's input is then stored in the passwd
variable without showing any characters the user types. This password is used to log into the device and for entering privileged EXEC mode. The rest of the script functions as before, establishing a connection to the network device, executing a command, and then disconnecting.
Sending Configuration Command
First, we're starting with the basics by sending a single configuration command using Netmiko. To keep things simple, we'll configure an NTP server on a device. The command we use for this is ntp server x.x.x.x
, where x.x.x.x
is the IP address of the NTP server. Here's a quick run-through of how to do this with Netmiko.
from netmiko import ConnectHandler
username = 'admin'
password = 'Cisco123'
device = {
"device_type": "cisco_ios",
"host": '10.10.10.10',
"username": username,
"password": password,
"secret": password
}
commands = ['ntp server 1.1.1.1']
connection = ConnectHandler(**device)
output = connection.send_config_set(commands)
print(output)
connection.disconnect()
Here, the first part of the code remains the same but the difference is that we specify the configuration command we want to send to the device in a list. Here, our command is ['ntp server 1.1.1.1']
. With the connection established, we send our configuration command using the send_config_set
method. This method takes our list of commands and applies them to the device.
Here is the output where you can see the send_config_set
method automatically enters configure terminal mode, execute the command and then exits from the config mode.
configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
c9300(config)#ntp server 1.1.1.1
c9300(config)#end
c9300#
You can also send multiple commands by adding the commands to the list as shown below.
from netmiko import ConnectHandler
username = 'admin'
password = 'Cisco123'
device = {
"device_type": "cisco_ios",
"host": '10.10.10.10',
"username": username,
"password": password,
"secret": password
}
commands = ['no ntp server 1.1.1.1', 'ntp server 8.8.8.8', 'ntp server 8.8.4.4']
connection = ConnectHandler(**device)
output = connection.send_config_set(commands)
print(output)
connection.disconnect()
configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
c9300(config)#no ntp server 1.1.1.1
c9300(config)#ntp server 8.8.8.8
c9300(config)#ntp server 8.8.4.4
c9300(config)#end
c9300#