ansible 2.21 compatibility issues for paramiko

Hi,

I’m maintaining an Ansible collection that currently supports multiple ansible-core versions.

Our module utility currently imports Paramiko as:

from ansible.module_utils.compat.paramiko import paramiko

This works with ansible-core 2.20 and earlier, but with ansible-core 2.21 we get:

ModuleNotFoundError: No module named 'ansible.module_utils.compat.paramiko'

I understand that ansible.module_utils.compat.paramiko was removed in ansible-core 2.21, and the recommended approach for 2.21+ is:

import paramiko

Our collection already has Paramiko listed in requirements.txt:

cryptography
paramiko
pexpect

My question is:

What is the recommended way for an Ansible collection to support both ansible-core 2.20 and earlier, as well as 2.21 and later?

Would the recommended implementation simply be:

import paramiko

and make Paramiko an explicit dependency of the collection?

Or should the collection use a compatibility pattern such as:

try:
    import paramiko
except ImportError:
    from ansible.module_utils.compat.paramiko import paramiko

The goal is to maintain a single codebase that works across the supported ansible-core versions without depending on an Ansible internal/removed module.

I also tested this with:

ansible-test sanity --test import --python 3.12

After changing to:

import paramiko

the error changed to:

ModuleNotFoundError: No module named 'paramiko'

even though Paramiko is installed in my development virtual environment:

paramiko==5.0.0

So I would also appreciate clarification on the recommended way to make a collection’s external Python dependency available to the ansible-test sanity import environment.

Thanks!

That’s the common way to handle such situations (you should wrap import paramiko in try: ... except ImportError: though, that’s what the module utils did as well). The paramiko wrapper in ansible.module_utils.compat didn’t do much anything, except disabling some warnings: ansible/lib/ansible/module_utils/compat/paramiko.py at stable-2.20 · ansible/ansible · GitHub

I would probably remove the import for ansible.module_utils.compat.paramiko completely and copy the suppression rules to your collection, so you have a single way how paramiko is imported.

When you say “copy the suppression rules to your collection”, could you point me to the specific Ansible suppression rule/configuration you’re referring to, and where it should be placed in the collection? How would it then work for ansible 2.20 and lower (many customers may be on that level).

The warnings that are suppressed are described here ansible/lib/ansible/module_utils/compat/paramiko.py at stable-2.20 · ansible/ansible · GitHub.

So you could replace that import in a backwards compatible way with something like this:
import warnings


PARAMIKO_IMPORT_ERR = None

try:
    with warnings.catch_warnings():
        # Blowfish has been moved, but the deprecated import is used by paramiko versions older than 2.9.5.
        # See: https://github.com/paramiko/paramiko/pull/2039
        warnings.filterwarnings('ignore', message='Blowfish has been ', category=UserWarning)
        # TripleDES has been moved, but the deprecated import is used by paramiko versions older than 3.3.2 and 3.4.1.
        # See: https://github.com/paramiko/paramiko/pull/2421
        warnings.filterwarnings('ignore', message='TripleDES has been ', category=UserWarning)
        import paramiko 
# paramiko and gssapi are incompatible and raise AttributeError not ImportError
# When running in FIPS mode, cryptography raises InternalError
# https://bugzilla.redhat.com/show_bug.cgi?id=1778939
except Exception as err:
    paramiko = None  # type: ignore[no-redef]
    PARAMIKO_IMPORT_ERR = err
1 Like