def _dumpxml(name, to_file="", **dargs): """ Get a xml from name. """ if not name: cmd = "virsh dumpxml %s" % name stdout = "error: command 'dumpxml' requires <domain> option" stderr = stdout exit_status = 1 result = process.CmdResult(cmd, stdout, stderr, exit_status) raise process.CmdError(cmd, result, "Virsh Command returned non-zero exit status") file_path = os.path.join(LibvirtXMLTestBase.__doms_dir__, '%s.xml' % name) if os.path.exists(file_path): xml_file = open(file_path, 'r') domain_xml = xml_file.read() else: xml_file = open(file_path, 'w') domain_xml = LibvirtXMLTestBase.__domain_xml__ % (name, LibvirtXMLTestBase._domuuid(None)) xml_file.write(domain_xml) xml_file.close() cmd = "virsh dumpxml %s" % name stdout = domain_xml stderr = "" exit_status = 0 return process.CmdResult(cmd, stdout, stderr, exit_status)
def test_empty(self): mock_result = process.CmdResult( command='lsblk --json --paths --inverse', stdout=b'{"blockdevices": []}') with unittest.mock.patch('avocado.utils.disk.process.run', return_value=mock_result): self.assertEqual(disk.get_disks(), [])
def test_disks(self): mock_result = process.CmdResult( command='lsblk --json --paths --inverse', stdout=LSBLK_OUTPUT) with unittest.mock.patch('avocado.utils.disk.process.run', return_value=mock_result): self.assertEqual(disk.get_disks(), ['/dev/vda'])
def _nodedev_dumpxml(name, options="", to_file=None, **dargs): # Must mirror virsh.nodedev_dumpxml() API but can't test this option if options != "": raise ValueError('Dummy virsh for testing does not support options' ' parameter') if to_file is not None: raise ValueError('Dummy virsh for testing does not support to_file' ' parameter') if name is not 'pci_0000_00_00_0': raise ValueError('Dummy virsh for testing only support ' ' device name pci_0000_00_00_0') xml = ("<device>" "<name>pci_0000_00_00_0</name>" "<path>/sys/devices/pci0000:00/0000:00:00.0</path>" "<parent>computer</parent>" "<capability type='pci'>" "<domain>0</domain>" "<bus>0</bus>" "<slot>0</slot>" "<function>0</function>" "<product id='0x25c0'>5000X Chipset Memory Controller Hub</product>" "<vendor id='0x8086'>Intel Corporation</vendor>" "</capability>" "</device>") return process.CmdResult('virsh nodedev-dumpxml pci_0000_00_00_0', xml, '', 0)
def run(command, ignore_status=False, quiet=True, timeout=60): """ Executes a command on the defined fabric hosts. This is basically a wrapper to fabric.operations.run, encapsulating the result on an avocado process.CmdResult object. This also assumes the fabric environment was previously (and properly) initialized. :param command: the command string to execute. :param ignore_status: Whether to not raise exceptions in case the command's return code is different than zero. :param timeout: Maximum time allowed for the command to return. :param quiet: Whether to not log command stdout/err. Default: True. :return: the result of the remote program's execution. :rtype: :class:`avocado.utils.process.CmdResult`. :raise fabric.exceptions.CommandTimeout: When timeout exhausted. """ result = process.CmdResult() start_time = time.time() end_time = time.time() + (timeout or 0) # Support timeout=None # Fabric sometimes returns NetworkError even when timeout not reached fabric_result = None fabric_exception = None while True: try: fabric_result = fabric.operations.run(command=command, quiet=quiet, warn_only=True, timeout=timeout, pty=False, combine_stderr=False) break except fabric.network.NetworkError as details: fabric_exception = details timeout = end_time - time.time() if time.time() > end_time: break if fabric_result is None: if fabric_exception is not None: raise fabric_exception # it's not None pylint: disable=E0702 else: raise fabric.network.NetworkError("Remote execution of '%s'" "failed without any " "exception. This should not " "happen." % command) end_time = time.time() duration = end_time - start_time result.command = command result.stdout = str(fabric_result.stdout) result.stderr = str(fabric_result.stderr) result.duration = duration result.exit_status = fabric_result.return_code result.failed = fabric_result.failed result.succeeded = fabric_result.succeeded if not ignore_status: if result.failed: raise process.CmdError(command=command, result=result) return result
def test_log(self): class CatchHandler(logging.NullHandler): """ Handler used just to confirm that a logging event happened """ def __init__(self, *args, **kwargs): super(CatchHandler, self).__init__(*args, **kwargs) self.caught_record = False def handle(self, record): self.caught_record = True read_fd, write_fd = os.pipe() result = process.CmdResult() logger = logging.getLogger("FDDrainerTests.test_log") handler = CatchHandler() logger.addHandler(handler) logger.setLevel(logging.DEBUG) fd_drainer = process.FDDrainer(read_fd, result, "test", logger=logger, verbose=True) fd_drainer.start() os.write(write_fd, b"should go to the log\n") os.close(write_fd) fd_drainer.flush() self.assertEqual(fd_drainer.data.getvalue(), b"should go to the log\n") self.assertTrue(handler.caught_record)
def run(self, command, timeout=60, ignore_status=False): """ Method to provide a utils.run-like interface to execute command on remote host or guest. :param timeout: Total time duration to wait for command return. :param ignore_status: If ignore_status=True, do not raise an exception, no matter what the exit code of the command is. Else, raise CmdError if exit code of command is not zero. """ # Redirect the stdout and stderr to file, Deviding error message # from output, and taking off the color of output. To return the same # result with utils.run() function. command = "%s 1>%s 2>%s" % (command, self.stdout_pipe, self.stderr_pipe) status, _ = self.session.cmd_status_output(command, timeout=timeout) output = self.session.cmd_output("cat %s;rm -f %s" % (self.stdout_pipe, self.stdout_pipe)) errput = self.session.cmd_output("cat %s;rm -f %s" % (self.stderr_pipe, self.stderr_pipe)) cmd_result = process.CmdResult(command=command, exit_status=status, stdout=output, stderr=errput) if status and (not ignore_status): raise process.CmdError(command, cmd_result) return cmd_result
def test_failure_no_capabilities(self): stdout = b"1: cap_chown,cap_dac_override" cmd_result = process.CmdResult(stdout=stdout, exit_status=1) with unittest.mock.patch('avocado.utils.process.run', return_value=cmd_result): capabilities = process.get_capabilities() self.assertEqual(capabilities, [])
def trigger_hpt_resize(session): """ Check the HPT order file and dmesg :param session: the session to guest :raise: test.fail if required message is not found """ hpt_order_path = "/sys/kernel/debug/powerpc/hpt_order" hpt_order = session.cmd_output('cat %s' % hpt_order_path).strip() hpt_order = int(hpt_order) logging.info('Current hpt_order is %d', hpt_order) hpt_order += 1 cmd = 'echo %d > %s' % (hpt_order, hpt_order_path) cmd_result = session.cmd_status_output(cmd) result = process.CmdResult(stderr=cmd_result[1], stdout=cmd_result[1], exit_status=cmd_result[0]) libvirt.check_exit_status(result) dmesg = session.cmd('dmesg') dmesg_content = params.get('dmesg_content').split('|') for content in dmesg_content: if content % hpt_order not in dmesg: test.fail("'%s' is missing in dmesg" % (content % hpt_order)) else: logging.info("'%s' is found in dmesg", content % hpt_order)
def test_nasty_str(self): result = process.CmdResult("ls", b"unicode_follows: \xc5\xa1", b"cp1250 follows: \xfd", 1, 2, 3, "wrong_encoding") err = process.CmdError("ls", result, "please don't crash") self.assertEqual(str(err), "Command 'ls' failed.\nstdout: " "b'unicode_follows: \\xc5\\xa1'\nstderr: " "b'cp1250 follows: \\xfd'\nadditional_info: " "please don't crash")
def test_nasty_str(self): result = process.CmdResult("ls", b"unicode_follows: \xc5\xa1", b"cp1250 follows: \xfd", 1, 2, 3, "wrong_encoding") self.assertEqual(str(result), "command: 'ls'\nexit_status: 1" "\nduration: 2\ninterrupted: False\npid: " "3\nencoding: 'wrong_encoding'\nstdout: " "b'unicode_follows: \\xc5\\xa1'\nstderr: " "b'cp1250 follows: \\xfd'")
def test_flush_on_handler_with_no_fileno(self): handler = logging.StreamHandler(io.StringIO()) log = logging.getLogger("test_flush_on_handler_with_no_fileno") log.addHandler(handler) read_fd, write_fd = os.pipe() result = process.CmdResult() fd_drainer = process.FDDrainer(read_fd, result, name="test", stream_logger=log) fd_drainer.start() os.close(write_fd) fd_drainer.flush()
def test_drain_from_pipe_fd(self): read_fd, write_fd = os.pipe() result = process.CmdResult() fd_drainer = process.FDDrainer(read_fd, result, "test") fd_drainer.start() for content in (b"foo", b"bar", b"baz", b"foo\nbar\nbaz\n\n"): os.write(write_fd, content) os.write(write_fd, b"finish") os.close(write_fd) fd_drainer.flush() self.assertEqual(fd_drainer.data.getvalue(), b"foobarbazfoo\nbar\nbaz\n\nfinish")
def set_secret_value(test, params): """ Set the secet value :params: the parameter dictionary """ uuid = params.get("secret_ref") options = params.get("set_secret_options") status_error = params.get("status_error", "no") secret_string = params.get("secret_base64_no_encoded") secret_encode = "yes" == params.get("secret_string_base64_encode", "yes") secret_file = "yes" == params.get("secret_file", "no") if options and "interactive" in options: cmd = "secret-set-value %s --%s" % (uuid, options) virsh_session = virsh.VirshSession(virsh_exec=virsh.VIRSH_EXEC, auto_close=True) virsh_session.sendline(cmd) # Wait for 5s time.sleep(5) virsh_session.sendline(secret_string) # Wait for 5s to get stripped output time.sleep(5) output = virsh_session.get_stripped_output() exit_status = 0 if "Secret value set" in output else 1 result = process.CmdResult(cmd, output, output, exit_status) else: result = virsh.secret_set_value(uuid, secret_string, options=options, encode=secret_encode, use_file=secret_file, debug=True) status = result.exit_status # Don't check result if we don't need to. if params.get("check_set_status", "yes") == "no": return # Check status_error if status_error == "yes": if status: logging.info("It's an expected %s", result.stderr) else: test.fail("%d not a expected command " "return value", status) elif status_error == "no": if status: test.fail(result.stderr) else: # Check secret value if check_secret(params): logging.info(result.stdout.strip()) else: test.fail("The secret value " "mismatch with result")
def test_get_capabilities(self): stdout = b"""1: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=eip""" cmd_result = process.CmdResult(stdout=stdout, exit_status=0) expected = ['cap_chown', 'cap_dac_override', 'cap_fowner', 'cap_fsetid', 'cap_kill', 'cap_setgid', 'cap_setuid', 'cap_setpcap', 'cap_net_bind_service', 'cap_net_raw', 'cap_sys_chroot', 'cap_mknod', 'cap_audit_write', 'cap_setfcap=eip'] with unittest.mock.patch('avocado.utils.process.run', return_value=cmd_result): capabilities = process.get_capabilities() self.assertEqual(capabilities, expected)
def test_get_capabilities_legacy(self): stderr = b"""Capabilities for `3114520': = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,38,39+ep""" cmd_result = process.CmdResult(stderr=stderr, exit_status=0) expected = [ "cap_chown", "cap_dac_override", "cap_dac_read_search", "cap_fowner", "cap_fsetid", "cap_kill", "cap_setgid", "cap_setuid", "cap_setpcap", "cap_linux_immutable", "cap_net_bind_service", "cap_net_broadcast", "cap_net_admin", "cap_net_raw", "cap_ipc_lock", "cap_ipc_owner", "cap_sys_module", "cap_sys_rawio", "cap_sys_chroot", "cap_sys_ptrace", "cap_sys_pacct", "cap_sys_admin", "cap_sys_boot", "cap_sys_nice", "cap_sys_resource", "cap_sys_time", "cap_sys_tty_config", "cap_mknod", "cap_lease", "cap_audit_write", "cap_audit_control", "cap_setfcap", "cap_mac_override", "cap_mac_admin", "cap_syslog", "cap_wake_alarm", "cap_block_suspend", "cap_audit_read", "38", "39+ep", ] with unittest.mock.patch("avocado.utils.process.run", return_value=cmd_result): capabilities = process.get_capabilities() self.assertEqual(capabilities, expected)
def cmd_result(self, cmd, ignore_status=False, debug=False, timeout=60): """ Mimic process.run() :param cmd: virtadmin command to send. :param timeout: Time we'll wait until the process is finished. :returns: The command result object. """ exit_status, stdout = self.cmd_status_output(cmd, timeout=timeout) stderr = '' # no way to retrieve this separately result = process.CmdResult(cmd, stdout, stderr, exit_status) if not ignore_status and exit_status: raise process.CmdError( cmd, result, "Virtadmin Command returned non-zero exit status") if debug: logging.debug(result) return result
def test_replace_incorrect_characters_in_log(self): data = io.StringIO() handler = logging.StreamHandler(data) log = logging.getLogger("test_replace_incorrect_characters_in_log") log.addHandler(handler) log.setLevel(logging.DEBUG) read_fd, write_fd = os.pipe() result = process.CmdResult(encoding='ascii') fd_drainer = process.FDDrainer(read_fd, result, name="test", stream_logger=log, verbose=True) fd_drainer.start() os.write(write_fd, b"Avok\xc3\xa1do") os.close(write_fd) fd_drainer._thread.join(60) self.assertFalse(fd_drainer._thread.is_alive()) # \n added by StreamLogger self.assertEqual(data.getvalue(), u"Avok\ufffd\ufffddo\n")
def check_hpt_order(session, resizing=''): """ Return htp order in hpt_order file by default If 'resizing' is disabled, test updating htp_order """ if not hpt_order_path: test.cancel('No hpt order path provided.') hpt_order = session.cmd_output('cat %s' % hpt_order_path).strip() hpt_order = int(hpt_order) logging.info('Current hpt_order is %d', hpt_order) if resizing == 'disabled': cmd_result = session.cmd_status_output( 'echo %d > %s' % (hpt_order + 1, hpt_order_path)) result = process.CmdResult(stderr=cmd_result[1], exit_status=cmd_result[0]) libvirt.check_exit_status(result, True) libvirt.check_result(result, error_msg) return hpt_order
def run(self, command, ignore_status=False, quiet=None, timeout=60): """ Run command inside the container """ def print_func(*args, **kwargs): # pylint: disable=W0613 """ Accept anything and does nothing """ if timeout is None: timeout = 31536000 # aexpect does not support None, use one year start = time.time() if quiet is not False: print_func = logging.getLogger('avocado.remote').debug status, output = self._docker.cmd_status_output(command, timeout=timeout, print_func=print_func) result = process.CmdResult(command, output, '', status, time.time() - start) if status and not ignore_status: raise process.CmdError(command, result, "in container %s" % self._docker_id) return result
def test_flush_on_closed_handler(self): handler = logging.StreamHandler(io.StringIO()) log = logging.getLogger("test_flush_on_closed_handler") log.addHandler(handler) read_fd, write_fd = os.pipe() result = process.CmdResult() fd_drainer = process.FDDrainer(read_fd, result, name="test", stream_logger=log) fd_drainer.start() os.close(write_fd) self.assertIsNotNone(fd_drainer._stream_logger) one_stream_closed = False for handler in fd_drainer._stream_logger.handlers: stream = getattr(handler, 'stream', None) if stream is not None: if hasattr(stream, 'close'): # force closing the handler's stream to check if # flush will adapt to it stream.close() one_stream_closed = True self.assertTrue(one_stream_closed) fd_drainer.flush()
def run(command, ignore_status=False, quiet=True, timeout=60): """ Executes a command on the defined fabric hosts. This is basically a wrapper to fabric.operations.run, encapsulating the result on an avocado process.CmdResult object. This also assumes the fabric environment was previously (and properly) initialized. :param command: the command string to execute. :param ignore_status: Whether to not raise exceptions in case the command's return code is different than zero. :param timeout: Maximum time allowed for the command to return. :param quiet: Whether to not log command stdout/err. Default: True. :return: the result of the remote program's execution. :rtype: :class:`avocado.utils.process.CmdResult`. :raise fabric.exceptions.CommandTimeout: When timeout exhausted. """ result = process.CmdResult() start_time = time.time() end_time = time.time() + (timeout or 0) # Support timeout=None # Fabric sometimes returns NetworkError even when timeout not reached fabric_result = None fabric_exception = None while True: try: fabric_result = fabric.operations.run(command=command, quiet=quiet, warn_only=True, timeout=timeout, pty=False, combine_stderr=False) break except fabric.network.NetworkError, details: fabric_exception = details timeout = end_time - time.time() if time.time() < end_time: break
def test_cmd_result_stdout_stderr_text(self): result = process.CmdResult() self.assertTrue(isinstance(result.stdout_text, str)) self.assertTrue(isinstance(result.stderr_text, str))
def test_cmd_result_stdout_stderr_bytes(self): result = process.CmdResult() self.assertTrue(isinstance(result.stdout, bytes)) self.assertTrue(isinstance(result.stderr, bytes))
def test_run_suite(self): """ Test RemoteTestRunner.run_suite() The general idea of this test is to: 1) Create the machinery necessary to get a RemoteTestRunner setup inside a job, or looking at it the other way around, to have a runner that is created with a valid job. 2) Mock the interactions with a remote host. This is done here basically by mocking 'Remote' and 'fabric' usage. 3) Provide a polluted JSON to be parsed by the RemoteTestRunner 4) Assert that those results are properly parsed into the job's result """ job_args = argparse.Namespace(test_result_total=1, remote_username='******', remote_hostname='hostname', remote_port=22, remote_password='******', remote_key_file=None, remote_timeout=60, show_job_log=False, mux_yaml=[ '~/avocado/tests/foo.yaml', '~/avocado/tests/bar/baz.yaml' ], dry_run=True, env_keep=None, reference=[ '/tests/sleeptest.py', '/tests/other/test', 'passtest.py' ]) job = None try: job = Job(job_args) job.setup() runner = avocado_runner_remote.RemoteTestRunner(job, job.result) return_value = (True, (version.MAJOR, version.MINOR)) runner.check_remote_avocado = mock.Mock(return_value=return_value) # These are mocked at their source, and will prevent fabric from # trying to contact remote hosts with mock.patch('avocado_runner_remote.Remote'): runner.remote = avocado_runner_remote.Remote( job_args.remote_hostname) # This is the result that the run_suite() will get from remote.run remote_run_result = process.CmdResult() remote_run_result.stdout = JSON_RESULTS remote_run_result.exit_status = 0 runner.remote.run = mock.Mock(return_value=remote_run_result) # We have to fake the uncompressing and removal of the zip # archive that was never generated on the "remote" end # This test could be expand by mocking creating an actual # zip file instead, but it's really overkill with mock.patch('avocado_runner_remote.archive.uncompress'): with mock.patch('avocado_runner_remote.os.remove'): runner.run_suite(None, None, 61) # The job was created with dry_run so it should have a zeroed id self.assertEqual(job.result.job_unique_id, '0' * 40) self.assertEqual(job.result.tests_run, 1) self.assertEqual(job.result.passed, 1) cmd_line = ('avocado run --force-job-id ' '0000000000000000000000000000000000000000 --json - ' '--archive /tests/sleeptest.py /tests/other/test ' 'passtest.py -m ~/avocado/tests/foo.yaml ' '~/avocado/tests/bar/baz.yaml --dry-run') runner.remote.run.assert_called_with(cmd_line, ignore_status=True, timeout=61) finally: if job: shutil.rmtree(job.args.base_logdir)
def test_run_suite(self): """ Test RemoteTestRunner.run_suite() The general idea of this test is to: 1) Create the machinery necessary to get a RemoteTestRunner setup inside a job, or looking at it the other way around, to have a runner that is created with a valid job. 2) Mock the interactions with a remote host. This is done here basically by mocking 'Remote' and 'fabric' usage. 3) Provide a polluted JSON to be parsed by the RemoteTestRunner 4) Assert that those results are properly parsed into the job's result """ job_args = {'test_result_total': 1, 'remote_username': '******', 'remote_hostname': 'hostname', 'remote_port': 22, 'remote_password': '******', 'remote_key_file': None, 'remote_timeout': 60, 'mux_yaml': ['~/avocado/tests/foo.yaml', '~/avocado/tests/bar/baz.yaml'], 'filter_by_tags': ["-foo", "-bar"], 'filter_by_tags_include_empty': False, 'env_keep': None, 'base_logdir': self.tmpdir.name, 'run.keep_tmp': 'on', 'run.store_logging_stream': [], 'run.dry_run.enabled': True, 'run.references': ['/tests/sleeptest.py', '/tests/other/test', 'passtest.py'], } with Job(job_args) as job: runner = avocado_runner_remote.RemoteTestRunner() return_value = (True, (version.MAJOR, version.MINOR)) runner.check_remote_avocado = unittest.mock.Mock(return_value=return_value) # These are mocked at their source, and will prevent fabric from # trying to contact remote hosts with unittest.mock.patch('avocado_runner_remote.Remote'): remote_hostname = job_args.get('remote_hostname') runner.remote = avocado_runner_remote.Remote(remote_hostname) # This is the result that the run_suite() will get from remote.run remote_run_result = process.CmdResult() remote_run_result.stdout = JSON_RESULTS remote_run_result.exit_status = 0 runner.remote.run = unittest.mock.Mock(return_value=remote_run_result) # We have to fake the uncompressing and removal of the zip # archive that was never generated on the "remote" end # This test could be expand by mocking creating an actual # zip file instead, but it's really overkill with unittest.mock.patch('avocado_runner_remote.archive.uncompress'): with unittest.mock.patch('avocado_runner_remote.os.remove'): runner.run_suite(job, job.result, None, None, 61) # The job was created with dry_run so it should have a zeroed id self.assertEqual(job.result.job_unique_id, '0' * 40) self.assertEqual(job.result.tests_run, 1) self.assertEqual(job.result.passed, 1) cmd_line = ('avocado run --force-job-id ' '0000000000000000000000000000000000000000 --json - ' '--archive /tests/sleeptest.py /tests/other/test ' 'passtest.py --mux-yaml ~/avocado/tests/foo.yaml ' '~/avocado/tests/bar/baz.yaml --dry-run --filter-' 'by-tags -foo --filter-by-tags -bar') runner.remote.run.assert_called_with(cmd_line, ignore_status=True, timeout=61)
def test_cmd_result_stdout_stderr_already_text(self): result = process.CmdResult() result.stdout = "supposed command output, but not as bytes" result.stderr = "supposed command error, but not as bytes" self.assertEqual(result.stdout, result.stdout_text) self.assertEqual(result.stderr, result.stderr_text)
def test_cmd_result_stdout_stderr_other_type(self): result = process.CmdResult() result.stdout = None result.stderr = None self.assertRaises(TypeError, lambda x: result.stdout_text) self.assertRaises(TypeError, lambda x: result.stderr_text)