Search code examples
ansibleansible-2.xjunos-automation

How can I dynamically change the extra vars passed to playbook


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?


Solution

  • 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') }}"