mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 01:30:20 +00:00

Without allow_unicode=True, unicode characters are processed through the str representer and on Python 2 are dumped as a Unicode code point (i.e. a literal \u0414). This commit makes allow_unicode=True the default in our salt.utils.yamlloader.safe_dump() helper. It also adds a new salt.utils.yamlloader.dump() helper which wraps yaml.dump() and also makes allow_unicode=True the default. To make importing and using our custom yaml loader/dumper easier, a convenience module called salt.utils.yaml has been added, which does a wildcard import from both salt.utils.yamldumper and salt.utils.yamlloader. Refs to yaml.load/dump and yaml.safe_load/safe_dump have been updated to salt.utils.yaml, to ensure that unicode is handled properly.
572 lines
20 KiB
Python
572 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
|
'''
|
|
Module for managing the Salt beacons on a minion
|
|
|
|
.. versionadded:: 2015.8.0
|
|
|
|
'''
|
|
|
|
# Import Python libs
|
|
from __future__ import absolute_import
|
|
import difflib
|
|
import logging
|
|
import os
|
|
|
|
# Import Salt libs
|
|
import salt.ext.six as six
|
|
import salt.utils.event
|
|
import salt.utils.files
|
|
import salt.utils.yaml
|
|
from salt.ext.six.moves import map
|
|
|
|
# Get logging started
|
|
log = logging.getLogger(__name__)
|
|
|
|
__func_alias__ = {
|
|
'list_': 'list',
|
|
'reload_': 'reload'
|
|
}
|
|
|
|
|
|
def list_(return_yaml=True,
|
|
include_pillar=True,
|
|
include_opts=True):
|
|
'''
|
|
List the beacons currently configured on the minion
|
|
|
|
:param return_yaml: Whether to return YAML formatted output,
|
|
default True
|
|
|
|
:param include_pillar: Whether to include beacons that are
|
|
configured in pillar, default is True.
|
|
|
|
:param include_opts: Whether to include beacons that are
|
|
configured in opts, default is True.
|
|
|
|
:return: List of currently configured Beacons.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.list
|
|
|
|
'''
|
|
beacons = None
|
|
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'list',
|
|
'include_pillar': include_pillar,
|
|
'include_opts': include_opts},
|
|
'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_list_complete', wait=30)
|
|
log.debug('event_ret {0}'.format(event_ret))
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret = {}
|
|
ret['result'] = False
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
return ret
|
|
|
|
if beacons:
|
|
if return_yaml:
|
|
tmp = {'beacons': beacons}
|
|
return salt.utils.yaml.safe_dump(tmp, default_flow_style=False)
|
|
else:
|
|
return beacons
|
|
else:
|
|
return {'beacons': {}}
|
|
|
|
|
|
def list_available(return_yaml=True):
|
|
'''
|
|
List the beacons currently available on the minion
|
|
|
|
:param return_yaml: Whether to return YAML formatted output, default True
|
|
:return: List of currently configured Beacons.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.list_available
|
|
|
|
'''
|
|
beacons = None
|
|
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'list_available'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_list_available_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret = {}
|
|
ret['result'] = False
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
return ret
|
|
|
|
if beacons:
|
|
if return_yaml:
|
|
tmp = {'beacons': beacons}
|
|
return salt.utils.yaml.safe_dump(tmp, default_flow_style=False)
|
|
else:
|
|
return beacons
|
|
else:
|
|
return {'beacons': {}}
|
|
|
|
|
|
def add(name, beacon_data, **kwargs):
|
|
'''
|
|
Add a beacon on the minion
|
|
|
|
:param name: Name of the beacon to configure
|
|
:param beacon_data: Dictionary or list containing configuration for beacon.
|
|
:return: Boolean and status message on success or failure of add.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.add ps "[{'salt-master': 'stopped', 'apache2': 'stopped'}]"
|
|
|
|
'''
|
|
ret = {'comment': 'Failed to add beacon {0}.'.format(name),
|
|
'result': False}
|
|
|
|
if name in list_(return_yaml=False):
|
|
ret['comment'] = 'Beacon {0} is already configured.'.format(name)
|
|
return ret
|
|
|
|
if name not in list_available(return_yaml=False):
|
|
ret['comment'] = 'Beacon "{0}" is not available.'.format(name)
|
|
return ret
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Beacon: {0} would be added.'.format(name)
|
|
else:
|
|
try:
|
|
# Attempt to load the beacon module so we have access to the validate function
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'name': name,
|
|
'beacon_data': beacon_data,
|
|
'func': 'validate_beacon'},
|
|
'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_validation_complete', wait=30)
|
|
valid = event_ret['valid']
|
|
vcomment = event_ret['vcomment']
|
|
|
|
if not valid:
|
|
ret['result'] = False
|
|
ret['comment'] = ('Beacon {0} configuration invalid, '
|
|
'not adding.\n{1}'.format(name, vcomment))
|
|
return ret
|
|
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
|
|
try:
|
|
res = __salt__['event.fire']({'name': name,
|
|
'beacon_data': beacon_data,
|
|
'func': 'add'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_add_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
if name in beacons and beacons[name] == beacon_data:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Added beacon: {0}.'.format(name)
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = event_ret['comment']
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
return ret
|
|
|
|
|
|
def modify(name, beacon_data, **kwargs):
|
|
'''
|
|
Modify an existing beacon
|
|
|
|
:param name: Name of the beacon to configure
|
|
:param beacon_data: Dictionary or list containing updated configuration for beacon.
|
|
:return: Boolean and status message on success or failure of modify.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.modify ps "[{'salt-master': 'stopped', 'apache2': 'stopped'}]"
|
|
'''
|
|
|
|
ret = {'comment': '',
|
|
'result': True}
|
|
|
|
current_beacons = list_(return_yaml=False)
|
|
if name not in current_beacons:
|
|
ret['comment'] = 'Beacon {0} is not configured.'.format(name)
|
|
return ret
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Beacon: {0} would be added.'.format(name)
|
|
else:
|
|
try:
|
|
# Attempt to load the beacon module so we have access to the validate function
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'name': name,
|
|
'beacon_data': beacon_data,
|
|
'func': 'validate_beacon'},
|
|
'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_validation_complete', wait=30)
|
|
valid = event_ret['valid']
|
|
vcomment = event_ret['vcomment']
|
|
|
|
if not valid:
|
|
ret['result'] = False
|
|
ret['comment'] = ('Beacon {0} configuration invalid, '
|
|
'not adding.\n{1}'.format(name, vcomment))
|
|
return ret
|
|
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon modify failed.'
|
|
|
|
if not valid:
|
|
ret['result'] = False
|
|
ret['comment'] = ('Beacon {0} configuration invalid, '
|
|
'not modifying.\n{1}'.format(name, vcomment))
|
|
return ret
|
|
|
|
_current = current_beacons[name]
|
|
_new = beacon_data
|
|
|
|
if _new == _current:
|
|
ret['comment'] = 'Job {0} in correct state'.format(name)
|
|
return ret
|
|
|
|
_current_lines = []
|
|
for _item in _current:
|
|
_current_lines.extend(['{0}:{1}\n'.format(key, value)
|
|
for (key, value) in six.iteritems(_item)])
|
|
_new_lines = []
|
|
for _item in _new:
|
|
_new_lines.extend(['{0}:{1}\n'.format(key, value)
|
|
for (key, value) in six.iteritems(_item)])
|
|
_diff = difflib.unified_diff(_current_lines, _new_lines)
|
|
|
|
ret['changes'] = {}
|
|
ret['changes']['diff'] = ''.join(_diff)
|
|
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'modify'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_modify_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
if name in beacons and beacons[name] == beacon_data:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Modified beacon: {0}.'.format(name)
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = event_ret['comment']
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
return ret
|
|
|
|
|
|
def delete(name, **kwargs):
|
|
'''
|
|
Delete a beacon item
|
|
|
|
:param name: Name of the beacon to delete
|
|
:return: Boolean and status message on success or failure of delete.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.delete ps
|
|
|
|
salt '*' beacons.delete load
|
|
|
|
'''
|
|
|
|
ret = {'comment': 'Failed to delete beacon {0}.'.format(name),
|
|
'result': False}
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Beacon: {0} would be deleted.'.format(name)
|
|
else:
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'name': name, 'func': 'delete'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_delete_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
if name not in beacons:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Deleted beacon: {0}.'.format(name)
|
|
return ret
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = event_ret['comment']
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon add failed.'
|
|
return ret
|
|
|
|
|
|
def save():
|
|
'''
|
|
Save all beacons on the minion
|
|
|
|
:return: Boolean and status message on success or failure of save.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.save
|
|
'''
|
|
|
|
ret = {'comment': [],
|
|
'result': True}
|
|
|
|
beacons = list_(return_yaml=False, include_pillar=False)
|
|
|
|
# move this file into an configurable opt
|
|
sfn = '{0}/{1}/beacons.conf'.format(__opts__['config_dir'],
|
|
os.path.dirname(__opts__['default_include']))
|
|
if beacons:
|
|
tmp = {'beacons': beacons}
|
|
yaml_out = salt.utils.yaml.safe_dump(tmp, default_flow_style=False)
|
|
else:
|
|
yaml_out = ''
|
|
|
|
try:
|
|
with salt.utils.files.fopen(sfn, 'w+') as fp_:
|
|
fp_.write(yaml_out)
|
|
ret['comment'] = 'Beacons saved to {0}.'.format(sfn)
|
|
except (IOError, OSError):
|
|
ret['comment'] = 'Unable to write to beacons file at {0}. Check permissions.'.format(sfn)
|
|
ret['result'] = False
|
|
return ret
|
|
|
|
|
|
def enable(**kwargs):
|
|
'''
|
|
Enable all beacons on the minion
|
|
|
|
:return: Boolean and status message on success or failure of enable.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.enable
|
|
'''
|
|
|
|
ret = {'comment': [],
|
|
'result': True}
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['comment'] = 'Beacons would be enabled.'
|
|
else:
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'enable'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_enabled_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
if 'enabled' in beacons and beacons['enabled']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Enabled beacons on minion.'
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = 'Failed to enable beacons on minion.'
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacons enable job failed.'
|
|
return ret
|
|
|
|
|
|
def disable(**kwargs):
|
|
'''
|
|
Disable all beaconsd jobs on the minion
|
|
|
|
:return: Boolean and status message on success or failure of disable.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.disable
|
|
'''
|
|
|
|
ret = {'comment': [],
|
|
'result': True}
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['comment'] = 'Beacons would be disabled.'
|
|
else:
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'disable'}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_disabled_complete', wait=30)
|
|
log.debug('event_ret {0}'.format(event_ret))
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
if 'enabled' in beacons and not beacons['enabled']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Disabled beacons on minion.'
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = 'Failed to disable beacons on minion.'
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacons enable job failed.'
|
|
return ret
|
|
|
|
|
|
def _get_beacon_config_dict(beacon_config):
|
|
beacon_config_dict = {}
|
|
if isinstance(beacon_config, list):
|
|
list(map(beacon_config_dict.update, beacon_config))
|
|
else:
|
|
beacon_config_dict = beacon_config
|
|
|
|
return beacon_config_dict
|
|
|
|
|
|
def enable_beacon(name, **kwargs):
|
|
'''
|
|
Enable beacon on the minion
|
|
|
|
:name: Name of the beacon to enable.
|
|
:return: Boolean and status message on success or failure of enable.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.enable_beacon ps
|
|
'''
|
|
|
|
ret = {'comment': [],
|
|
'result': True}
|
|
|
|
if not name:
|
|
ret['comment'] = 'Beacon name is required.'
|
|
ret['result'] = False
|
|
return ret
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['comment'] = 'Beacon {0} would be enabled.'.format(name)
|
|
else:
|
|
_beacons = list_(return_yaml=False)
|
|
if name not in _beacons:
|
|
ret['comment'] = 'Beacon {0} is not currently configured.'.format(name)
|
|
ret['result'] = False
|
|
return ret
|
|
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'enable_beacon', 'name': name}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_enabled_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
beacon_config_dict = _get_beacon_config_dict(beacons[name])
|
|
|
|
if 'enabled' in beacon_config_dict and beacon_config_dict['enabled']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Enabled beacon {0} on minion.'.format(name)
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = 'Failed to enable beacon {0} on minion.'.format(name)
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = event_ret['comment']
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon enable job failed.'
|
|
return ret
|
|
|
|
|
|
def disable_beacon(name, **kwargs):
|
|
'''
|
|
Disable beacon on the minion
|
|
|
|
:name: Name of the beacon to disable.
|
|
:return: Boolean and status message on success or failure of disable.
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt '*' beacons.disable_beacon ps
|
|
'''
|
|
|
|
ret = {'comment': [],
|
|
'result': True}
|
|
|
|
if not name:
|
|
ret['comment'] = 'Beacon name is required.'
|
|
ret['result'] = False
|
|
return ret
|
|
|
|
if 'test' in kwargs and kwargs['test']:
|
|
ret['comment'] = 'Beacons would be enabled.'
|
|
else:
|
|
_beacons = list_(return_yaml=False)
|
|
if name not in _beacons:
|
|
ret['comment'] = 'Beacon {0} is not currently configured.'.format(name)
|
|
ret['result'] = False
|
|
return ret
|
|
|
|
try:
|
|
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
|
res = __salt__['event.fire']({'func': 'disable_beacon', 'name': name}, 'manage_beacons')
|
|
if res:
|
|
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_disabled_complete', wait=30)
|
|
if event_ret and event_ret['complete']:
|
|
beacons = event_ret['beacons']
|
|
beacon_config_dict = _get_beacon_config_dict(beacons[name])
|
|
|
|
if 'enabled' in beacon_config_dict and not beacon_config_dict['enabled']:
|
|
ret['result'] = True
|
|
ret['comment'] = 'Disabled beacon {0} on minion.'.format(name)
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = 'Failed to disable beacon on minion.'
|
|
else:
|
|
ret['result'] = False
|
|
ret['comment'] = event_ret['comment']
|
|
return ret
|
|
except KeyError:
|
|
# Effectively a no-op, since we can't really return without an event system
|
|
ret['comment'] = 'Event module not available. Beacon disable job failed.'
|
|
return ret
|