Ansible one email alert for all hosts
Does anyone know how to club all output in 1 email alert? i created a playbook to check read-only filesystem and alert when it finds one. but the problem is if 2 servers have the problem i get 2 email alerts. i need single alert for all problematic hosts. Any help will be appreciated. :) i tried many thing but i get single alert for single hosts and it floods my inbox.
---
- hosts: free
remote_user: snail
gather_facts: false
tasks:
- name: run echo Command
shell: if grep "[[:space:]]ro[[:space:],]" /proc/mounts | grep -v tmpfs > /dev/null ; then echo 'read-only-FS';else echo 'all_good'; fi
register: outcome
- name: get the IP address
shell: hostname
register: host_name
- name: Send Mail Alert
local_action: mail
host="mailxxxx"
subject="[Read-Only FS] Testing Mail"
body="Read-Only FileSystem on {{ host_name.stdout }}"
to="[email protected]"
from="root"
when: outcome.stdout == "read-only-FS"
The easiest that I can think about is following:
- Check if there is file with some "random" name like "/tmp/ansible-mail". This should be
delegate_to=localhost
andrun_once=True
. If there is file it should be removed. (file module withstate=absent
) - Check if FS has changed - register it.
- If 2 is changed append to this file - Because multiple host might want to append the
serial=1
might be an option, delegate_to=localhost. - Check if there is this "random" file - register it. Delegate to localhost and set run_once.
- If 4 is true. Then run mailing. Once more with delegation and run_once.
If there is need I can make simple POC.
EDIT:
---
- hosts: tmp1, tmp2
# user, become etc should normaly be set in ansible.cfg
user: ansible
become: true
vars:
mail_body_file: "/tmp/ansible-mailing-tmp123"
tasks:
- name: Check if there is {{mail_body_file}}
file:
state: absent
path: "{{mail_body_file}}"
delegate_to: localhost
run_once: true
become: false
- name: Task looking for change, command always reports change so I used it for testing
command: "echo 123"
register: my_test
- name: Create {{mail_body_file}}
file:
state: touch
path: "{{mail_body_file}}"
delegate_to: localhost
run_once: true
become: false
# Use shell because lineinfile has race condition
# with echo >> we put race condition problem on filesystem
- name: add line about change on host
shell: "echo {{inventory_hostname}} has change >> {{mail_body_file}}"
delegate_to: localhost
when: my_test.changed
become: false
- name: get the mail body to variable
register: mail_body
command: "cat {{mail_body_file}}"
delegate_to: localhost
run_once: True
- name: mail about change
mail:
body: "{{mail_body.stdout}}"
to: "[email protected]"
from: "root"
subject: "Test"
delegate_to: localhost
run_once: True
Please note that you should test it.
A playbook like this could work.
We have two separate plays in the playbook, in the first play we connect to the remote hosts and gather results. In the second play we run on local host, and send the mail. An inline jinja template is used that will loop over all the hosts that match the hosts pattern defined in hosts_pattern
.
---
- name: gather data
hosts: linux_systems
gather_facts: false
tasks:
- name: Command to check systems for ead only mounts
shell: >
if grep "[[:space:]]ro[[:space:],]" /proc/mounts |
grep -v tmpfs > /dev/null ; then
echo 'read-only-FS';else echo 'all_good'; fi
register: read_only_check
- name: Get the output of `hostname`
command: hostname
register: host_name
- name: email report
hosts: localhost
gather_facts: false
vars:
hosts_pattern: linux_systems
tasks:
- name: Email report of systems with read only filesystems
mail:
host: some-smtp.server.example.com
port: 25
to: John Smith <[email protected]>
subject: "[Read-Only FS] Testing Mail"
body: |
Report from playbook check_readonly_filesystems.
{% for host_item in lookup('inventory_hostnames', hosts_pattern, wantlist=True) %}
{% if hostvars[host_item]['read_only_check'] is defined and
hostvars[host_item]['read_only_check'].stdout is defined and
hostvars[host_item]['read_only_check'].stdout == "read-only-FS" %}
Read only - {{ hostvars[host_item]['inventory_hostname'] }} aka {{ hostvars[host_item]['host_name'].stdout }}
{% else %}
Good host - {{ hostvars[host_item]['inventory_hostname'] }} aka {{ hostvars[host_item]['host_name'].stdout }}
{% endif %}
{% endfor %}