Check if service exists with Ansible
Asked Answered
O

9

66

I have an Ansible playbook for deploying a Java app as an init.d daemon.

Being a beginner in both Ansible and Linux I'm having trouble to conditionally execute tasks on a host based on the host's status.

Namely I have some hosts having the service already present and running where I want to stop it before doing anything else. And then there might be new hosts, which don't have the service yet. So I can't simply use service: name={{service_name}} state=stopped, because this will fail on new hosts.

How I can I achieve this? Here's what I have so far:

  - name: Check if Service Exists
    shell: "if chkconfig --list | grep -q my_service;   then echo true;   else echo false; fi;"
    register: service_exists

# This should only execute on hosts where the service is present
  - name: Stop Service
    service: name={{service_name}} state=stopped
    when: service_exists
    register: service_stopped

# This too
  - name: Remove Old App Folder
    command: rm -rf {{app_target_folder}}
    when: service_exists

# This should be executed on all hosts, but only after the service has stopped, if it was present
  - name: Unpack App Archive
    unarchive: src=../target/{{app_tar_name}} dest=/opt
Odle answered 19/5, 2015 at 14:27 Comment(0)
O
52

Of course I could also just check if the wrapper script exists in /etc/init.d. So this is what I ended up with:

  - name: Check if Service Exists
    stat: path=/etc/init.d/{{service_name}}
    register: service_status

  - name: Stop Service
    service: name={{service_name}} state=stopped
    when: service_status.stat.exists
    register: service_stopped
Odle answered 19/5, 2015 at 15:38 Comment(9)
you can also use ignore_errors, especially with the register, to detect this.Alikee
Yeah, I shied away from ignore_errors, because I was afraid of false negatives due to typos and such. Also I'm trying to convince my team of investing into automating our config management and don't want the first thing to show them look like some kind of hack. :)Odle
solution also works on ubuntu, and should probably work on most linux distros.Geis
ansible is also adding a must_exist flag to the service module in v2.0, which will eliminate the need for the first check: docs.ansible.com/service_module.htmlGeis
@RyanTuck there is no must_exist parameter listed on the page you linked. And 2.0 is officially released now.Cognition
@aef, it turns out that it was removed again before release see here: github.com/ansible/ansible/issues/13259Nitrous
This only partially worked for me on Mint 18.2. Some of my systemd services had init scripts but not all of them, beware of that and check if using this methodErek
FYI: in ubuntu you shoud look inside /etc/systemd/systemEntree
Further to @darkwing's and @abzarak's comments: Rather than checking for the init script wrapper, it's possible to check for /etc/systemd/system/{{service_name}}.service directly. A lot of newer systems and package releases are dropping the SysVinit wrapper scripts.Jessikajessup
L
72

See the service_facts module, new in Ansible 2.5.

- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "'docker' in services"
Laaspere answered 10/7, 2018 at 5:40 Comment(6)
This seems promising but its output seems inconsistent with the expected service name, at least for checking the telnet.socket service, which shows up in ansible_facts.services as [email protected].Popham
Service facts are gathered under the hostvars[$host]['services']Cathiecathleen
Using when: "'docker' in services" does fail -- the gathered facts are indexed using the full name (docker.service) instead of docker. Might be platform dependent but at least on CentOS the facts are only using the full names.Zina
There's an important note on how to access service names on the relevant module page that's worth noting. With that in mind, I've successfully used: when: ansible_facts.services['service-name.service'] is defined on UbuntuOd
It seems service_facts is kind of "slow" I prefer to ceck for service file existanceEntree
It also seems that this is skipped for localhost causing me to do this in my case (I can ignore localhost but want to catch errors): when: "ansible_facts.services is defined and 'docker' in ansible_facts.services"Passbook
O
52

Of course I could also just check if the wrapper script exists in /etc/init.d. So this is what I ended up with:

  - name: Check if Service Exists
    stat: path=/etc/init.d/{{service_name}}
    register: service_status

  - name: Stop Service
    service: name={{service_name}} state=stopped
    when: service_status.stat.exists
    register: service_stopped
Odle answered 19/5, 2015 at 15:38 Comment(9)
you can also use ignore_errors, especially with the register, to detect this.Alikee
Yeah, I shied away from ignore_errors, because I was afraid of false negatives due to typos and such. Also I'm trying to convince my team of investing into automating our config management and don't want the first thing to show them look like some kind of hack. :)Odle
solution also works on ubuntu, and should probably work on most linux distros.Geis
ansible is also adding a must_exist flag to the service module in v2.0, which will eliminate the need for the first check: docs.ansible.com/service_module.htmlGeis
@RyanTuck there is no must_exist parameter listed on the page you linked. And 2.0 is officially released now.Cognition
@aef, it turns out that it was removed again before release see here: github.com/ansible/ansible/issues/13259Nitrous
This only partially worked for me on Mint 18.2. Some of my systemd services had init scripts but not all of them, beware of that and check if using this methodErek
FYI: in ubuntu you shoud look inside /etc/systemd/systemEntree
Further to @darkwing's and @abzarak's comments: Rather than checking for the init script wrapper, it's possible to check for /etc/systemd/system/{{service_name}}.service directly. A lot of newer systems and package releases are dropping the SysVinit wrapper scripts.Jessikajessup
E
10

I modified Florian's answer to only use the return code of the service command (this worked on Mint 18.2)

- name: Check if Logstash service exist
  shell: service logstash status 
  register: logstash_status
  failed_when: not(logstash_status.rc == 3 or logstash_status.rc == 0)

- name: Check if Logstash service exist
  service:
    name: logstash
    state: stopped 
  when: logstash_status.rc == 0
Erek answered 30/4, 2018 at 16:42 Comment(0)
S
9

It would be nice if the "service" module could handle "unrecognized service" errors.

This is my approach, using the service command instead of checking for an init script:

- name: check for apache
  shell: "service apache2 status"
  register: _svc_apache
  failed_when: >
    _svc_apache.rc != 0 and ("unrecognized service" not in _svc_apache.stderr)

- name: disable apache
  service: name=apache2 state=stopped enabled=no
  when: "_svc_apache.rc == 0"
  • check the exit code of "service status" and accept the exit code 0 when the output contains "unrecognized service"
  • if the exit code was 0, that service is installed (stopped or running)
Spode answered 17/3, 2016 at 12:8 Comment(0)
P
4

Building on @Maciej's answer for RedHat 8, and combining it with the comments made on it.

This is how I managed to stop Celery only if it has already been installed:

- name: Populate service facts
  service_facts:
- debug:
    msg: httpd installed!
  when: ansible_facts.services['httpd.service'] is defined

- name: Stop celery.service
  service:
    name: celery.service
    state: stopped
    enabled: true
  when: ansible_facts.services['celery.service'] is defined

You can drop the debug statement--it's there just to confirm that ansible_facts is working.

Purport answered 2/6, 2022 at 19:41 Comment(0)
F
3

Another approach for systemd (from Jakuje):

- name: Check if cups-browsed service exists
  command: systemctl cat cups-browsed
  check_mode: no
  register: cups_browsed_exists
  changed_when: False
  failed_when: cups_browsed_exists.rc not in [0, 1]

- name: Stop cups-browsed service
  systemd:
    name: cups-browsed
    state: stopped 
  when: cups_browsed_exists.rc == 0
Flytrap answered 10/9, 2019 at 11:0 Comment(0)
I
2

This way using only the service module has worked for us:

- name: Disable *service_name*
  service:
    name: *service_name*
    enabled: no
    state: stopped
  register: service_command_output
  failed_when: >
    service_command_output|failed
    and 'unrecognized service' not in service_command_output.msg
    and 'Could not find the requested service' not in service_command_output.msg
Ingham answered 26/5, 2021 at 20:28 Comment(0)
G
-1

My few cents. The same approach as above but for kubernetes

Check if kublete service is running

- name: "Obtain state of kublet service"
  command: systemctl status kubelet.service
  register: kubelet_status
  failed_when: kubelet_status.rc > 3

Display debug message if kublet service is not running

- debug:
    msg: "{{ kubelet_status.stdout }}"
  when: "'running' not in kubelet_status.stdout"
Glaucoma answered 15/2, 2019 at 9:42 Comment(2)
Unfortunately this approach won't work if the service does not exist (and that's what OP asked for). With your code you would still get Unit kubelet.service could not be found error.Zina
@Zina You are right. This solution assumes that service is installed on a host as the questions states "I have some hosts having the service already present and running" but fails when service is absent. Thus I up vote your comment :)Glaucoma
D
-1

You can use the service_facts module since Ansible 2.5. But you need to know that the output are the real name of the service like docker.service or [email protected]. So you have different options like:

- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "'docker.service' in services"

Or you can search for the string beginning with the service name; that is much more reliable, because the service names are different between the distributions:

- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "services.keys()|list|select('search', '^docker')|length >0"
Dislocation answered 18/7, 2022 at 9:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.