verb
verb

Reputation: 89

Calculate disk space with Ansible

I need to go over 100 machines and get the total size of /var. Then aggregate the space for all machines and print it. I made several tries to get my data without success and I'm seeking for help.

I am not sure that using facts is the right way and there might be other modules I'm not aware off that can do the job. Here what I have tried so far.

---
- hosts: servers
  remote_user: ansible
  become: true
  gather_facts: no
  tasks:
  - name: Initialize an empty list for our strings
    set_fact:
      my_strings: []
  - name: fetching
    shell: du -sh /var 
    register: var
  - name: Append string to list
    set_fact:
      my_strings: "{{ my_strings + [ var.stdout ] }}"
  - name: print output
    debug: 
      msg: "-> {{ my_strings }}"

Upvotes: 0

Views: 2252

Answers (1)

Zeitounator
Zeitounator

Reputation: 44615

You can use facts but you are using them the wrong way. What you have to understand is that:

  • a fact is local to a particular host.
  • a task is running on a particular host at a time (eventually in parallel) and uses the facts from that host.

So adding disk spaces as list elements like you are trying to do will basically add nothing but the individual calculated space in a single element list for each machine.

What you can do is collect individual facts on each machine and then manipulate them, even sum them if they have a compatible type... This is possible using the magic variable hostvars and a few filters you will have to get familiar with going through the following documentations:

Here is an example playbook that you can run without having to connect anywhere. I created 3 fake hosts connecting to my local machine just for that purpose. Replace/adapt with your own inventory and you should be fine. I tried to demonstrate a few data manipulation techniques in ansible. You can combine those differently and use others to suit your exact needs. I hope it will help to clarify the problem anyway.

This is my example inventories/demo/hosts.yml

---
all:
  vars:
    ansible_connection: local
    ansible_python_interpreter: /usr/bin/python3
  children:
    servers:
      hosts:
        demo1:
        demo2:
        demo3:

The example var_size.yml playbook:

---
- name: Get /var size on all servers hosts
  hosts: servers
  gather_facts: false

  tasks:
    - name: Fetching /var size on hosts
      command: du -s /var
      register: du_var
      become: true
      changed_when: false

    # This is absolutely needed as registers don't survive current play
    # See next play on localhost only
    - name: Store actual size for hosts in a fact
      vars:
        size_regex: >-
          ^(\d+)\s*/var
        replace: >-
          \g<1>
      set_fact:
        var_size_bytes: "{{ du_var.stdout | regex_replace(size_regex, replace) }}"

    # As a first example, demonstrate we can simply display sizes
    # with the natural play host loop
    - name: Show human readable size of /var on each host
      debug:
        msg: "/var size is: {{ var_size_bytes | int | human_readable }}"

# Now we can work on consolidating
- name: Consolidate data we gathered
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Create a dict of sizes extracting all relevant hostvars
      # This fact is set on localhost as this is the only play target
      set_fact:
        var_sizes_host_dict: "{{ groups['servers'] | map('extract', hostvars) | items2dict(key_name='inventory_hostname', value_name='var_size_bytes') }}"

    - name: Show the raw dict with bytes sizes
      debug:
        var: var_sizes_host_dict

    - name: Display sizes for all hosts in human readable format
      vars:
        hostnames: "{{ var_sizes_host_dict.keys() }}"
        hr_sizes: "{{ var_sizes_host_dict.values() | map('int') | map('human_readable') }}"
        var_sizes_host_dict_hr: "{{ hostnames | zip(hr_sizes) | items2dict(key_name=0, value_name=1) }}"
      debug:
        msg: "{{ var_sizes_host_dict_hr }}"

    - name: Sum all /var sizes
      vars:
        host_number: "{{ var_sizes_host_dict | length }}"
        total_space: "{{ var_sizes_host_dict.values() | map('int') | sum }}"
        raw_message: |-
          The total number of host is: {{ host_number }}
          The total /var space used is: {{ total_space | int | human_readable }}
      debug:
        msg: "{{ raw_message.split('\n') }}"

Which gives:

$ ansible-playbook -i inventories/demo var_size.yml 

PLAY [Get /var size on all demo hosts] *************************************************************************************************************************************************************************************************

TASK [Fetching /var size on hosts] *****************************************************************************************************************************************************************************************************
ok: [demo3]
ok: [demo1]
ok: [demo2]

TASK [Store actual size for hosts in a fact] *******************************************************************************************************************************************************************************************
ok: [demo2]
ok: [demo1]
ok: [demo3]

TASK [Show human readable size of /var on each host] ***********************************************************************************************************************************************************************************
ok: [demo1] => {
    "msg": "/var size is: 4.31 MB"
}
ok: [demo3] => {
    "msg": "/var size is: 4.31 MB"
}
ok: [demo2] => {
    "msg": "/var size is: 4.31 MB"
}

PLAY [Consolidate data we gathered] ****************************************************************************************************************************************************************************************************

TASK [Create a dict of sizes extracting all relevant hostvars] *************************************************************************************************************************************************************************
ok: [localhost]

TASK [Show the raw dict with bytes sizes] **********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "var_sizes_host_dict": {
        "demo1": "4518993",
        "demo2": "4518993",
        "demo3": "4518993"
    }
}

TASK [Display sizes for all hosts in human readable format] ****************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": {
        "demo1": "4.31 MB",
        "demo2": "4.31 MB",
        "demo3": "4.31 MB"
    }
}

TASK [Sum all /var sizes] **************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "The total number of host is: 3",
        "The total /var space used is: 12.93 MB"
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************
demo1                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
demo2                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
demo3                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Upvotes: 1

Related Questions