ansible-junos-stdlib icon indicating copy to clipboard operation
ansible-junos-stdlib copied to clipboard

juniper.device.config fail silently

Open codot-fr opened this issue 3 years ago • 3 comments

Issue Type

  • Bug Report

Module Name

juniper.device.config

juniper.device collection and Python libraries version

ansible [core 2.11.2]
  config file = /spine/spine/iac/ansible-runner/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110]
  jinja version = 3.0.1
  libyaml = True

amqp==5.0.6
ansible-core==2.11.2
ansible-runner==2.0.1
asgiref==3.4.1
attrs==21.2.0
bcrypt==3.2.0
billiard==3.6.4.0
celery==5.1.2
certifi==2021.5.30
cffi==1.14.6
chardet==4.0.0
click==7.1.2
click-didyoumean==0.0.3
click-plugins==1.1.1
click-repl==0.2.0
colorama==0.4.4
configparser==5.2.0
coverage==5.5
cryptography==3.4.7
Django==3.2.5
django-celery-beat==2.2.1
django-celery-results==2.2.0
django-crispy-forms==1.12.0
django-debug-toolbar==3.2.1
django-extensions==3.1.3
django-guardian==2.4.0
django-guid==3.2.0
django-pgcrypto-fields==2.6.0
django-polymorphic==3.0.0
django-smuggler==1.0.3
django-timezone-field==4.2.1
djangorestframework==3.12.4
djangorestframework-guardian==0.3.0
docutils==0.17.1
elasticsearch==7.13.3
factory-boy==3.2.0
Faker==8.10.1
fluent-logger==0.10.0
fqdn==1.5.1
future==0.18.2
gitdb==4.0.7
GitPython==3.1.24
gunicorn==20.1.0
h11==0.12.0
icdiff==2.0.4
idna==2.10
inflection==0.5.1
iniconfig==1.1.1
Jinja2==3.0.1
jmespath==0.10.0
jsnapy==1.3.6
junos-eznc==2.6.3
jxmlease==1.0.3
kombu==5.1.0
lockfile==0.12.2
lxml==4.6.3
MarkupSafe==2.0.1
mixer==7.2.0
msgpack==1.0.2
ncclient==0.6.9
netaddr==0.8.0
packaging==21.0
paramiko==2.7.2
pexpect==4.8.0
pluggy==0.13.1
prompt-toolkit==3.0.19
psycopg2==2.9.1
ptyprocess==0.7.0
py==1.10.0
pycparser==2.20
PyNaCl==1.4.0
pyparsing==2.4.7
pyserial==3.5
pytest==6.2.4
pytest-celery==0.0.0
pytest-cov==2.12.1
pytest-django==4.4.0
pytest-factoryboy==2.1.0
pytest-mock==3.6.1
python-crontab==2.5.1
python-daemon==2.3.0
python-dateutil==2.8.1
pytz==2021.1
PyYAML==5.4.1
requests==2.25.1
resolvelib==0.5.4
scp==0.13.6
six==1.16.0
smmap==4.0.0
sqlparse==0.4.1
text-unidecode==1.3
toml==0.10.2
transitions==0.8.8
typing-extensions==3.10.0.2
urllib3==1.26.6
uvicorn==0.14.0
vine==5.0.0
wcwidth==0.2.5
xmltodict==0.12.0
yamlordereddictloader==0.4.0
zxcvbn==4.4.28

Collection        Version
----------------- -------
ansible.netcommon 2.5.0
ansible.posix     1.3.0
ansible.utils     2.4.3
community.general 3.4.0
juniper.device    1.0.1

CALLBACKS_ENABLED(/xxx/xxx/iac/ansible-runner/ansible.cfg) = ['profile_tasks']
DEFAULT_JINJA2_NATIVE(/xxx/xxx/iac/ansible-runner/ansible.cfg) = True
HOST_KEY_CHECKING(/xxx/xxx/iac/ansible-runner/ansible.cfg) = False
INJECT_FACTS_AS_VARS(/xxx/xxx/iac/ansible-runner/ansible.cfg) = False

OS / Environment

ex4650-48y-8c 19.3R2-S4.5

Summary

Okay, it's quite tricky. I'm not sure it's related directly to the collection code but it's quite specific to this collection.

For the context:

  • I have a container with Python and ansible installed
  • Ansible playbooks are run through Python ansible-runner via Celery tasks
  • We use PyCharm to run the container and debug Celery tasks
  • Up until now, everything was working fine, we were using the "old" collection (https://galaxy.ansible.com/junipernetworks/junos)
  • We changed to this collection and now the playbook fail silently but only when the docker compose is run through PyCharm

We could not identify any difference between the two environments. But I would like your help to figure out why the module fail silently without any stderr. If we had some kind of error message we could narrow the issue more precisely.

Steps to reproduce

It's quite tricky as the playbook is successful when running the container through docker compose directly but fail when the docker compose is run through PyCharm.

Expected results

Here's a successful run through a direct docker-compose

TASK [Apply config on Junos] ***************************************************
task path: /xxx/xxx/iac/subnets/project/tasks/apply_config.yml:2
Wednesday 22 December 2021  09:48:05 +0100 (0:00:00.255)       0:00:04.820 **** 
<10.111.0.113> ESTABLISH LOCAL CONNECTION FOR USER: root
META: noop
<10.111.0.113> EXEC /bin/sh -c 'echo ~root && sleep 0'
<10.111.0.113> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892 `" && echo ansible-tmp-1640162885.8019924-137-30247405225892="` echo /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892 `" ) && sleep 0'
Including module_utils file ansible/__init__.py
Including module_utils file ansible/module_utils/__init__.py
Including module_utils file ansible/module_utils/basic.py
Including module_utils file ansible/module_utils/_text.py
Including module_utils file ansible/module_utils/common/_collections_compat.py
Including module_utils file ansible/module_utils/common/__init__.py
Including module_utils file ansible/module_utils/common/_json_compat.py
Including module_utils file ansible/module_utils/common/_utils.py
Including module_utils file ansible/module_utils/common/arg_spec.py
Including module_utils file ansible/module_utils/common/file.py
Including module_utils file ansible/module_utils/common/parameters.py
Including module_utils file ansible/module_utils/common/collections.py
Including module_utils file ansible/module_utils/common/process.py
Including module_utils file ansible/module_utils/common/sys_info.py
Including module_utils file ansible/module_utils/common/text/converters.py
Including module_utils file ansible/module_utils/common/text/__init__.py
Including module_utils file ansible/module_utils/common/text/formatters.py
Including module_utils file ansible/module_utils/common/validation.py
Including module_utils file ansible/module_utils/common/warnings.py
Including module_utils file ansible/module_utils/compat/selectors.py
Including module_utils file ansible/module_utils/compat/__init__.py
Including module_utils file ansible/module_utils/compat/_selectors2.py
Including module_utils file ansible/module_utils/compat/selinux.py
Including module_utils file ansible/module_utils/distro/__init__.py
Including module_utils file ansible/module_utils/distro/_distro.py
Including module_utils file ansible/module_utils/errors.py
Including module_utils file ansible/module_utils/parsing/convert_bool.py
Including module_utils file ansible/module_utils/parsing/__init__.py
Including module_utils file ansible/module_utils/pycompat24.py
Including module_utils file ansible/module_utils/six/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/configuration.py
Including module_utils file ansible_collections/__init__.py
Including module_utils file ansible_collections/juniper/__init__.py
Including module_utils file ansible_collections/juniper/device/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/juniper_junos_common.py
Including module_utils file ansible/module_utils/connection.py
Including module_utils file ansible/module_utils/common/json.py
<ex> Attempting python interpreter discovery
<10.111.0.113> EXEC /bin/sh -c 'echo PLATFORM; uname; echo FOUND; command -v '"'"'/usr/bin/python'"'"'; command -v '"'"'python3.9'"'"'; command -v '"'"'python3.8'"'"'; command -v '"'"'python3.7'"'"'; command -v '"'"'python3.6'"'"'; command -v '"'"'python3.5'"'"'; command -v '"'"'python2.7'"'"'; command -v '"'"'python2.6'"'"'; command -v '"'"'/usr/libexec/platform-python'"'"'; command -v '"'"'/usr/bin/python3'"'"'; command -v '"'"'python'"'"'; echo ENDFOUND && sleep 0'
<10.111.0.113> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
Using module file /root/.ansible/collections/ansible_collections/juniper/device/plugins/modules/config.py
<10.111.0.113> PUT /root/.ansible/tmp/ansible-local-281v4h5kp6/tmpcqwi7rim TO /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892/AnsiballZ_config.py
<10.111.0.113> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892/ /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892/AnsiballZ_config.py && sleep 0'
<10.111.0.113> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892/AnsiballZ_config.py && sleep 0'
<10.111.0.113> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1640162885.8019924-137-30247405225892/ > /dev/null 2>&1 && sleep 0'
[DEPRECATION WARNING]: Distribution debian 11 on host ex should use 
/usr/bin/python3, but is using /usr/bin/python for backward compatibility with 
prior Ansible releases. A future Ansible release will default to using the 
discovered platform python for this host. See https://docs.ansible.com/ansible/
2.11/reference_appendices/interpreter_discovery.html for more information. This
 feature will be removed in version 2.12. Deprecation warnings can be disabled 
by setting deprecation_warnings=False in ansible.cfg.
ok: [ex] => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "invocation": {
        "module_args": {
            "attempts": null,
            "baud": null,
            "check": false,
            "check_commit_wait": null,
            "comment": null,
            "commit": null,
            "commit_empty_changes": false,
            "config_mode": "private",
            "confirmed": null,
            "console": null,
            "cs_passwd": null,
            "cs_user": null,
            "dest": null,
            "dest_dir": null,
            "diff": null,
            "diffs_file": null,
            "filter": null,
            "format": null,
            "host": "10.111.0.113",
            "ignore_warning": [
                "statement not found"
            ],
            "level": null,
            "lines": [
                "set vlans 6e55324b-3e4e-4958-a7aa-82339338365b",
                "set vlans 6e55324b-3e4e-4958-a7aa-82339338365b vlan-id 2001",
                "set vlans 6e55324b-3e4e-4958-a7aa-82339338365b description \"Test COD 2\"",
                "set groups 0005c4cf-419f-65a3-2864-9440c924a0b4 interfaces <xe-*> unit 0 family ethernet-switching vlan members 6e55324b-3e4e-4958-a7aa-82339338365b"
            ],
            "load": "merge",
            "logdir": null,
            "logfile": null,
            "mode": null,
            "model": null,
            "namespace": null,
            "options": {},
            "passwd": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "port": 830,
            "remove_ns": null,
            "retrieve": null,
            "return_output": true,
            "rollback": null,
            "src": null,
            "ssh_config": null,
            "ssh_private_key_file": null,
            "template": null,
            "timeout": 10,
            "url": null,
            "user": "antemeta",
            "vars": null
        }
    },
    "msg": "Configuration has been: opened, loaded, diffed, closed."
}

Actual results

Here's a failed silent run through a PyCharm triggered docker-compose

TASK [Apply config on Junos] ***************************************************
task path: /xxx/xxx/iac/subnets/project/tasks/apply_config.yml:2
Wednesday 22 December 2021  09:40:43 +0100 (0:00:01.067)       0:00:12.870 **** 
<10.111.0.113> ESTABLISH LOCAL CONNECTION FOR USER: root
<10.111.0.113> EXEC /bin/sh -c 'echo ~root && sleep 0'
META: noop
<10.111.0.113> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543 `" && echo ansible-tmp-1640162443.8914657-143-37148930553543="` echo /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543 `" ) && sleep 0'
Including module_utils file ansible/__init__.py
Including module_utils file ansible/module_utils/__init__.py
Including module_utils file ansible/module_utils/basic.py
Including module_utils file ansible/module_utils/_text.py
Including module_utils file ansible/module_utils/common/_collections_compat.py
Including module_utils file ansible/module_utils/common/__init__.py
Including module_utils file ansible/module_utils/common/_json_compat.py
Including module_utils file ansible/module_utils/common/_utils.py
Including module_utils file ansible/module_utils/common/arg_spec.py
Including module_utils file ansible/module_utils/common/file.py
Including module_utils file ansible/module_utils/common/parameters.py
Including module_utils file ansible/module_utils/common/collections.py
Including module_utils file ansible/module_utils/common/process.py
Including module_utils file ansible/module_utils/common/sys_info.py
Including module_utils file ansible/module_utils/common/text/converters.py
Including module_utils file ansible/module_utils/common/text/__init__.py
Including module_utils file ansible/module_utils/common/text/formatters.py
Including module_utils file ansible/module_utils/common/validation.py
Including module_utils file ansible/module_utils/common/warnings.py
Including module_utils file ansible/module_utils/compat/selectors.py
Including module_utils file ansible/module_utils/compat/__init__.py
Including module_utils file ansible/module_utils/compat/_selectors2.py
Including module_utils file ansible/module_utils/compat/selinux.py
Including module_utils file ansible/module_utils/distro/__init__.py
Including module_utils file ansible/module_utils/distro/_distro.py
Including module_utils file ansible/module_utils/errors.py
Including module_utils file ansible/module_utils/parsing/convert_bool.py
Including module_utils file ansible/module_utils/parsing/__init__.py
Including module_utils file ansible/module_utils/pycompat24.py
Including module_utils file ansible/module_utils/six/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/configuration.py
Including module_utils file ansible_collections/__init__.py
Including module_utils file ansible_collections/juniper/__init__.py
Including module_utils file ansible_collections/juniper/device/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/__init__.py
Including module_utils file ansible_collections/juniper/device/plugins/module_utils/juniper_junos_common.py
Including module_utils file ansible/module_utils/connection.py
Including module_utils file ansible/module_utils/common/json.py
<ex> Attempting python interpreter discovery
<10.111.0.113> EXEC /bin/sh -c 'echo PLATFORM; uname; echo FOUND; command -v '"'"'/usr/bin/python'"'"'; command -v '"'"'python3.9'"'"'; command -v '"'"'python3.8'"'"'; command -v '"'"'python3.7'"'"'; command -v '"'"'python3.6'"'"'; command -v '"'"'python3.5'"'"'; command -v '"'"'python2.7'"'"'; command -v '"'"'python2.6'"'"'; command -v '"'"'/usr/libexec/platform-python'"'"'; command -v '"'"'/usr/bin/python3'"'"'; command -v '"'"'python'"'"'; echo ENDFOUND && sleep 0'
<10.111.0.113> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
Using module file /root/.ansible/collections/ansible_collections/juniper/device/plugins/modules/config.py
<10.111.0.113> PUT /root/.ansible/tmp/ansible-local-34f1d58xfx/tmp99d45fpk TO /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543/AnsiballZ_config.py
<10.111.0.113> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543/ /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543/AnsiballZ_config.py && sleep 0'
<10.111.0.113> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543/AnsiballZ_config.py && sleep 0'
<10.111.0.113> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1640162443.8914657-143-37148930553543/ > /dev/null 2>&1 && sleep 0'
[DEPRECATION WARNING]: Distribution debian 11 on host ex should use 
/usr/bin/python3, but is using /usr/bin/python for backward compatibility with 
prior Ansible releases. A future Ansible release will default to using the 
discovered platform python for this host. See https://docs.ansible.com/ansible/
2.11/reference_appendices/interpreter_discovery.html for more information. This
 feature will be removed in version 2.12. Deprecation warnings can be disabled 
by setting deprecation_warnings=False in ansible.cfg.
fatal: [ex]: FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "module_stderr": "",
    "module_stdout": "\n{\"msg\": \"Configuration has been: opened, loaded, diffed, closed.\", \"changed\": false, \"failed\": false, \"invocation\": {\"module_args\": {\"lines\": [\"set vlans 6e55324b-3e4e-4958-a7aa-82339338365b\", \"set vlans 6e55324b-3e4e-4958-a7aa-82339338365b vlan-id 2001\", \"set vlans 6e55324b-3e4e-4958-a7aa-82339338365b description \\\"Test COD 2\\\"\", \"set groups 0005c4cf-419f-65a3-2864-9440c924a0b4 interfaces <xe-*> unit 0 family ethernet-switching vlan members 6e55324b-3e4e-4958-a7aa-82339338365b\"], \"config_mode\": \"private\", \"load\": \"merge\", \"ignore_warning\": [\"statement not found\"], \"check\": false, \"host\": \"10.111.0.113\", \"user\": \"antemeta\", \"passwd\": \"VALUE_SPECIFIED_IN_NO_LOG_PARAMETER\", \"timeout\": 10, \"return_output\": true, \"options\": {}, \"commit_empty_changes\": false, \"rollback\": null, \"src\": null, \"template\": null, \"vars\": null, \"url\": null, \"format\": null, \"model\": null, \"remove_ns\": null, \"namespace\": null, \"diff\": null, \"diffs_file\": null, \"dest_dir\": null, \"retrieve\": null, \"filter\": null, \"dest\": null, \"commit\": null, \"confirmed\": null, \"comment\": null, \"check_commit_wait\": null, \"cs_user\": null, \"cs_passwd\": null, \"ssh_private_key_file\": null, \"ssh_config\": null, \"mode\": null, \"console\": null, \"port\": 830, \"baud\": null, \"attempts\": null, \"logfile\": null, \"logdir\": null, \"level\": null}}}\u001b[0m\n\u001b[0m\u001b[0m",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 0
}

codot-fr avatar Dec 22 '21 10:12 codot-fr

I can add that the issue is in the module_stdout string, that fails to parse as a json. Because of the trailing \u001b[0m\n\u001b[0m\u001b[0m.

It seems to be terminal configuration escape codes. But I fail to see why they are there.

dr4Ke avatar Dec 22 '21 14:12 dr4Ke

This issue is similar to https://github.com/ansible/ansible/issues/13895

The solution was to introduce a newline before the json output

         self.do_cleanup_files()
        print('\n%s' % self.jsonify(kwargs))
        sys.exit(0) 

Similarly, we can add a newline after the json output. And the json output is successfully interpreted, the task succeeds.

         self.do_cleanup_files()
        print('\n%s\n' % self.jsonify(kwargs))
        sys.exit(0) 

dr4Ke avatar Dec 22 '21 15:12 dr4Ke

We have nailed the issue to the jsnapy and colorama python modules being installed. As we don't need jsnapy, we removed it. I don't really understand why it is happening, because we don't use this module. But at the end of the juniper.device.config task, the colorama module is called and append these color control characters at the end of stdout.

dr4Ke avatar Dec 23 '21 13:12 dr4Ke