How to run plays with conditionals based on network os

Hello,

I created a playbook that has 3 plays. I want to run the playbook against an inventory file containing a mix of EOS, IOS, and NXOS-based devices. The plays are just gathering facts for the particular OS. I’m using a debug to print the model and version. Each play has a when statement that looks at ansible_network_os. The issue is that I can only get the model and version of the very last play. The other two plays produce the following error:

FAILED! => {"msg": "The task includes an option with an undefined variable.. 'dict object' has no attribute 'ansible_facts'\n\nThe error appears to be in '/home/corpaldorf/ansible/network/playbooks/gather-os-version.yml': line 27, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Display results\n ^ here\n"}

If I change the order of the plays, the last one always works. Here is the playbook:

- name: Model and OS Version
  hosts: all
  gather_facts: true
  tasks:
    - name: Model/Version - IOS
      cisco.ios.ios_facts:
        gather_subset: all
      register: facts
      when:
        - ansible_network_os == "cisco.ios.ios"
  
    - name: Model/Version - EOS
      arista.eos.eos_facts:
        gather_subset: all
      register: facts
      when:
        - ansible_network_os == "arista.eos.eos"

    - name: Model/Version - NXOS
      cisco.nxos.nxos_facts:
        gather_subset: all
      register: facts
      when:
        - ansible_network_os == "cisco.nxos.nxos"

    - name: Display results
      ansible.builtin.debug:
        msg:
          - "Device Model: {{ facts.ansible_facts.ansible_net_model }}"
          - "Device OS Version: {{ facts.ansible_facts.ansible_net_version }}"

What could I be doing wrong ? I’m pretty sure I’m doing something wrong :wink:

I’m just doing this as a learning exercise, so I’m 100% sure there are better ways to do this. My goal is to have this playbook dump the results into a CSV.

The reason this only works for the last task is because register still catches the status of a skipped task.

And you don’t need to register a variable as ansible_facts is directly accessible:

   - name: Model/Version - NXOS
      cisco.nxos.nxos_facts:
        gather_subset: all
      when:
        - ansible_network_os == "cisco.nxos.nxos"

    - name: Display results
      ansible.builtin.debug:
        msg:
          - "Device Model: {{ ansible_facts.ansible_net_model }}"
          - "Device OS Version: {{ ansible_facts.ansible_net_version }}"

Thanks,

Assuming I updated the playbook correctly, I now receive the following error for all the devices:

fatal: [west-01-sp04]: FAILED! => {"msg": "The task includes an option with an undefined variable.. 'dict object' has no attribute 'ansible_net_model'\n\nThe error appears to be in '/home/XXXX/ansible/network/playbooks/gather-os-version.yml': line 24, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Display results\n      ^ here\n"}

Here is the complete playbook:

- name: Model and OS Version
  hosts: all
  gather_facts: true
  tasks:
    - name: Model/Version - IOS
      cisco.ios.ios_facts:
        gather_subset: all
      when:
        - ansible_network_os == "cisco.ios.ios"

    - name: Model/Version - NXOS
      cisco.nxos.nxos_facts:
        gather_subset: all
      when:
        - ansible_network_os == "cisco.nxos.nxos"

    - name: Model/Version - EOS
      arista.eos.eos_facts:
        gather_subset: all
      when:
        - ansible_network_os == "arista.eos.eos"

    - name: Display results
      ansible.builtin.debug:
        msg:
          - "Device Model: {{ ansible_facts.ansible_net_model }}"
          - "Device OS Version: {{ ansible_facts.ansible_net_version }}"

I ended up changing the “Display results” play to the following:

    - name: Display results
      ansible.builtin.debug:
        msg:
          - "Device Model: {{ ansible_facts.net_model }}"
          - "Device OS Version: {{ ansible_facts.net_version }}"

This worked, I’m just not sure why that worked.

1 Like

ansible_facts is a dictionary containing all the facts gathered by ansible and this dictionary does not contain the element ansible_net_model.

The ansible_net_model variable is a shorthand for ansible_facts.net_model, so you can use whichever you prefer.

A will also suggest that you only gather the facts that you actually need, as fact gathering is resource intensive. ansible_facts.net_mode and ansible_facts.net_version are always returned, so gather_subset: min should be enough. However, in this case you can actually reduce the playbook to the following, as gather_facts: true will take care of the OS related logic:

- name: Model and OS Version
  hosts: all
  gather_facts: true
  tasks:

    - name: Display results
      ansible.builtin.debug:
        msg:
          - "Device Model: {{ ansible_facts.net_model }}"
          - "Device OS Version: {{ ansible_facts.net_version }}"

Note that you could be best off setting this in ansible.cfg:

[defaults]
inject_facts_as_vars = False

For security and future compatibility reasons:

A new configuration variable, inject_facts_as_vars, has been added to ansible.cfg. Its default setting, ‘True’, keeps the 2.4 behavior of facts variables being set in the old ansible_* locations (while also writing them to the new namespace). This variable is expected to be set to ‘False’ in a future release. When inject_facts_as_vars is set to False, you must refer to ansible_facts through the new ansible_facts.* namespace.

And only using the ansible_facts.net_model variable not ansible_net_model.

2 Likes

You can just use the built in gather_facts (which is the default fact gathering now) and avoid all these issues. It is smart by default and knows about many (but not all) network_os, in this case ios, eos and nxos are covered and it would just apply the right module for the right OS.

For those not covered in the defaults you can use ansible_facts_modules at the group or host level to define which fact modules to use for that group/host.

3 Likes