diff --git a/docs/changelog b/docs/changelog index bd0bc324..b1acf329 100644 --- a/docs/changelog +++ b/docs/changelog @@ -2,6 +2,7 @@ Changelog --------- next: + * Core: Add CDIST_REMOTE_COPY/EXEC env variables and multiplexing options for default scp/ssh (Darko Poljak) * Types: Remove bashisms in scripts (Darko Poljak) * Core: Fix bug in remote command with environment (Darko Poljak) * Core: Fix bug in local code execution (Darko Poljak) diff --git a/docs/man/cdist-reference.text.sh b/docs/man/cdist-reference.text.sh index 0a9d76ef..0aaaec0a 100755 --- a/docs/man/cdist-reference.text.sh +++ b/docs/man/cdist-reference.text.sh @@ -249,6 +249,12 @@ CDIST_OVERRIDE:: CDIST_ORDER_DEPENDENCY:: Create dependencies based on the execution order (see cdist-manifest(7)) +CDIST_REMOTE_EXEC:: + Use this command for remote execution (should behave like ssh) + +CDIST_REMOTE_COPY:: + Use this command for remote copy (should behave like scp) + SEE ALSO -------- - cdist(1) diff --git a/docs/man/man1/cdist.text b/docs/man/man1/cdist.text index c09d8f41..e29ae3ae 100644 --- a/docs/man/man1/cdist.text +++ b/docs/man/man1/cdist.text @@ -138,6 +138,11 @@ CDIST_LOCAL_SHELL:: CDIST_REMOTE_SHELL:: Selects shell for remote scirpt execution, defaults to /bin/sh +CDIST_REMOTE_EXEC:: + Use this command for remote execution (should behave like ssh) + +CDIST_REMOTE_COPY:: + Use this command for remote copy (should behave like scp) EXIT STATUS ----------- diff --git a/scripts/cdist b/scripts/cdist index 39449666..fecf61d0 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # # 2010-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2016 Darko Poljak (darko.poljak at gmail.com) # # This file is part of cdist. # @@ -20,6 +21,28 @@ # # +def inspect_ssh_mux_opts(control_path_dir="~/.ssh/"): + """Inspect whether or not ssh supports multiplexing options""" + import subprocess + import os + + control_path = os.path.join(control_path_dir, "cdist.master-%l-%r@%h:%p") + wanted_mux_opts = { + "ControlPath": control_path, + "ControlMaster": "auto", + "ControlPersist": "125", + } + mux_opts = " ".join([" -o {}={}".format(x, + wanted_mux_opts[x]) for x in wanted_mux_opts]) + try: + subprocess.check_output("ssh {}".format(mux_opts), + stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + subproc_output = e.output.decode().lower() + if "bad configuration option" in subproc_output: + return "" + return mux_opts + def commandline(): """Parse command line""" import argparse @@ -27,6 +50,9 @@ def commandline(): import cdist.banner import cdist.config import cdist.shell + import tempfile + import shutil + import os # Construct parser others can reuse parser = {} @@ -73,14 +99,17 @@ def commandline(): parser['config'].add_argument('-s', '--sequential', help='Operate on multiple hosts sequentially (default)', action='store_false', dest='parallel') + # remote-copy and remote-exec defaults are environment variables + # if set; if not then None - these will be futher handled after + # parsing to determine implementation default parser['config'].add_argument('--remote-copy', help='Command to use for remote copy (should behave like scp)', action='store', dest='remote_copy', - default=cdist.REMOTE_COPY) + default=os.environ.get('CDIST_REMOTE_COPY')) parser['config'].add_argument('--remote-exec', help='Command to use for remote execution (should behave like ssh)', action='store', dest='remote_exec', - default=cdist.REMOTE_EXEC) + default=os.environ.get('CDIST_REMOTE_EXEC')) parser['config'].set_defaults(func=cdist.config.Config.commandline) # Shell @@ -101,6 +130,32 @@ def commandline(): logging.root.setLevel(logging.INFO) if args.debug: logging.root.setLevel(logging.DEBUG) + args_dict = vars(args) + # if command with remote_copy and remote_exec params + if 'remote_copy' in args_dict and 'remote_exec' in args_dict: + # if out_path is not set then create temp dir here so + # Local uses it for base_path and ssh mux socket is + # created in it. + if args_dict['out_path'] is None: + args.out_path = tempfile.mkdtemp() + is_temp_dir = True + else: + is_temp_dir = False + # if remote-exec and/or remote-copy args are None then user + # didn't specify command line options nor env vars: + # inspect multiplexing options for default cdist.REMOTE_COPY/EXEC + if args_dict['remote_copy'] is None or args_dict['remote_exec'] is None: + control_path_dir = args.out_path + # only rmtree if it is temp directory; + # if user specifies out_path do not remove it + if is_temp_dir: + import atexit + atexit.register(lambda: shutil.rmtree(control_path_dir)) + mux_opts = inspect_ssh_mux_opts(control_path_dir) + if args_dict['remote_exec'] is None: + args.remote_exec = cdist.REMOTE_EXEC + mux_opts + if args_dict['remote_copy'] is None: + args.remote_copy = cdist.REMOTE_COPY + mux_opts log.debug(args) log.info("version %s" % cdist.VERSION)