From 6c81bf3d61da7712e4f706f5a79399cd9be62b0c Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Thu, 5 Nov 2015 13:14:10 -0800 Subject: [PATCH 1/3] Make ignoring keyboardinterrupts optional --- psshlib/cli.py | 2 ++ psshlib/manager.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/psshlib/cli.py b/psshlib/cli.py index e359652..c284d76 100644 --- a/psshlib/cli.py +++ b/psshlib/cli.py @@ -91,6 +91,8 @@ def common_parser(): 'spaces, quotes, and backslashes') connection_group.add_option('-X', '--extra-arg', dest='extra', action='append', metavar='ARG', help='Extra command-line argument') + connection_group.add_option('-k', '--allow-keyboard-interrupts', dest='allow_keyboard_interrupts', + action='store_true', help='Keyboard interrupts will terminate the process.') map(parser.add_option_group, [connection_group, output_group, filter_group]) parser.group_map = { # used so subparsers can easily find option groups diff --git a/psshlib/manager.py b/psshlib/manager.py index 5cd79f1..496b18f 100644 --- a/psshlib/manager.py +++ b/psshlib/manager.py @@ -136,11 +136,16 @@ def _run(self): # This exception handler tries to clean things up and prints # out a nice status message for each interrupted host. self.interrupted() + if self.opts.allow_keyboard_interrupts: + raise + except KeyboardInterrupt: # This exception handler doesn't print out any fancy status # information--it just stops. pass + if self.opts.allow_keyboard_interrupts: + raise if writer: writer.signal_quit() From 0527e9ca6af47aaec14016ace6810ced68375519 Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Thu, 5 Nov 2015 13:19:33 -0800 Subject: [PATCH 2/3] Remove some trailing whitespace --- psshlib/cli.py | 62 +++++++++++++++++++++++----------------------- psshlib/manager.py | 14 +++++------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/psshlib/cli.py b/psshlib/cli.py index c284d76..b54d87e 100644 --- a/psshlib/cli.py +++ b/psshlib/cli.py @@ -174,7 +174,7 @@ def run(self, hosts=[], args=None, opts=None): args = args or self.args opts = opts or self.opts hosts = hosts or ServerPool(opts) - + if args is None: raise Exception elif not hosts: @@ -183,7 +183,7 @@ def run(self, hosts=[], args=None, opts=None): self.setup(opts) manager = self.setup_manager(hosts, args, opts) - + psshutil.run_manager(manager) exitcode = self.teardown_manager(manager) @@ -231,19 +231,19 @@ def pssh_option_parser(): help='when used with the --script option, will do two things differently: ' '1) transfer the script to /root instead of /tmp, 2) run the script ' 'as root, not the login user') - pssh_group.add_option('--args', dest='script_args', + pssh_group.add_option('--args', dest='script_args', help='companion option for --script. Passes SCRIPT_ARGS as arguments' ' to the script run on the remote host.') pssh_group.add_option('--env', dest='env', action='append', metavar='SCRIPT_ENV', default=[], help='specify key=value pairs to inject into the environment of a running ' '--script, e.g. --env="FOO=BAR". Can be specified multiple times') - pssh_group.add_option('--runtime', + pssh_group.add_option('--runtime', help='specify the runtime to use when running the script from --script') pssh_group.add_option('--copy-to', default='/tmp', help='where to remotely copy scripts passed via --script (defaults to ' '/root if --sudo is passed, otherwise /tmp)') - + parser.add_option_group(pssh_group) parser.group_map['pssh_group'] = pssh_group @@ -256,10 +256,10 @@ def parse_args(self): defaults = common_defaults(timeout=_DEFAULT_TIMEOUT) parser.set_defaults(**defaults) opts, args = parser.parse_args() - + if len(args) == 0 and not opts.send_input and not opts.script: parser.error('Command not specified.') - + if not opts.host_files and not opts.host_strings: parser.error('Hosts not specified.') @@ -268,7 +268,7 @@ def parse_args(self): if opts.copy_to and not opts.copy_to.startswith('/'): parser.error('Remote script directory must be a path') - + return opts, args def setup(self, opts): @@ -323,7 +323,7 @@ def _generate_script_envelope(self): else: envelope = ( "cat > %(script)s; CATRET=$?; chmod 700 %(script)s; %(environ)s %(runner)s %(script_args)s; RET=$((CATRET+$?));" - "rm -f %(script)s; exit $RET" + "rm -f %(script)s; exit $RET" ) return envelope % { 'script': script, @@ -367,7 +367,7 @@ def setup_manager(self, hosts, args, opts): cmd.append(cmdline) t = SshTask(host, port, user, cmd, cmdline, opts, stdin) manager.add_task(t) - + return manager def teardown_manager(self, manager): @@ -406,16 +406,16 @@ def parse_args(self): defaults = common_defaults() parser.set_defaults(**defaults) opts, args = parser.parse_args() - + if len(args) < 1: parser.error('Paths not specified.') - + if len(args) < 2: parser.error('Remote path not specified.') - + if not opts.host_files and not opts.host_strings: parser.error('Hosts not specified.') - + return opts, args def setup(self, opts): @@ -475,16 +475,16 @@ def parse_args(self): defaults = common_defaults(timeout=_DEFAULT_TIMEOUT) parser.set_defaults(**defaults) opts, args = parser.parse_args() - + if len(args) < 1: parser.error('Pattern not specified.') - + if len(args) > 1: parser.error('Extra arguments given after the pattern.') - + if not opts.host_files and not opts.host_strings: parser.error('Hosts not specified.') - + return opts, args def setup(self, opts): @@ -549,19 +549,19 @@ def parse_args(self): defaults = common_defaults() parser.set_defaults(**defaults) opts, args = parser.parse_args() - + if len(args) < 1: parser.error('Paths not specified.') - + if len(args) < 2: parser.error('Remote path not specified.') - + if len(args) > 2: parser.error('Extra arguments given after the remote path.') - + if not opts.host_files and not opts.host_strings: parser.error('Hosts not specified.') - + return opts, args def setup(self, opts): @@ -587,7 +587,7 @@ def setup_manager(self, hosts, args, opts): ssh += ['-p', port] if opts.ssh_args: ssh += [opts.ssh_args] - + cmd = ['rsync', '-e', ' '.join(ssh)] if opts.verbose: cmd.append('-v') @@ -631,7 +631,7 @@ def pslurp_option_parser(): action='store_true', help='recusively copy directories (OPTIONAL)') pslurp_group.add_option('-L', '--localdir', dest='localdir', help='output directory for remote file copies') - + parser.add_option_group(pslurp_group) parser.group_map['pslurp_group'] = pslurp_group @@ -643,19 +643,19 @@ def parse_args(self): defaults = common_defaults() parser.set_defaults(**defaults) opts, args = parser.parse_args() - + if len(args) < 1: parser.error('Paths not specified.') - + if len(args) < 2: parser.error('Local path not specified.') - + if len(args) > 2: parser.error('Extra arguments given after the local path.') - + if not opts.host_files and not opts.host_strings: parser.error('Hosts not specified.') - + return opts, args def setup(self, opts): @@ -714,7 +714,7 @@ def teardown_manager(self, manager): return 3 for status in statuses: if status == 255: - return 4 + return 4 for status in statuses: if status != 0: return 5 diff --git a/psshlib/manager.py b/psshlib/manager.py index 496b18f..0ea7763 100644 --- a/psshlib/manager.py +++ b/psshlib/manager.py @@ -52,7 +52,7 @@ def __init__(self, opts): self.progress_bar = opts.progress_bar self.test_cases = opts.test_cases - + def _setup_progress_bar(self): """ This should be called after ``self.tasks`` is populated """ @@ -61,7 +61,7 @@ def _setup_progress_bar(self): def _split_manager(self): # set up the test manager and add first n tasks - new_opts = deepcopy(self.opts) + new_opts = deepcopy(self.opts) new_opts.__dict__['test_cases'] = None # remove test_cases option, or there'll be a recursion error new_opts.__dict__['summary'] = None # don't print summary now, do it later test_man = self.__class__(new_opts) @@ -82,7 +82,7 @@ def _split_manager(self): # add remaining tasks map(finish_man.add_task, self.tasks[slice(self.test_cases, None)]) psshutil.run_manager(finish_man) - + return test_man, finish_man def run(self): @@ -92,7 +92,7 @@ def run(self): man1, man2 = self._split_manager() self.done = man1.done + man2.done else: - self._run() + self._run() self.tally_results() @@ -102,15 +102,15 @@ def run(self): if self.opts.fork_hosts: failed_file = open(self.opts.fork_hosts + '.failed.lst', 'w') passed_file = open(self.opts.fork_hosts + '.passed.lst', 'w') - + for i in self.ssh_failed + self.killed + self.cmd_failed: failed_file.write(i.host + '\n') - + for i in self.succeeded: passed_file.write(i.host + '\n') return [task.exitstatus for task in self.done] - + def _run(self): try: if self.outdir or self.errdir: From f3ce7ab984e7c8e85d5fa0f1f53a2ff18f782bc4 Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Thu, 5 Nov 2015 13:28:13 -0800 Subject: [PATCH 3/3] Remove extraneous pass --- psshlib/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/psshlib/manager.py b/psshlib/manager.py index 0ea7763..823c33d 100644 --- a/psshlib/manager.py +++ b/psshlib/manager.py @@ -143,7 +143,6 @@ def _run(self): except KeyboardInterrupt: # This exception handler doesn't print out any fancy status # information--it just stops. - pass if self.opts.allow_keyboard_interrupts: raise