How can I dynamically change the extra vars passed to playbook

1.7k views Asked by At

I am trying to install a software on a set of hosts based on a condition. After executing the play I want to count on how many hosts the installation happened and how many were skipped.

I have tried declaring a variable inside the play before calling the task like below but the variable is overwritten for every host so I am not able to get the actual count: its always 1 or 0

Play:

- name: Install Junos OS
  hosts: reachable_a_hosts
  connection: local
  gather_facts: no
  roles:
    - Juniper.junos
  vars:
    credentials:
      host: "{{ loopback_v4 }}"
      username: "username"
    
      port: "{{ port }}"
      timeout: 60
  tasks:
  - name: Extra variable test
      debug:
        msg: "upgrade count {{ upgrade_count }}"
  - name: OS Install
    import_role:
      name: os_install
      tasks_from: change__os
  - name: debug
    debug:
      msg: "upgrade count {{ upgrade_count }}"

Task:

---
- name: Increment installation count
  set_fact:
    upgrade_count : ( {{ upgrade_count|int + 1|int }} )
  when: switch_os_version != OS_version

output:

TASK [debug] *************************************
ok: [site1] => {
    "msg": "upgrade count (1)"
}
ok: [site2] => {
    "msg": "upgrade count (1)"
}

So I passed the variable as command line using -e. Now the output is always 0, no matter whether condition satisfy or not

updated output

TASK [os_install : Increment installation count] *********************
ok: [site1]
ok: [site2]


TASK [debug] *************************************
ok: [site1] => {
    "msg": "upgrade count 0"
}
ok: [site2] => {
    "msg": "upgrade count 0"
}

Can someone help me to make the variable behave like global so that I can get the actual count?

2

There are 2 answers

9
Zeitounator On BEST ANSWER

As you found out, set_fact is.... setting facts i.e. setting value for vars which are specific to the current targeted host in the play loop. Moreover, as reported in my comment, extra vars cannot be changed during a play because they have the highest level of precedence. None of these will fit for a counter as you are trying to do.

Meanwhile, there are several ways to implement your requirement. An easy one IMO.

  1. choose a var that you know does not exists on the host. For the example I'll choose _OS_upgraded
  2. set this var to true on each machine being installed/upgraded, false overwise. You can drop your when clause and set directly the result of your test.
    - name: Set info about upgrade.
      set_fact:
        _OS_upgraded: "{{ switch_os_version != OS_version }}"
    
  3. Once done you can easily count the hosts using the magic variable hostvars and some filters
    - name: Give some info about the tasks
      vars:
        processed_hosts: "{{ hostvars | dict2items | selectattr('value._OS_upgraded', 'defined') | list }}"
        num_host: "{{ processed_hosts | count }}"
        upgraded_hosts: "{{ processed_hosts | selectattr('value._OS_upgraded') | list | count }}"
        untouched_hosts: "{{ processed_hosts | rejectattr('value._OS_upgraded') | list | count }}"
        message: |-
          We processed {{ num_host }} hosts
          {{ upgraded_hosts }} were upgraded
          {{ untouched_hosts }} were left alone
      debug:
        msg: "{{ message.split('\n') }}"
    
1
rassakra On

To resolve this issue you can use ansible register ,Ansible registers are used when you want to capture the output of a task to a variable. You can then use the value of these registers for different scenarios like a conditional statement, logging etc. Example:

---
- name: Increment installation count
  set_fact:
    upgrade_count : ( {{ upgrade_count|int + 1|int }} )
  register: upgrade_count # add this line
  when: switch_os_version != OS_version

and the Playbook :

- name: Install Junos OS
  hosts: reachable_a_hosts
  connection: local
  gather_facts: no
  roles:
    - Juniper.junos
  vars:
    credentials:
      host: "{{ loopback_v4 }}"
      username: "username"
    
      port: "{{ port }}"
      timeout: 60
  tasks:
  - name: Extra variable test
      debug:
        msg: "upgrade count {{ upgrade_count }}"
  - name: OS Install
    import_role:
      name: os_install
      tasks_from: change__os
  - name: debug
    debug:
      msg: "upgrade count {{ upgrade_count }}"

For more information visit this example