Merge pull request #42290 from isbm/isbm-module_run_parambug_42270_217

Backport of #42270
This commit is contained in:
Nicole Thomas 2017-07-27 16:30:05 -06:00 committed by GitHub
commit 22eea389fa
3 changed files with 60 additions and 15 deletions

View file

@ -122,13 +122,12 @@ State Module Changes
# After
run_something:
module.run:
mymodule.something:
- mymodule.something:
- name: some name
- first_arg: one
- second_arg: two
- do_stuff: True
Since a lot of users are already using :py:func:`module.run
<salt.states.module.run>` states, this new behavior must currently be
explicitly turned on, to allow users to take their time updating their SLS
@ -136,6 +135,36 @@ State Module Changes
the next feature release of Salt (Oxygen) and the old usage will no longer be
supported at that time.
Another feature of the new :py:func:`module.run <salt.states.module.run>` is that
it allows calling many functions in a single batch, such as:
.. code-block:: yaml
run_something:
module.run:
- mymodule.function_without_parameters:
- mymodule.another_function:
- myparam
- my_other_param
In a rare case that you have a function that needs to be called several times but
with the different parameters, an additional feature of "tagging" is to the
rescue. In order to tag a function, use a colon delimeter. For example:
.. code-block:: yaml
run_something:
module.run:
- mymodule.same_function:1:
- mymodule.same_function:2:
- myparam
- my_other_param
- mymodule.same_function:3:
- foo: bar
The example above will run `mymodule.same_function` three times with the
different parameters.
To enable the new behavior for :py:func:`module.run <salt.states.module.run>`,
add the following to the minion config file:
@ -143,6 +172,7 @@ State Module Changes
use_superseded:
- module.run
- The default for the ``fingerprint_hash_type`` option used in the ``present``
function in the :mod:`ssh <salt.states.ssh_know_hosts>` state changed from
``md5`` to ``sha256``.

View file

@ -262,6 +262,7 @@ def run(**kwargs):
missing = []
tests = []
for func in functions:
func = func.split(':')[0]
if func not in __salt__:
missing.append(func)
elif __opts__['test']:
@ -284,8 +285,9 @@ def run(**kwargs):
failures = []
success = []
for func in functions:
_func = func.split(':')[0]
try:
func_ret = _call_function(func, returner=kwargs.get('returner'),
func_ret = _call_function(_func, returner=kwargs.get('returner'),
func_args=kwargs.get(func))
if not _get_result(func_ret, ret['changes'].get('ret', {})):
if isinstance(func_ret, dict):
@ -313,22 +315,35 @@ def _call_function(name, returner=None, **kwargs):
'''
argspec = salt.utils.args.get_function_argspec(__salt__[name])
func_kw = dict(zip(argspec.args[-len(argspec.defaults or []):], # pylint: disable=incompatible-py3-code
argspec.defaults or []))
func_args = []
for funcset in kwargs.get('func_args') or {}:
if isinstance(funcset, dict):
func_kw.update(funcset)
argspec.defaults or []))
arg_type, na_type, kw_type = [], {}, False
for funcset in reversed(kwargs.get('func_args') or []):
if not isinstance(funcset, dict):
kw_type = True
if kw_type:
if isinstance(funcset, dict):
arg_type += funcset.values()
na_type.update(funcset)
else:
arg_type.append(funcset)
else:
func_args.append(funcset)
func_kw.update(funcset)
arg_type.reverse()
_exp_prm = len(argspec.args or []) - len(argspec.defaults or [])
_passed_prm = len(arg_type)
missing = []
for arg in argspec.args:
if arg not in func_kw:
missing.append(arg)
if na_type and _exp_prm > _passed_prm:
for arg in argspec.args:
if arg not in func_kw:
missing.append(arg)
if missing:
raise SaltInvocationError('Missing arguments: {0}'.format(', '.join(missing)))
elif _exp_prm > _passed_prm:
raise SaltInvocationError('Function expects {0} parameters, got only {1}'.format(
_exp_prm, _passed_prm))
mret = __salt__[name](*func_args, **func_kw)
mret = __salt__[name](*arg_type, **func_kw)
if returner is not None:
returners = salt.loader.returners(__opts__, __salt__)
if returner in returners:

View file

@ -122,7 +122,7 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin):
with patch.dict(module.__salt__, {CMD: _mocked_func_named}):
with patch.dict(module.__opts__, {'use_superseded': ['module.run']}):
ret = module.run(**{CMD: None})
assert ret['comment'] == "'{0}' failed: Missing arguments: name".format(CMD)
assert ret['comment'] == "'{0}' failed: Function expects 1 parameters, got only 0".format(CMD)
def test_run_correct_arg(self):
'''
@ -131,7 +131,7 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin):
'''
with patch.dict(module.__salt__, {CMD: _mocked_func_named}):
with patch.dict(module.__opts__, {'use_superseded': ['module.run']}):
ret = module.run(**{CMD: [{'name': 'Fred'}]})
ret = module.run(**{CMD: ['Fred']})
assert ret['comment'] == '{0}: Success'.format(CMD)
assert ret['result']