Adding list_available to show loaded & available beacons on the minion. Swapping out the way we validate beacon configuration to be able to support adding & modifying custom beacons.

This commit is contained in:
Gareth J. Greenaway 2017-08-23 08:30:09 -07:00
parent d5abd98406
commit 803247f842
3 changed files with 117 additions and 59 deletions

View file

@ -214,26 +214,42 @@ class Beacon(object):
return True
def list_available_beacons(self):
'''
List the available beacons
'''
_beacons = ['{0}'.format(_beacon.replace('.beacon', ''))
for _beacon in list(self.beacons) if '.beacon' in _beacon]
# Fire the complete event back along with the list of beacons
evt = salt.utils.event.get_event('minion', opts=self.opts)
evt.fire_event({'complete': True, 'beacons': _beacons},
tag='/salt/minion/minion_beacons_list_available_complete')
return True
def validate_beacon(self, name, beacon_data):
'''
Return available beacon functions
'''
validate_str = '{}.validate'
validate_str = '{}.validate'.format(name)
# Run the validate function if it's available,
# otherwise there is a warning about it being missing
if validate_str in self.beacons:
valid, vcomment = self.beacons[validate_str](b_config[mod])
if not valid:
log.info('Beacon %s configuration invalid, '
'not running.\n%s', mod, vcomment)
continue
if 'enabled' in beacon_data:
del beacon_data['enabled']
valid, vcomment = self.beacons[validate_str](beacon_data)
else:
log.info('Beacon {0} does not have a validate'
' function, skipping validation.'.format(name))
valid = True
# Fire the complete event back along with the list of beacons
evt = salt.utils.event.get_event('minion', opts=self.opts)
log.debug('=== self.beacons {} ==='.format(list(self.beacons)))
evt.fire_event({'complete': True, 'beacons': list(self.beacons)},
tag='/salt/minion/minion_available_beacons')
evt.fire_event({'complete': True,
'vcomment': vcomment,
'valid': valid},
tag='/salt/minion/minion_beacon_validation_complete')
return True

View file

@ -1930,6 +1930,8 @@ class Minion(MinionBase):
self.beacons.disable_beacon(name)
elif func == u'list':
self.beacons.list_beacons()
elif func == u'list_available':
self.beacons.list_available_beacons()
elif func == u'validate_beacon':
self.beacons.validate_beacon(name, beacon_data)

View file

@ -11,6 +11,7 @@ from __future__ import absolute_import
import difflib
import logging
import os
import six
import yaml
# Import Salt libs
@ -69,6 +70,47 @@ def list_(return_yaml=True):
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}
yaml_out = yaml.safe_dump(tmp, default_flow_style=False)
return yaml_out
else:
return beacons
else:
return {'beacons': {}}
def add(name, beacon_data, **kwargs):
'''
Add a beacon on the minion
@ -95,43 +137,34 @@ def add(name, beacon_data, **kwargs):
ret['result'] = True
ret['comment'] = 'Beacon: {0} would be added.'.format(name)
else:
# Attempt to load the beacon module so we have access to the validate function
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, 'func': 'available_beacons'}, 'manage_beacons')
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_available_beacons', wait=30)
log.debug('=== event_ret {} ==='.format(event_ret))
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_validation_complete', wait=30)
valid = event_ret['valid']
vcomment = event_ret['vcomment']
beacon_module = __import__('salt.beacons.' + name, fromlist=['validate'])
log.debug('Successfully imported beacon.')
except ImportError:
ret['comment'] = 'Beacon {0} does not exist'.format(name)
return ret
if not valid:
ret['result'] = False
ret['comment'] = ('Beacon {0} configuration invalid, '
'not adding.\n{1}'.format(name, vcomment))
return ret
# Attempt to validate
if hasattr(beacon_module, 'validate'):
_beacon_data = beacon_data
if 'enabled' in _beacon_data:
del _beacon_data['enabled']
valid, vcomment = beacon_module.validate(_beacon_data)
else:
log.info('Beacon {0} does not have a validate'
' function, skipping validation.'.format(name))
valid = True
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:
eventer = salt.utils.event.get_event('minion', opts=__opts__)
res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'add'}, 'manage_beacons')
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)
log.debug('=== event_ret {} ==='.format(event_ret))
if event_ret and event_ret['complete']:
beacons = event_ret['beacons']
if name in beacons and beacons[name] == beacon_data:
@ -171,29 +204,32 @@ def modify(name, beacon_data, **kwargs):
ret['result'] = True
ret['comment'] = 'Beacon: {0} would be added.'.format(name)
else:
# Attempt to load the beacon module so we have access to the validate function
try:
beacon_module = __import__('salt.beacons.' + name, fromlist=['validate'])
log.debug('Successfully imported beacon.')
except ImportError:
ret['comment'] = 'Beacon {0} does not exist'.format(name)
return ret
# 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']
# Attempt to validate
if hasattr(beacon_module, 'validate'):
_beacon_data = beacon_data
if 'enabled' in _beacon_data:
del _beacon_data['enabled']
valid, vcomment = beacon_module.validate(_beacon_data)
else:
log.info('Beacon {0} does not have a validate'
' function, skipping validation.'.format(name))
valid = True
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 adding.\n{1}'.format(name, vcomment))
'not modifying.\n{1}'.format(name, vcomment))
return ret
_current = current_beacons[name]
@ -203,10 +239,14 @@ def modify(name, beacon_data, **kwargs):
ret['comment'] = 'Job {0} in correct state'.format(name)
return ret
_current_lines = ['{0}:{1}\n'.format(key, value)
for (key, value) in sorted(_current.items())]
_new_lines = ['{0}:{1}\n'.format(key, value)
for (key, value) in sorted(_new.items())]
_current_lines = []
for _item in _current:
_current_lines.extend(['{0}:{1}\n'.format(key, value)
for (key, value) in six.iteritems(_current[0])])
_new_lines = []
for _item in _new:
_new_lines.extend(['{0}:{1}\n'.format(key, value)
for (key, value) in six.iteritems(_new[0])])
_diff = difflib.unified_diff(_current_lines, _new_lines)
ret['changes'] = {}