summaryrefslogtreecommitdiff
path: root/system/openwrt_init.py
blob: 7b4f7f79d37d9917d1221b1451b5f4ba9ba28b13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016, Andrew Gaffney <andrew@agaffney.org>
#
# 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/>.

ANSIBLE_METADATA = {'status': ['preview'],
                    'supported_by': 'committer',
                    'version': '1.0'}

DOCUMENTATION = '''
module: openwrt_init
author:
    - "Andrew Gaffney (@agaffney)"
version_added: "2.3"
short_description:  Manage services on OpenWrt.
description:
    - Controls OpenWrt services on remote hosts.
options:
    name:
        required: true
        description:
            - Name of the service.
        aliases: ['service']
    state:
        required: false
        default: null
        choices: [ 'started', 'stopped', 'restarted', 'reloaded' ]
        description:
            - C(started)/C(stopped) are idempotent actions that will not run commands unless necessary.
              C(restarted) will always bounce the service. C(reloaded) will always reload.
    enabled:
        required: false
        choices: [ "yes", "no" ]
        default: null
        description:
            - Whether the service should start on boot. B(At least one of state and enabled are required.)
    pattern:
        required: false
        description:
        - If the service does not respond to the 'running' command, name a
          substring to look for as would be found in the output of the I(ps)
          command as a stand-in for a 'running' result.  If the string is found,
          the service will be assumed to be running.
notes:
    - One option other than name is required.
requirements:
    - An OpenWrt system
'''

EXAMPLES = '''
# Example action to start service httpd, if not running
- openwrt_init:
    state: started
    name: httpd

# Example action to stop service cron, if running
- openwrt_init:
    name: cron
    state: stopped

# Example action to reload service httpd, in all cases
- openwrt_init:
    name: httpd
    state: reloaded

# Example action to enable service httpd
- openwrt_init:
    name: httpd
    enabled: yes
'''

RETURN = '''
'''

import os
import glob
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes, to_native

module = None
init_script = None

# ===============================
# Check if service is enabled
def is_enabled():
    (rc, out, err) = module.run_command("%s enabled" % init_script)
    if rc == 0:
        return True
    return False

# ===========================================
# Main control flow

def main():
    global module, init_script
    # init
    module = AnsibleModule(
        argument_spec = dict(
                name = dict(required=True, type='str', aliases=['service']),
                state = dict(choices=['started', 'stopped', 'restarted', 'reloaded'], type='str'),
                enabled = dict(type='bool'),
                pattern = dict(required=False, default=None),
            ),
            supports_check_mode=True,
            required_one_of=[['state', 'enabled']],
        )

    # initialize
    service = module.params['name']
    init_script = '/etc/init.d/' + service
    rc = 0
    out = err = ''
    result = {
        'name':  service,
        'changed': False,
    }

    # check if service exists
    if not os.path.exists(init_script):
        module.fail_json(msg='service %s does not exist' % service)

    # Enable/disable service startup at boot if requested
    if module.params['enabled'] is not None:
        # do we need to enable the service?
        enabled = is_enabled()

        # default to current state
        result['enabled'] = enabled

        # Change enable/disable if needed
        if enabled != module.params['enabled']:
            result['changed'] = True
            if module.params['enabled']:
                action = 'enable'
            else:
                action = 'disable'

            if not module.check_mode:
                (rc, out, err) = module.run_command("%s %s" % (init_script, action))
                # openwrt init scripts can return a non-zero exit code on a successful 'enable'
                # command if the init script doesn't contain a STOP value, so we ignore the exit
                # code and explicitly check if the service is now in the desired state
                if is_enabled() != module.params['enabled']:
                    module.fail_json(msg="Unable to %s service %s: %s" % (action, service, err))

            result['enabled'] = not enabled

    if module.params['state'] is not None:
        running = False

        # check if service is currently running
        if module.params['pattern']:
            # Find ps binary
            psbin = module.get_bin_path('ps', True)

            # this should be busybox ps, so we only want/need to the 'w' option
            (rc, psout, pserr) = module.run_command('%s w' % psbin)
            # If rc is 0, set running as appropriate
            if rc == 0:
                lines = psout.split("\n")
                for line in lines:
                    if module.params['pattern'] in line and not "pattern=" in line:
                        # so as to not confuse ./hacking/test-module
                        running = True
                        break
        else:
            (rc, out, err) = module.run_command("%s running" % init_script)
            if rc == 0:
                running = True

        # default to desired state
        result['state'] = module.params['state']

        # determine action, if any
        action = None
        if module.params['state'] == 'started':
            if not running:
                action = 'start'
                result['changed'] = True
        elif module.params['state'] == 'stopped':
            if running:
                action = 'stop'
                result['changed'] = True
        else:
            action = module.params['state'][:-2] # remove 'ed' from restarted/reloaded
            result['state'] = 'started'
            result['changed'] = True

        if action:
            if not module.check_mode:
                (rc, out, err) = module.run_command("%s %s" % (init_script, action))
                if rc != 0:
                    module.fail_json(msg="Unable to %s service %s: %s" % (action, service, err))


    module.exit_json(**result)

if __name__ == '__main__':
    main()