PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/module.py |
r""" Execution of Salt modules from within states ============================================ .. note:: As of the 3005 release, you no longer need to opt-in to the new style of calling ``module.run``. The following config can be removed from ``/etc/salt/minion``: .. code-block:: yaml use_superseded: - module.run Both 'new' and 'legacy' styles of calling ``module.run`` are supported. With `module.run` these states allow individual execution module calls to be made via states. Here's a contrived example, to show you how it's done: .. code-block:: yaml # New Style test.random_hash: module.run: - test.random_hash: - size: 42 - hash_type: sha256 # Legacy Style test.random_hash: module.run: - size: 42 - hash_type: sha256 In the new style, the state ID (``test.random_hash``, in this case) is irrelevant when using ``module.run``. It could have very well been written: .. code-block:: yaml Generate a random hash: module.run: - test.random_hash: - size: 42 - hash_type: sha256 For a simple state like that it's not a big deal, but if the module you're using has certain parameters, things can get cluttered, fast. Using the contrived custom module (stuck in ``/srv/salt/_modules/foo.py``, or your configured file_roots_): .. code-block:: python def bar(name, names, fun, state, saltenv): return "Name: {name} Names: {names} Fun: {fun} State: {state} Saltenv: {saltenv}".format(**locals()) Your legacy state has to look like this: .. code-block:: yaml # Legacy style Unfortunate example: module.run: - name: foo.bar - m_name: Some name - m_names: - Such names - very wow - m_state: Arkansas - m_fun: Such fun - m_saltenv: Salty With the new style it's much cleaner: .. code-block:: yaml # New style Better: module.run: - foo.bar: - name: Some name - names: - Such names - very wow - state: Arkansas - fun: Such fun - saltenv: Salty The new style also allows multiple modules in one state. For instance, you can do this: .. code-block:: yaml Do many things: module.run: - test.random_hash: - size: 10 - hash_type: md5 # Note the `:` at the end - test.true: - test.arg: - this - has - args - and: kwargs - isn't: that neat? # Note the `:` at the end, too - test.version: - test.fib: - 4 Where in the legacy style you would have had to split your states like this: .. code-block:: yaml test.random_hash: module.run: - size: 10 - hash_type: md5 test.nop: module.run test.arg: module.run: - args: - this - has - args - kwargs: and: kwargs isn't: that neat? test.version: module.run Another difference is that in the legacy style, unconsumed arguments to the ``module`` state were simply passed into the module function being executed: .. code-block:: yaml show off module.run with args: module.run: - name: test.random_hash - size: 42 - hash_type: sha256 The new style is much more explicit, with the arguments and keyword arguments being nested under the name of the function: .. code-block:: yaml show off module.run with args: module.run: # Note the lack of `name: `, and trailing `:` - test.random_hash: - size: 42 - hash_type: sha256 If the function takes ``*args``, they can be passed in as well: .. code-block:: yaml args and kwargs: module.run: - test.arg: - isn't - this - fun - this: that - salt: stack Modern Examples --------------- Here are some other examples using the modern ``module.run``: .. code-block:: yaml fetch_out_of_band: module.run: - git.fetch: - cwd: /path/to/my/repo - user: myuser - opts: '--all' A more complex example: .. code-block:: yaml eventsviewer: module.run: - task.create_task: - name: events-viewer - user_name: System - action_type: Execute - cmd: 'c:\netops\scripts\events_viewer.bat' - trigger_type: 'Daily' - start_date: '2017-1-20' - start_time: '11:59PM' It is sometimes desirable to trigger a function call after a state is executed, for this the :mod:`module.wait <salt.states.module.wait>` state can be used: .. code-block:: yaml add example to hosts: file.append: - name: /etc/hosts - text: 203.0.113.13 example.com # New Style mine.send: module.wait: # Again, note the trailing `:` - hosts.list_hosts: - watch: - file: add example to hosts Legacy (Default) Examples ------------------------- If you're using the legacy ``module.run``, due to how the state system works, if a module function accepts an argument called, ``name``, then ``m_name`` must be used to specify that argument, to avoid a collision with the ``name`` argument. Here is a list of keywords hidden by the state system, which must be prefixed with ``m_``: * fun * name * names * state * saltenv For example: .. code-block:: yaml disable_nfs: module.run: - name: service.disable - m_name: nfs Note that some modules read all or some of the arguments from a list of keyword arguments. For example: .. code-block:: yaml mine.send: module.run: - func: network.ip_addrs - kwargs: interface: eth0 .. code-block:: yaml cloud.create: module.run: - func: cloud.create - provider: test-provider - m_names: - test-vlad - kwargs: { ssh_username: 'ubuntu', image: 'ami-8d6d9daa', securitygroup: 'default', size: 'c3.large', location: 'ap-northeast-1', delvol_on_destroy: 'True' } Other modules take the keyword arguments using this style: .. code-block:: yaml mac_enable_ssh: module.run: - name: system.set_remote_login - enable: True Another example that creates a recurring task that runs a batch file on a Windows system: .. code-block:: yaml eventsviewer: module.run: - name: task.create_task - m_name: 'events-viewer' - user_name: System - kwargs: { action_type: 'Execute', cmd: 'c:\netops\scripts\events_viewer.bat', trigger_type: 'Daily', start_date: '2017-1-20', start_time: '11:59PM' } .. _file_roots: https://docs.saltproject.io/en/latest/ref/configuration/master.html#file-roots """ import logging import salt.loader import salt.utils.args import salt.utils.functools import salt.utils.jid from salt.exceptions import SaltInvocationError log = logging.getLogger(__name__) def wait(name, **kwargs): """ Run a single module function only if the watch statement calls it ``name`` The module function to execute ``**kwargs`` Pass any arguments needed to execute the function .. note:: Like the :mod:`cmd.run <salt.states.cmd.run>` state, this state will return ``True`` but not actually execute, unless one of the following two things happens: 1. The state has a :ref:`watch requisite <requisites-watch>`, and the state which it is watching changes. 2. Another state has a :ref:`watch_in requisite <requisites-watch-in>` which references this state, and the state wth the ``watch_in`` changes. """ return {"name": name, "changes": {}, "result": True, "comment": ""} # Alias module.watch to module.wait watch = salt.utils.functools.alias_function(wait, "watch") def run(**kwargs): """ Run a single module function or a range of module functions in a batch. Supersedes ``module.run`` function, which requires ``m_`` prefix to function-specific parameters. :param returner: Specify a common returner for the whole batch to send the return data :param kwargs: Pass any arguments needed to execute the function(s) .. code-block:: yaml some_id_of_state: module.run: - network.ip_addrs: - interface: eth0 - cloud.create: - names: - test-isbm-1 - test-isbm-2 - ssh_username: sles - image: sles12sp2 - securitygroup: default - size: 'c3.large' - location: ap-northeast-1 - delvol_on_destroy: True :return: """ # Detect if this call is using legacy or new style syntax. legacy_run = False keys = list(kwargs) ignored_kwargs = ["name", "__reqs__", "sfun"] for item in ignored_kwargs: if item in keys: keys.remove(item) # The rest of the keys should be function names for new-style syntax for name in keys: if name.find(".") == -1: legacy_run = True if not keys and kwargs: legacy_run = True if legacy_run: log.debug("Detected legacy module.run syntax: %s", __low__["__id__"]) return _legacy_run(**kwargs) else: log.debug("Using new style module.run syntax: %s", __low__["__id__"]) return _run(**kwargs) def _run(**kwargs): if "name" in kwargs: kwargs.pop("name") ret = { "name": list(kwargs), "changes": {}, "comment": "", "result": None, } functions = [func for func in kwargs if "." in func] missing = [] tests = [] for func in functions: func = func.split(":")[0] if func not in __salt__: missing.append(func) elif __opts__["test"]: tests.append(func) if not functions: ret["comment"] = "No function provided." ret["result"] = False return ret if tests or missing: ret["comment"] = " ".join( [ missing and "Unavailable function{plr}: {func}.".format( plr=(len(missing) > 1 or ""), func=(", ".join(missing) or "") ) or "", tests and "Function{plr} {func} to be executed.".format( plr=(len(tests) > 1 or ""), func=", ".join(tests) or "" ) or "", ] ).strip() if missing: ret["result"] = False return ret failures = [] success = [] for func in functions: _func = func.split(":")[0] try: 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): failures.append( "'{}' failed: {}".format( func, func_ret.get("comment", "(error message N/A)") ) ) if func_ret is False: failures.append(f"'{func}': {func_ret}") else: success.append( "{}: {}".format( func, ( func_ret.get("comment", "Success") if isinstance(func_ret, dict) else func_ret ), ) ) ret["changes"][func] = func_ret except (SaltInvocationError, TypeError) as ex: failures.append(f"'{func}' failed: {ex}") ret["comment"] = ", ".join(failures + success) ret["result"] = not bool(failures) return ret def _call_function(name, returner=None, func_args=None, func_kwargs=None): """ Calls a function from the specified module. :param str name: module.function of the function to call :param dict returner: Returner specification to use. :param list func_args: List with args and dicts of kwargs (one dict per kwarg) to pass to the function. :return: Result of the function call """ if func_args is None: func_args = [] if func_kwargs is None: func_kwargs = {} mret = salt.utils.functools.call_function(__salt__[name], *func_args, **func_kwargs) if returner is not None: returners = salt.loader.returners(__opts__, __salt__) if returner in returners: returners[returner]( { "id": __opts__["id"], "ret": mret, "fun": name, "jid": salt.utils.jid.gen_jid(__opts__), } ) return mret def _legacy_run(name, **kwargs): """ .. deprecated:: 2017.7.0 Function name stays the same, behaviour will change. Run a single module function ``name`` The module function to execute ``returner`` Specify the returner to send the return of the module execution to ``kwargs`` Pass any arguments needed to execute the function """ ret = {"name": name, "changes": {}, "comment": "", "result": None} if name not in __salt__: ret["comment"] = f"Module function {name} is not available" ret["result"] = False return ret if __opts__["test"]: ret["comment"] = f"Module function {name} is set to execute" return ret aspec = salt.utils.args.get_function_argspec(__salt__[name]) args = [] defaults = {} arglen = 0 deflen = 0 if isinstance(aspec.args, list): arglen = len(aspec.args) if isinstance(aspec.defaults, tuple): deflen = len(aspec.defaults) # Match up the defaults with the respective args for ind in range(arglen - 1, -1, -1): minus = arglen - ind if deflen - minus > -1: defaults[aspec.args[ind]] = aspec.defaults[-minus] # overwrite passed default kwargs for arg in defaults: if arg == "name": if "m_name" in kwargs: defaults[arg] = kwargs.pop("m_name") elif arg == "fun": if "m_fun" in kwargs: defaults[arg] = kwargs.pop("m_fun") elif arg == "state": if "m_state" in kwargs: defaults[arg] = kwargs.pop("m_state") elif arg == "saltenv": if "m_saltenv" in kwargs: defaults[arg] = kwargs.pop("m_saltenv") if arg in kwargs: defaults[arg] = kwargs.pop(arg) missing = set() for arg in aspec.args: if arg == "name": rarg = "m_name" elif arg == "fun": rarg = "m_fun" elif arg == "names": rarg = "m_names" elif arg == "state": rarg = "m_state" elif arg == "saltenv": rarg = "m_saltenv" else: rarg = arg if rarg not in kwargs and arg not in defaults: missing.add(rarg) continue if arg in defaults: args.append(defaults[arg]) else: args.append(kwargs.pop(rarg)) if missing: comment = "The following arguments are missing:" for arg in missing: comment += f" {arg}" ret["comment"] = comment ret["result"] = False return ret if aspec.varargs: if aspec.varargs == "name": rarg = "m_name" elif aspec.varargs == "fun": rarg = "m_fun" elif aspec.varargs == "names": rarg = "m_names" elif aspec.varargs == "state": rarg = "m_state" elif aspec.varargs == "saltenv": rarg = "m_saltenv" else: rarg = aspec.varargs if rarg in kwargs: varargs = kwargs.pop(rarg) if not isinstance(varargs, list): msg = "'{0}' must be a list." ret["comment"] = msg.format(aspec.varargs) ret["result"] = False return ret args.extend(varargs) nkwargs = {} if aspec.keywords and aspec.keywords in kwargs: nkwargs = kwargs.pop(aspec.keywords) if not isinstance(nkwargs, dict): msg = "'{0}' must be a dict." ret["comment"] = msg.format(aspec.keywords) ret["result"] = False return ret try: if aspec.keywords: mret = __salt__[name](*args, **nkwargs) else: mret = __salt__[name](*args) except Exception as e: # pylint: disable=broad-except ret["comment"] = "Module function {} threw an exception. Exception: {}".format( name, e ) ret["result"] = False return ret else: if mret is not None or mret != {}: ret["changes"]["ret"] = mret if "returner" in kwargs: ret_ret = { "id": __opts__["id"], "ret": mret, "fun": name, "jid": salt.utils.jid.gen_jid(__opts__), } returners = salt.loader.returners(__opts__, __salt__) if kwargs["returner"] in returners: returners[kwargs["returner"]](ret_ret) ret["comment"] = f"Module function {name} executed" ret["result"] = _get_result(mret, ret["changes"]) return ret def _get_result(func_ret, changes): res = True # if mret is a dict and there is retcode and its non-zero if isinstance(func_ret, dict) and func_ret.get("retcode", 0) != 0: res = False # if its a boolean, return that as the result elif isinstance(func_ret, bool): res = func_ret else: changes_ret = changes.get("ret", {}) if isinstance(changes_ret, dict): if isinstance(changes_ret.get("result", {}), bool): res = changes_ret.get("result", {}) elif changes_ret.get("retcode", 0) != 0: res = False # Explore dict in depth to determine if there is a # 'result' key set to False which sets the global # state result. else: res = _get_dict_result(changes_ret) return res def _get_dict_result(node): ret = True for key, val in node.items(): if key == "result" and val is False: ret = False break elif isinstance(val, dict): ret = _get_dict_result(val) if ret is False: break return ret mod_watch = salt.utils.functools.alias_function(run, "mod_watch")