Using ansible to manage disk space

2019-02-06 19:29发布

问题:

Simple ask: I want to delete some files if partition utilization goes over a certain percentage.

I have access to "size_total" and "size_available" via "ansible_mounts". i.e.:

ansible myhost -m setup -a 'filter=ansible_mounts'
myhost | success >> {
"ansible_facts": {
    "ansible_mounts": [
        {
            "device": "/dev/mapper/RootVolGroup00-lv_root", 
            "fstype": "ext4", 
            "mount": "/", 
            "options": "rw", 
            "size_available": 5033046016, 
            "size_total": 8455118848
        }, 

How do I access those values, and how would I perform actions conditionally based on them using Ansible?

回答1:

Slava's answer definitely was on the right track, here is what I used:

- name: test for available disk space
  assert:
    that: 
      - not {{ item.mount == '/' and ( item.size_available < item.size_total - ( item.size_total|float * 0.8 ) ) }}
      - not {{ item.mount == '/var' and ( item.size_available < item.size_total - ( item.size_total|float * 0.8 ) ) }}
  with_items: ansible_mounts
  ignore_errors: yes
  register: disk_free

- name: free disk space
  command: "/some/command/that/fixes/it"
  when: disk_free|failed

The assert task simply tests for a condition, by setting ignore_errors, and registering the result of the test to a new variable we can perform a conditional task later in the play instead of just failing when the result of the assert fails.

The tests themselves could probably be written more efficiently, but at the cost of readability. So I didn't use a multiple-list loop in the example. In this case the task loops over each item in the list of mounted filesystems (an ansible-created fact, called ansible_mounts.)

By negating the test we avoid failing on file system mounts not in our list, then simple math handles the rest. The part that tripped me up was that the size_available and size_total variables were strings, so a jinja filter converts them to a float before calculating the percentage.



回答2:

In my case, all I care about is the root partition. But I found when using the example from frameloss above, that I needed a negated 'or' condition, because each mount point will get tested against the assertion. If more than one mount point existed, then that meant the assertion would always fail. In my example, I'm testing for if the size_available is less than 50% of size_total directly, rather than calculate it as frameloss did.

Secondly, at least in the version of ansible I used, it was necessary to include the {{ }} around the variable in with_items. A mistake that I made that wasn't in the example above was not aligning the 'when' clause at the same indentation as the 'fail' directive. ( If that mistake is made, then the solution does not work... )

# This works with ansible 2.2.1.0
- hosts: api-endpoints
  become: True
  tasks:

    - name: Test disk space available
      assert:
        that:
            - item.mount != '/' or {{ item.mount == '/' and item.size_available > (item.size_total|float * 0.4) }}
      with_items: '{{ ansible_mounts }}'
      ignore_errors: yes
      register: disk_free

    - name: Fail when disk space needs attention
      fail:
         msg: 'Disk space needs attention.'
      when: disk_free|failed


回答3:

I didn't test it but I suggest to try something like this:

file:
   dest: /path/to/big/file
   state: absent
   when: "{% for point in ansible_mounts %}{% if point.mount == '/' and point.size_available > (point.size_total / 100 * 85) %}true{% endif %}{% endfor %}" == "true"

In this example, we iterate over mount points and find "/", after that we calculate is there utilization goes over 85 percentage and prints "true" if it's true. Next, we compare that string and decide should this file be deleted.

Inspired by examples from the following blog: https://blog.codecentric.de/en/2014/08/jinja2-better-ansible-playbooks-templates/



回答4:

My solution

- name: cleanup logs, free disk space below 20%
  sudo: yes
  command: find /var -name "*.log" \( \( -size +50M -mtime +7 \) -o -mtime +30 \) -exec truncate {} --size 0 \;
  when: "item.mount == '/var' and ( item.size_available < item.size_total * 0.2 )"
  with_items: ansible_mounts

This will truncate any *.log files on the volume /var that are either older than 7 days and greater than 50M or older than 30 days if the free disk space falls below 20%.



标签: ansible