service 7.16 KB
Newer Older
1 2
#!/usr/bin/python

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

20 21 22
SERVICE = None
CHKCONFIG = None

Nikhil Singh committed
23
def _find_binaries(m):
24 25
    # list of possible paths for service/chkconfig binaries
    # with the most probable first
26 27
    global CHKCONFIG
    global SERVICE
28
    global INITCTL
29
    paths = ['/sbin', '/usr/sbin', '/bin', '/usr/bin']
30
    binaries = [ 'service', 'chkconfig', 'update-rc.d', 'initctl']
31 32 33 34 35
    location = dict()

    for binary in binaries:
        location[binary] = None

36 37
    for binary in binaries:
        for path in paths:
38 39 40 41 42 43 44 45 46
            if os.path.exists(path + '/' + binary):
                location[binary] = path + '/' + binary
                break

    if location.get('chkconfig', None):
        CHKCONFIG = location['chkconfig']
    elif location.get('update-rc.d', None):
        CHKCONFIG = location['update-rc.d']
    else:
Nikhil Singh committed
47
        m.fail_json(msg='unable to find chkconfig or update-rc.d binary')
48 49 50
    if location.get('service', None):
        SERVICE = location['service']
    else:
Nikhil Singh committed
51
        m.fail_json(msg='unable to find service binary')
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    if location.get('initctl', None):
        INITCTL = location['initctl']
    else:
        INITCTL = None

def _get_service_status(name):
    rc, status_stdout, status_stderr = _run("%s %s status" % (SERVICE, name))

    # set the running state to None because we don't know it yet
    running = None

    # Check if we got upstart on the system and then the job state
    if INITCTL != None:
        # check the job status by upstart response
        initctl_rc, initctl_status_stdout, initctl_status_stderr = _run("%s status %s" % (INITCTL, name))
        if initctl_status_stdout.find("stop/waiting") != -1:
            running = False
        elif initctl_status_stdout.find("start/running") != -1:
            running = True

    # if the job status is still not known check it by response code
    if running == None:
        if rc == 3:
            running = False
        elif rc == 0:
            running = True

    # if the job status is still not known check it by status output keywords
    if running == None:
        # first tranform the status output that could irritate keyword matching
82 83
        cleanout = status_stdout.lower().replace(name.lower(), '')
        if "stop" in cleanout:
84
            running = False
85
        elif "run" in cleanout and "not" in cleanout:
86
            running = False
87
        elif "run" in cleanout and "not" not in cleanout:
88
            running = True
89
        elif "start" in cleanout and "not" not in cleanout:
90
            running = True
91 92 93
        elif 'could not access pid file' in cleanout:
            running = False
        elif 'is dead and pid file exists' in cleanout:
94
            running = False
95 96 97 98 99 100 101 102 103

    # if the job status is still not known check it by special conditions
    if running == None:
        if name == 'iptables' and status_stdout.find("ACCEPT") != -1:
            # iptables status command output is lame
            # TODO: lookup if we can use a return code for this instead?
            running = True
    
    return running
104

105
def _run(cmd):
106
    # returns (rc, stdout, stderr) from shell command
107 108 109 110
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    stdout, stderr = process.communicate()
    return (process.returncode, stdout, stderr) 

111 112

def _do_enable(name, enable):
113 114 115 116 117 118 119 120 121 122 123 124 125
    # we change argument depending on real binary used
    # update-rc.d wants enable/disable while
    # chkconfig wants on/off
    valid_argument = dict({'on' : 'on', 'off' : 'off'})

    if CHKCONFIG.endswith("update-rc.d"):
        valid_argument['on'] = "enable"
        valid_argument['off'] = "disable"

    if enable.lower() in ['on', 'true', 'yes', 'enable']:
        rc, stdout, stderr = _run("%s %s %s" % (CHKCONFIG, name, valid_argument['on']))
    elif enable.lower() in ['off', 'false', 'no', 'disable']:
        rc, stdout, stderr = _run("%s %s %s" % (CHKCONFIG, name, valid_argument['off']))
126 127 128
    
    return rc, stdout, stderr
    
Nikhil Singh committed
129 130 131 132 133
def main():
    module = AnsibleModule(
        argument_spec = dict(
            name = dict(required=True),
            state = dict(choices=['running', 'started', 'stopped', 'restarted', 'reloaded']),
Michael DeHaan committed
134
            enable = dict(choices=BOOLEANS)
Nikhil Singh committed
135 136 137 138 139 140
        )
    )

    p = module.params
    name = p['name']
    state = p.get('state', None)
Michael DeHaan committed
141
    enable = module.bool(p.get('enable', None))
Nikhil Singh committed
142 143 144 145

    # ===========================================
    # find binaries locations on minion
    _find_binaries(module)
146
    
Nikhil Singh committed
147 148 149
    # ===========================================
    # get service status
    running = _get_service_status(name)
150

Nikhil Singh committed
151 152 153
    # ===========================================
    # Some common variables
    changed = False
154
    rc = 0
155
    err = ''
Nikhil Singh committed
156 157
    out = ''
    
158 159 160 161 162 163
    if enable:
        rc_enable, out_enable, err_enable = _do_enable(name, enable)
        rc += rc_enable
        out += out_enable
        err += err_enable

164
    if state and running == None:
Nikhil Singh committed
165 166
        module.fail_json(msg="failed determining the current service state => state stays unchanged", changed=False)

167
    elif state:
168 169 170 171
        # a state change command has been requested

        # ===========================================
        # determine if we are going to change anything
Nikhil Singh committed
172
        if not running and state in ["started", "running"]:
173
            changed = True
Nikhil Singh committed
174
        elif running and state in ["stopped","reloaded"]:
175 176 177 178 179 180 181
            changed = True
        elif state == "restarted":
            changed = True

        # ===========================================
        # run change commands if we need to
        if changed:
Nikhil Singh committed
182
            if state in ['started', 'running']:
183 184 185
                rc_state, stdout, stderr = _run("%s %s start" % (SERVICE, name))
            elif state == 'stopped':
                rc_state, stdout, stderr = _run("%s %s stop" % (SERVICE, name))
cocoy committed
186 187
            elif state == 'reloaded':
                rc_state, stdout, stderr = _run("%s %s reload" % (SERVICE, name))
188 189 190
            elif state == 'restarted':
                rc1, stdout1, stderr1 = _run("%s %s stop" % (SERVICE, name))
                rc2, stdout2, stderr2 = _run("%s %s start" % (SERVICE, name))
191
                rc_state = rc + rc1 + rc2
192 193
                stdout = stdout1 + stdout2
                stderr = stderr1 + stderr2
194

195 196
            out += stdout
            err += stderr
197
            rc = rc + rc_state
198

199
    if rc != 0:
Nikhil Singh committed
200
        module.fail_json(msg=err)
201

202
    result = {"changed": changed}
203
    rc, stdout, stderr = _run("%s %s status" % (SERVICE, name))
Nikhil Singh committed
204
    module.exit_json(**result);
205
    
206

Nikhil Singh committed
207 208
# this is magic, see lib/ansible/module_common.py 
#<<INCLUDE_ANSIBLE_MODULE_COMMON>> 
209

Nikhil Singh committed
210
main()
211