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/ |
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/runner.py |
""" Execute salt convenience routines """ import logging import os import salt.exceptions import salt.loader import salt.minion import salt.utils.args import salt.utils.event import salt.utils.files import salt.utils.user from salt.client import mixins from salt.output import display_output from salt.utils.lazy import verify_fun log = logging.getLogger(__name__) class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin): """ The interface used by the :command:`salt-run` CLI tool on the Salt Master It executes :ref:`runner modules <all-salt.runners>` which run on the Salt Master. Importing and using ``RunnerClient`` must be done on the same machine as the Salt Master and it must be done using the same user that the Salt Master is running as. Salt's :conf_master:`external_auth` can be used to authenticate calls. The eauth user must be authorized to execute runner modules: (``@runner``). Only the :py:meth:`master_call` below supports eauth. """ client = "runner" tag_prefix = "run" @property def functions(self): if not hasattr(self, "_functions"): if not hasattr(self, "utils"): self.utils = salt.loader.utils(self.opts) # Must be self.functions for mixin to work correctly :-/ try: self._functions = salt.loader.runner( self.opts, utils=self.utils, context=self.context ) except AttributeError: # Just in case self.utils is still not present (perhaps due to # problems with the loader), load the runner funcs without them self._functions = salt.loader.runner(self.opts, context=self.context) return self._functions def _reformat_low(self, low): """ Format the low data for RunnerClient()'s master_call() function This also normalizes the following low data formats to a single, common low data structure. Old-style low: ``{'fun': 'jobs.lookup_jid', 'jid': '1234'}`` New-style: ``{'fun': 'jobs.lookup_jid', 'kwarg': {'jid': '1234'}}`` CLI-style: ``{'fun': 'jobs.lookup_jid', 'arg': ['jid="1234"']}`` """ fun = low.pop("fun") verify_fun(self.functions, fun) eauth_creds = { i: low.pop(i) for i in [ "username", "password", "eauth", "token", "client", "user", "key", ] if i in low } # Run name=value args through parse_input. We don't need to run kwargs # through because there is no way to send name=value strings in the low # dict other than by including an `arg` array. _arg, _kwarg = salt.utils.args.parse_input(low.pop("arg", []), condition=False) _kwarg.update(low.pop("kwarg", {})) # If anything hasn't been pop()'ed out of low by this point it must be # an old-style kwarg. _kwarg.update(low) # Finally, mung our kwargs to a format suitable for the byzantine # load_args_and_kwargs so that we can introspect the function being # called and fish for invalid kwargs. munged = [] munged.extend(_arg) munged.append(dict(__kwarg__=True, **_kwarg)) arg, kwarg = salt.minion.load_args_and_kwargs( self.functions[fun], munged, ignore_invalid=True ) return dict(fun=fun, kwarg={"kwarg": kwarg, "arg": arg}, **eauth_creds) def cmd_async(self, low): """ Execute a runner function asynchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured and the user is authorized to execute runner functions: (``@runner``). .. code-block:: python runner.cmd_async({ 'fun': 'jobs.list_jobs', 'username': 'saltdev', 'password': 'saltdev', 'eauth': 'pam', }) """ reformatted_low = self._reformat_low(low) return mixins.AsyncClientMixin.cmd_async(self, reformatted_low) def cmd_sync(self, low, timeout=None, full_return=False): """ Execute a runner function synchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured and the user is authorized to execute runner functions: (``@runner``). .. code-block:: python runner.cmd_sync({ 'fun': 'jobs.list_jobs', 'username': 'saltdev', 'password': 'saltdev', 'eauth': 'pam', }) """ reformatted_low = self._reformat_low(low) return mixins.SyncClientMixin.cmd_sync( self, reformatted_low, timeout, full_return ) def cmd( self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False, ): # pylint: disable=useless-super-delegation """ Execute a function .. code-block:: python >>> opts = salt.config.master_config('/etc/salt/master') >>> runner = salt.runner.RunnerClient(opts) >>> runner.cmd('jobs.list_jobs', []) { '20131219215650131543': { 'Arguments': [300], 'Function': 'test.sleep', 'StartTime': '2013, Dec 19 21:56:50.131543', 'Target': '*', 'Target-type': 'glob', 'User': 'saltdev' }, '20131219215921857715': { 'Arguments': [300], 'Function': 'test.sleep', 'StartTime': '2013, Dec 19 21:59:21.857715', 'Target': '*', 'Target-type': 'glob', 'User': 'saltdev' }, } """ return super().cmd(fun, arg, pub_data, kwarg, print_event, full_return) class Runner(RunnerClient): """ Execute the salt runner interface """ def __init__(self, opts, context=None): super().__init__(opts, context=context) self.returners = salt.loader.returners(opts, self.functions, context=context) self.outputters = salt.loader.outputters(opts) def print_docs(self): """ Print out the documentation! """ arg = self.opts.get("fun", None) docs = super().get_docs(arg) for fun in sorted(docs): display_output(f"{fun}:", "text", self.opts) print(docs[fun]) # TODO: move to mixin whenever we want a salt-wheel cli def run(self, full_return=False): """ Execute the runner sequence """ import salt.minion ret = {} if self.opts.get("doc", False): self.print_docs() else: low = {"fun": self.opts["fun"]} try: # Allocate a jid async_pub = self._gen_async_pub(jid=self.opts.get("jid")) self.jid = async_pub["jid"] fun_args = salt.utils.args.parse_input( self.opts["arg"], no_parse=self.opts.get("no_parse", []) ) verify_fun(self.functions, low["fun"]) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low["fun"]], fun_args ) low["arg"] = args low["kwarg"] = kwargs if self.opts.get("eauth"): if "token" in self.opts: try: with salt.utils.files.fopen( os.path.join(self.opts["cachedir"], ".root_key"), "r" ) as fp_: low["key"] = salt.utils.stringutils.to_unicode( fp_.readline() ) except OSError: low["token"] = self.opts["token"] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials if "token" not in low and "key" not in low and self.opts["eauth"]: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.opts) res = resolver.cli(self.opts["eauth"]) if self.opts["mktoken"] and res: tok = resolver.token_cli(self.opts["eauth"], res) if tok: low["token"] = tok.get("token", "") if not res: log.error("Authentication failed") return ret low.update(res) low["eauth"] = self.opts["eauth"] else: user = salt.utils.user.get_specific_user() if low["fun"] in ["state.orchestrate", "state.orch", "state.sls"]: low["kwarg"]["orchestration_jid"] = async_pub["jid"] # Run the runner! if self.opts.get("async", False): if self.opts.get("eauth"): async_pub = self.cmd_async(low) else: async_pub = self.asynchronous( self.opts["fun"], low, user=user, pub=async_pub ) # by default: info will be not enougth to be printed out ! log.warning( "Running in asynchronous mode. Results of this execution may " "be collected by attaching to the master event bus or " "by examining the master job cache, if configured. " "This execution is running under tag %s", async_pub["tag"], ) return async_pub["jid"] # return the jid # otherwise run it in the main process if self.opts.get("show_jid"): print(f"jid: {self.jid}") if self.opts.get("eauth"): ret = self.cmd_sync(low) if isinstance(ret, dict) and set(ret) == {"data", "outputter"}: outputter = ret["outputter"] ret = ret["data"] else: outputter = None display_output(ret, outputter, self.opts) else: ret = self._proc_function( instance=self, opts=self.opts, fun=self.opts["fun"], low=low, user=user, tag=async_pub["tag"], jid=async_pub["jid"], daemonize=False, full_return=full_return, ) except salt.exceptions.SaltException as exc: with salt.utils.event.get_event("master", opts=self.opts) as evt: evt.fire_event( { "success": False, "return": f"{exc}", "retcode": 254, "fun": self.opts["fun"], "fun_args": fun_args, "jid": self.jid, }, tag=f"salt/run/{self.jid}/ret", ) # Attempt to grab documentation if "fun" in low: ret = self.get_docs("{}*".format(low["fun"])) else: ret = None # If we didn't get docs returned then # return the `not availble` message. if not ret: ret = f"{exc}" if not self.opts.get("quiet", False): display_output(ret, "nested", self.opts) else: log.debug("Runner return: %s", ret) return ret