mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
Merge pull request #16339 from cachedout/module_fail_reasons
Module failure reporting
This commit is contained in:
commit
d4eaf2323e
@ -154,6 +154,15 @@ The ``__virtual__`` function is used to return either a
|
||||
False is returned then the module is not loaded, if a string is returned then
|
||||
the module is loaded with the name of the string.
|
||||
|
||||
.. note::
|
||||
|
||||
Optionally, modules may additionally return a list of reasons that a module could
|
||||
not be loaded. For example, if a dependency for 'my_mod' was not met, a
|
||||
__virtual__ function could do as follows:
|
||||
|
||||
return False, ['My Module must be installed before this module can be
|
||||
used.']
|
||||
|
||||
This means that the package manager modules can be presented as the ``pkg`` module
|
||||
regardless of what the actual module is named.
|
||||
|
||||
|
@ -103,7 +103,12 @@ class ZeroMQCaller(object):
|
||||
ret['jid']
|
||||
)
|
||||
if fun not in self.minion.functions:
|
||||
sys.stderr.write('Function {0} is not available\n'.format(fun))
|
||||
sys.stderr.write('Function {0} is not available.'.format(fun))
|
||||
mod_name = fun.split('.')[0]
|
||||
if mod_name in self.minion.function_errors:
|
||||
sys.stderr.write(' Possible reasons: {0}\n'.format(self.minion.function_errors[mod_name]))
|
||||
else:
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(-1)
|
||||
try:
|
||||
sdata = {
|
||||
|
@ -110,7 +110,7 @@ def _create_loader(
|
||||
)
|
||||
|
||||
|
||||
def minion_mods(opts, context=None, whitelist=None):
|
||||
def minion_mods(opts, context=None, whitelist=None, include_errors=False):
|
||||
'''
|
||||
Load execution modules
|
||||
|
||||
@ -136,7 +136,8 @@ def minion_mods(opts, context=None, whitelist=None):
|
||||
functions = load.gen_functions(
|
||||
pack,
|
||||
whitelist=whitelist,
|
||||
provider_overrides=True
|
||||
provider_overrides=True,
|
||||
include_errors=include_errors
|
||||
)
|
||||
# Enforce dependencies of module functions from "functions"
|
||||
Depends.enforce_dependencies(functions)
|
||||
@ -707,11 +708,12 @@ class Loader(object):
|
||||
return funcs
|
||||
|
||||
def gen_functions(self, pack=None, virtual_enable=True, whitelist=None,
|
||||
provider_overrides=False):
|
||||
provider_overrides=False, include_errors=False):
|
||||
'''
|
||||
Return a dict of functions found in the defined module_dirs
|
||||
'''
|
||||
funcs = {}
|
||||
error_funcs = {}
|
||||
self.load_modules()
|
||||
for mod in self.modules:
|
||||
# If this is a proxy minion then MOST modules cannot work. Therefore, require that
|
||||
@ -765,12 +767,16 @@ class Loader(object):
|
||||
if virtual_enable:
|
||||
# if virtual modules are enabled, we need to look for the
|
||||
# __virtual__() function inside that module and run it.
|
||||
(virtual_ret, virtual_name) = self.process_virtual(mod,
|
||||
module_name)
|
||||
(virtual_ret, virtual_name, virtual_errors) = self.process_virtual(
|
||||
mod,
|
||||
module_name)
|
||||
|
||||
# if process_virtual returned a non-True value then we are
|
||||
# supposed to not process this module
|
||||
if virtual_ret is not True:
|
||||
# If a module has information about why it could not be loaded, record it
|
||||
if virtual_errors:
|
||||
error_funcs[module_name] = virtual_errors
|
||||
continue
|
||||
|
||||
# update our module name to reflect the virtual name
|
||||
@ -807,10 +813,15 @@ class Loader(object):
|
||||
not str(mod.__name__).startswith('salt.loaded.ext.grain'))
|
||||
):
|
||||
mod.__salt__ = funcs
|
||||
# if include_errors:
|
||||
# mod.__errors__ = error_funcs
|
||||
elif not in_pack(pack, '__salt__') and \
|
||||
(str(mod.__name__).startswith('salt.loaded.int.grain') or
|
||||
str(mod.__name__).startswith('salt.loaded.ext.grain')):
|
||||
mod.__salt__.update(funcs)
|
||||
# mod.__errors__ = error_funcs
|
||||
if include_errors:
|
||||
funcs['_errors'] = error_funcs
|
||||
return funcs
|
||||
|
||||
def load_modules(self):
|
||||
@ -1024,16 +1035,23 @@ class Loader(object):
|
||||
# if they are not intended to run on the given platform or are missing
|
||||
# dependencies.
|
||||
try:
|
||||
error_reasons = []
|
||||
if hasattr(mod, '__virtual__') and inspect.isfunction(mod.__virtual__):
|
||||
if self.opts.get('virtual_timer', False):
|
||||
start = time.time()
|
||||
virtual = mod.__virtual__()
|
||||
if isinstance(virtual, tuple):
|
||||
error_reasons = virtual[1]
|
||||
virtual = virtual[0]
|
||||
end = time.time() - start
|
||||
msg = 'Virtual function took {0} seconds for {1}'.format(
|
||||
end, module_name)
|
||||
log.warning(msg)
|
||||
else:
|
||||
virtual = mod.__virtual__()
|
||||
if isinstance(virtual, tuple):
|
||||
error_reasons = virtual[1]
|
||||
virtual = virtual[0]
|
||||
# Get the module's virtual name
|
||||
virtualname = getattr(mod, '__virtualname__', virtual)
|
||||
if not virtual:
|
||||
@ -1054,7 +1072,7 @@ class Loader(object):
|
||||
)
|
||||
)
|
||||
|
||||
return (False, module_name)
|
||||
return (False, module_name, error_reasons)
|
||||
|
||||
# At this point, __virtual__ did not return a
|
||||
# boolean value, let's check for deprecated usage
|
||||
@ -1136,9 +1154,9 @@ class Loader(object):
|
||||
),
|
||||
exc_info=True
|
||||
)
|
||||
return (False, module_name)
|
||||
return (False, module_name, error_reasons)
|
||||
|
||||
return (True, module_name)
|
||||
return (True, module_name, [])
|
||||
|
||||
def _apply_outputter(self, func, mod):
|
||||
'''
|
||||
@ -1158,6 +1176,8 @@ class Loader(object):
|
||||
gen = self.gen_functions(pack=pack, whitelist=whitelist)
|
||||
for key, fun in gen.items():
|
||||
# if the name (after '.') is "name", then rename to mod_name: fun
|
||||
if key == '_errors':
|
||||
continue
|
||||
if key[key.index('.') + 1:] == name:
|
||||
funcs[key[:key.index('.')]] = fun
|
||||
return funcs
|
||||
@ -1216,7 +1236,7 @@ class Loader(object):
|
||||
continue
|
||||
grains_data.update(ret)
|
||||
for key, fun in funcs.items():
|
||||
if key.startswith('core.'):
|
||||
if key.startswith('core.') or key == '_errors':
|
||||
continue
|
||||
try:
|
||||
ret = fun()
|
||||
|
@ -278,7 +278,9 @@ class SMinion(object):
|
||||
self.opts['id'],
|
||||
self.opts['environment'],
|
||||
).compile_pillar()
|
||||
self.functions = salt.loader.minion_mods(self.opts)
|
||||
self.functions = salt.loader.minion_mods(self.opts, include_errors=True)
|
||||
self.function_errors = self.functions['_errors']
|
||||
self.functions.pop('_errors') # Keep the funcs clean
|
||||
self.returners = salt.loader.returners(self.opts, self.functions)
|
||||
self.states = salt.loader.states(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions)
|
||||
@ -606,7 +608,7 @@ class Minion(MinionBase):
|
||||
).compile_pillar()
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.mod_opts = self._prep_mod_opts()
|
||||
self.functions, self.returners = self._load_modules()
|
||||
self.functions, self.returners, self.function_errors = self._load_modules()
|
||||
self.matcher = Matcher(self.opts, self.functions)
|
||||
self.proc_dir = get_proc_dir(opts['cachedir'])
|
||||
self.schedule = salt.utils.schedule.Schedule(
|
||||
@ -815,14 +817,16 @@ class Minion(MinionBase):
|
||||
log.error('Unable to enforce modules_max_memory because resource is missing')
|
||||
|
||||
self.opts['grains'] = salt.loader.grains(self.opts, force_refresh)
|
||||
functions = salt.loader.minion_mods(self.opts)
|
||||
functions = salt.loader.minion_mods(self.opts, include_errors=True)
|
||||
returners = salt.loader.returners(self.opts, functions)
|
||||
errors = functions['_errors']
|
||||
functions.pop('_errors')
|
||||
|
||||
# we're done, reset the limits!
|
||||
if modules_max_memory is True:
|
||||
resource.setrlimit(resource.RLIMIT_AS, old_mem_limit)
|
||||
|
||||
return functions, returners
|
||||
return functions, returners, errors
|
||||
|
||||
def _fire_master(self, data=None, tag=None, events=None, pretag=None):
|
||||
'''
|
||||
@ -952,7 +956,7 @@ class Minion(MinionBase):
|
||||
'''
|
||||
if isinstance(data['fun'], string_types):
|
||||
if data['fun'] == 'sys.reload_modules':
|
||||
self.functions, self.returners = self._load_modules()
|
||||
self.functions, self.returners, self.function_errors = self._load_modules()
|
||||
self.schedule.functions = self.functions
|
||||
self.schedule.returners = self.returners
|
||||
if isinstance(data['fun'], tuple) or isinstance(data['fun'], list):
|
||||
@ -1088,6 +1092,9 @@ class Minion(MinionBase):
|
||||
ret['out'] = 'nested'
|
||||
else:
|
||||
ret['return'] = '{0!r} is not available.'.format(function_name)
|
||||
mod_name = function_name.split('.')[0]
|
||||
if mod_name in minion_instance.function_errors:
|
||||
ret['return'] += ' Possible reasons: {0!r}'.format(minion_instance.function_errors[mod_name])
|
||||
ret['out'] = 'nested'
|
||||
|
||||
ret['jid'] = data['jid']
|
||||
@ -1405,7 +1412,7 @@ class Minion(MinionBase):
|
||||
'''
|
||||
Refresh the functions and returners.
|
||||
'''
|
||||
self.functions, self.returners = self._load_modules(force_refresh)
|
||||
self.functions, self.returners, _ = self._load_modules(force_refresh)
|
||||
self.schedule.functions = self.functions
|
||||
self.schedule.returners = self.returners
|
||||
|
||||
@ -2610,7 +2617,7 @@ class ProxyMinion(Minion):
|
||||
).compile_pillar()
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.mod_opts = self._prep_mod_opts()
|
||||
self.functions, self.returners = self._load_modules()
|
||||
self.functions, self.returners, self.function_errors = self._load_modules()
|
||||
self.matcher = Matcher(self.opts, self.functions)
|
||||
self.proc_dir = get_proc_dir(opts['cachedir'])
|
||||
self.schedule = salt.utils.schedule.Schedule(
|
||||
|
@ -296,7 +296,7 @@ def load_states():
|
||||
for mod in load.modules:
|
||||
module_name = mod.__name__.rsplit('.', 1)[-1]
|
||||
|
||||
(virtual_ret, virtual_name) = load.process_virtual(mod, module_name)
|
||||
(virtual_ret, virtual_name, _) = load.process_virtual(mod, module_name)
|
||||
|
||||
# if the module returned a True value and a new name use that
|
||||
# otherwise use the default module name
|
||||
|
Loading…
Reference in New Issue
Block a user