"Fossies" - the Fresh Open Source Software Archive

Member "salt-3002.2/salt/netapi/__init__.py" (18 Nov 2020, 9449 Bytes) of package /linux/misc/salt-3002.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "__init__.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3002.1_vs_3002.2.

    1 """
    2 Make api awesomeness
    3 """
    4 
    5 
    6 import copy
    7 import inspect
    8 import logging
    9 import os
   10 
   11 import salt.auth
   12 import salt.client
   13 import salt.client.ssh.client
   14 import salt.config
   15 import salt.daemons.masterapi
   16 import salt.exceptions
   17 import salt.log  # pylint: disable=W0611
   18 import salt.runner
   19 import salt.syspaths
   20 import salt.utils.args
   21 import salt.utils.minions
   22 import salt.wheel
   23 from salt.defaults import DEFAULT_TARGET_DELIM
   24 from salt.ext import six
   25 
   26 log = logging.getLogger(__name__)
   27 
   28 
   29 class NetapiClient:
   30     """
   31     Provide a uniform method of accessing the various client interfaces in Salt
   32     in the form of low-data data structures. For example:
   33 
   34     >>> client = NetapiClient(__opts__)
   35     >>> lowstate = {'client': 'local', 'tgt': '*', 'fun': 'test.ping', 'arg': ''}
   36     >>> client.run(lowstate)
   37     """
   38 
   39     def __init__(self, opts):
   40         self.opts = opts
   41         apiopts = copy.deepcopy(self.opts)
   42         apiopts["enable_ssh_minions"] = True
   43         apiopts["cachedir"] = os.path.join(opts["cachedir"], "saltapi")
   44         if not os.path.exists(apiopts["cachedir"]):
   45             os.makedirs(apiopts["cachedir"])
   46         self.resolver = salt.auth.Resolver(apiopts)
   47         self.loadauth = salt.auth.LoadAuth(apiopts)
   48         self.key = salt.daemons.masterapi.access_keys(apiopts)
   49         self.ckminions = salt.utils.minions.CkMinions(apiopts)
   50 
   51     def _is_master_running(self):
   52         """
   53         Perform a lightweight check to see if the master daemon is running
   54 
   55         Note, this will return an invalid success if the master crashed or was
   56         not shut down cleanly.
   57         """
   58         # Windows doesn't have IPC. Assume the master is running.
   59         # At worse, it will error 500.
   60         if salt.utils.platform.is_windows():
   61             return True
   62 
   63         if self.opts["transport"] == "tcp":
   64             ipc_file = "publish_pull.ipc"
   65         else:
   66             ipc_file = "workers.ipc"
   67         return os.path.exists(os.path.join(self.opts["sock_dir"], ipc_file))
   68 
   69     def _prep_auth_info(self, clear_load):
   70         sensitive_load_keys = []
   71         key = None
   72         if "token" in clear_load:
   73             auth_type = "token"
   74             err_name = "TokenAuthenticationError"
   75             sensitive_load_keys = ["token"]
   76             return auth_type, err_name, key, sensitive_load_keys
   77         elif "eauth" in clear_load:
   78             auth_type = "eauth"
   79             err_name = "EauthAuthenticationError"
   80             sensitive_load_keys = ["username", "password"]
   81             return auth_type, err_name, key, sensitive_load_keys
   82         raise salt.exceptions.EauthAuthenticationError(
   83             "No authentication credentials given"
   84         )
   85 
   86     def _authorize_ssh(self, low):
   87         auth_type, err_name, key, sensitive_load_keys = self._prep_auth_info(low)
   88         auth_check = self.loadauth.check_authentication(low, auth_type, key=key)
   89         auth_list = auth_check.get("auth_list", [])
   90         error = auth_check.get("error")
   91         if error:
   92             raise salt.exceptions.EauthAuthenticationError(error)
   93         delimiter = low.get("kwargs", {}).get("delimiter", DEFAULT_TARGET_DELIM)
   94         _res = self.ckminions.check_minions(
   95             low["tgt"], low.get("tgt_type", "glob"), delimiter
   96         )
   97         minions = _res.get("minions", list())
   98         missing = _res.get("missing", list())
   99         authorized = self.ckminions.auth_check(
  100             auth_list,
  101             low["fun"],
  102             low.get("arg", []),
  103             low["tgt"],
  104             low.get("tgt_type", "glob"),
  105             minions=minions,
  106         )
  107         if not authorized:
  108             raise salt.exceptions.EauthAuthenticationError(
  109                 "Authorization error occurred."
  110             )
  111 
  112     def run(self, low):
  113         """
  114         Execute the specified function in the specified client by passing the
  115         lowstate
  116         """
  117         # Eauth currently requires a running daemon and commands run through
  118         # this method require eauth so perform a quick check to raise a
  119         # more meaningful error.
  120         if not self._is_master_running():
  121             raise salt.exceptions.SaltDaemonNotRunning("Salt Master is not available.")
  122 
  123         if low.get("client") not in CLIENTS:
  124             raise salt.exceptions.SaltInvocationError(
  125                 "Invalid client specified: '{}'".format(low.get("client"))
  126             )
  127 
  128         if not ("token" in low or "eauth" in low):
  129             raise salt.exceptions.EauthAuthenticationError(
  130                 "No authentication credentials given"
  131             )
  132 
  133         if low.get("raw_shell") and not self.opts.get("netapi_allow_raw_shell"):
  134             raise salt.exceptions.EauthAuthenticationError(
  135                 "Raw shell option not allowed."
  136             )
  137 
  138         if low["client"] == "ssh":
  139             self._authorize_ssh(low)
  140 
  141         l_fun = getattr(self, low["client"])
  142         f_call = salt.utils.args.format_call(l_fun, low)
  143         return l_fun(*f_call.get("args", ()), **f_call.get("kwargs", {}))
  144 
  145     def local_async(self, *args, **kwargs):
  146         """
  147         Run :ref:`execution modules <all-salt.modules>` asynchronously
  148 
  149         Wraps :py:meth:`salt.client.LocalClient.run_job`.
  150 
  151         :return: job ID
  152         """
  153         local = salt.client.get_local_client(mopts=self.opts)
  154         return local.run_job(*args, **kwargs)
  155 
  156     def local(self, *args, **kwargs):
  157         """
  158         Run :ref:`execution modules <all-salt.modules>` synchronously
  159 
  160         See :py:meth:`salt.client.LocalClient.cmd` for all available
  161         parameters.
  162 
  163         Sends a command from the master to the targeted minions. This is the
  164         same interface that Salt's own CLI uses. Note the ``arg`` and ``kwarg``
  165         parameters are sent down to the minion(s) and the given function,
  166         ``fun``, is called with those parameters.
  167 
  168         :return: Returns the result from the execution module
  169         """
  170         local = salt.client.get_local_client(mopts=self.opts)
  171         return local.cmd(*args, **kwargs)
  172 
  173     def local_subset(self, *args, **kwargs):
  174         """
  175         Run :ref:`execution modules <all-salt.modules>` against subsets of minions
  176 
  177         .. versionadded:: 2016.3.0
  178 
  179         Wraps :py:meth:`salt.client.LocalClient.cmd_subset`
  180         """
  181         local = salt.client.get_local_client(mopts=self.opts)
  182         return local.cmd_subset(*args, **kwargs)
  183 
  184     def local_batch(self, *args, **kwargs):
  185         """
  186         Run :ref:`execution modules <all-salt.modules>` against batches of minions
  187 
  188         .. versionadded:: 0.8.4
  189 
  190         Wraps :py:meth:`salt.client.LocalClient.cmd_batch`
  191 
  192         :return: Returns the result from the exeuction module for each batch of
  193             returns
  194         """
  195         local = salt.client.get_local_client(mopts=self.opts)
  196         return local.cmd_batch(*args, **kwargs)
  197 
  198     def ssh(self, *args, **kwargs):
  199         """
  200         Run salt-ssh commands synchronously
  201 
  202         Wraps :py:meth:`salt.client.ssh.client.SSHClient.cmd_sync`.
  203 
  204         :return: Returns the result from the salt-ssh command
  205         """
  206         ssh_client = salt.client.ssh.client.SSHClient(
  207             mopts=self.opts, disable_custom_roster=True
  208         )
  209         return ssh_client.cmd_sync(kwargs)
  210 
  211     def runner(self, fun, timeout=None, full_return=False, **kwargs):
  212         """
  213         Run `runner modules <all-salt.runners>` synchronously
  214 
  215         Wraps :py:meth:`salt.runner.RunnerClient.cmd_sync`.
  216 
  217         Note that runner functions must be called using keyword arguments.
  218         Positional arguments are not supported.
  219 
  220         :return: Returns the result from the runner module
  221         """
  222         kwargs["fun"] = fun
  223         runner = salt.runner.RunnerClient(self.opts)
  224         return runner.cmd_sync(kwargs, timeout=timeout, full_return=full_return)
  225 
  226     def runner_async(self, fun, **kwargs):
  227         """
  228         Run `runner modules <all-salt.runners>` asynchronously
  229 
  230         Wraps :py:meth:`salt.runner.RunnerClient.cmd_async`.
  231 
  232         Note that runner functions must be called using keyword arguments.
  233         Positional arguments are not supported.
  234 
  235         :return: event data and a job ID for the executed function.
  236         """
  237         kwargs["fun"] = fun
  238         runner = salt.runner.RunnerClient(self.opts)
  239         return runner.cmd_async(kwargs)
  240 
  241     def wheel(self, fun, **kwargs):
  242         """
  243         Run :ref:`wheel modules <all-salt.wheel>` synchronously
  244 
  245         Wraps :py:meth:`salt.wheel.WheelClient.master_call`.
  246 
  247         Note that wheel functions must be called using keyword arguments.
  248         Positional arguments are not supported.
  249 
  250         :return: Returns the result from the wheel module
  251         """
  252         kwargs["fun"] = fun
  253         wheel = salt.wheel.WheelClient(self.opts)
  254         return wheel.cmd_sync(kwargs)
  255 
  256     def wheel_async(self, fun, **kwargs):
  257         """
  258         Run :ref:`wheel modules <all-salt.wheel>` asynchronously
  259 
  260         Wraps :py:meth:`salt.wheel.WheelClient.master_call`.
  261 
  262         Note that wheel functions must be called using keyword arguments.
  263         Positional arguments are not supported.
  264 
  265         :return: Returns the result from the wheel module
  266         """
  267         kwargs["fun"] = fun
  268         wheel = salt.wheel.WheelClient(self.opts)
  269         return wheel.cmd_async(kwargs)
  270 
  271 
  272 CLIENTS = [
  273     name
  274     for name, _ in inspect.getmembers(
  275         NetapiClient, predicate=inspect.ismethod if six.PY2 else None
  276     )
  277     if not (name == "run" or name.startswith("_"))
  278 ]