diff --git a/salt/cli/caller.py b/salt/cli/caller.py index 10938a34cb..672593f93f 100644 --- a/salt/cli/caller.py +++ b/salt/cli/caller.py @@ -165,6 +165,11 @@ class BaseCaller(object): ret['jid'] ) if fun not in self.minion.functions: + docs = self.minion.functions['sys.doc']('{0}*'.format(fun)) + if docs: + ret['out'] = 'nested' + ret['return'] = docs + return ret sys.stderr.write(self.minion.functions.missing_fun_string(fun)) mod_name = fun.split('.')[0] if mod_name in self.minion.function_errors: diff --git a/salt/client/mixins.py b/salt/client/mixins.py index 09680a0561..5ee62bbb33 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -5,6 +5,7 @@ A collection of mixins useful for the various *Client interfaces # Import Python libs from __future__ import absolute_import, print_function, with_statement +import fnmatch import signal import logging import weakref @@ -436,10 +437,19 @@ class SyncClientMixin(object): Return a dictionary of functions and the inline documentation for each ''' if arg: - target_mod = arg + '.' if not arg.endswith('.') else arg - docs = [(fun, self.functions[fun].__doc__) - for fun in sorted(self.functions) - if fun == arg or fun.startswith(target_mod)] + if '*' in arg: + target_mod = arg + _use_fnmatch = True + else: + target_mod = arg + '.' if not arg.endswith('.') else arg + log.debug('target_mod {}'.format(target_mod)) + if _use_fnmatch: + docs = [(fun, self.functions[fun].__doc__) + for fun in fnmatch.filter(self.functions, target_mod)] + else: + docs = [(fun, self.functions[fun].__doc__) + for fun in sorted(self.functions) + if fun == arg or fun.startswith(target_mod)] else: docs = [(fun, self.functions[fun].__doc__) for fun in sorted(self.functions)] diff --git a/salt/minion.py b/salt/minion.py index 309d6e926b..faa093e7b3 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -1520,12 +1520,16 @@ class Minion(MinionBase): ret['return'] = '{0}: {1}'.format(msg, traceback.format_exc()) ret['out'] = 'nested' else: - ret['return'] = minion_instance.functions.missing_fun_string(function_name) - mod_name = function_name.split('.')[0] - if mod_name in minion_instance.function_errors: - ret['return'] += ' Possible reasons: \'{0}\''.format( - minion_instance.function_errors[mod_name] - ) + docs = minion_instance.functions['sys.doc']('{0}*'.format(function_name)) + if docs: + ret['return'] = docs + else: + ret['return'] = minion_instance.functions.missing_fun_string(function_name) + mod_name = function_name.split('.')[0] + if mod_name in minion_instance.function_errors: + ret['return'] += ' Possible reasons: \'{0}\''.format( + minion_instance.function_errors[mod_name] + ) ret['success'] = False ret['retcode'] = 254 ret['out'] = 'nested' diff --git a/salt/runner.py b/salt/runner.py index d9a04da015..c04f14aeec 100644 --- a/salt/runner.py +++ b/salt/runner.py @@ -276,7 +276,13 @@ class Runner(RunnerClient): 'fun_args': fun_args, 'jid': self.jid}, tag='salt/run/{0}/ret'.format(self.jid)) - ret = '{0}'.format(exc) + # Attempt to grab documentation + ret = self.get_docs('{0}*'.format(low['fun'])) + + # If we didn't get docs returned then + # return the `not availble` message. + if not ret: + ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) else: