From b69fb8a1ca2e13b7e48b3cfb089dd2f6ca707c60 Mon Sep 17 00:00:00 2001 From: Andrew Gaffney Date: Fri, 28 Oct 2016 09:29:31 -0600 Subject: [PATCH] Add openwrt_init module for managing services on OpenWrt (#3312) --- .../modules/extras/system/openwrt_init.py | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 lib/ansible/modules/extras/system/openwrt_init.py diff --git a/lib/ansible/modules/extras/system/openwrt_init.py b/lib/ansible/modules/extras/system/openwrt_init.py new file mode 100644 index 0000000000..0a92b18e93 --- /dev/null +++ b/lib/ansible/modules/extras/system/openwrt_init.py @@ -0,0 +1,189 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) 2016, Andrew Gaffney +# +# 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 . + +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 + +# =========================================== +# Main control flow + +def main(): + # 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 = False + (rc, out, err) = module.run_command("%s enabled" % init_script) + + if rc == 0: + enabled = True + + # 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)) + if rc != 0: + 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) + + (rc, psout, pserr) = module.run_command('%s auxwww' % 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()