from __future__ import absolute_import import os import difflib import six import json from salt.exceptions import SaltInvocationError """ Interaction with the circusd daemon =================================== .. code-block:: yaml my_watcher: circusd.watcher_running: - watch: - file: /etc/circusd/conf.d/my_watcher.ini """ def _parse_output(ouptput): try: result = json.loads(ouptput) except ValueError: return None if isinstance(result, dict) and ('status' in result): return result else: return None def _error(ret, error_msg): ret['result'] = False ret['comment'] = error_msg return ret def watcher_running(name, endpoint=None, ssh=None, ssh_keyfile=None, timeout=None, bin_env=None, restart=None): """ Ensure that a circus watcher is running. name The name of the watcher as it appears in 'circusctl status' endpoint circusctl connection endpoint ssh circusctl SSH connection details in the form user@host:port ssh_keyfile circusctl SSH keyfile path timeout circusctl timeout bin_env path to circusctl binary or path to virtualenv with circusctl installed """ ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} is_test = True if __opts__['test'] else False output = __salt__['circusd.status'](name, json=True, endpoint=endpoint, ssh=ssh, ssh_keyfile=ssh_keyfile, timeout=timeout, bin_env=bin_env) result = _parse_output(output) if result is None: return _error(ret, 'circusctl: {0}'.format(output)) # Known status values at the time of writing: # - error # - stopping # - stopped # - starting # - active if result['status'] == 'error': return _error(ret, 'circusctl: watcher {0} not found'.format(name)) is_stopped = result['status'] in ('stopping', 'stopped') start_prefix = '' if is_stopped else 're' verb = 'is set to be' if is_test else 'was' if is_stopped: ret['comment'] = 'Watcher {0} {1} started'.format(name, verb) ret['changes'][name] = True elif restart: ret['comment'] = 'Watcher {0} {1} restarted'.format(name, verb) ret['changes'][name] = True else: ret['comment'] = 'Watcher {0} is already running'.format(name) ret['result'] = True return ret if is_test: ret['result'] = None return ret output = __salt__['circusd.{0}start'.format(start_prefix)](name, json=True, waiting=True, endpoint=endpoint, ssh=ssh, ssh_keyfile=ssh_keyfile, timeout=timeout, bin_env=bin_env) result = _parse_output(output) if result is None: return _error(ret, 'circusctl: {0}'.format(output)) if result['status'] == 'error': return _error(ret, 'circusctl: {0}'.format(result['reason'])) if result['status'] != 'ok': return _error(ret, 'circusctl: {0}'.format(output)) if not result['info']['started'] and not result['info']['kept']: return _error(ret, 'Watcher {0} failed to start'.format(name)) ret['result'] = True return ret # NOTE: As of Salt version 2016.3.2, mod_watch is not called if both # watcher_running and the watched state report changes, even though the # documenation states that mod_watch is called whenever the watched state # report changes. This means we cannot do a 'circusctl reloadconfig' in # mod_watch as it won't be called if the watcher wasn't running and # watcher_running has to start it (thereby causing it to return changes). # Instead, we get reloadconfig to run before this state using a combination # of 'onchanges' requisites and 'test.succeed_with(out)_changes' states. def mod_watch(name, endpoint=None, ssh=None, ssh_keyfile=None, timeout=None, bin_env=None, **kwargs): # Always restart the watcher on changes. return watcher_running(name=name, endpoint=endpoint, ssh=ssh, ssh_keyfile=ssh_keyfile, timeout=timeout, bin_env=bin_env, restart=True)