Score:0

Ansible :: Am I not using the `cisco.ios.ios_facts` module?

ca flag

I'm trying to build a simple POC ansible playbook that logs into a Cisco router and pulls the config. About my environment:

  • My Ubuntu is Ubuntu 18.04.5 LTS
  • My Ansible is version ansible 2.10.7 (Python ver 3.6.9)
  • My Ansible-Playbook is version 2.10.7 (Python ver 3.6.9)
  • The router is running Cisco IOS Version 15.4(3)M3 (not IOS XR)

From my research (here and here), I'm pretty certain that I need to use the cisco.ios.ios_facts module. I'm also pretty sure that module is installed on my Ansible server:

me@ubuntu01:~$
me@ubuntu01:~$ ansible-galaxy collection list | grep cisco.ios
cisco.ios                     1.3.0
cisco.iosxr                   1.2.1
me@ubuntu01:~$

Okay, here's my inventory file:

[myrouters]
10.10.10.101

[myrouters:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_ssh_user=user101
ansible_ssh_password=password101
ansible_become=no

Simple enough. I note that when I run ansible-playbook in triple verbose mode (-vvv), the inventory file is successfully parsed. So that's half the battle.

Here's my playbook ("CiscoPlaybook.yml"), which is largely copied from this example:

---
- name: "test baby test"
  hosts: myrouters
  gather_facts: yes

- tasks:
  - name: Gather only the config and default facts
    cisco.ios.ios_facts:
      gather_subset:
      - config

All I want Ansible to do is SSH to the router and grab the device's config. When I run ansible-playbook with this command...

ansible-playbook CiscoPlaybook.yml -i /home/me/inventory.txt -vvv

...I get an enormous amount of output. Like, I said, the inventory file is successfully parsed. But things go off the rails when the playbook goes into action. I'll post the longer error messages below, but this message in the output caught my eye:

<10.10.10.101> EXEC /bin/sh -c '/usr/bin/python && sleep 0'

Okay, does this mean that Ansible SSH'ed to the router and tried to issue the "/bin/sh -c '/usr/bin/python && sleep 0'" command? Why would it do that? I'm instructing it to use the cisco.ios.ios_facts module. I thought that this module told Ansible how to expect and interact with Cisco devices.

If I'm not using the cisco.ios.ios_facts module, can someone point out where I am going wrong?

The entire novel-length output of running the playbook is below, FYI. Thank you.


me@ubuntu01:~$
me@ubuntu01:~$
me@ubuntu01:~$ ansible-playbook CiscoPlaybook.yml -i /home/me/inventory.txt -vvv
ansible-playbook 2.10.7
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/me/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/dist-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.6.9 (default, Jan 26 2021, 15:33:00) [GCC 8.4.0]
Using /etc/ansible/ansible.cfg as config file
host_list declined parsing /home/me/inventory.txt as it did not pass its verify_file() method
script declined parsing /home/me/inventory.txt as it did not pass its verify_file() method
auto declined parsing /home/me/inventory.txt as it did not pass its verify_file() method
yaml declined parsing /home/me/inventory.txt as it did not pass its verify_file() method
Parsed /home/me/inventory.txt inventory source with ini plugin
redirecting (type: action) cisco.ios.ios_facts to cisco.ios.ios
redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
redirecting (type: callback) ansible.builtin.timer to ansible.posix.timer
redirecting (type: callback) ansible.builtin.profile_tasks to ansible.posix.profile_tasks
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: CiscoPlaybook.yml *************************************************************************************************
2 plays in CiscoPlaybook.yml

PLAY [test baby test] *********************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
task path: /data/home/me/CiscoPlaybook.yml:2
Wednesday 12 April 2023  17:04:13 +0000 (0:00:00.076)       0:00:00.076 *******
[WARNING]: Ignoring timeout(10) for cisco.ios.ios_facts
<135.25.133.135> Attempting python interpreter discovery
<135.25.133.135> ESTABLISH LOCAL CONNECTION FOR USER: me
<135.25.133.135> EXEC /bin/sh -c 'echo PLATFORM; uname; echo FOUND; command -v '"'"'/usr/bin/python'"'"'; 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'
<135.25.133.135> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
Using module file /usr/local/lib/python3.6/dist-packages/ansible_collections/cisco/ios/plugins/modules/ios_facts.py
Pipelining is enabled.
<135.25.133.135> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
fatal: [135.25.133.135]: FAILED! => changed=false
  ansible_facts: {}
  failed_modules:
    cisco.ios.ios_facts:
      ansible_facts:
        discovered_interpreter_python: /usr/bin/python
      deprecations:
      - msg: Distribution Ubuntu 18.04 on host 135.25.133.135 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.10/reference_appendices/interpreter_discovery.html for more information
        version: '2.12'
      exception: |-
        WARNING: The below traceback may *not* be related to the actual failure.
          File "/tmp/ansible_cisco.ios.ios_facts_payload_bEKSd8/ansible_cisco.ios.ios_facts_payload.zip/ansible_collections/ansible/netcommon/plugins/module_utils/network/common/network.py", line 251, in get_capabilities
            capabilities = Connection(module._socket_path).get_capabilities()
          File "/tmp/ansible_cisco.ios.ios_facts_payload_bEKSd8/ansible_cisco.ios.ios_facts_payload.zip/ansible/module_utils/connection.py", line 195, in __rpc__
            raise ConnectionError(to_text(msg, errors='surrogate_then_replace'), code=code)
      failed: true
      invocation:
        module_args:
          gather_network_resources: null
          gather_subset:
          - '!config'
          provider: null
      msg: No authentication methods available
  msg: |-
    The following modules failed to execute: cisco.ios.ios_facts

PLAY RECAP ********************************************************************************************************************
135.25.133.135             : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Playbook run took 0 days, 0 hours, 0 minutes, 2 seconds
Wednesday 12 April 2023  17:04:16 +0000 (0:00:02.521)       0:00:02.597 *******
===============================================================================
Gathering Facts --------------------------------------------- 2.52s
/data/home/me/CiscoPlaybook.yml:2 ---------------------------------------------
me@ubuntu01:~$
Ginnungagap avatar
gu flag
Did you try and obfuscate the IPs in your logs?
Pete avatar
ca flag
@Ginnungagap Yes, if only to mask my company's private data. You may notice some discrepancies in the output for that reason. Thanks
Ginnungagap avatar
gu flag
It would help if you were consistent because as it stands, you have an incoherent inventory when compared to your playbook run. Unless the IPs are publicly routable, obfuscation them is of dubious added value, but if you feel the need to do so anyway, at least map each IP to a single obfuscated IP so they remain identifiable within your question.
Pete avatar
ca flag
@Ginnungagap Yeah, that's a good point, I'll work on being better at that in the future. FWIW, there was only ever one router and thus only one IP in my example, so 10.10.10.101 and 133.x.x.x are really the same device. Thanks for the constructive feedback
Score:2
ca flag

Okay, does this mean that Ansible SSH'ed to the router and tried to issue the "/bin/sh -c '/usr/bin/python && sleep 0'" command?

Right, that's the case.

Why would it do that?

There is a difference in between Ansible Facts and ios_facts module – Module to collect facts from remote devices.

In example to quote from How Ansible gather_facts and sets variables:

By default, when Ansible first starts executing a play it will implicitly run the setup module on all the remote hosts involved in the play. This is called the "fact gathering" step, and is controlled by the gather_facts option in the play or the gathering option in ansible.cfg. The facts gathered in this step include things like operating system flavor, release, information about interfaces and disks, and a variety of other host metadata.

Therefore even if

I'm instructing it to use the cisco.ios.ios_facts module. I thought that this module told Ansible how to expect and interact with Cisco devices.

you'll observe the behavior that Ansible try to execute general fact gathering via setup.py first and additionally after the device dedicated fact gathering.

Am I not using the cisco.ios.ios_facts module?

You are, but later in the playbook.

If I'm not using the cisco.ios.ios_facts module, can someone point out where I am going wrong?

So you are currently using both, in your case you'll need to set gather_facts: false to execute the device dedicated module only.

Further Reading

Score:0
cn flag

Set gather_facts to false on a playbook level, other than that your playbook seems okay.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.