Python, with its simplicity and versatility, has emerged as a powerful programming language for infrastructure automation. It is the best option for engineers and administrators to start programming and automate their work. With many useful libraries, Python allows you to quickly implement an infrastructure management strategy tailored to your situation.
This article is an introduction to Paramiko and Netmiko, which are the most popular Python SSH libraries. It should be valuable to both administrators and developers who want to start their automation journey and become aware of the differences between server host automation and network device automation.
Agentless automation with SSH
In agentless automation, automation tasks are performed on remote systems without software agents installed on those systems. Unlike agent-based automation implemented with tools such as Puppet or Chef, agentless automation relies on remote management protocols such as SSH or WinRM to execute commands remotely. Agentless automation is easier to implement because it reuses existing management protocols. It also has lower administrative costs since one, or at most several, central automation agents need to be deployed and managed.
The most popular tool used to establish agentless automation is SSH (Secure Shell). SSH is a protocol for secure system administration, file transfers, and other communication across the Internet or other untrusted networks. It encrypts identities, passwords and transmitted data to prevent eavesdropping and theft. SSH is widely used because it provides security using various authentication and encryption methods and is standardized. Thanks to many existing implementations, including open source, SSH is in widespread use. SSH is used to configure services and retrieve configuration settings and state parameters. It is one of the key components of higher-level automation tools such as Ansible, Napalm, and Nornir.
Paramiko
Paramiko is a handy LGPL-licensed library that implements the SSHv2 protocol. It is written in pure Python, so it can be easily installed and used to connect to any system that provides an SSH service. For these reasons, it has become a first choice if someone wants to play with agentless automation. However, simplicity of use is not everything. Its functionality and implementation are modeled on OpenSSH, the most commonly used SSH protocol implementation. This origin makes Paramiko versatile and very useful in more complex scenarios.
That is possible because it provides full implementation of all internal SSH components, i.e.: transport layer (RFC4252 ), user authentication (RFC4253 ) and connection protocols (RFC4254 ). Additionally, it implements Secure File Transfer Protocol (SFTP) over SSH channels, which can be used to manage remote files and transfer them in both directions.
Paramiko usage example
Let's skip the details of Paramiko’s low-level SSH components and focus on the fact that Paramiko provides a high-level API that simplifies the creation of SSH clients, servers or port forwarders. We can check the use of Paramiko in a simple scenario. We would like to connect to a Linux host and fetch all network interfaces. The use of Paramiko.SSHClient is shown in Listing 1.
import paramiko
ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
key = paramiko.ECDSAKey.from_private_key_file('host_ecdsa_256.key')
ssh.connect('192.168.0.1', username='user', pkey=key)
stdin, stdout, stderr = ssh.exec_command('ip addr show')
output = stdout.read().decode()
ssh.close()
Listing. 1 Example implementation of SSH client using Paramiko
Looking at the code, we can see that once the SSHClient instance is created, a policy is established to warn about unknown host keys. WarningPolicy allows you to seamlessly connect to any new host, while RejectPolicy should be used in production environments. Later, a private ECDSA key is loaded to connect to the host machine (Paramiko can also use RSA, DSA or Ed25519 keys). Finally, a connection to the server is established and the required Linux command is remotely executed on the host. The command result is read from the standard output and decoded as UTF-8. Unfortunately, this is only a raw string of characters, so additional operations must be performed to parse it; e.g. the IP addresses of the interfaces. Extracting the required information from command results is beyond the scope of the Paramiko library.
Executing simple commands in the SSH EXEC channel
The Paramiko usage example in the previous section uses the SSHClient.exec_command method. This method creates an SSH EXEC channel. An EXEC channel accepts a single command or a set of commands, but does so only at the time the channel is created. The SSH server executes the commands received from the channel and returns all generated output when execution is complete.
Linux SSH servers execute commands from the EXEC channel mainly in non-interactive and non-login shells. It is important to know what environment a command is being executed in as it can have a significant impact on the behavior of commands, especially those that are parameterized by environment variables. This may happen because some startup scripts (e.g. ~/.bash_profile) may not execute and environment variables may be set differently. By default, the EXEC channel doesn’t use terminal emulation to avoid commands to do fancy things like adjusting output to terminal width and height, pagination, and coloring. In summary, the EXEC channel is ideal for automating simple commands for which you can collect output.
The SSH SHELL channel for interactive applications
When an interactive application needs to be automated, a different type of SSH channel is required. The recommended channel type is SSH SHELL. Thanks to the SHELL channel, additional input data can be provided to the application, such as additional "yes/no" confirmations, option selections or set values. This channel continuously puts command results into the standard output stream. Linux SSH servers execute commands from the SHELL channel mainly in interactive and login shells. This is the same environment where you log in manually using an SSH terminal client.
In Paramiko, the output of the SHELL channel is exposed in the form of stdin and stderr streams, which require more complex handling, similar to the use of network sockets. All command output becomes unstructured in the output stream, and the automation code must recognize different parts of the results. The time aspects of communication, such as the waiting time for specific information, should also be properly determined.
Why isn’t Paramiko enough for network automation?
Most Linux and BSD systems use OpenSSH as their implementation of the SSH protocol. This implementation runs as a daemon called sshd. Compared to servers, network devices have their own SSH server implementation quirks which causes important differences when establishing a connection. An example of such a dissimilarity is how the user is authenticated. In the case of hosts, mechanisms built into the SSH protocol are always used for this purpose. However, in the case of network devices, there are cases where user authentication takes place in a separate application launched immediately after establishing an SSH session.
The second significant difference between network devices and hosts is the use of specialized CLI shells that allow easy navigation through a large number of available commands. Network equipment manufacturers treat shells as their intellectual property and trademark, and carefully cultivate differences in the behavior of their shells. This causes shells from different manufacturers to behave differently and have different command hierarchies. Therefore, implementing automation for network devices using Paramiko itself is much more complicated than it might initially seem. A few years ago, the user support for Paramiko (aka GitHub issues) was full of problems with people trying to connect to various network devices or execute specific commands. Currently, such unfortunate people are quickly redirected to another library: Netmiko.
Check out our other related content:
- Python Nornir for simplifying network automation: code examples
- Network automation tools comparison in code examples: Terraform, Ansible, and Python SDK
Python Netmiko library to rescue
Netmiko is an open-source, Python MIT-licensed library designed to interact with a wide range of network devices (Cisco, Juniper, Arista, Huawei, and many others ). It hides low-level differences between different vendors and provides a high-level API that exposes common behaviors of many web CLI shells (e.g. the existence of operational/configuration modes) in a consistent way.
Netmiko uses several communication technologies that enable the automation of network devices. The most important of these is SSH, which is supported on Netmiko using Paramiko. SCP is available through the scp library (it is also implemented on top of Paramiko). Netmiko also supports telnet (using telnetlib ) and serial (using pyserial ). Additionally, SNMP (pysnmp ) is used, but only for automatic device type detection, which helps select the appropriate device driver.
Netmiko usage example
Let's demonstrate using Netmiko to connect to a Cisco IOS router to obtain the IP addresses of all interfaces. Please refer to Listing. 2 below.
import netmiko
cisco = {
'device_type': 'cisco_ios',
'host': '192.168.0.10',
'username': 'user',
'use_keys': True,
'key_file': 'cisco_rsa.key',
'disabled_algorithms': {'pubkeys':
['rsa-sha2-512', 'rsa-sha2-256']}
}
with netmiko.ConnectHandler(**cisco) as ssh:
interfaces = ssh.send_command('show ip interface brief',
use_textfsm=True)
ip_addresses = (interface['ipaddr'] for interface in interfaces)
Listing. 2 Example implementation of SSH client using Paramiko
This example uses the netmiko.ConnectHandler factory function. ConnectHandler requires device type information and other access arguments. Using these arguments, ConnectHandler creates a dedicated SSH client for the Cisco IOS router. The appropriate Cisco command is then sent. The use_textfsm argument is set so that send_command returns fully parsed interface information using NTC templates (this is a large collection of TestFSM templates for parsing network device text output). Since the returned output of the command is a list of Python dictionaries describing the attributes of each interface, the user of the Netmiko library can easily filter the required information (i.e. interface IP addresses).
If the type of network device is not known, the Netmiko library user should first try using the netmiko.ssh_autodetect module. It will most likely provide the correct device type.
What problems does Netmiko solve?
A network automation engineer’s work is much easier using Netmiko. The list of the most important Netmiko functionalities is presented in Fig. 5.
The library includes a long list of dedicated SSH clients for many types of network devices. Each Netmiko client implements the necessary quirks enabling successful login to a specific network platform (i.e. non-standard user authentication methods, additional user confirmations immediately after the login, skipping login banners). After logging in, the library user can easily change CLI mode to privileged and configuration mode. Additionally, each dedicated SSH client includes the expected prompts for a specific mode.
Netmiko also provides a number of features required to successfully execute commands. First, device-specific window size settings are applied to created SSH channels and pagination is disabled, so that structured and multiline commands produce clear results. Secondly, Netmiko supports parsing command output using TestFSM , Genie and TTP libraries. Some network devices copy commands to output, but Netmiko can automatically remove them from the generated output. The library also provides useful commands and their options to handle commands that generate additional questions instead of the expected prompts.
Many network devices experience problems with slowdown periods when responding to commands. For this reason, Netmiko implements a hierarchy of delay factors as well as different command timing strategies. Finally, it provides a convenient method for saving/committing a new configuration.
What if performance really matters?
Both Paramiko and Netmiko are implemented in pure Python, so there are situations where their low performance is problematic. This may be important when building automation systems for hundreds or thousands of nodes. In this case, there are alternative open-source Python SSH libraries that could be a better fit:
- parallel-ssh - an asynchronous SSH client library under the LGPL license. It is based on ssh2-python (Cython wrapper around C libssh2 ) and gevent .
- asyncssh - another asynchronous SSH client that is published under the EPL license. It is mostly written in Python using asyncio .
Both libraries excel when used directly to process hundreds of calls in parallel, as documented here . However, you should be prepared for installation issues and problems in successful connections to many network devices.
Here, you can watch a video that provides practical insights and innovative approaches to tackling complex network firewall upgrades by presenting a real-world case study involving the automation of firewall upgrades:
Summary
This article introduced you to the two most popular Python libraries for the SSH protocol. The first is Paramiko, a solid solution that will help you automate your work on servers. With Paramiko, you can automate both simple applications or scripts as well as programs with more interactive behavior. The second library is Netmiko, which is indispensable when you want to remotely manage network devices. It supports many important behaviors of various network equipment manufacturers and provides parsed command results.
However, when performance is a deciding factor, it's worth trying one of the asynchronous SSH implementations such as parallel-ssh or asyncssh.