Repository: ansible/ansible-modules-core

Issue:
systemd module failing on systemctl show for Fedora systems
<!--- Verify first that your issue/request is not already reported in GitHub -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the plugin/module/task -->
systemd module
##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.0.0
  config file = 
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
Default config

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
Manager: Fedora 25
Managed: Fedora 25 or Fedora Atomic Host 25

##### SUMMARY
<!--- Explain the problem briefly -->
When using the Ansible systemd module to manage a Fedora 25 or Fedora 25 Atomic Host system, the module exits reporting there was a failure running `systemctl show` when the service does not exist.  On these systems `systemctl show` exits with a result code of 1 which causes the module to exit at this [line](https://github.com/ansible/ansible-modules-core/blob/b835ae271734821c8e41ce8cd278a01266a2247b/system/systemd.py#L278).

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```
# hosts
localhost ansible_connection=local
fedora-atomic-25  ansible_host=192.168.122.100 ansible_user=root
rhel-atomic-7.3.0 ansible_host=192.168.122.114 ansible_user=root
```

```
# test.yml
---
- name: Test Playbook
  hosts: all
  gather_facts: false

  tasks:
    - name: Test systemd
      systemd:
        name: foo.service
        state: started
      register: task_result
      failed_when: "task_result|failed and 'Could not find' not in task_result.msg"

    - debug: var=task_result
```
```
> $ ansible-playbook test.yml -i hosts                                                                  

PLAY [Test Playbook] ***********************************************************

TASK [Test systemd] ************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "failed_when_result": true, "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"}
fatal: [fedora-atomic-25]: FAILED! => {"changed": false, "failed": true, "failed_when_result": true, "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"}
ok: [rhel-atomic-7.3.0]

TASK [debug] *******************************************************************
ok: [rhel-atomic-7.3.0] => {
    "task_result": {
        "changed": false, 
        "failed": false, 
        "failed_when_result": false, 
        "msg": "Could not find the requested service \"'foo.service'\": "
    }
}
	to retry, use: --limit @/home/rteague/dev/test.retry

PLAY RECAP *********************************************************************
fedora-atomic-25           : ok=0    changed=0    unreachable=0    failed=1   
localhost                  : ok=0    changed=0    unreachable=0    failed=1   
rhel-atomic-7.3.0          : ok=2    changed=0    unreachable=0    failed=0   
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expected the systemd module would exit in the same way for Fedora 25 or RHEL 7 based hosts.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
> $ ansible-playbook test.yml -i hosts -vvvv                                                            
No config file found; using defaults
Loading callback plugin default of type stdout, v2.0 from /usr/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc

PLAYBOOK: test.yml *************************************************************
1 plays in test.yml

PLAY [Test Playbook] ***********************************************************

TASK [Test systemd] ************************************************************
task path: /home/rteague/dev/test.yml:7
Using module file /usr/lib/python2.7/site-packages/ansible/modules/core/system/systemd.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: rteague
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853 `" && echo ansible-tmp-1479909211.15-148173905006853="` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853 `" ) && sleep 0'
Using module file /usr/lib/python2.7/site-packages/ansible/modules/core/system/systemd.py
Using module file /usr/lib/python2.7/site-packages/ansible/modules/core/system/systemd.py
<192.168.122.114> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.100> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.100> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.122.100 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832 `" && echo ansible-tmp-1479909211.15-107834051487832="` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832 `" ) && sleep 0'"'"''
<192.168.122.114> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.122.114 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903 `" && echo ansible-tmp-1479909211.15-246554718521903="` echo $HOME/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903 `" ) && sleep 0'"'"''
<localhost> PUT /tmp/tmpBCo7OZ TO /home/rteague/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853/systemd.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/rteague/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853/ /home/rteague/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853/systemd.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/rteague/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853/systemd.py; rm -rf "/home/rteague/.ansible/tmp/ansible-tmp-1479909211.15-148173905006853/" > /dev/null 2>&1 && sleep 0'
fatal: [localhost]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "failed_when_result": true, 
    "invocation": {
        "module_args": {
            "daemon_reload": false, 
            "enabled": null, 
            "masked": null, 
            "name": "foo.service", 
            "state": "started", 
            "user": false
        }, 
        "module_name": "systemd"
    }, 
    "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"
}
<192.168.122.114> PUT /tmp/tmpaP4ypw TO /root/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903/systemd.py
<192.168.122.114> SSH: EXEC sftp -b - -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.122.114]'
<192.168.122.100> PUT /tmp/tmpe0lZof TO /root/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832/systemd.py
<192.168.122.100> SSH: EXEC sftp -b - -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.122.100]'
<192.168.122.114> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.114> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.122.114 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903/ /root/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903/systemd.py && sleep 0'"'"''
<192.168.122.114> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.114> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.122.114 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903/systemd.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1479909211.15-246554718521903/" > /dev/null 2>&1 && sleep 0'"'"''
<192.168.122.100> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.100> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.122.100 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832/ /root/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832/systemd.py && sleep 0'"'"''
<192.168.122.100> ESTABLISH SSH CONNECTION FOR USER: root
<192.168.122.100> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/rteague/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.122.100 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832/systemd.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1479909211.15-107834051487832/" > /dev/null 2>&1 && sleep 0'"'"''
ok: [rhel-atomic-7.3.0] => {
    "changed": false, 
    "failed": false, 
    "failed_when_result": false, 
    "invocation": {
        "module_args": {
            "daemon_reload": false, 
            "enabled": null, 
            "masked": null, 
            "name": "foo.service", 
            "state": "started", 
            "user": false
        }, 
        "module_name": "systemd"
    }, 
    "msg": "Could not find the requested service \"'foo.service'\": "
}
fatal: [fedora-atomic-25]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "failed_when_result": true, 
    "invocation": {
        "module_args": {
            "daemon_reload": false, 
            "enabled": null, 
            "masked": null, 
            "name": "foo.service", 
            "state": "started", 
            "user": false
        }, 
        "module_name": "systemd"
    }, 
    "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"
}

TASK [debug] *******************************************************************
task path: /home/rteague/dev/test.yml:14
ok: [rhel-atomic-7.3.0] => {
    "task_result": {
        "changed": false, 
        "failed": false, 
        "failed_when_result": false, 
        "msg": "Could not find the requested service \"'foo.service'\": "
    }
}
	to retry, use: --limit @/home/rteague/dev/test.retry

PLAY RECAP *********************************************************************
fedora-atomic-25           : ok=0    changed=0    unreachable=0    failed=1   
localhost                  : ok=0    changed=0    unreachable=0    failed=1   
rhel-atomic-7.3.0          : ok=2    changed=0    unreachable=0    failed=0   

```



Discussion:
<!-- @mtnbikenc: Thanks for filing this bug report! -->
@ansible ping, this issue is waiting for your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_bug_new --->
FYI - systemd v232:
https://github.com/systemd/systemd/commit/bd5b9f0a12dd9c1947b11534e99c395ddf44caa9

Currently installed on Fedora 25:
```
[root@localhost ~]# rpm -q systemd
systemd-231-10.fc25.x86_64
```
I recently merged #5543, which should fix this, can you confirm?
Unfortunately it does not.  The systemd module still exits on failure to run `systemctl show` given the rc=1 exit code of systemd (systemd-231-10.fc25.x86_64).

https://github.com/ansible/ansible-modules-core/blob/351dd9344e4f9c41d47ba574f554eb16c15c2ce2/system/systemd.py#L278

Current versions of systemd:
Fedora 25 (Atomic Host): systemd-231-10.fc25.x86_64
RHEL Atomic 7.3.0: systemd-219-30.el7_3.3.x86_64

Testing:
```
> $ ansible-playbook test.yml -i hosts                                                                                                                                                       

PLAY [Test Playbook] *************************************************************************************************************************************************************************

TASK [Test systemd] **************************************************************************************************************************************************************************
ok: [fedora-25]
ok: [fedora-atomic-25]
ok: [rhel-atomic-7.3.0]

TASK [debug] *********************************************************************************************************************************************************************************
ok: [fedora-25] => {
    "task_result": {
        "changed": false, 
        "failed": false, 
        "failed_when_result": false, 
        "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"
    }
}
ok: [fedora-atomic-25] => {
    "task_result": {
        "changed": false, 
        "failed": false, 
        "failed_when_result": false, 
        "msg": "failure 1 running systemctl show for 'foo.service': Unit foo.service could not be found.\n"
    }
}
ok: [rhel-atomic-7.3.0] => {
    "task_result": {
        "changed": false, 
        "failed": false, 
        "failed_when_result": false, 
        "msg": "Could not find the requested service foo.service: cannot check nor set state"
    }
}

PLAY RECAP ***********************************************************************************************************************************************************************************
fedora-25                  : ok=2    changed=0    unreachable=0    failed=0   
fedora-atomic-25           : ok=2    changed=0    unreachable=0    failed=0   
rhel-atomic-7.3.0          : ok=2    changed=0    unreachable=0    failed=0   

                                                                                                                                                                                              
> $ ansible --version                                                                                                                                                                        
ansible 2.3.0 (devel 9d0b8c8545) last updated 2016/11/23 13:03:13 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 351dd9344e) last updated 2016/11/23 13:03:46 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 43bb97bc37) last updated 2016/11/23 13:03:46 (GMT -400)
  config file = 
  configured module search path = Default w/o overrides
```
great ... inconsistent return codes ....



Resolving patch:
diff --git a/system/systemd.py b/system/systemd.py
--- a/system/systemd.py
+++ b/system/systemd.py
@@ -272,44 +272,41 @@ def main():
         if rc != 0:
             module.fail_json(msg='failure %d during daemon-reload: %s' % (rc, err))
 
-    # check service data
-    (rc, out, err) = module.run_command("%s show '%s'" % (systemctl, unit))
-    if rc != 0:
-        module.fail_json(msg='failure %d running systemctl show for %r: %s' % (rc, unit, err))
-
     found = False
     is_initd = sysv_exists(unit)
     is_systemd = False
 
-    # load return of systemctl show into dictionary for easy access and return
-    multival = []
-    if out:
-        k = None
-        for line in to_native(out).split('\n'): # systemd can have multiline values delimited with {}
-            if line.strip():
-                if k is None:
-                    if '=' in line:
-                        k,v = line.split('=', 1)
-                        if v.lstrip().startswith('{'):
-                            if not v.rstrip().endswith('}'):
-                                multival.append(line)
-                                continue
-                        result['status'][k] = v.strip()
-                        k = None
-                else:
-                    if line.rstrip().endswith('}'):
-                        result['status'][k] = '\n'.join(multival).strip()
-                        multival = []
-                        k = None
+    # check service data, cannot error out on rc as it changes across versions, assume not found
+    (rc, out, err) = module.run_command("%s show '%s'" % (systemctl, unit))
+    if rc == 0:
+        # load return of systemctl show into dictionary for easy access and return
+        multival = []
+        if out:
+            k = None
+            for line in to_native(out).split('\n'): # systemd can have multiline values delimited with {}
+                if line.strip():
+                    if k is None:
+                        if '=' in line:
+                            k,v = line.split('=', 1)
+                            if v.lstrip().startswith('{'):
+                                if not v.rstrip().endswith('}'):
+                                    multival.append(line)
+                                    continue
+                            result['status'][k] = v.strip()
+                            k = None
                     else:
-                        multival.append(line)
-
-        is_systemd = 'LoadState' in result['status'] and result['status']['LoadState'] != 'not-found'
-
-        # Check for loading error
-        if is_systemd and 'LoadError' in result['status']:
-            module.fail_json(msg="Error loading unit file '%s': %s" % (unit, result['status']['LoadError']))
-
+                        if line.rstrip().endswith('}'):
+                            result['status'][k] = '\n'.join(multival).strip()
+                            multival = []
+                            k = None
+                        else:
+                            multival.append(line)
+
+            is_systemd = 'LoadState' in result['status'] and result['status']['LoadState'] != 'not-found'
+
+            # Check for loading error
+            if is_systemd and 'LoadError' in result['status']:
+                module.fail_json(msg="Error loading unit file '%s': %s" % (unit, result['status']['LoadError']))
 
     # Does service exist?
     found = is_systemd or is_initd


Test patch: