Repository: ansible/ansible

Issue:
service_facts returns misleading state on systemd hosts
##### SUMMARY
The system module service_facts returns misleading state information on systemd hosts.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
service_facts

##### ANSIBLE VERSION
```
ansible 2.5.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/USER/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Feb 20 2018, 09:19:12) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)]
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
Ansible controller: RHEL 7.5
Remote host: RHEL 7.4

##### STEPS TO REPRODUCE
The service_facts module returns one of two states for each service it discovers: either "running" or "stopped".  

```json
{
    "ansible_facts": {    
        "services": {
            "rsyncd.service": {
                "name": "rsyncd.service",
                "source": "systemd",
                "state": "stopped"
            },
            "rsyslog.service": {
                "name": "rsyslog.service",
                "source": "systemd",
                "state": "running"
            },
            ... 
        }  
    }
}
```

On systemd hosts, the module obtains state information via `systemctl list-unit-files --type=service` (see [line 181 in module source code](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/system/service_facts.py#L181)). However, this command does not report running/stopped status. Instead, it reports enabled/disable status which is not the same thing. Enabled/disabled determines whether the service will be started by systemd _on reboot_, but says nothing about whether it is currently running or stopped.

Here are two examples:

**1. Service disabled & started:**

```bash
# On remote host
systemctl disable <service>
systemctl start <service>
systemctl status <service>
# systemctl reports: loaded, disabled, active/running
```

**2. Service enabled & stopped:**

```bash
# On remote host
systemctl enable <service>
systemctl stop <service>
systemctl status <service>
# systemctl reports: loaded, enabled, inactive/dead
```

**On the Ansible controller,** use the service_facts module to query the <service> just configured on the remote host.

##### EXPECTED RESULTS
**1. Service disabled & started:** service_facts should report "running"

**2. Service enabled & stopped:** service_facts should report "stopped"

##### ACTUAL RESULTS
**1. Service disabled & started:** service_facts reports "stopped"

**2. Service enabled & stopped:** service_facts reports "running"

These misleading reports are caused by the test in [line 186 of the source code](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/system/service_facts.py#L186). The service_facts module explicitly tests whether the service is "enabled" and equates that with running/stopped, which is incorrect for systemd/systemctl.



Discussion:
Files identified in the description:
* [lib/ansible/modules/system/service_facts.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/system/service_facts.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @maxamillion
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Since service_facts is not presently reliable for systemd, as a workaround I'm querying the service I'm interested in with `command: systemctl is-active <service>`
@thessell thanks for the report, you are absolutely correct. This was an oversight on my part. I'm going to submit a patch to fix this and probably backport. Then add a new feature for Ansible 2.7 that adds both running state and enabled/disabled state.

Resolving patch:
diff --git a/lib/ansible/modules/system/service_facts.py b/lib/ansible/modules/system/service_facts.py
--- a/lib/ansible/modules/system/service_facts.py
+++ b/lib/ansible/modules/system/service_facts.py
@@ -178,16 +178,16 @@ def gather_services(self):
         systemctl_path = self.module.get_bin_path("systemctl", opt_dirs=["/usr/bin", "/usr/local/bin"])
         if systemctl_path is None:
             return None
-        rc, stdout, stderr = self.module.run_command("%s list-unit-files --type=service | tail -n +2 | head -n -2" % systemctl_path, use_unsafe_shell=True)
-        for line in stdout.split("\n"):
-            line_data = line.split()
-            if len(line_data) != 2:
-                continue
-            if line_data[1] == "enabled":
+        rc, stdout, stderr = self.module.run_command("%s list-units --no-pager --type service --all" % systemctl_path, use_unsafe_shell=True)
+        for line in [svc_line for svc_line in stdout.split('\n') if '.service' in svc_line and 'not-found' not in svc_line]:
+            service_name = line.split()[0]
+            if "running" in line:
                 state_val = "running"
             else:
+                if 'failed' in line:
+                    service_name = line.split()[1]
                 state_val = "stopped"
-            services[line_data[0]] = {"name": line_data[0], "state": state_val, "source": "systemd"}
+            services[service_name] = {"name": service_name, "state": state_val, "source": "systemd"}
         return services
 
 


Test patch: