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/cli/ |
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/cli/cp.py |
""" The cp module is used to execute the logic used by the salt-cp command line application, salt-cp is NOT intended to broadcast large files, it is intended to handle text files. Salt-cp can be used to distribute configuration files """ import base64 import errno import logging import os import re import sys import salt.client import salt.output import salt.utils.files import salt.utils.gzip_util import salt.utils.itertools import salt.utils.minions import salt.utils.parsers import salt.utils.platform import salt.utils.stringutils import salt.utils.verify log = logging.getLogger(__name__) class SaltCPCli(salt.utils.parsers.SaltCPOptionParser): """ Run the salt-cp command line client """ def run(self): """ Execute salt-cp """ self.parse_args() cp_ = SaltCP(self.config) cp_.run() class SaltCP: """ Create a salt cp object, used to distribute simple files with salt """ def __init__(self, opts): self.opts = opts self.is_windows = salt.utils.platform.is_windows() def _mode(self, path): if self.is_windows: return None try: return int(oct(os.stat(path).st_mode)[-4:], 8) except (TypeError, IndexError, ValueError): return None def _recurse(self, path): """ Get a list of all specified files """ files = {} empty_dirs = [] try: sub_paths = os.listdir(path) except OSError as exc: if exc.errno == errno.ENOENT: # Path does not exist sys.stderr.write(f"{path} does not exist\n") sys.exit(42) elif exc.errno in (errno.EINVAL, errno.ENOTDIR): # Path is a file (EINVAL on Windows, ENOTDIR otherwise) files[path] = self._mode(path) else: if not sub_paths: empty_dirs.append(path) for fn_ in sub_paths: files_, empty_dirs_ = self._recurse(os.path.join(path, fn_)) files.update(files_) empty_dirs.extend(empty_dirs_) return files, empty_dirs def _list_files(self): files = {} empty_dirs = set() for fn_ in self.opts["src"]: files_, empty_dirs_ = self._recurse(fn_) files.update(files_) empty_dirs.update(empty_dirs_) return files, sorted(empty_dirs) def _file_dict(self, fn_): """ Take a path and return the contents of the file as a string """ if not os.path.isfile(fn_): err = f"The referenced file, {fn_} is not available." sys.stderr.write(err + "\n") sys.exit(42) with salt.utils.files.fopen(fn_, "r") as fp_: data = fp_.read() return {fn_: data} def _load_files(self): """ Parse the files indicated in opts['src'] and load them into a python object for transport """ files = {} for fn_ in self.opts["src"]: if os.path.isfile(fn_): files.update(self._file_dict(fn_)) elif os.path.isdir(fn_): salt.utils.stringutils.print_cli( "{} is a directory, only files are supported " 'in non-chunked mode. Use "--chunked" command ' "line argument.".format(fn_) ) sys.exit(1) return files def run(self): """ Make the salt client call """ if self.opts["chunked"]: ret = self.run_chunked() else: ret = self.run_oldstyle() salt.output.display_output(ret, self.opts.get("output", "nested"), self.opts) def run_oldstyle(self): """ Make the salt client call in old-style all-in-one call method """ arg = [self._load_files(), self.opts["dest"]] args = [ self.opts["tgt"], "cp.recv", arg, self.opts["timeout"], ] selected_target_option = self.opts.get("selected_target_option", None) if selected_target_option is not None: args.append(selected_target_option) with salt.client.get_local_client(self.opts["conf_file"]) as local: return local.cmd(*args) def run_chunked(self): """ Make the salt client call in the new fasion chunked multi-call way """ files, empty_dirs = self._list_files() dest = self.opts["dest"] gzip = self.opts["gzip"] tgt = self.opts["tgt"] timeout = self.opts["timeout"] selected_target_option = self.opts.get("selected_target_option") dest_is_dir = ( bool(empty_dirs) or len(files) > 1 or bool(re.search(r"[\\/]$", dest)) ) reader = ( salt.utils.gzip_util.compress_file if gzip else salt.utils.itertools.read_file ) _res = salt.utils.minions.CkMinions(self.opts).check_minions( tgt, tgt_type=selected_target_option or "glob" ) minions = _res["minions"] def _get_remote_path(fn_): if fn_ in self.opts["src"]: # This was a filename explicitly passed on the CLI return ( os.path.join(dest, os.path.basename(fn_)) if dest_is_dir else dest ) else: for path in self.opts["src"]: relpath = os.path.relpath(fn_, path + os.sep) if relpath.startswith(parent): # File is not within this dir continue return os.path.join(dest, os.path.basename(path), relpath) else: # pylint: disable=useless-else-on-loop # Should not happen log.error("Failed to find remote path for %s", fn_) return None ret = {} parent = ".." + os.sep with salt.client.get_local_client(self.opts["conf_file"]) as local: for fn_, mode in files.items(): remote_path = _get_remote_path(fn_) index = 1 failed = {} for chunk in reader(fn_, chunk_size=self.opts["salt_cp_chunk_size"]): chunk = base64.b64encode(salt.utils.stringutils.to_bytes(chunk)) append = index > 1 log.debug( "Copying %s to %starget '%s' as %s%s", fn_, f"{selected_target_option} " if selected_target_option else "", tgt, remote_path, f" (chunk #{index})" if append else "", ) args = [ tgt, "cp.recv_chunked", [remote_path, chunk, append, gzip, mode], timeout, ] if selected_target_option is not None: args.append(selected_target_option) result = local.cmd(*args) if not result: # Publish failed msg = ( "Publish failed.{} It may be necessary to " "decrease salt_cp_chunk_size (current value: " "{})".format( " File partially transferred." if index > 1 else "", self.opts["salt_cp_chunk_size"], ) ) for minion in minions: ret.setdefault(minion, {})[remote_path] = msg break for minion_id, minion_ret in result.items(): ret.setdefault(minion_id, {})[remote_path] = minion_ret # Catch first error message for a given minion, we will # rewrite the results after we're done iterating through # the chunks. if minion_ret is not True and minion_id not in failed: failed[minion_id] = minion_ret index += 1 for minion_id, msg in failed.items(): ret[minion_id][remote_path] = msg for dirname in empty_dirs: remote_path = _get_remote_path(dirname) log.debug( "Creating empty dir %s on %starget '%s'", dirname, f"{selected_target_option} " if selected_target_option else "", tgt, ) args = [tgt, "cp.recv_chunked", [remote_path, None], timeout] if selected_target_option is not None: args.append(selected_target_option) for minion_id, minion_ret in local.cmd(*args).items(): ret.setdefault(minion_id, {})[remote_path] = minion_ret return ret