1""" 2Minion side functions for salt-cp 3""" 4 5import base64 6import errno 7import fnmatch 8import logging 9import os 10import urllib.parse 11 12import salt.crypt 13import salt.fileclient 14import salt.minion 15import salt.transport.client 16import salt.utils.data 17import salt.utils.files 18import salt.utils.gzip_util 19import salt.utils.path 20import salt.utils.templates 21import salt.utils.url 22from salt.exceptions import CommandExecutionError 23 24log = logging.getLogger(__name__) 25 26__proxyenabled__ = ["*"] 27 28 29def _auth(): 30 """ 31 Return the auth object 32 """ 33 if "auth" not in __context__: 34 __context__["auth"] = salt.crypt.SAuth(__opts__) 35 return __context__["auth"] 36 37 38def _gather_pillar(pillarenv, pillar_override): 39 """ 40 Whenever a state run starts, gather the pillar data fresh 41 """ 42 pillar = salt.pillar.get_pillar( 43 __opts__, 44 __grains__, 45 __opts__["id"], 46 __opts__["saltenv"], 47 pillar_override=pillar_override, 48 pillarenv=pillarenv, 49 ) 50 ret = pillar.compile_pillar() 51 if pillar_override and isinstance(pillar_override, dict): 52 ret.update(pillar_override) 53 return ret 54 55 56def recv(files, dest): 57 """ 58 Used with salt-cp, pass the files dict, and the destination. 59 60 This function receives small fast copy files from the master via salt-cp. 61 It does not work via the CLI. 62 """ 63 ret = {} 64 for path, data in files.items(): 65 if os.path.basename(path) == os.path.basename(dest) and not os.path.isdir(dest): 66 final = dest 67 elif os.path.isdir(dest): 68 final = os.path.join(dest, os.path.basename(path)) 69 elif os.path.isdir(os.path.dirname(dest)): 70 final = dest 71 else: 72 return "Destination unavailable" 73 74 try: 75 with salt.utils.files.fopen(final, "w+") as fp_: 76 fp_.write(data) 77 ret[final] = True 78 except OSError: 79 ret[final] = False 80 81 return ret 82 83 84def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): 85 """ 86 This function receives files copied to the minion using ``salt-cp`` and is 87 not intended to be used directly on the CLI. 88 """ 89 if "retcode" not in __context__: 90 __context__["retcode"] = 0 91 92 def _error(msg): 93 __context__["retcode"] = 1 94 return msg 95 96 if chunk is None: 97 # dest is an empty dir and needs to be created 98 try: 99 os.makedirs(dest) 100 except OSError as exc: 101 if exc.errno == errno.EEXIST: 102 if os.path.isfile(dest): 103 return "Path exists and is a file" 104 else: 105 return _error(exc.__str__()) 106 return True 107 108 chunk = base64.b64decode(chunk) 109 110 open_mode = "ab" if append else "wb" 111 try: 112 fh_ = salt.utils.files.fopen(dest, open_mode) # pylint: disable=W8470 113 except OSError as exc: 114 if exc.errno != errno.ENOENT: 115 # Parent dir does not exist, we need to create it 116 return _error(exc.__str__()) 117 try: 118 os.makedirs(os.path.dirname(dest)) 119 except OSError as makedirs_exc: 120 # Failed to make directory 121 return _error(makedirs_exc.__str__()) 122 fh_ = salt.utils.files.fopen(dest, open_mode) # pylint: disable=W8470 123 124 try: 125 # Write the chunk to disk 126 fh_.write(salt.utils.gzip_util.uncompress(chunk) if compressed else chunk) 127 except OSError as exc: 128 # Write failed 129 return _error(exc.__str__()) 130 else: 131 # Write successful 132 if not append and mode is not None: 133 # If this is the first chunk we're writing, set the mode 134 # log.debug('Setting mode for %s to %s', dest, oct(mode)) 135 log.debug("Setting mode for %s to %s", dest, mode) 136 try: 137 os.chmod(dest, mode) 138 except OSError: 139 return _error(exc.__str__()) 140 return True 141 finally: 142 try: 143 fh_.close() 144 except AttributeError: 145 pass 146 147 148def _mk_client(): 149 """ 150 Create a file client and add it to the context. 151 152 Each file client needs to correspond to a unique copy 153 of the opts dictionary, therefore it's hashed by the 154 id of the __opts__ dict 155 """ 156 if "cp.fileclient_{}".format(id(__opts__)) not in __context__: 157 __context__[ 158 "cp.fileclient_{}".format(id(__opts__)) 159 ] = salt.fileclient.get_file_client(__opts__) 160 161 162def _client(): 163 """ 164 Return a client, hashed by the list of masters 165 """ 166 _mk_client() 167 return __context__["cp.fileclient_{}".format(id(__opts__))] 168 169 170def _render_filenames(path, dest, saltenv, template, **kw): 171 """ 172 Process markup in the :param:`path` and :param:`dest` variables (NOT the 173 files under the paths they ultimately point to) according to the markup 174 format provided by :param:`template`. 175 """ 176 if not template: 177 return (path, dest) 178 179 # render the path as a template using path_template_engine as the engine 180 if template not in salt.utils.templates.TEMPLATE_REGISTRY: 181 raise CommandExecutionError( 182 "Attempted to render file paths with unavailable engine {}".format(template) 183 ) 184 185 kwargs = {} 186 kwargs["salt"] = __salt__ 187 if "pillarenv" in kw or "pillar" in kw: 188 pillarenv = kw.get("pillarenv", __opts__.get("pillarenv")) 189 kwargs["pillar"] = _gather_pillar(pillarenv, kw.get("pillar")) 190 else: 191 kwargs["pillar"] = __pillar__ 192 kwargs["grains"] = __grains__ 193 kwargs["opts"] = __opts__ 194 kwargs["saltenv"] = saltenv 195 196 def _render(contents): 197 """ 198 Render :param:`contents` into a literal pathname by writing it to a 199 temp file, rendering that file, and returning the result. 200 """ 201 # write out path to temp file 202 tmp_path_fn = salt.utils.files.mkstemp() 203 with salt.utils.files.fopen(tmp_path_fn, "w+") as fp_: 204 fp_.write(salt.utils.stringutils.to_str(contents)) 205 data = salt.utils.templates.TEMPLATE_REGISTRY[template]( 206 tmp_path_fn, to_str=True, **kwargs 207 ) 208 salt.utils.files.safe_rm(tmp_path_fn) 209 if not data["result"]: 210 # Failed to render the template 211 raise CommandExecutionError( 212 "Failed to render file path with error: {}".format(data["data"]) 213 ) 214 else: 215 return data["data"] 216 217 path = _render(path) 218 dest = _render(dest) 219 return (path, dest) 220 221 222def get_file( 223 path, dest, saltenv="base", makedirs=False, template=None, gzip=None, **kwargs 224): 225 """ 226 .. versionchanged:: 2018.3.0 227 ``dest`` can now be a directory 228 229 Used to get a single file from the salt master 230 231 CLI Example: 232 233 .. code-block:: bash 234 235 salt '*' cp.get_file salt://path/to/file /minion/dest 236 237 Template rendering can be enabled on both the source and destination file 238 names like so: 239 240 .. code-block:: bash 241 242 salt '*' cp.get_file "salt://{{grains.os}}/vimrc" /etc/vimrc template=jinja 243 244 This example would instruct all Salt minions to download the vimrc from a 245 directory with the same name as their os grain and copy it to /etc/vimrc 246 247 For larger files, the cp.get_file module also supports gzip compression. 248 Because gzip is CPU-intensive, this should only be used in scenarios where 249 the compression ratio is very high (e.g. pretty-printed JSON or YAML 250 files). 251 252 Use the *gzip* named argument to enable it. Valid values are 1..9, where 1 253 is the lightest compression and 9 the heaviest. 1 uses the least CPU on 254 the master (and minion), 9 uses the most. 255 256 There are two ways of defining the fileserver environment (a.k.a. 257 ``saltenv``) from which to retrieve the file. One is to use the ``saltenv`` 258 parameter, and the other is to use a querystring syntax in the ``salt://`` 259 URL. The below two examples are equivalent: 260 261 .. code-block:: bash 262 263 salt '*' cp.get_file salt://foo/bar.conf /etc/foo/bar.conf saltenv=config 264 salt '*' cp.get_file salt://foo/bar.conf?saltenv=config /etc/foo/bar.conf 265 266 .. note:: 267 It may be necessary to quote the URL when using the querystring method, 268 depending on the shell being used to run the command. 269 """ 270 (path, dest) = _render_filenames(path, dest, saltenv, template, **kwargs) 271 272 path, senv = salt.utils.url.split_env(path) 273 if senv: 274 saltenv = senv 275 276 if not hash_file(path, saltenv): 277 return "" 278 else: 279 return _client().get_file(path, dest, makedirs, saltenv, gzip) 280 281 282def envs(): 283 """ 284 List available environments for fileserver 285 286 CLI Example 287 288 .. code-block:: bash 289 290 salt '*' cp.envs 291 """ 292 return _client().envs() 293 294 295def get_template( 296 path, dest, template="jinja", saltenv="base", makedirs=False, **kwargs 297): 298 """ 299 Render a file as a template before setting it down. 300 Warning, order is not the same as in fileclient.cp for 301 non breaking old API. 302 303 CLI Example: 304 305 .. code-block:: bash 306 307 salt '*' cp.get_template salt://path/to/template /minion/dest 308 """ 309 if "salt" not in kwargs: 310 kwargs["salt"] = __salt__ 311 if "pillar" not in kwargs: 312 kwargs["pillar"] = __pillar__ 313 if "grains" not in kwargs: 314 kwargs["grains"] = __grains__ 315 if "opts" not in kwargs: 316 kwargs["opts"] = __opts__ 317 return _client().get_template(path, dest, template, makedirs, saltenv, **kwargs) 318 319 320def get_dir(path, dest, saltenv="base", template=None, gzip=None, **kwargs): 321 """ 322 Used to recursively copy a directory from the salt master 323 324 CLI Example: 325 326 .. code-block:: bash 327 328 salt '*' cp.get_dir salt://path/to/dir/ /minion/dest 329 330 get_dir supports the same template and gzip arguments as get_file. 331 """ 332 (path, dest) = _render_filenames(path, dest, saltenv, template, **kwargs) 333 334 return _client().get_dir(path, dest, saltenv, gzip) 335 336 337def get_url(path, dest="", saltenv="base", makedirs=False, source_hash=None): 338 """ 339 .. versionchanged:: 2018.3.0 340 ``dest`` can now be a directory 341 342 Used to get a single file from a URL. 343 344 path 345 A URL to download a file from. Supported URL schemes are: ``salt://``, 346 ``http://``, ``https://``, ``ftp://``, ``s3://``, ``swift://`` and 347 ``file://`` (local filesystem). If no scheme was specified, this is 348 equivalent of using ``file://``. 349 If a ``file://`` URL is given, the function just returns absolute path 350 to that file on a local filesystem. 351 The function returns ``False`` if Salt was unable to fetch a file from 352 a ``salt://`` URL. 353 354 dest 355 The default behaviour is to write the fetched file to the given 356 destination path. If this parameter is omitted or set as empty string 357 (``''``), the function places the remote file on the local filesystem 358 inside the Minion cache directory and returns the path to that file. 359 360 .. note:: 361 362 To simply return the file contents instead, set destination to 363 ``None``. This works with ``salt://``, ``http://``, ``https://`` 364 and ``file://`` URLs. The files fetched by ``http://`` and 365 ``https://`` will not be cached. 366 367 saltenv : base 368 Salt fileserver environment from which to retrieve the file. Ignored if 369 ``path`` is not a ``salt://`` URL. 370 371 source_hash 372 If ``path`` is an http(s) or ftp URL and the file exists in the 373 minion's file cache, this option can be passed to keep the minion from 374 re-downloading the file if the cached copy matches the specified hash. 375 376 .. versionadded:: 2018.3.0 377 378 CLI Example: 379 380 .. code-block:: bash 381 382 salt '*' cp.get_url salt://my/file /tmp/this_file_is_mine 383 salt '*' cp.get_url http://www.slashdot.org /tmp/index.html 384 """ 385 if isinstance(dest, str): 386 result = _client().get_url( 387 path, dest, makedirs, saltenv, source_hash=source_hash 388 ) 389 else: 390 result = _client().get_url( 391 path, None, makedirs, saltenv, no_cache=True, source_hash=source_hash 392 ) 393 if not result: 394 log.error( 395 "Unable to fetch file %s from saltenv %s.", 396 salt.utils.url.redact_http_basic_auth(path), 397 saltenv, 398 ) 399 if result: 400 return salt.utils.stringutils.to_unicode(result) 401 return result 402 403 404def get_file_str(path, saltenv="base"): 405 """ 406 Download a file from a URL to the Minion cache directory and return the 407 contents of that file 408 409 Returns ``False`` if Salt was unable to cache a file from a URL. 410 411 CLI Example: 412 413 .. code-block:: bash 414 415 salt '*' cp.get_file_str salt://my/file 416 """ 417 fn_ = cache_file(path, saltenv) 418 if isinstance(fn_, str): 419 try: 420 with salt.utils.files.fopen(fn_, "r") as fp_: 421 return salt.utils.stringutils.to_unicode(fp_.read()) 422 except OSError: 423 return False 424 return fn_ 425 426 427def cache_file(path, saltenv="base", source_hash=None, verify_ssl=True): 428 """ 429 Used to cache a single file on the Minion 430 431 Returns the location of the new cached file on the Minion 432 433 source_hash 434 If ``name`` is an http(s) or ftp URL and the file exists in the 435 minion's file cache, this option can be passed to keep the minion from 436 re-downloading the file if the cached copy matches the specified hash. 437 438 .. versionadded:: 2018.3.0 439 440 verify_ssl 441 If ``False``, remote https file sources (``https://``) and source_hash 442 will not attempt to validate the servers certificate. Default is True. 443 444 .. versionadded:: 3002 445 446 CLI Example: 447 448 .. code-block:: bash 449 450 salt '*' cp.cache_file salt://path/to/file 451 452 There are two ways of defining the fileserver environment (a.k.a. 453 ``saltenv``) from which to cache the file. One is to use the ``saltenv`` 454 parameter, and the other is to use a querystring syntax in the ``salt://`` 455 URL. The below two examples are equivalent: 456 457 .. code-block:: bash 458 459 salt '*' cp.cache_file salt://foo/bar.conf saltenv=config 460 salt '*' cp.cache_file salt://foo/bar.conf?saltenv=config 461 462 If the path being cached is a ``salt://`` URI, and the path does not exist, 463 then ``False`` will be returned. 464 465 .. note:: 466 It may be necessary to quote the URL when using the querystring method, 467 depending on the shell being used to run the command. 468 """ 469 path = salt.utils.data.decode(path) 470 saltenv = salt.utils.data.decode(saltenv) 471 472 contextkey = "{}_|-{}_|-{}".format("cp.cache_file", path, saltenv) 473 474 path_is_remote = ( 475 urllib.parse.urlparse(path).scheme in salt.utils.files.REMOTE_PROTOS 476 ) 477 try: 478 if path_is_remote and contextkey in __context__: 479 # Prevent multiple caches in the same salt run. Affects remote URLs 480 # since the master won't know their hash, so the fileclient 481 # wouldn't be able to prevent multiple caches if we try to cache 482 # the remote URL more than once. 483 if os.path.isfile(__context__[contextkey]): 484 return __context__[contextkey] 485 else: 486 # File is in __context__ but no longer exists in the minion 487 # cache, get rid of the context key and re-cache below. 488 # Accounts for corner case where file is removed from minion 489 # cache between cp.cache_file calls in the same salt-run. 490 __context__.pop(contextkey) 491 except AttributeError: 492 pass 493 494 path, senv = salt.utils.url.split_env(path) 495 if senv: 496 saltenv = senv 497 498 result = _client().cache_file( 499 path, saltenv, source_hash=source_hash, verify_ssl=verify_ssl 500 ) 501 if not result: 502 log.error("Unable to cache file '%s' from saltenv '%s'.", path, saltenv) 503 if path_is_remote: 504 # Cache was successful, store the result in __context__ to prevent 505 # multiple caches (see above). 506 __context__[contextkey] = result 507 return result 508 509 510def cache_dest(url, saltenv="base"): 511 """ 512 .. versionadded:: 3000 513 514 Returns the expected cache path for the file, if cached using 515 :py:func:`cp.cache_file <salt.modules.cp.cache_file>`. 516 517 .. note:: 518 This only returns the _expected_ path, it does not tell you if the URL 519 is really cached. To check if the URL is cached, use 520 :py:func:`cp.is_cached <salt.modules.cp.is_cached>` instead. 521 522 CLI Examples: 523 524 .. code-block:: bash 525 526 salt '*' cp.cache_dest https://foo.com/bar.rpm 527 salt '*' cp.cache_dest salt://my/file 528 salt '*' cp.cache_dest salt://my/file saltenv=dev 529 """ 530 return _client().cache_dest(url, saltenv) 531 532 533def cache_files(paths, saltenv="base"): 534 """ 535 Used to gather many files from the Master, the gathered files will be 536 saved in the minion cachedir reflective to the paths retrieved from the 537 Master 538 539 CLI Example: 540 541 .. code-block:: bash 542 543 salt '*' cp.cache_files salt://pathto/file1,salt://pathto/file1 544 545 There are two ways of defining the fileserver environment (a.k.a. 546 ``saltenv``) from which to cache the files. One is to use the ``saltenv`` 547 parameter, and the other is to use a querystring syntax in the ``salt://`` 548 URL. The below two examples are equivalent: 549 550 .. code-block:: bash 551 552 salt '*' cp.cache_files salt://foo/bar.conf,salt://foo/baz.conf saltenv=config 553 salt '*' cp.cache_files salt://foo/bar.conf?saltenv=config,salt://foo/baz.conf?saltenv=config 554 555 The querystring method is less useful when all files are being cached from 556 the same environment, but is a good way of caching files from multiple 557 different environments in the same command. For example, the below command 558 will cache the first file from the ``config1`` environment, and the second 559 one from the ``config2`` environment. 560 561 .. code-block:: bash 562 563 salt '*' cp.cache_files salt://foo/bar.conf?saltenv=config1,salt://foo/bar.conf?saltenv=config2 564 565 .. note:: 566 It may be necessary to quote the URL when using the querystring method, 567 depending on the shell being used to run the command. 568 """ 569 return _client().cache_files(paths, saltenv) 570 571 572def cache_dir( 573 path, saltenv="base", include_empty=False, include_pat=None, exclude_pat=None 574): 575 """ 576 Download and cache everything under a directory from the master 577 578 579 include_pat : None 580 Glob or regex to narrow down the files cached from the given path. If 581 matching with a regex, the regex must be prefixed with ``E@``, 582 otherwise the expression will be interpreted as a glob. 583 584 .. versionadded:: 2014.7.0 585 586 exclude_pat : None 587 Glob or regex to exclude certain files from being cached from the given 588 path. If matching with a regex, the regex must be prefixed with ``E@``, 589 otherwise the expression will be interpreted as a glob. 590 591 .. note:: 592 593 If used with ``include_pat``, files matching this pattern will be 594 excluded from the subset of files defined by ``include_pat``. 595 596 .. versionadded:: 2014.7.0 597 598 CLI Examples: 599 600 .. code-block:: bash 601 602 salt '*' cp.cache_dir salt://path/to/dir 603 salt '*' cp.cache_dir salt://path/to/dir include_pat='E@*.py$' 604 """ 605 return _client().cache_dir(path, saltenv, include_empty, include_pat, exclude_pat) 606 607 608def cache_master(saltenv="base"): 609 """ 610 Retrieve all of the files on the master and cache them locally 611 612 CLI Example: 613 614 .. code-block:: bash 615 616 salt '*' cp.cache_master 617 """ 618 return _client().cache_master(saltenv) 619 620 621def cache_local_file(path): 622 """ 623 Cache a local file on the minion in the localfiles cache 624 625 CLI Example: 626 627 .. code-block:: bash 628 629 salt '*' cp.cache_local_file /etc/hosts 630 """ 631 if not os.path.exists(path): 632 return "" 633 634 path_cached = is_cached(path) 635 636 # If the file has already been cached, return the path 637 if path_cached: 638 path_hash = hash_file(path) 639 path_cached_hash = hash_file(path_cached) 640 641 if path_hash["hsum"] == path_cached_hash["hsum"]: 642 return path_cached 643 644 # The file hasn't been cached or has changed; cache it 645 return _client().cache_local_file(path) 646 647 648def list_states(saltenv="base"): 649 """ 650 List all of the available state modules in an environment 651 652 CLI Example: 653 654 .. code-block:: bash 655 656 salt '*' cp.list_states 657 """ 658 return _client().list_states(saltenv) 659 660 661def list_master(saltenv="base", prefix=""): 662 """ 663 List all of the files stored on the master 664 665 CLI Example: 666 667 .. code-block:: bash 668 669 salt '*' cp.list_master 670 """ 671 return _client().file_list(saltenv, prefix) 672 673 674def list_master_dirs(saltenv="base", prefix=""): 675 """ 676 List all of the directories stored on the master 677 678 CLI Example: 679 680 .. code-block:: bash 681 682 salt '*' cp.list_master_dirs 683 """ 684 return _client().dir_list(saltenv, prefix) 685 686 687def list_master_symlinks(saltenv="base", prefix=""): 688 """ 689 List all of the symlinks stored on the master 690 691 CLI Example: 692 693 .. code-block:: bash 694 695 salt '*' cp.list_master_symlinks 696 """ 697 return _client().symlink_list(saltenv, prefix) 698 699 700def list_minion(saltenv="base"): 701 """ 702 List all of the files cached on the minion 703 704 CLI Example: 705 706 .. code-block:: bash 707 708 salt '*' cp.list_minion 709 """ 710 return _client().file_local_list(saltenv) 711 712 713def is_cached(path, saltenv="base"): 714 """ 715 Returns the full path to a file if it is cached locally on the minion 716 otherwise returns a blank string 717 718 CLI Example: 719 720 .. code-block:: bash 721 722 salt '*' cp.is_cached salt://path/to/file 723 """ 724 return _client().is_cached(path, saltenv) 725 726 727def hash_file(path, saltenv="base"): 728 """ 729 Return the hash of a file, to get the hash of a file on the 730 salt master file server prepend the path with salt://<file on server> 731 otherwise, prepend the file with / for a local file. 732 733 CLI Example: 734 735 .. code-block:: bash 736 737 salt '*' cp.hash_file salt://path/to/file 738 """ 739 path, senv = salt.utils.url.split_env(path) 740 if senv: 741 saltenv = senv 742 743 return _client().hash_file(path, saltenv) 744 745 746def stat_file(path, saltenv="base", octal=True): 747 """ 748 Return the permissions of a file, to get the permissions of a file on the 749 salt master file server prepend the path with salt://<file on server> 750 otherwise, prepend the file with / for a local file. 751 752 CLI Example: 753 754 .. code-block:: bash 755 756 salt '*' cp.stat_file salt://path/to/file 757 """ 758 path, senv = salt.utils.url.split_env(path) 759 if senv: 760 saltenv = senv 761 762 stat = _client().hash_and_stat_file(path, saltenv)[1] 763 if stat is None: 764 return stat 765 return salt.utils.files.st_mode_to_octal(stat[0]) if octal is True else stat[0] 766 767 768def push(path, keep_symlinks=False, upload_path=None, remove_source=False): 769 """ 770 WARNING Files pushed to the master will have global read permissions.. 771 772 Push a file from the minion up to the master, the file will be saved to 773 the salt master in the master's minion files cachedir 774 (defaults to ``/var/cache/salt/master/minions/minion-id/files``) 775 776 Since this feature allows a minion to push a file up to the master server 777 it is disabled by default for security purposes. To enable, set 778 ``file_recv`` to ``True`` in the master configuration file, and restart the 779 master. 780 781 keep_symlinks 782 Keep the path value without resolving its canonical form 783 784 upload_path 785 Provide a different path inside the master's minion files cachedir 786 787 remove_source 788 Remove the source file on the minion 789 790 .. versionadded:: 2016.3.0 791 792 CLI Example: 793 794 .. code-block:: bash 795 796 salt '*' cp.push /etc/fstab 797 salt '*' cp.push /etc/system-release keep_symlinks=True 798 salt '*' cp.push /etc/fstab upload_path='/new/path/fstab' 799 salt '*' cp.push /tmp/filename remove_source=True 800 """ 801 log.debug("Trying to copy '%s' to master", path) 802 if "../" in path or not os.path.isabs(path): 803 log.debug("Path must be absolute, returning False") 804 return False 805 if not keep_symlinks: 806 path = os.path.realpath(path) 807 if not os.path.isfile(path): 808 log.debug("Path failed os.path.isfile check, returning False") 809 return False 810 auth = _auth() 811 812 if upload_path: 813 if "../" in upload_path: 814 log.debug("Path must be absolute, returning False") 815 log.debug("Bad path: %s", upload_path) 816 return False 817 load_path = upload_path.lstrip(os.sep) 818 else: 819 load_path = path.lstrip(os.sep) 820 # Normalize the path. This does not eliminate 821 # the possibility that relative entries will still be present 822 load_path_normal = os.path.normpath(load_path) 823 824 # If this is Windows and a drive letter is present, remove it 825 load_path_split_drive = os.path.splitdrive(load_path_normal)[1] 826 827 # Finally, split the remaining path into a list for delivery to the master 828 load_path_list = [_f for _f in load_path_split_drive.split(os.sep) if _f] 829 830 load = { 831 "cmd": "_file_recv", 832 "id": __opts__["id"], 833 "path": load_path_list, 834 "size": os.path.getsize(path), 835 "tok": auth.gen_token(b"salt"), 836 } 837 838 with salt.transport.client.ReqChannel.factory(__opts__) as channel: 839 with salt.utils.files.fopen(path, "rb") as fp_: 840 init_send = False 841 while True: 842 load["loc"] = fp_.tell() 843 load["data"] = fp_.read(__opts__["file_buffer_size"]) 844 if not load["data"] and init_send: 845 if remove_source: 846 try: 847 salt.utils.files.rm_rf(path) 848 log.debug("Removing source file '%s'", path) 849 except OSError: 850 log.error("cp.push failed to remove file '%s'", path) 851 return False 852 return True 853 ret = channel.send(load) 854 if not ret: 855 log.error( 856 "cp.push Failed transfer failed. Ensure master has " 857 "'file_recv' set to 'True' and that the file " 858 "is not larger than the 'file_recv_size_max' " 859 "setting on the master." 860 ) 861 return ret 862 init_send = True 863 864 865def push_dir(path, glob=None, upload_path=None): 866 """ 867 Push a directory from the minion up to the master, the files will be saved 868 to the salt master in the master's minion files cachedir (defaults to 869 ``/var/cache/salt/master/minions/minion-id/files``). It also has a glob 870 for matching specific files using globbing. 871 872 .. versionadded:: 2014.7.0 873 874 Since this feature allows a minion to push files up to the master server it 875 is disabled by default for security purposes. To enable, set ``file_recv`` 876 to ``True`` in the master configuration file, and restart the master. 877 878 upload_path 879 Provide a different path and directory name inside the master's minion 880 files cachedir 881 882 CLI Example: 883 884 .. code-block:: bash 885 886 salt '*' cp.push /usr/lib/mysql 887 salt '*' cp.push /usr/lib/mysql upload_path='/newmysql/path' 888 salt '*' cp.push_dir /etc/modprobe.d/ glob='*.conf' 889 """ 890 if "../" in path or not os.path.isabs(path): 891 return False 892 tmpupload_path = upload_path 893 path = os.path.realpath(path) 894 if os.path.isfile(path): 895 return push(path, upload_path=upload_path) 896 else: 897 filelist = [] 898 for root, _, files in salt.utils.path.os_walk(path): 899 filelist += [os.path.join(root, tmpfile) for tmpfile in files] 900 if glob is not None: 901 filelist = [ 902 fi for fi in filelist if fnmatch.fnmatch(os.path.basename(fi), glob) 903 ] 904 if not filelist: 905 return False 906 for tmpfile in filelist: 907 if upload_path and tmpfile.startswith(path): 908 tmpupload_path = os.path.join( 909 os.path.sep, 910 upload_path.strip(os.path.sep), 911 tmpfile.replace(path, "").strip(os.path.sep), 912 ) 913 ret = push(tmpfile, upload_path=tmpupload_path) 914 if not ret: 915 return ret 916 return True 917