def setup_method(self, _): """Initialize default properties and instances.""" # pylint: disable=attribute-defined-outside-init self.worker = ConcreteBaseWorker({}, transports.Target( cumin.nodeset('node[1-2]'))) self.commands = [ transports.Command('command1'), transports.Command('command2') ]
def uptime( self, print_progress_bars: bool = True) -> List[Tuple[NodeSet, float]]: """Get current uptime. Arguments: print_progress_bars (bool, optional): whether to print Cumin's progress bars to stderr. Returns: list: a list of 2-element :py:class:`tuple` instances with hosts :py:class:`ClusterShell.NodeSet.NodeSet` as first item and :py:class:`float` uptime as second item. Raises: spicerack.remote.RemoteError: if unable to parse the output as an uptime. """ results = self.run_sync( transports.Command("cat /proc/uptime", timeout=10), is_safe=True, print_output=False, print_progress_bars=print_progress_bars, ) logger.debug("Got uptime for hosts %s", self._hosts) # Callback to extract the uptime from /proc/uptime (i.e. getting 12345.67 from '12345.67 123456789.00'). return RemoteHosts.results_to_list( results, callback=lambda output: float(output.split()[0]))
def reboot(self, batch_size: int = 1, batch_sleep: Optional[float] = 180.0) -> None: """Reboot hosts. Arguments: batch_size (int, optional): how many hosts to reboot in parallel. batch_sleep (float, optional): how long to sleep between one reboot and the next. """ if len(self._hosts ) == 1: # Temporary workaround until T213296 is fixed. batch_sleep = None logger.info( "Rebooting %d hosts in batches of %d with %.1fs of sleep in between: %s", len(self._hosts), batch_size, batch_sleep if batch_sleep is not None else 0.0, self._hosts, ) self.run_sync( transports.Command("reboot-host", timeout=30), batch_size=batch_size, batch_sleep=batch_sleep, )
def run_cumin(label, hosts_query, commands, timeout=30, installer=False, ignore_exit=False): """Run a remote command via Cumin. Arguments: label -- label to identify the caller in messages and logs hosts_query -- the query for the hosts selection to pass to cumin commands -- the list of commands to be executed timeout -- a timeout in seconds for each command. [optional, default: 30] installer -- whether the host will reboot into the installer or not, """ if installer: config = cumin_config_installer if 'SSH_AUTH_SOCK' in os.environ: del os.environ['SSH_AUTH_SOCK'] else: config = cumin_config hosts = query.Query(config).execute(hosts_query) target = transports.Target(hosts) worker = transport.Transport.new(config, target) ok_codes = None if ignore_exit: ok_codes = [] worker.commands = [transports.Command(command, timeout=timeout, ok_codes=ok_codes) for command in commands] worker.handler = 'async' exit_code = worker.execute() if exit_code != 0: raise RuntimeError('Failed to {label}'.format(label=label)) return exit_code, worker
def test_ok_codes_setter(self): """Should set the ok_codes to its value, unset it if None is passed.""" command = transports.Command('command1') assert command._ok_codes is None for i in range(256): codes = [i] command.ok_codes = codes assert command._ok_codes == codes codes.insert(0, 0) command.ok_codes = codes assert command._ok_codes == codes command.ok_codes = None assert command._ok_codes is None command.ok_codes = [] assert command._ok_codes == [] with pytest.raises(transports.WorkerError, match='ok_codes must be a list'): command.ok_codes = 'invalid_value' message = 'must be a list of integers in the range' for i in (-1, 0.0, 100.0, 256, 'invalid_value'): codes = [i] with pytest.raises(transports.WorkerError, match=message): command.ok_codes = codes codes.insert(0, 0) with pytest.raises(transports.WorkerError, match=message): command.ok_codes = codes
def __init__(self): """Initialize test commands.""" self.commands = [ { 'command': 'command1' }, { 'command': 'command1', 'timeout': 5 }, { 'command': 'command1', 'ok_codes': [0, 255] }, { 'command': 'command1', 'timeout': 5, 'ok_codes': [0, 255] }, { 'command': self.command_with_options }, { 'command': self.command_with_options, 'timeout': 5 }, { 'command': self.command_with_options, 'ok_codes': [0, 255] }, { 'command': self.command_with_options, 'timeout': 5, 'ok_codes': [0, 255] }, { 'command': self.command_with_nested_quotes }, { 'command': self.command_with_nested_quotes, 'timeout': 5 }, { 'command': self.command_with_nested_quotes, 'ok_codes': [0, 255] }, { 'command': self.command_with_nested_quotes, 'timeout': 5, 'ok_codes': [0, 255] }, ] for command in self.commands: command['obj'] = transports.Command( command['command'], timeout=command.get('timeout', None), ok_codes=command.get('ok_codes', None))
def test_ok_codes_getter_empty(self): """Should return the ok_codes set, [0] otherwise.""" # Test empty list command = transports.Command('command1') assert command.ok_codes == [0] command.ok_codes = [] assert command.ok_codes == [] command.ok_codes = [1, 255] assert command.ok_codes == [1, 255]
def test_timeout_setter(self): """Should set the timeout to its value, converted to float if integer. Unset it if None is passed.""" command = transports.Command('command1') command.timeout = 1.0 assert command._timeout == pytest.approx(1.0) command.timeout = None assert command._timeout is None command.timeout = 1 assert command._timeout == pytest.approx(1.0) with pytest.raises(transports.WorkerError, match='timeout must be a positive float'): command.timeout = -1.0
def test_eq(self, command): """A Command instance can be compared to another or to a string with the equality operator.""" assert command['obj'] == transports.Command( command['command'], timeout=command.get('timeout', None), ok_codes=command.get('ok_codes', None)) if command.get('timeout', None) is None and command.get( 'ok_codes', None) is None: assert command['obj'] == command['command'] with pytest.raises(ValueError, match='Unable to compare instance of'): command['obj'] == 1 # pylint: disable=pointless-statement
def run(args, config): """Execute the commands on the selected hosts and print the results. Arguments: args: ArgumentParser instance with parsed command line arguments config: a dictionary with the parsed configuration file """ hosts = get_hosts(args, config) if not hosts: return 0 target = transports.Target(hosts, batch_size=args.batch_size['value'], batch_size_ratio=args.batch_size['ratio'], batch_sleep=args.batch_sleep) worker = transport.Transport.new(config, target) ok_codes = None if args.ignore_exit_codes: ok_codes = [] worker.commands = [ transports.Command(command, timeout=args.timeout, ok_codes=ok_codes) for command in args.commands ] worker.timeout = args.global_timeout worker.handler = args.mode worker.success_threshold = args.success_percentage / 100 exit_code = worker.execute() if args.interactive: # Define a help function h() that will be available in the interactive shell to print the help message. # The name is to not shadow the Python built-in help() that might be usefult too to inspect objects. def h(): # pylint: disable=possibly-unused-variable,invalid-name """Print the help message in interactive shell.""" tqdm.write(INTERACTIVE_BANNER) code.interact(banner=INTERACTIVE_BANNER, local=locals()) elif args.output is not None: tqdm.write(OUTPUT_SEPARATOR) print_output(args.output, worker) return exit_code
def test_ne(self, command): """A Command instance can be compared to another or to a string with the inequality operator.""" # Different command with same or differnt properties assert command['obj'] != transports.Command( Commands.different_command, timeout=command.get('timeout', None), ok_codes=command.get('ok_codes', None)) assert command['obj'] != transports.Command(Commands.different_command, timeout=999, ok_codes=command.get( 'ok_codes', None)) assert command['obj'] != transports.Command(Commands.different_command, timeout=command.get( 'timeout', None), ok_codes=[99]) assert command['obj'] != transports.Command( Commands.different_command, timeout=999, ok_codes=[99]) assert command['obj'] != Commands.different_command # Same command, properties different assert command['obj'] != transports.Command(command['command'], timeout=999, ok_codes=command.get( 'ok_codes', None)) assert command['obj'] != transports.Command(command['command'], timeout=command.get( 'timeout', None), ok_codes=[99]) assert command['obj'] != transports.Command( command['command'], timeout=999, ok_codes=[99]) if command.get('timeout', None) is not None or command.get( 'ok_codes', None) is not None: assert command['obj'] != command['command'] with pytest.raises(ValueError, match='Unable to compare instance of'): command['obj'] == 1 # pylint: disable=pointless-statement
def test_eq_equivalent(self): """Two Commadn instances with equivalent comamnds just formatted differently should be considered equal.""" command1 = transports.Command(Commands.command_with_options) command2 = transports.Command(Commands.command_with_options_equivalent) assert command1 == command2