1

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?

Zeitounator
  • 38,476
  • 7
  • 53
  • 66
senthil
  • 85
  • 8
  • Short answer to your title question: you can't. Extra vars have the highest level of precedence and cannot be changed. See https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#understanding-variable-precedence – Zeitounator Jun 25 '21 at 15:30

2 Answers2

0

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

rassakra
  • 1,062
  • 5
  • 9
  • Unless there is something I did not get from your example, this won't work as `upgrade_count` will be set to 1 on each target machine. (or will eventually fail as it does not have a default value). Moreover, registering `upgrade_count` on a task using `set_fact` to set the same `upgrade_count` var is rather awkward... – Zeitounator Jun 25 '21 at 15:43
0

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') }}"
    
Zeitounator
  • 38,476
  • 7
  • 53
  • 66
  • I am getting below error after trying this – senthil Jun 25 '21 at 16:53
  • I am getting below error fatal: [pakzha991]: FAILED! => {"msg": "An unhandled exception occurred while templating 'We processed {{ num_host }} hosts\n{{ upgraded_hosts }} were upgraded\n{{ untouched_hosts }} were left alone'. Error was a , original message: An unhandled exception occurred while templating '{{ processed_hosts | selectattr('value._OS_upgraded') | count }}'. Error was a , – senthil Jun 25 '21 at 17:03
  • I have included the set_fact inside my task file and added the info in play file – senthil Jun 25 '21 at 17:04
  • 1) please delete your partial comments when making mistakes. 2) avoid putting large portion of codes in comments: edit your question instead 3) I suspect you are using ansible < 2.10, I made some changes that will make the code compatible with all versions. – Zeitounator Jun 25 '21 at 17:17
  • i have debugged to print the 'processed_hosts' alone in msg the value is "msg": "Hello world!", not sure what is happening – senthil Jun 25 '21 at 17:29
  • You have a typo somewhere. "hello world" is the default debug message when none is provided. – Zeitounator Jun 25 '21 at 17:36
  • its working now, thank you!! but the output is printed twice, any other way to restrict the output for once ok: [site1] => { "msg": [ "We processed 2 hosts", "2 were upgraded", "0 were left alone" ] } ok: [site2] => { "msg": [ "We processed 2 hosts", "2 were upgraded", "0 were left alone" ] } – senthil Jun 25 '21 at 19:19
  • I figured out by setting run_once: true. Kudos!! – senthil Jun 25 '21 at 19:39
  • A better way IMO is to create that reporting task in a second play targeted to localhost only – Zeitounator Jun 26 '21 at 08:59