Home > front end >  When usage with Ansible Handlers
When usage with Ansible Handlers

Time:01-01

having an ansible playbook that will run on all hosts including Debian based & RedHat based servers this will install mariadb on RHEL based servers only doing some configuration changes, start the service once the service started handlers are used to change the root password but the problem is handlers seems to be applied on all the hosts instead of RHEL based servers only, when statement is not working with handlers, check the following code:


---

  - name: "Install & Configure MariaDB server on RHEL8"
    hosts: all
    vars:
      required_distribution: "RedHat"
      required_version: "8"

      my_packages:
        - mariadb-server
        - python3-PyMySQL
    tasks:
      - name: "check & install MariaDB on RHEL 8 only"
        yum:
          name: "{{ item }}"
          state: present
        loop: "{{ my_packages }}"
        when: "ansible_distribution == required_distribution and ansible_distribution_major_version == required_version"

      - name: "start & enable MariaDb service"
        service:
          name: mariadb
          state: started
          enabled: true
        when: "ansible_distribution == required_distribution and ansible_distribution_major_version == required_version"
        notify:
          - root_password

    handlers:
      - name: root_password
        mysql_user:
          name: root
          password: password
        when: "ansible_distribution == required_distribution and ansible_distribution_major_version == required_version"

getting this warning:

RUNNING HANDLER [root_password] ****************************************************************************************************************************************************
task path: /home/student/labs/lab6/lab6.yaml:29
[WARNING]: Module did not set no_log for update_********
changed: [server-b] => {"changed": true, "msg": "Password updated (new style)", "user": "root"}
changed: [server-a] => {"changed": true, "msg": "Password updated (new style)", "user": "root"}
changed: [server-d] => {"changed": true, "msg": "Password updated (new style)", "user": "root"}
changed: [server-c] => {"changed": true, "msg": "Password updated (new style)", "user": "root"}
META: ran handlers
META: ran handlers

what I am considering here is the handler is also applying on ubuntu-a server & giving me "[WARNING]: Module did not set no_log for update_**"

if this warning is not coming from ubuntu-a server then how to remove this and what's the issue in my playbook, is this only applied on RHEL based servers on handlers as well?

I am also looking for some effective way of writing the playbook so that each time I don't have to write when block to filter out my managed hosts it must be checked once (may be at play level) and will apply on all tasks

CodePudding user response:

Given the inventory

shell> cat hosts
host_A
host_B

[test_ubuntu]
host_A ansible_host=10.1.0.184

[test_ubuntu:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/bin/python3.9

[test_centos]
host_B ansible_host=10.1.0.74

[test_centos:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/bin/python3.6

Q: "Effective way of writing the playbook to filter out my managed hosts."

A: Use inventory plugin constructed. See

shell> ansible-doc -t inventory ansible.builtin.constructed
  1. Cache the facts. Configure cache, e.g.
shell> cat ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_cache
fact_caching_prefix = ansible_facts_
fact_caching_timeout = 86400
...

and run the playbook

shell> cat pb2.yml
- hosts: all
  tasks:
    - debug:
        msg: Completed.

This will create the cache

shell> tree /tmp/ansible_cache/
/tmp/ansible_cache/
├── ansible_facts_host_A
└── ansible_facts_host_B
  1. Create the inventory
shell> tree inventory/
inventory/
├── 01-hosts
└── 02-constructed.yml

0 directories, 2 files
shell> cat inventory/01-hosts 
host_A
host_B

[test_ubuntu]
host_A ansible_host=10.1.0.184

[test_ubuntu:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/bin/python3.9

[test_centos]
host_B ansible_host=10.1.0.74

[test_centos:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/bin/python3.6
shell> cat inventory/02-constructed.yml 
plugin: ansible.builtin.constructed
strict: true
groups:
  centos_8: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '8'
  ubuntu_20: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version == '20'

Test the inventory

shell> ansible-inventory -i inventory --graph
@all:
  |--@centos_8:
  |  |--host_B
  |--@test_centos:
  |  |--host_B
  |--@test_ubuntu:
  |  |--host_A
  |--@ubuntu_20:
  |  |--host_A
  |--@ungrouped:

You can see that the plugin constructed created groups centos_8 and ubuntu_20.

  1. Use the groups in a play. For example,
shell> cat pb3.yml
- hosts: centos_8

  tasks:

    - setup:
        gather_subset: distribution
    - debug:
        msg: |
          ansible_distribution: {{ ansible_distribution }}
          ansible_distribution_major_version: {{ ansible_distribution_major_version }}

    - debug:
        msg: Trigger handler
      changed_when: true
      notify: root_password

  handlers:

    - name: root_password
      debug:
        msg: Change root password

gives

shell> ansible-playbook -i inventory pb3.yml 

PLAY [centos_8] ******************************************************************************

TASK [setup] *********************************************************************************
ok: [host_B]

TASK [debug] *********************************************************************************
ok: [host_B] => 
  msg: |-
    ansible_distribution: CentOS
    ansible_distribution_major_version: 8

TASK [debug] *********************************************************************************
changed: [host_B] => 
  msg: Trigger handler

RUNNING HANDLER [root_password] **************************************************************
ok: [host_B] => 
  msg: Change root password

PLAY RECAP ***********************************************************************************
host_B: ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Q: "The handler is also applying on Ubuntu."

A: In your code, there is no reason for this. For example, given the data

  required_distribution: "CentOS"
  required_version: "8"

the tasks

    - setup:
        gather_subset: distribution
    - debug:
        msg: |
          ansible_distribution: {{ ansible_distribution }}
          ansible_distribution_major_version: {{ ansible_distribution_major_version }}

give

ok: [host_A] => 
  msg: |-
    ansible_distribution: Ubuntu
    ansible_distribution_major_version: 20
ok: [host_B] => 
  msg: |-
    ansible_distribution: CentOS
    ansible_distribution_major_version: 8

Your condition works as expected

    - debug:
        msg: |
          ansible_distribution is {{ required_distribution }} and
          ansible_distribution_major_version is {{ required_version }}
      when: "ansible_distribution == required_distribution and ansible_distribution_major_version == required_version"

gives

skipping: [host_A]
ok: [host_B] => 
  msg: |-
    ansible_distribution is CentOS and
    ansible_distribution_major_version is 8

You don't have to quote the condition

      when: ansible_distribution == required_distribution and ansible_distribution_major_version == required_version

You can format it

      when: ansible_distribution == required_distribution and
            ansible_distribution_major_version == required_version

, or even better

      when:
        - ansible_distribution == required_distribution
        - ansible_distribution_major_version == required_version

All options give the same result.

Test the handler. There is no reason to put the condition into the handler when the condition is in the task that will trigger it

  handlers:

    - name: root_password
      debug:
        msg: Change root password

The task below will trigger the handler for required_distribution and required_version only

    - debug:
        msg: Trigger handler
      changed_when: true
      notify: root_password
      when:
        - ansible_distribution == required_distribution
        - ansible_distribution_major_version == required_version

gives

TASK [debug] *********************************************************************************
skipping: [host_A]
changed: [host_B] => 
  msg: Trigger handler

RUNNING HANDLER [root_password] **************************************************************
ok: [host_B] => 
  msg: Change root password

PLAY RECAP ***********************************************************************************

  • Example of a complete playbook for testing
- hosts: all
  gather_facts: false

  vars:

    required_distribution: "CentOS"
    required_version: "8"

  tasks:

    - setup:
        gather_subset: distribution
    - debug:
        msg: |
          ansible_distribution: {{ ansible_distribution }}
          ansible_distribution_major_version: {{ ansible_distribution_major_version }}

    - debug:
        msg: |
          ansible_distribution is {{ required_distribution }} and
          ansible_distribution_major_version is {{ required_version }}
      when: "ansible_distribution == required_distribution and ansible_distribution_major_version == required_version"

    - debug:
        msg: |
          ansible_distribution is {{ required_distribution }} and
          ansible_distribution_major_version is {{ required_version }}
      when: ansible_distribution == required_distribution and ansible_distribution_major_version == required_version

    - debug:
        msg: |
          ansible_distribution is {{ required_distribution }} and
          ansible_distribution_major_version is {{ required_version }}
      when: ansible_distribution == required_distribution and
            ansible_distribution_major_version == required_version

    - debug:
        msg: |
          ansible_distribution is {{ required_distribution }} and
          ansible_distribution_major_version is {{ required_version }}
      when:
        - ansible_distribution == required_distribution
        - ansible_distribution_major_version == required_version

    - debug:
        msg: Trigger handler
      changed_when: true
      notify: root_password
      when:
        - ansible_distribution == required_distribution
        - ansible_distribution_major_version == required_version

  handlers:

    - name: root_password
      debug:
        msg: Change root password
  • The tree of this project
shell> tree .
.
├── ansible.cfg
├── hosts
├── inventory
│   ├── 01-hosts
│   └── 02-constructed.yml
├── pb2.yml
├── pb3.yml
└── pb.yml

1 directory, 7 files

CodePudding user response:

I don't think that message indicates the handler is running on Ubuntu, but it's hard to say without seeing your inventory and other more info.

Anyways, if you want to only apply when "when" once (good impulse, you have two options:

  1. Put all of your tasks in a "block" and apply the when to the block. You shouldn't even need then when on the handler if all of the tasks are skipped using the same when.

  2. If what you really want is to bail on the play altogether on non RedHat 8 hosts, you could use end_host like this, as the first task in your play.

# Example showing how to end the play for specific targets
- name: End the play for hosts are not the desired versions
  ansible.builtin.meta: end_host
  when: ansible_distribution != required_distribution or ansible_distribution_major_version != required_version

  • Related