libssh blog

With the recent release of the ansible.netcommon Collection version 3.0.0, we have promoted two features as standard: libssh transport and import_modules. These features provide increased performance and resiliency for your network automation. These two features, which have been available since July 2020 (libssh) and March 2021 (import_modules), are now turned on by default for playbooks running the latest version of the netcommon Collection, so let's take a look at what makes these changes so exciting!

The road to libssh

Libssh support was formally announced in November 2020 for FIPS mode compatibility and speed. This blog goes into great detail about why we started this change, and it's worth a read if you want to know more about how we use paramiko or libssh in the network_cli connection plugin. I'm going to try not to rehash everything from that post, but I do want to take a little time to revisit security and speed to show what libssh brings to the experience of using ansible with network devices.

FIPS Mode

One of the earliest issues we identified with paramiko, our earlier SSH transport plugin, is that it was not FIPS 140 compliant. This meant that environments that mandate FIPS mode to be enabled cannot use paramiko as their SSH transport mechanism. As of release 0.9.0, libssh has achieved FIPS 140-2 compatibility by offloading its cryptographic implementations to OpenSSL, which is a FIPS 140-2 validated module on Red Hat Enterprise Linux 8.

We can use libssh the same way we have with paramiko, but now we have the benefit of doing so with a library that has been verified and uses the same cryptographic implementations as the rest of the system.

Speed

One of the more common complaints about paramiko is its speed. Paramiko is a pure-python SSH implementation, so it can be used in places that the OpenSSH binary cannot, and in the case of network modules we use it to get a finer control over the SSH transport, reading from and writing to the channel directly. This means we can react to output from the device as it happens.

While this is amazing, it is not the speediest of implementations. With everything save the cryptography dependency implemented in plain Python, the performance leaves much to be desired. The libssh plugin, on the other hand, uses the libssh C library to do almost all of the work. We can spend as little time and energy as possible translating the Python objects to C and bundling the response back along with any error handling that needs to be done, and rely on the libssh library to do all the work of establishing connections and exchanging data.

Some notes

While the default action is indeed to use libssh as the SSH implementation, it is not a completely automatic transition in all cases. Before you can use libssh in your playbooks, you still need to ensure that the ansible-pylibssh wrapper is installed. If it is not installed, a warning will appear like this:

[WARNING]: ansible-pylibssh not installed, falling back to paramiko
You can then install the latest version of the ansible-pylibssh library from PyPI with the following command:
pip install ansible-pylibssh

The versions on PyPI are precompiled wheels that include the libssh library, and is available for a variety of hardware platforms including x86_64, aarch64, ppc64le, and others.

On the importance of import_modules

In my opinion, the subject of import_modules hasn’t been given much attention before now. What it does is reasonably straightforward: import_modules provides direct module execution on the control node. This has an impressive impact on the performance of modules using persistent connections. 

In normal playbook execution, Ansible collects all the modules and plugins it will need to run, zips them up into a single file, sends that file over the network, then the remote host can unzip them and run the tasks on the remote host. Network modules do not run on the remote host, but they still go through that process before being executed... the only difference is that the remote host is the control node! The import_modules option skips the compress-copy-decompress steps and instead runs the modules in the same process as the action plugin. Since we already have all the files we need to run the task, this skips the time associated with relatively slow writes to the disk before the module can run.

Unlike with libssh, there's nothing to set or configure. Playbooks running with the latest version of ansible.netcommon will automatically start using this feature unless it has been explicitly turned off or is running on Python 2.7 or in async mode, in which case it will automatically be disabled.

What can I do next?

Whether you are beginning your automation journey or are a seasoned veteran, there are a variety of resources to enhance your automation knowledge: