鱼鱼鱼三条鱼
鱼鱼鱼三条鱼

Reputation: 69

How to save the return values of different hosts with separate variables


This is my playbook:

---

- hosts: server 
  gather_facts: false
  tasks:
  - name: 
    shell: ping -c 1 -W 1 <ip_addr>
    register: shell_result_var
  - name:
    set_fact:
      time_fact: "{{ ((shell_result_var['stdout_lines'][1] | split('='))[3] | split(' '))[0] }}"

execute ansible-playbook playbook.yml -vvv

TASK [set_fact] **************************************************************************************************************************************************************
task path: /home/playbook.yml:9
ok: [*.*.*.*] => {"ansible_facts": {"time_fact": "56.9"}, "changed": false}
ok: [*.*.*.*] => {"ansible_facts": {"time_fact": "32.1"}, "changed": false}
META: ran handlers
META: ran handlers

I want to save these two strings("56.9" and "32.1" ) in different variables and convert them into numbers to calculate them

Upvotes: 1

Views: 1864

Answers (3)

PixelTutorials
PixelTutorials

Reputation: 11

You can access the variables of each host using the special variable hostvars. Using, for example, run_once you can then run the "calculation operations" that you want a single time on the ansible provisioner.

- hosts: server
  name: https://stackoverflow.com/q/70506128/13953427
  become: no
  gather_facts: false
  
  tasks:
    - name: Test reachability to 1.1.1.1
      ansible.builtin.shell: ping -c 1 -W 1 1.1.1.1 | grep --only-matching "time=.*"
      register: ping_result

    - name: extract time from ping output
      ansible.builtin.set_fact:
        ping_number: "{{ ping_result.stdout | regex_replace(' ms','') | regex_replace('time=','') | float }}"

    - name: debug ping time of each host
      ansible.builtin.debug:
        var: ping_number

    - name: set total ping time once for all hosts
      ansible.builtin.set_fact:
        total: "{{ total | default(0.0) | float + hostvars[item].ping_number | float  }}"
      loop: "{{ ansible_play_hosts }}"
      run_once: true

    - name: set average ping time once for all hosts
      ansible.builtin.set_fact:
        average_ping_time: "{{ total|float / ansible_play_hosts|length }}"
      run_once: true

    - name: debug total ping time
      ansible.builtin.debug:
        var: total
      run_once: true

    - name: debug average ping time
      ansible.builtin.debug:
        var: average_ping_time
      run_once: true
PLAY [https://stackoverflow.com/q/70506128/13953427] ***************************************************************************************************

TASK [Test reachability to 1.1.1.1] ********************************************************************************************************************
Tuesday 28 December 2021  13:28:11 +0000 (0:00:00.028)       0:00:00.028 ****** 
changed: [host1]
changed: [host2]

TASK [gather time as float] ****************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.705)       0:00:00.733 ****** 
ok: [host1]
ok: [host2]

TASK [print individual time] ***************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.083)       0:00:00.817 ****** 
ok: [host1] => 
  ping_number: '1.71'
ok: [host2] => 
  ping_number: '3.89'

TASK [gather] ******************************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.100)       0:00:00.917 ****** 
ok: [host1 -> localhost] => (item=host1)
ok: [host1 -> localhost] => (item=host2)

TASK [do something] ************************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.042)       0:00:00.960 ****** 
ok: [host1 -> localhost]

TASK [do something] ************************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.022)       0:00:00.983 ****** 
ok: [host1 -> localhost] => 
  total: '5.6'

TASK [do something] ************************************************************************************************************************************
Tuesday 28 December 2021  13:28:12 +0000 (0:00:00.022)       0:00:01.005 ****** 
ok: [host1 -> localhost] => 
  average_ping_time: '2.8'

PLAY RECAP *********************************************************************************************************************************************
host1               : ok=7    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host2               : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Upvotes: 0

Zeitounator
Zeitounator

Reputation: 44615

From your question and different comments, I'm not exactly sure what you want to calculate. So here is a very basic example getting a sum and an average over all hosts.

You can have a look a the documentations for jinja2 filter and ansible filters for more info on the expression I used in my playbook.

Please note that jinja2 outputs strings so values passed to set_fact are converted back to strings. Hence the use of numerous float filters at different points in the playbook is not an over-precaution but absolutely needed.

I used the following "fake" inventory ìnv.yml of two hosts all resolving to my local machine.

---
all:
    vars:
        ansible_connection: local
    hosts:
        test1:
        test2:

And the test.yml playbook:

---
- hosts: all
  gather_facts: false

  tasks:
    - name: Run ping command
      shell: ping -c 1 -W 1 192.168.0.2
      register: ping_cmd
      changed_when: false
    
    - name: set ping time for each host
      set_fact:
        ping_time: "{{ ((ping_cmd.stdout_lines[1] | split('='))[3] | split(' '))[0] }}"
        
    - name: set total ping time once for all hosts
      set_fact:
        total_ping_time: "{{ play_hosts | map('extract', hostvars)
          | map(attribute='ping_time') | map('float') | sum }}"
      run_once: true

    - name: set mean ping time once for all hosts
      set_fact:
        mean_ping_time: "{{ ((total_ping_time | float) / (play_hosts | length)) }}"
      run_once: true

    - name: show the result
      debug:
        msg: "For this host, ping_time is {{ ping_time }}s
        for a total of {{ total_ping_time | float | round(3) }}s
        and an average of {{ mean_ping_time | float | round(3) }}s"

Which gives:

$ ansible-playbook -i inv.yml test.yml 

PLAY [all] ********************************************************************************************************************************************************************************************************

TASK [Run ping command] *******************************************************************************************************************************************************************************************
ok: [test1]
ok: [test2]

TASK [set ping time for each host] ********************************************************************************************************************************************************************************
ok: [test1]
ok: [test2]

TASK [set total ping time once for all hosts] *********************************************************************************************************************************************************************
ok: [test1]

TASK [set mean ping time once for all hosts] **********************************************************************************************************************************************************************
ok: [test1]

TASK [show the result] ********************************************************************************************************************************************************************************************
ok: [test1] => {
    "msg": "For this host, ping_time is 0.137s for a total of 0.214s and an average of 0.107s"
}
ok: [test2] => {
    "msg": "For this host, ping_time is 0.077s for a total of 0.214s and an average of 0.107s"
}

PLAY RECAP ********************************************************************************************************************************************************************************************************
test1                      : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test2                      : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Upvotes: 0

Kevin C
Kevin C

Reputation: 5740

I want to save these two strings("56.9" and "32.1" ) in different variables and convert them into numbers to calculate them

The output of the ping command is a float, so I treat it as such. I've added an example, so you can perform calculations with it.

- hosts:
    - test-multi-01
  gather_facts: false
  tasks:
    - name:
      shell: 'ping -c 1 -W 1 <ip> |  grep -Po "time=.*"'
      register: shell_result_var

    - set_fact:
        ping_number: "{{ shell_result_var.stdout | regex_replace(' ms','') | regex_replace('time=','') | float }}"

    - debug:
        msg: "{{ ping_number | float + 0.1 }}"

Produces:

ok: [test-multi-01] => {
    "msg": "0.33499999999999996"
}

Upvotes: 2

Related Questions