Ansible synchronize and perform action if changed - ansible

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

Related

Ansible: How to start stopped services?

I created a playbook that uses ansible_facts.services to restart specific services on multiple RHEL servers. The services that get restarted begin with a specific name and may or may not exist on the different hosts where the playbook is ran.
I have this working correctly but I would also like to add a follow up task that checks the services that were restarted in the previous task to make sure they are running. The task would need to only check the services that were restarted then start the service if the status is stopped.
Please recommend the best way to do this. Include code if possible. Thanks!
It's sounds odd, but you may try to use handlers (to react on 'changes') and use check_mode for the module to assure the state.
Try to add 'notify' to the restarting task, and check_mode: true to the handler. Handler is, basically, the same systemd module, but without ability to change.
... Or, if you want to test your infrastructure, may be you may like testinfra (pytest-testinfra) as a tool to check your infra.
I understand your question that you like to make sure that a list of serivces is running.
- name: Loop over all services and print name if not running
debug:
msg: "{{ item }}"
when:
- ansible_facts.services[item].state != 'running'
with_items: "{{ ansible_facts.services }}"
Based on that example you could start the service if the status is stopped.
Further Q&A
Ansible: How to get disabled but running services?
How to list only the running services with ansible_facts?
How to check service exists and is not installed in the server using service_facts module in an Ansible playbook?

Is it possible to use a state for ansible?

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.

Supressing handler in Ansible

My role have two handlers: start service and restart service. I want to run start service only if service was installed, and I want to restart service if I changed service configuration file.
(That were normal ansible handlers, now complicated part starts)
I want to avoid doing restart if I start application (started handler was executed).
Basically, if:
1. package was installed
2. config was changed
=> start service
if:
1. package wasn't changed
2. config was changed
=> restart service
I tried to play with listen, but it's designed for different purposes and can't help here.
Can someone give me an idea who to do this?

Ansible Handler notify vs register

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

Ansible, run task if playbook includes role

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)).

Resources