How to share handlers? - ansible

The docs says:
Since handlers are tasks too, you can also include handler files from the ‘handlers:’ section.
What I do, playbook.yml:
- hosts: all
handlers:
- include: handlers.yml
# - name: h1
# debug: msg=h1
tasks:
- debug: msg=test
notify: h1
changed_when: true
handlers.yml:
- name: h1
debug: msg=h1
Then,
$ ansible-playbook playbook.yml -i localhost, -k -e ansible_python_interpreter=python2 -v
...
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "test"
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0
...
But when I uncomment the lines, I see
$ ansible-playbook playbook.yml -i localhost, -k -e ansible_python_interpreter=python2 -v
...
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "test"
}
RUNNING HANDLER [h1] ***********************************************************
ok: [localhost] => {
"msg": "h1"
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0
...
I'm running ansible-2.1.0.0.
What am I doing wrong? That's the first thing I'd like to know. Workarounds come second.
UPD
Includes can also be used in the ‘handlers’ section, for instance, if you want to define how to restart apache, you only have to do that once for all of your playbooks. You might make a handlers.yml that looks like:
---
# this might be in a file like handlers/handlers.yml
- name: restart apache
service: name=apache state=restarted
And in your main playbook file, just include it like so, at the bottom of a play:
handlers:
- include: handlers/handlers.yml

Depending on the size of your plays a better solution might be to use roles. Ansible has some discussion why roles are a good idea.
Tasks go in roles/mystuff/tasks/main.yml and roles/somethingelse/tasks/main.yml. You can share handlers between the roles, by creating a role containing only handlers roles/myhandlers/handlers/main.yml and make both roles depend on the myhandlers role:
roles/mystuff/meta/main.yml and roles/somethingelse/meta/main.yml:
---
dependencies:
- myhandlers
More on dependencies in https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#using-role-dependencies

To moderators. Read my question carefully please. That's the answer to my question. And I'm totally aware that SO is not a forum.
That's a bug in ansible-2.1. The credit goes to udondan who found the issue.

Related

Single Ansible task for become yes and no

I have a lot of tasks running for the user and superuser. Most of them load configs via the template module. Question:
Is it possible to complete somehow the same task for both a regular user and a superuser? Those. do not do two tasks, one with become: no, and the other with yes.
Are there any conditions for template to distinguish become, so that at least one template is used.
I have quite a lot of experience with ansible and have read a lot of documentation and googled. Probably what I need is simply impossible, but suddenly someone came up with a crutch.
Thanks.
Ansible evaluates the become statement only once for the task, so if
you were to write a task like this:
- become: "{{ item|bool }}"
command: id
register: result
loop:
- false
- true
It would always run with become: false; if you were to switch the
order of the items in the loop, it would always run with become: true. However, the become_user setting can be set from a loop
variable, so you can do something like this:
- become: true
become_user: "{{ item }}"
command: id
register: result
loop:
- root
- "{{ ansible_user_id }}"
- debug:
var: result.results|map(attribute='stdout')|list
Running these tasks on my local system results in the following output:
TASK [command] *****************************************************************
changed: [localhost] => (item=root)
changed: [localhost] => (item=lars)
TASK [debug] *******************************************************************
ok: [localhost] => {
"result.results|map(attribute='stdout')|list": [
"uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
"uid=1000(lars) gid=1000(lars) groups=1000(lars),10(wheel),18(dialout),983(libvirt) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023"
]
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How do i use pre task module in ansible to validate input parameters?

I want to validate few things before i run my main play in Ansible. For example below command is taking 2 input arguments from the user so I want to validate them before executing the main tasks.
ansible-playbook -i my-inventory my-main.yml --tags=repodownload -e release_version=5.0.0-07 -e target_env=dev/prod/preprod
In the above case, release_version should not be empty and target_env must be these type of values -
5.0.0.34
I want to display a message to user about what is wrong. How do i achieve it?
Any help is appreciated.
If you absolutely need the user to provide the variables, I would first of all use vars_prompt so that the variable value is asked interactively if user forgot to provide them as extra vars. This also makes a good inline documentation.
Then you can use pre_tasks to validate the input that was provided, either interactively or as an extra var. For validation, I usually use the fail module. The point here is to use run_once: true to force the test to run only once even if there are several hosts in your play.
Here is an example based on your input. Adapt to your exact needs
---
- name: Prompt and validation demo
hosts: all
gather_facts: false
vars:
_allowed_envs:
- dev
- preprod
- prod
vars_prompt:
- name: release_version
prompt: "What is the release version ? [w.x.y-z]"
private: no
- name: target_env
prompt: "What is the target environment ? [{{ _allowed_envs | join(', ') }}]"
private: no
pre_tasks:
- name: Make sure version is ok
fail:
msg: >-
Release version is not formatted correctly. Please make sure
it is of the form w.x.y-zz
when: not release_version is regex('\d*(\.\d*){2}-\d\d')
run_once: true
- name: Make sure target_env is allowed
fail:
msg: >-
Environment "{{ target_env }}" is not allowed.
Please choose a target environment in {{ _allowed_envs | join(', ') }}
when: not target_env in _allowed_envs
run_once: true
tasks:
- name: "Dummy task just to have a complete playbook for the example"
debug:
msg: "Deploying version {{ release_version }} for environment {{ target_env }} on {{ inventory_hostname }}"
And here are some examples launching the playbook:
##########################
# Fully interactive runs #
##########################
$ ansible-playbook -i localhost, playbook.yml
What is the release version ? [w.x.y-z]: wrong
What is the target environment ? [dev, preprod, prod]: prod
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Release version is not formatted correctly. Please make sure it is of the form w.x.y-zz"}
NO MORE HOSTS LEFT ***************************************************
PLAY RECAP **********************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
$ ansible-playbook -i localhost, playbook.yml
What is the release version ? [w.x.y-z]: 1.2.3-44
What is the target environment ? [dev, preprod, prod]: dev
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment dev on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
###############
# Hybrid run #
###############
$ ansible-playbook -i localhost, playbook.yml -e target_env=prod
What is the release version ? [w.x.y-z]: 1.2.3-44
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment prod on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
###################
# Fully automated #
###################
$ ansible-playbook -i localhost, playbook.yml -e target_env=prod -e release_version=1.2.3-44
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment prod on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0

Run tasks with a certain tag within role in Ansible

I have the following folder-structure, inspired by the best-practices section in Ansibles documentation:
my-playbook.yml
my-role
|
|── tasks
|
|── my-task.yml
I have tagged the tasks within the my-task.yml file which is part of a role. I execute the playbook using ansible-playbook.yml --tags "mytag". Unfortunately, all tasks are skipped. Can I only filter tasks directly part of the playbook?
Within my playbook, I do something like
- hosts: ansible_server
connection: local
gather_facts: no
roles:
- validate_properties
Thanks in advance!
What you should do is call the role from a task by using the include_role module. On that task you can apply tags. Take this playbook, for example:
---
- name: Tag role test
hosts: local
connection: local
gather_facts: no
tasks:
- include_role:
name: debug
tags:
- dont_run
- debug:
msg: Solo shot first
tags:
- run
Where my role/debug consists of just a task that prints Hello, world!.
If you call this playbook directly you get this output:
PLAY [Tag role test]
TASK [debug : debug]
ok: [localhost] =>
msg: Hello, world!
TASK [debug]
ok: [localhost] =>
msg: Solo shot first
PLAY RECAP
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But, if you exclude the dont_run task like this:
ansible-playbook tag_roles.yml --skip-tags dont_run
This is the output:
PLAY [Diff test]
TASK [debug]
ok: [localhost] =>
msg: Solo shot first
PLAY RECAP
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I hope it helps.
You have to tag the subtasks also with your tag u want to run to:
Main Task:
- name: "test tags on sub task"
include_tasks: subtask.yml
with_items: "{{ myList }}"
loop_control:
label: item
tags: test
Sub task:
debug: msg="Sub Task"
tags: test

How to wait for a variable to be defined?

Is there a way to make a playbook waiting till a variable is defined?
To reduce some time in the execution of a playbook, I would like to to split it into multiple and start them at the same time. Some of them need a variables, which are defined in the other playbooks.
Is it possible?
IMHO it's not possible. Global scope is set only by config, environment variables and the command line.
Other variables are shared in the scope of a play. It is possible to import more playbooks into one playbook with import_playbook and share variables among the playbooks. But, it's not possible to let the imported playbooks run asynchronously and let them wait for each other.
An option would be to use an external shared memory (e.g. database) and to start such playbooks separately. For example, to share variables among the playbooks at the controller, a simple ini file would do the job.
$ cat shared-vars.ini
[global]
The playbook below
- hosts: localhost
tasks:
- wait_for:
path: "{{ playbook_dir }}/shared-vars.ini"
search_regex: "^shared_var1\\s*=(.*)"
- debug:
msg: "{{ lookup('ini', 'shared_var1 file=shared-vars.ini') }}"
waits for a variable shared_var1 in the file shared-vars.ini
$ ansible-playbook wait_for_var.yml
PLAY [localhost] *******************************************************
TASK [wait_for] ********************************************************
Next playbook
- hosts: localhost
tasks:
- ini_file:
path: "{{ playbook_dir }}/shared-vars.ini"
section: global
option: shared_var1
value: Test value set by declare_var.yml
writes the variable shared_var1 into the file shared-vars.ini
$ ansible-playbook declare_var.yml
PLAY [localhost] *******************************************************
TASK [ini_file] ********************************************************
changed: [localhost]
PLAY RECAP *************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0
First playbook which was waiting for the variable continues
TASK [debug] ***********************************************************
ok: [localhost] => {
"msg": "Test value set by declare_var.yml"
}
PLAY RECAP *************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0

ansible \ any reason why .yml role element don't run roles/x/tasks/main.yml

I'm doing exactly like all the tutorials, don't have typos, and even able to run alone the main.yml inside /roles/x
but when I run the play that should call it - nothing really happens
parent
---
- name: Install / Upgrade tagger
hosts: tagger
roles:
- tagger
/roles/tagger/tasks/main.yml
---
- command: echo 1
need to say I'm running everything in localhost.
tried also
ansible-playbook -i "localhost" -c local tagger.yml
ansible-playbook -i "localhost" -c local tagger.yml
[WARNING]: Host file not found: localhost
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [build tagger docker] *****************************************************
TASK [setup] *******************************************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
Using the commandline you gave:
$ ansible-playbook -i "localhost" -c local tagger.yml
ERROR: Unable to find an inventory file, specify one with -i ?
With the obvious correction (adding a comma):
$ ansible-playbook -i "localhost," -c local tagger.yml
PLAY [Install / Upgrade tagger] ***********************************************
skipping: no hosts matched
PLAY RECAP ********************************************************************
That still doesn't match your output, but it does indicate the problem. localhost is never tagger. Perhaps you are using a hosts.ini file and not telling us about it? Or a specific version of ansible that is different than mine? In any case, I changed hosts: tagger to hosts: all as follows:
---
- name: Install / Upgrade tagger
hosts: all
roles:
- tagger
I then reran:
$ ansible-playbook -i "localhost," -c local tagger.yml
PLAY [Install / Upgrade tagger] ***********************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [tagger | command echo 1] ***********************************************
changed: [localhost]
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
So there are the two fixes necessary.

Resources