Repository: ansible/ansible-modules-core

Issue:
service module fails to start service when both init.d and systemd scripts exist
This is originally reported in
- https://github.com/ansible/ansible-modules-core/pull/232#issuecomment-63016019
- https://github.com/ansible/ansible-modules-core/pull/232#issuecomment-63119092

In situations where there is both a systemd .service unit file and a init.d startup script, the service module will detect and run the init.d script instead of calling systemd. In this case, I'm on Fedora 20, but I suspect this is true on RHEL/Centos7 as well.

A quick synopsis of the comments above:

With both in place, `self.svc_cmd` returns `/usr/bin/service`
When I delete the script in init.d, `self.svc_cmd` returns `/usr/bin/systemctl`

I suspect line ~475: It's finding service, but prioritizes that over systemd:

```
        # Locate a tool for runtime service management (start, stop etc.)
        if location.get('service', None) and os.path.exists("/etc/init.d/%s" % self.name):
            # SysV init script
            self.svc_cmd = location['service']
        elif location.get('start', None) and os.path.exists("/etc/init/%s.conf" % self.name):

```

Final thought. There are other places, where it does a secondary `systemctl` type check:

```
        # Locate a tool for enable options
        if location.get('chkconfig', None) and os.path.exists("/etc/init.d/%s" % self.name):
            if check_systemd(self.name):
                # service is managed by systemd
                self.enable_cmd = location['systemctl']
            else:
                # we are using a standard SysV service
                self.enable_cmd = location['chkconfig']

```



Discussion:

Resolving patch:
diff --git a/system/service.py b/system/service.py
--- a/system/service.py
+++ b/system/service.py
@@ -393,7 +393,12 @@ def get_service_tools(self):
         for binary in binaries:
             location[binary] = self.module.get_bin_path(binary)
 
-        def check_systemd(name):
+        for initdir in initpaths:
+            initscript = "%s/%s" % (initdir,self.name)
+            if os.path.isfile(initscript):
+                self.svc_initscript = initscript
+
+        def check_systemd(name, initscript):
             # verify systemd is installed (by finding systemctl)
             if not location.get('systemctl', False):
                 return False
@@ -437,46 +442,25 @@ def check_systemd(name):
                 if line.startswith(template_name):
                     self.__systemd_unit = name
                     return True
+
+            # systemd also handles init scripts (and is enabled at this point)
+            if initscript:
+                return True
+
             return False
 
-        # Locate a tool for enable options
-        if location.get('chkconfig', None) and os.path.exists("/etc/init.d/%s" % self.name):
-            if check_systemd(self.name):
-                # service is managed by systemd
-                self.enable_cmd = location['systemctl']
-            else:
-                # we are using a standard SysV service
-                self.enable_cmd = location['chkconfig']
-        elif location.get('update-rc.d', None):
-            if check_systemd(self.name):
-                # service is managed by systemd
-                self.enable_cmd = location['systemctl']
-            elif location['initctl'] and os.path.exists("/etc/init/%s.conf" % self.name):
-                # service is managed by upstart
-                self.enable_cmd = location['initctl']
-            elif location['update-rc.d'] and os.path.exists("/etc/init.d/%s" % self.name):
-                # service is managed by with SysV init scripts, but with update-rc.d
-                self.enable_cmd = location['update-rc.d']
-            else:
-                self.module.fail_json(msg="service not found: %s" % self.name)
-        elif location.get('rc-service', None) and not location.get('systemctl', None):
-            # service is managed by OpenRC
-            self.svc_cmd = location['rc-service']
-            self.enable_cmd = location['rc-update']
-            return
-        elif check_systemd(self.name):
+        # Locate a tool to enable/disable a service
+        if check_systemd(self.name, self.svc_initscript):
             # service is managed by systemd
             self.enable_cmd = location['systemctl']
+            self.svc_cmd = location['systemctl']
+
         elif location['initctl'] and os.path.exists("/etc/init/%s.conf" % self.name):
             # service is managed by upstart
             self.enable_cmd = location['initctl']
-
-        # if this service is managed via upstart, get the current upstart version
-        if self.enable_cmd == location['initctl']:
-            # default the upstart version to something we can compare against
+            # set the upstart version based on the output of 'initctl version'
             self.upstart_version = LooseVersion('0.0.0')
             try:
-                # set the upstart version based on the output of 'initctl version'
                 version_re = re.compile(r'\(upstart (.*)\)')
                 rc,stdout,stderr = self.module.run_command('initctl version')
                 if rc == 0:
@@ -484,33 +468,39 @@ def check_systemd(name):
                     if res:
                         self.upstart_version = LooseVersion(res.groups()[0])
             except:
-                # we'll use the default of 0.0.0 since we couldn't
-                # detect the current upstart version above
-                pass
+                pass  # we'll use the default of 0.0.0
 
-        # Locate a tool for runtime service management (start, stop etc.)
-        if location.get('service', None) and os.path.exists("/etc/init.d/%s" % self.name):
-            # SysV init script
-            self.svc_cmd = location['service']
-        elif location.get('start', None) and os.path.exists("/etc/init/%s.conf" % self.name):
-            # upstart -- rather than being managed by one command, start/stop/restart are actual commands
-            self.svc_cmd = ''
-        else:
-            # still a SysV init script, but /sbin/service isn't installed
-            for initdir in initpaths:
-                initscript = "%s/%s" % (initdir,self.name)
-                if os.path.isfile(initscript):
-                    self.svc_initscript = initscript
+            if location.get('start', False):
+                # upstart -- rather than being managed by one command, start/stop/restart are actual commands
+                self.svc_cmd = ''
+
+        elif location.get('rc-service', False):
+            # service is managed by OpenRC
+            self.svc_cmd = location['rc-service']
+            self.enable_cmd = location['rc-update']
+            return # already have service start/stop tool too!
 
-        # couldn't find anything yet, assume systemd
-        if self.svc_cmd is None and self.svc_initscript is None:
-            if location.get('systemctl'):
-                self.svc_cmd = location['systemctl']
+        elif self.svc_initscript:
+            # service is managed by with SysV init scripts
+            if location.get('update-rc.d', False):
+                # and uses update-rc.d
+                self.enable_cmd = location['update-rc.d']
+            elif location.get('chkconfig', False):
+                # and uses chkconfig
+                self.enable_cmd = location['chkconfig']
+
+        if self.enable_cmd is None:
+            self.module.fail_json(msg="no service or tool found for: %s" % self.name)
+
+        # If no service control tool selected yet, try to see if 'service' is available
+        if not self.svc_cmd and location.get('service', False):
+            self.svc_cmd = location['service']
 
+        # couldn't find anything yet
         if self.svc_cmd is None and not self.svc_initscript:
             self.module.fail_json(msg='cannot find \'service\' binary or init script for service,  possible typo in service name?, aborting')
 
-        if location.get('initctl', None):
+        if location.get('initctl', False):
             self.svc_initctl = location['initctl']
 
     def get_systemd_status_dict(self):


Test patch: