def ssh_copy_id(log, addr, passwords, hop=None): """ Use "ssh-copy-id" to copy ssh id, try passwords if asked for. """ session = None try: cmd = ("ssh-copy-id -o StrictHostKeyChecking=no -o ControlMaster=auto " "-o ControlPath='/var/tmp/%r@%h-%p' " "-o ControlPersist=60 -o UserKnownHostsFile=/dev/null " f"root@{addr}") if hop: cmd = hop.get_ssh_cmd() + " -t " + cmd session = aexpect.Expect(cmd, output_func=log.debug, output_prefix=">> ") try: session.read_until_any_line_matches(["password:"******"password:"******">> ") return False finally: if session: session.close()
def test_badly_behaved(self): """ Make sure avocado can cleanly get out of a loop of badly behaved tests. """ bad_test_basename = ('wontquit-%s' % data_factory.generate_random_string(5)) bad_test = script.TemporaryScript(bad_test_basename, BAD_TEST, 'avocado_interrupt_test', mode=DEFAULT_MODE) bad_test.save() os.chdir(basedir) cmd_line = ('./scripts/avocado run --sysinfo=off --job-results-dir %s ' '%s %s %s' % (self.tmpdir, bad_test.path, bad_test.path, bad_test.path)) proc = aexpect.Expect(command=cmd_line, linesep='') proc.read_until_last_line_matches(os.path.basename(bad_test.path)) proc.sendline('\x03') proc.read_until_last_line_matches('Interrupt requested. Waiting 2 ' 'seconds for test to finish ' '(ignoring new Ctrl+C until then)') # We have to actually wait 2 seconds until the ignore window is over time.sleep(2.5) proc.sendline('\x03') proc.read_until_last_line_matches('TESTS TIME : %d s') wait.wait_for(lambda: not proc.is_alive(), timeout=1) # Make sure the bad test will be really gone from the process table def wait_until_no_badtest(): bad_test_processes = [] old_psutil = False try: process_list = psutil.pids() except AttributeError: process_list = psutil.get_pid_list() old_psutil = True for p in process_list: try: p_obj = psutil.Process(p) if p_obj is not None: if old_psutil: cmdline_list = psutil.Process(p).cmdline else: cmdline_list = psutil.Process(p).cmdline() if bad_test.path in " ".join(cmdline_list): bad_test_processes.append(p_obj) # psutil.NoSuchProcess happens when the original # process already ended and left the process table except psutil.NoSuchProcess: pass return len(bad_test_processes) == 0 wait.wait_for(wait_until_no_badtest, timeout=2) # Make sure the Killing test subprocess message did appear self.assertIn('Killing test subprocess', proc.get_output())
def remote_scp(command, password_list, log_filename=None, transfer_timeout=600, login_timeout=20): """ Transfer files using SCP, given a command line. :param command: The command to execute (e.g. "scp -r foobar root@localhost:/tmp/"). :param password_list: Password list to send in reply to a password prompt. :param log_filename: If specified, log all output to this file :param transfer_timeout: The time duration (in seconds) to wait for the transfer to complete. :param login_timeout: The maximal time duration (in seconds) to wait for each step of the login procedure (i.e. the "Are you sure" prompt or the password prompt) :raise: Whatever _remote_scp() raises """ logging.debug("Trying to SCP with command '%s', timeout %ss", command, transfer_timeout) if log_filename: output_func = utils_misc.log_line output_params = (log_filename,) else: output_func = None output_params = () session = aexpect.Expect(command, output_func=output_func, output_params=output_params) try: _remote_scp(session, password_list, transfer_timeout, login_timeout) finally: session.close()
def test_well_behaved(self): """ Make sure avocado can cleanly get out of a loop of well behaved tests. """ good_test_basename = ('goodtest-%s.py' % data_factory.generate_random_string(5)) good_test = script.TemporaryScript(good_test_basename, GOOD_TEST, 'avocado_interrupt_test', mode=DEFAULT_MODE) good_test.save() os.chdir(basedir) cmd_line = ('%s run --sysinfo=off --job-results-dir %s ' '%s %s %s' % (AVOCADO, self.tmpdir, good_test.path, good_test.path, good_test.path)) proc = aexpect.Expect(command=cmd_line, linesep='') proc.read_until_last_line_matches(os.path.basename(good_test.path)) proc.sendline('\x03') proc.read_until_last_line_matches('TESTS TIME : %d s') wait.wait_for(lambda: not proc.is_alive(), timeout=1) # Make sure the good test will be really gone from the process table def wait_until_no_goodtest(): good_test_processes = [] old_psutil = False try: process_list = psutil.pids() except AttributeError: process_list = psutil.get_pid_list() old_psutil = True for p in process_list: try: p_obj = psutil.Process(p) if p_obj is not None: if old_psutil: cmdline_list = psutil.Process(p).cmdline else: try: cmdline_list = psutil.Process(p).cmdline() except psutil.AccessDenied: cmdline_list = [] if good_test.path in " ".join(cmdline_list): good_test_processes.append(p_obj) # psutil.NoSuchProcess happens when the original # process already ended and left the process table except psutil.NoSuchProcess: pass return len(good_test_processes) == 0 wait.wait_for(wait_until_no_goodtest, timeout=2) # Make sure the Killing test subprocess message is not there self.assertNotIn('Killing test subprocess', proc.get_output()) # Make sure the Interrupted requested sentence is there self.assertIn( 'Interrupt requested. Waiting 2 seconds for test to ' 'finish (ignoring new Ctrl+C until then)', proc.get_output())
def scp_to_remote(host, port, username, password, local_path, remote_path, limit="", output_func=None, timeout=600): """ Copy files to a remote host (guest) through scp. Args: limit: Speed limit of file transfer, it means bandwidth. """ transfer_timeout = timeout login_timeout = 60 if limit != "": limit = "-l %s" % (limit) command = "scp" command += (" -r " "-v -o UserKnownHostsFile=/dev/null " "-o StrictHostKeyChecking=no " "-o PreferredAuthentications=password %s " r"-P %s %s %s@\[%s\]:%s" % (limit, port, pipes.quote(local_path), username, host, pipes.quote(remote_path))) logging.debug("Trying to SCP with command '%s', timeout %ss", command, transfer_timeout) output_params = () session = aexpect.Expect(command, output_func=output_func, output_params=output_params) try: _scp_operation(session, password, transfer_timeout, login_timeout) finally: session.close()
def send_data_from_guest_to_host(guest_session, nc_vsock_bin, guest_cid, tmp_file, file_size=1000): """ Generate a temp file and transfer it from guest to host via vsock :param guest_session: Guest session object :param nc_vsock_bin: Path to nc-vsock binary :param guest_cid: Guest cid to connected :param file_size: Desired file size to be transferred :return: The host nc-vsock connection process """ cmd_generate = 'dd if=/dev/urandom of=%s count=%s bs=1M' % (tmp_file, file_size) guest_session.cmd_status(cmd_generate, timeout=600) port = random.randrange(1, 6000) cmd_transfer = '%s -l %s < %s' % (nc_vsock_bin, port, tmp_file) error_context.context( 'Transfer file from guest via command: %s' % cmd_transfer, logging.info) guest_session.sendline(cmd_transfer) cmd_receive = '%s %s %s > %s' % (nc_vsock_bin, guest_cid, port, tmp_file) return aexpect.Expect(cmd_receive, auto_close=True, output_func=utils_misc.log_line, output_params=('%s.log' % tmp_file, ))
def _check_install_key_required(self): def _safe_ssh_ping(): try: self._ssh_ping() return True except (SSHPermissionDeniedError, process.CmdError): return None if not self.key_file and self.password: try: self._ssh_ping() except (SSHPermissionDeniedError, process.CmdError): copy_id_cmd = ('ssh-copy-id -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p %s %s@%s' % (self.port, self.user, self.hostname)) while True: try: expect = aexpect.Expect(copy_id_cmd) expect.read_until_output_matches(['.*password:'******'Waiting for password-less SSH') if result is None: raise SSHPermissionDeniedError('Unable to configure ' 'password less SSH. ' 'Output of %s: %s' % (copy_id_cmd, expect.get_output())) else: self.log.info('Successfully configured SSH key auth')
def test_kill_stopped_sleep(self): sleep = process.run("which sleep", ignore_status=True, shell=True) if sleep.exit_status: self.skipTest("Sleep binary not found in PATH") sleep = "'%s 60'" % sleep.stdout.strip() proc = aexpect.Expect("./scripts/avocado run %s --job-results-dir %s " "--sysinfo=off --job-timeout 3" % (sleep, self.tmpdir)) proc.read_until_output_matches(["\(1/1\)"], timeout=3, internal_timeout=0.01) # We need pid of the avocado, not the shell executing it pid = int(process.get_children_pids(proc.get_pid())[0]) os.kill(pid, signal.SIGTSTP) # This freezes the process deadline = time.time() + 9 while time.time() < deadline: if not proc.is_alive(): break time.sleep(0.1) else: proc.kill(signal.SIGKILL) self.fail("Avocado process still alive 5s after job-timeout:\n%s" % proc.get_output()) output = proc.get_output() self.assertIn( "ctrl+z pressed, stopping test", output, "SIGTSTP " "message not in the output, test was probably not " "stopped.") self.assertIn( "TIME", output, "TIME not in the output, avocado " "probably died unexpectadly") self.assertEqual(proc.get_status(), 1, "Avocado did not finish with " "1.")
def _test_unix_communication(session, guest_path, host_path, test): """ Test unix socket communication between host and guest through channel """ nc_session = aexpect.Expect('nc -U "%s"' % host_path) try: # Test message passing from guest to host msg = "What hath God wrought" try: session.cmd_status('echo "%s" > %s' % (msg, guest_path), timeout=1) except aexpect.ShellTimeoutError as detail: pass nc_session.read_until_last_line_matches(msg, timeout=1) # Test message passing from host to guest nc_session.sendline(msg) try: session.cmd_output('cat %s' % guest_path, timeout=1) except aexpect.ShellTimeoutError as detail: if detail.output.strip() != msg: test.fail("Expect receive '%s' in guest, " "But got %s" % (msg, detail.output)) finally: nc_session.close()
def open_session(self, a_id): """ Restore connection to existing session identified by a_id """ # Allow this to be called more than once w/o consequence self.close_session(warn_if_nonexist=self.used) aexpect.Expect(a_id=a_id) self.used = True
def new_session(self, command): """ Create and set new opaque session object """ # Allow this to be called more than once w/o consequence self.close_session(warn_if_nonexist=self.used) self.session = aexpect.Expect(command, auto_close=False) self.used = True
def remote_commander(client, host, port, username, password, prompt, linesep="\n", log_filename=None, timeout=10, path=None): """ Log into a remote host (guest) using SSH/Telnet/Netcat. :param client: The client to use ('ssh', 'telnet' or 'nc') :param host: Hostname or IP address :param port: Port to connect to :param username: Username (if required) :param password: Password (if required) :param prompt: Shell prompt (regular expression) :param linesep: The line separator to use when sending lines (e.g. '\\n' or '\\r\\n') :param log_filename: If specified, log all output to this file :param timeout: The maximal time duration (in seconds) to wait for each step of the login procedure (i.e. the "Are you sure" prompt or the password prompt) :param path: The path to place where remote_runner.py is placed. :raise LoginBadClientError: If an unknown client is requested :raise: Whatever handle_prompts() raises :return: A ShellSession object. """ if path is None: path = data_dir.get_tmp_dir() if client == "ssh": cmd = ("ssh -o UserKnownHostsFile=/dev/null " "-o PreferredAuthentications=password " "-p %s %s@%s %s agent_base64" % (port, username, host, os.path.join(path, "remote_runner.py"))) elif client == "telnet": cmd = "telnet -l %s %s %s" % (username, host, port) elif client == "nc": cmd = "nc %s %s" % (host, port) else: raise LoginBadClientError(client) logging.debug("Login command: '%s'", cmd) session = aexpect.Expect(cmd, linesep=linesep) try: handle_prompts(session, username, password, prompt, timeout) except Exception: session.close() raise if log_filename: log_file = utils_misc.get_log_filename(log_filename) session.set_output_func(utils_misc.log_line) session.set_output_params((log_file,)) session.set_log_file(os.path.basename(log_file)) session.send_ctrl("raw") # Wrap io interfaces. inw = messenger.StdIOWrapperInBase64(session._get_fd("tail")) outw = AexpectIOWrapperOut(session) # Create commander cmd = remote_master.CommanderMaster(inw, outw, False) return cmd
def nc_vsock_connect(nc_vsock_bin, guest_cid, port): """ Connect to vsock port from host, cmd: nc-vsock $guest_cid $port :param nc_vsock_bin: path of binary nc-vsock :param guest_cid: guest cid to connect :param port: port to connect :return: The vsock session from host side, being waiting for input """ nc_vsock_cmd = "%s %s %s" % (nc_vsock_bin, guest_cid, port) logging.info("Connect to the vsock port on host: %s", nc_vsock_cmd) return aexpect.Expect(nc_vsock_cmd, auto_close=False, output_func=utils_misc.log_line, output_params=("vsock_%s_%s" % (guest_cid, port), ))
def test_kill_stopped_sleep(self): sleep = process.run("which sleep", ignore_status=True, shell=True) if sleep.exit_status: self.skipTest("Sleep binary not found in PATH") sleep = "'%s 60'" % sleep.stdout.strip() proc = aexpect.Expect("./scripts/avocado run %s --job-results-dir %s " "--sysinfo=off --job-timeout 3" % (sleep, self.tmpdir)) proc.read_until_output_matches(["\(1/1\)"], timeout=3, internal_timeout=0.01) # We need pid of the avocado, not the shell executing it pid = int(process.get_children_pids(proc.get_pid())[0]) os.kill(pid, signal.SIGTSTP) # This freezes the process deadline = time.time() + 9 while time.time() < deadline: if not proc.is_alive(): break time.sleep(0.1) else: proc.kill(signal.SIGKILL) self.fail("Avocado process still alive 5s after job-timeout:\n%s" % proc.get_output()) output = proc.get_output() self.assertIn( "ctrl+z pressed, stopping test", output, "SIGTSTP " "message not in the output, test was probably not " "stopped.") self.assertIn( "TIME", output, "TIME not in the output, avocado " "probably died unexpectadly") self.assertEqual(proc.get_status(), 8, "Avocado did not finish with " "1.") sleep_dir = astring.string_to_safe_path("1-" + sleep[1:-1]) debug_log = os.path.join(self.tmpdir, "latest", "test-results", sleep_dir, "debug.log") debug_log = open(debug_log).read() self.assertIn( "Runner error occurred: Timeout reached", debug_log, "Runner error occurred: Timeout reached message not " "in the test's debug.log:\n%s" % debug_log) self.assertNotIn( "Traceback (most recent", debug_log, "Traceback " "present in the test's debug.log file, but it was " "suppose to be stopped and unable to produce it.\n" "%s" % debug_log)
def test_well_behaved(self): """ Make sure avocado can cleanly get out of a loop of well behaved tests. """ good_test_basename = ('goodtest-%s.py' % data_factory.generate_random_string(5)) good_test = script.TemporaryScript(good_test_basename, GOOD_TEST, 'avocado_interrupt_test', mode=0755) good_test.save() os.chdir(basedir) cmd_line = ( './scripts/avocado run --sysinfo=off --job-results-dir %s ' '%s %s %s' % (self.tmpdir, good_test.path, good_test.path, good_test.path)) proc = aexpect.Expect(command=cmd_line, linesep='') proc.read_until_last_line_matches(os.path.basename(good_test.path)) proc.sendline('\x03') proc.read_until_last_line_matches('TIME : %d s') wait.wait_for(lambda: not proc.is_alive(), timeout=1) # Make sure the good test will be really gone from the process table def wait_until_no_goodtest(): good_test_processes = [] for p in psutil.pids(): p_obj = None try: p_obj = psutil.Process(p) except psutil.NoSuchProcess: pass if p_obj is not None: if good_test.path in " ".join(psutil.Process(p).cmdline()): good_test_processes.append(p_obj) return len(good_test_processes) == 0 wait.wait_for(wait_until_no_goodtest, timeout=2) # Make sure the Killing test subprocess message is not there self.assertNotIn('Killing test subprocess', proc.get_output()) # Make sure the Interrupted requested sentence is there self.assertIn( 'Interrupt requested. Waiting 2 seconds for test to ' 'finish (ignoring new Ctrl+C until then)', proc.get_output())
def vsock_connect(tool_bin, guest_cid, port): """ Connect to vsock port from host :param tool_bin: path of binary vsock test tool :param guest_cid: guest cid to connect :param port: port to connect :return: The vsock session from host side, being waiting for input """ if "ncat" in tool_bin: conn_cmd = "%s --vsock %s %s" % (tool_bin, guest_cid, port) if "nc-vsock" in tool_bin: conn_cmd = "%s %s %s" % (tool_bin, guest_cid, port) LOG_JOB.info("Connect to the vsock port on host: %s", conn_cmd) return aexpect.Expect(conn_cmd, auto_close=False, output_func=utils_misc.log_line, output_params=("vsock_%s_%s" % (guest_cid, port), ))
def run(test, params, env): """ Vsock basic function test 1. Boot guest with vhost-vsock-pci device 2. Download and compile nc-vsock on both guest and host 3. Start listening inside guest, nc-vsock -l $port 4. Connect guest CID from host, nc-vsock $guest_cid $port 5. Input character, e.g. 'Hello world' 6. Check if guest receive the content correctly :param test: QEMU test object :param params: Dictionary with the test parameters :param env: Dictionary with test environment """ def clean(tmp_file=None): """ Clean the environment """ cmd_rm = "rm -rf %s*" % nc_vsock_bin if tmp_file: cmd_rm += "; rm -rf %s" % tmp_file session.cmd_output_safe(cmd_rm) process.system(cmd_rm, ignore_status=True) if host_vsock_session.is_alive(): host_vsock_session.close() session.close() vm = env.get_vm(params["main_vm"]) tmp_file = "/tmp/vsock_file_%s" % utils_misc.generate_random_string(6) session = vm.wait_for_login() # TODO: Close selinux as temporary workaround for qemu bug 1656738 # should be removed when fixed session.cmd_output("setenforce 0") nc_vsock_bin = compile_nc_vsock(test, vm, session) vsock_dev = params["vsocks"].split()[0] guest_cid = vm.devices.get(vsock_dev).get_param("guest-cid") port = random.randrange(1, 6000) nc_vsock_listen(nc_vsock_bin, port, session) host_vsock_session = nc_vsock_connect(nc_vsock_bin, guest_cid, port) connected_str = r"Connection from cid*" send_data = "Hello world" check_received_data(test, session, connected_str) error_context.context('Input "Hello world" to vsock.', logging.info) host_vsock_session.sendline(send_data) check_received_data(test, session, send_data) host_vsock_session.close() try: session.read_up_to_prompt(timeout=10) except aexpect.ExpectTimeoutError: test.fail("nc-vsock listening prcoess inside guest" " does not exit after close host nc-vsock connection.") finally: session.close() # Transfer data from guest to host session = vm.wait_for_login() cmd_generate = 'dd if=/dev/urandom of=%s count=1000 bs=1M' % tmp_file session.cmd_status(cmd_generate) cmd_transfer = '%s -l %s < %s &' % (nc_vsock_bin, port, tmp_file) error_context.context( 'Transfer file from guest via command: %s' % cmd_transfer, logging.info) session.cmd_status(cmd_transfer) time.sleep(1) # wait nc-vsock start listening cmd_receive = '%s %s %s > %s' % (nc_vsock_bin, guest_cid, port, tmp_file) rec_session = aexpect.Expect(cmd_receive, auto_close=True, output_func=utils_misc.log_line, output_params=('%s.log' % tmp_file, )) utils_misc.wait_for(lambda: not rec_session.is_alive(), timeout=20) cmd_chksum = 'md5sum %s' % tmp_file md5_origin = session.cmd_output(cmd_chksum).split()[0] md5_received = process.system_output(cmd_chksum).split()[0].decode() if md5_received != md5_origin: clean(tmp_file) test.fail('Data transfer not integrated, the original md5 value' ' is %s, while the md5 value received on host is %s' % (md5_origin, md5_received)) clean(tmp_file)