So after reading Ansible docs, I found out that Handlers are only fired when tasks report changes, so for example:
some tasks ...
notify: nginx_restart
# our handler
- name: nginx_restart
vs
some tasks ...
register: nginx_restart
# do this after nginx_restart changes
when: nginx_restart|changed
Is there any difference between these 2 methods? When should I use each of them?
For me, register seems to have more functionality here, unless I am missing something...
There are some differences and which is better depends on the situation.
Handlers will only be visible in the output if they have actually been executed. Not notified, there will be no skipped tasks in Ansibles output. Tasks always have output no matter if skipped, executed with change or without. (except they are excluded via tags/skip-tags)
Handlers can be called from any role. This gets handy if you have more complex roles which depend on each other. Let's say you have a role to manage iptables but which rules you define is actually depending on other roles (e.g. database role, redis role etc...) Each role can add their rules to a config file and at the end you notify the iptables role to reload iptables if changed.
Handlers by default get executed at the end of the playbook. Tasks will get executed immediately where they are defined. This way you could configure all your applications and at the end the service restart for all changed apps will be triggered per handler. This can be dangerous though. In case your playbook fails after a handler has been notified, the handler will actually not be called. If you run the playbook again, the triggering task may not have a changed state any longer, therefore not notifying the handler. This results in Ansible actually not being idempotent. Since Ansible 1.9.1 you can call Ansible with the --force-handler option or define force_handlers = True in your ansible.cfg to even fire all notified handlers after the playbook failed. (See docs)
If you need your handlers to be fired at a specific point (for example you configured your system to use an internal DNS and now want to resolve a host through this DNS) you can flush all handlers by defining a task like:
- meta: flush_handlers
A handler would be called only once no matter how many times it was notified. Imagine you have a service that depends on multiple config files (for example bind/named: rev, zone, root.db, rndc.key, named.conf) and you want to restart named if any of these files changed. With handlers you simply would notify from every single task that managed those files. Otherwise you need to register 5 useless vars, and then check them all in your restart task.
Personally I prefer handlers. It appears much cleaner than dealing with register. Tasks triggered per register was safer before Ansible 1.9.1.
On the Ansible Variables page, you can see how register works.
Another major use of variables is running a command and using the result of that command to save the result into a variable.
Registered variables are just like facts:
Effectively registered variables are just like facts.
This is very different from notify, which triggers handlers. It does not save or store variables or facts.
with ignore_errors: True you can avoid the failed handler from stopping other handlers defined after it continue to run
Related
I have a task that shuts down any running processes using my application files and it is called twice in the deployment from different roles.
The problem is that in between the two roles someone starts a new process.
Ansible skips the second stop task because it thinks the "Conditional result was False"
This then causes the deployment to fail.
How can I force ansible to not check and always run the task?
It is at a task level and not a role level so I can't use always.
How can I force Ansible to not check and always run the task?
Depending on the tasks (annot. which are not given or described in your question), to do so you may need to set
when: true
check_mode: false
Further Documentation
Basic conditionals with when
Enforcing or preventing check mode on tasks
Defining failure
You might not understand what I am trying to say in the question. Before stating the problem, I would give an example to understand even more.
Let's take terraform for the example.
Imagine we use terraform to spin up an EC2 instance in the aws. So, we are currently storing the state locally. Let's say we need to add a tag to the ec2 instance.
So what we do is, we add the this small block to achieve that.
tags = {
Name = "HelloWorld"
}
Nothing complex, just simple as it is. So, as we add a tag, the terraform will look for any changes in the state and as it found a tag has been added, it runs and only add the tag without recreating the whole instance. (There can be other scenarios where it needs to recreate the instance but let's leave those for now)
So, as I mentioned, terraform doesn't recreate the instance for adding a tag. If we want to add another tag, it will add the tag without recreating the instance. Just as simple as that.
doesn't matter how many times we run the terraform apply it doesn't do any changes to existing resources unless we made any in the terraform files.
So, let's now come to the real question.
Let's say we want to install a httpd using ansible. So I write ansible playbook. And it will use the yum package and install and then start and enable the service.
Okay it is simple like that.
Let's say we we run the same playbook for the second time, now will try to execute the same commands from scratch without checking first whether it has executed this playbook before?
I have couple of experiences where I try to install some services and when I try to execute the same playbook again, it fails.
Is it possible to preserve the state in ansible just like we do in terraform so it always only execute the newer changes other than executing from the start on the second run?
You seem to be describing idempotence:
Idempotence is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application.
It's up to you to make sure your playbooks are idempotent in ansible. You could do this for your example using the package and service modules.
- name: Setup Apache
hosts: webservers
tasks:
- name: Install Apache
package:
name: httpd
state: present
- name: Start and enable Apache
service:
name: httpd
state: started
enabled: yes
Ansible doesn't achieve idempotence by retaining state on the the controller. Each module should check the state of the host it's operating on to determine what changes it needs to make to achieve the state specified by the playbook. If it determines that it doesn't need to make any changes then that's what it should report back to the controller.
I want to write a playbook to synchronize a source file to a destination host and restart tomcat/apache if the file changed. The documentation on synchronize does not give any example on if this is possible. Can anyone provide some pointers?
If you're only changing one file, you probably want to use copy instead of synchronize. However, this approach should work either way.
The handler system is designed for this sort of thing. The documentation there provides an example of bouncing memcached after a configuration file change:
Here’s an example of restarting two services when the contents of a
file change, but only if the file changes:
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
The things listed in the notify section of a task are called handlers.
Handlers are lists of tasks, not really any different from regular
tasks, that are referenced by a globally unique name, and are notified
by notifiers. If nothing notifies a handler, it will not run.
Regardless of how many tasks notify a handler, it will run only once,
after all of the tasks complete in a particular play.
Here’s an example handlers section:
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
Assume that a normal deployment script does a lot of things and many of them are related to preparing the OS itself.
These tasks are taking a LOT of time to run even if there is nothing new to do and I want to prevent running them more often than, let's say once a day.
I know that I can use tags to filter what I am running but that's not the point: I need to make ansible aware that these "sections" executed successfully one hour ago and due to this, it would skip the entire block now.
I was expecting that caching of facts was supposed to do this but somehow I wasnt able to see any read case.
You need to figure out how to determine what "executed successfully" means. Is is just that a given playbook ran to completion? Certain roles ran to completion? Certain indicators exist that allow you determine success?
As you mention, I don't think fact caching is going to help you here unless you want to introduce custom facts into each host (http://docs.ansible.com/ansible/playbooks_variables.html#local-facts-facts-d)
I typically come up with a set of variables/facts that indicate a role has already been run. Sometimes this involves making shell calls and registering vars, looking at gathered facts and determining if certain files exist. My typical pattern for a role looks something like this
roles/my_role/main.yml
- name: load facts
include: facts.yml
- name: run config tasks if needed
include: config.yml
when: fact_var1 and fact_vars2 and inv_var1 and reg_var1
You could also dynamically write a yaml variable file that get's included in your playbooks and contains variables about the configured state of your environment. This is a more global option and doesn't really work if you need to look at the configured status of individual machines. An extension of this would be to write status variables to host_vars or group_vars inventory variable files. These would get loaded automatically on a host by host basis.
Unfortunately, as far as I know, fact caching only caches host based facts such as those created by the setup module so wouldn't allow you to use set_fact to register a fact that, for example, a role had been completed and then conditionally check for that at the start of the role.
Instead you might want to consider using Ansible with another product to orchestrate it in a more complex fashion.
Let's imagine a playbook with following roles: base, monitoring, nginx and another playbook with only base and nginx.
Now I want in monitoring role to run a task only if playbook includes nginx role, because for monitoring nginx I have to pass a little bit different configuration to monitoring service.
How to execute a task what dependes on another role existence?
While my workaround in the comments might have worked for you, I feel it's still not the best approach. It's not modular. For example in a situation where you change monitoring system, you'd need to go into each role and check if it has monitoring component and update that...Not the most optimal way.
Perhaps a better way would be to still include a separate monitoring role, but there execute specific tasks using playbook conditionals. For example, nginx monitoring task would execute only when this server is part of your [webservers] group. Or when a certain variable is set to a specific value or some other appropriate conditional is met.
It is possible to set fact with set_fact in nginx role (set_fact: nginx=True) and then check it in monitoring role and execute task when fact is defined and true ( when: (ansible_facts['nginx'] is defined) and (ansible_facts['nginx'] == True)).