ansible/library/web_infrastructure/supervisorctl

220 lines
7.8 KiB
Text
Raw Normal View History

2012-08-08 16:59:46 +02:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Matt Wright <matt@nobien.net>
#
# 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/>.
#
import os
2012-08-08 16:59:46 +02:00
DOCUMENTATION = '''
---
module: supervisorctl
short_description: Manage the state of a program or group of programs running via supervisord
description:
- Manage the state of a program or group of programs running via supervisord
version_added: "0.7"
options:
name:
description:
- The name of the supervisord program/group to manage. It will be taken as group name when it end with a colon I(:).
required: true
default: null
config:
description:
- configuration file path, passed as -c to supervisorctl.
required: false
default: null
version_added: "1.3"
server_url:
description:
- URL on which supervisord server is listening, passed as -s to supervisorctl.
required: false
default: null
version_added: "1.3"
username:
description:
- username to use for authentication with server, passed as -u to supervisorctl.
required: false
default: null
version_added: "1.3"
password:
description:
- password to use for authentication with server, passed as -p to supervisorctl.
required: false
default: null
version_added: "1.3"
state:
description:
- The desired state of program/group. Affected programs' name will be returned in I(affected) field of the result.
required: true
default: null
choices: [ "present", "started", "stopped", "restarted" ]
supervisorctl_path:
description:
- Path to supervisorctl executable to use.
required: false
default: null
version_added: "1.4"
notes:
- When C(state) = I(present), will call C(supervisorctl reread) then call C(supervisorctl add) if the program/group is not exists.
- When C(state) = I(restarted), will call C(supervisorctl update) then call C(supervisorctl restart).
requirements: [ "supervisorctl" ]
author: Matt Wright, Aaron Wang <inetfuture@gmail.com>
'''
2013-06-02 00:36:44 +02:00
EXAMPLES = '''
# Manage the state of program to be in 'started' state.
- supervisorctl: name=my_app state=started
# Manage the state of program group to be in 'started' state.
- supervisorctl: name='my_apps:' state=started
# Restart my_app, reading supervisorctl configuration from a specified file.
- supervisorctl: name=my_app state=restarted config=/var/opt/my_project/supervisord.conf
# Restart my_app, connecting to supervisord with credentials and server URL.
- supervisorctl: name=my_app state=restarted username=test password=testpass server_url=http://localhost:9001
2013-06-02 00:36:44 +02:00
'''
2012-08-08 16:59:46 +02:00
def main():
arg_spec = dict(
name=dict(required=True),
config=dict(required=False),
server_url=dict(required=False),
username=dict(required=False),
password=dict(required=False),
supervisorctl_path=dict(required=False),
2012-08-30 18:37:02 +02:00
state=dict(required=True, choices=['present', 'started', 'restarted', 'stopped'])
2012-08-08 16:59:46 +02:00
)
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
2012-08-08 16:59:46 +02:00
name = module.params['name']
is_group = False
if name.endswith(':'):
is_group = True
name = name.rstrip(':')
2012-08-08 16:59:46 +02:00
state = module.params['state']
config = module.params.get('config')
server_url = module.params.get('server_url')
username = module.params.get('username')
password = module.params.get('password')
supervisorctl_path = module.params.get('supervisorctl_path')
if supervisorctl_path:
supervisorctl_path = os.path.expanduser(supervisorctl_path)
if os.path.exists(supervisorctl_path) and module.is_executable(supervisorctl_path):
supervisorctl_args = [supervisorctl_path]
else:
module.fail_json(
msg="Provided path to supervisorctl does not exist or isn't executable: %s" % supervisorctl_path)
else:
supervisorctl_args = [module.get_bin_path('supervisorctl', True)]
2013-07-20 18:47:46 +02:00
if config:
supervisorctl_args.extend(['-c', os.path.expanduser(config)])
2013-07-20 18:47:46 +02:00
if server_url:
supervisorctl_args.extend(['-s', server_url])
if username:
supervisorctl_args.extend(['-u', username])
if password:
supervisorctl_args.extend(['-p', password])
def run_supervisorctl(cmd, name=None, **kwargs):
args = list(supervisorctl_args) # copy the master args
args.append(cmd)
if name:
args.append(name)
return module.run_command(args, **kwargs)
def get_matched_processes():
matched = []
rc, out, err = run_supervisorctl('status')
for line in out.splitlines():
# One status line may look like one of these two:
# process not in group:
# echo_date_lonely RUNNING pid 7680, uptime 13:22:18
# process in group:
# echo_date_group:echo_date_00 RUNNING pid 7681, uptime 13:22:18
fields = [field for field in line.split(' ') if field != '']
process_name = fields[0]
status = fields[1]
if is_group:
# If there is ':', this process must be in a group.
if ':' in process_name:
group = process_name.split(':')[0]
if group != name:
continue
else:
continue
2012-08-30 18:37:02 +02:00
else:
if process_name != name:
continue
2012-08-30 18:37:02 +02:00
matched.append((process_name, status))
return matched
2012-08-08 16:59:46 +02:00
def take_action_on_processes(processes, status_filter, action, expected_result):
to_take_action_on = []
for process_name, status in processes:
if status_filter(status):
to_take_action_on.append(process_name)
2012-08-08 16:59:46 +02:00
if len(to_take_action_on) == 0:
module.exit_json(changed=False, name=name, state=state)
if module.check_mode:
module.exit_json(changed=True)
for process_name in to_take_action_on:
rc, out, err = run_supervisorctl(action, process_name)
if '%s: %s' % (process_name, expected_result) not in out:
module.fail_json(msg=out)
2012-08-08 16:59:46 +02:00
module.exit_json(changed=True, name=name, state=state, affected=to_take_action_on)
2012-08-08 16:59:46 +02:00
if state == 'restarted':
rc, out, err = run_supervisorctl('update')
processes = get_matched_processes()
take_action_on_processes(processes, lambda s: True, 'restart', 'started')
2012-08-08 16:59:46 +02:00
processes = get_matched_processes()
2012-08-08 16:59:46 +02:00
if state == 'present':
if len(processes) > 0:
module.exit_json(changed=False, name=name, state=state)
2012-08-08 16:59:46 +02:00
if module.check_mode:
module.exit_json(changed=True)
run_supervisorctl('reread', check_rc=True)
rc, out, err = run_supervisorctl('add', name)
if '%s: added process group' % name in out:
2012-08-08 16:59:46 +02:00
module.exit_json(changed=True, name=name, state=state)
else:
module.fail_json(msg=out, name=name, state=state)
2012-08-08 16:59:46 +02:00
if state == 'started':
take_action_on_processes(processes, lambda s: s != 'RUNNING', 'start', 'started')
2012-08-08 16:59:46 +02:00
if state == 'stopped':
take_action_on_processes(processes, lambda s: s == 'RUNNING', 'stop', 'stopped')
2012-08-08 16:59:46 +02:00
2013-12-02 21:13:49 +01:00
# import module snippets
from ansible.module_utils.basic import *
2012-08-08 16:59:46 +02:00
main()