X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FQemuUtils.py;h=99fb7f4b8da39e0a295149fa57f6fdedde4bbc38;hp=1664b0b916f03bea14cf815159abfdd9ef978dea;hb=4c6fe5602edcbd9857a846e5b13a21d5c671a2c8;hpb=0fc813b1694a6ae70b759e7ca96741f21f81b051 diff --git a/resources/libraries/python/QemuUtils.py b/resources/libraries/python/QemuUtils.py index 1664b0b916..99fb7f4b8d 100644 --- a/resources/libraries/python/QemuUtils.py +++ b/resources/libraries/python/QemuUtils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -20,7 +20,7 @@ from robot.api import logger from resources.libraries.python.ssh import SSH, SSHTimeout from resources.libraries.python.constants import Constants -from resources.libraries.python.topology import NodeType +from resources.libraries.python.topology import NodeType, Topology class QemuUtils(object): @@ -28,8 +28,9 @@ class QemuUtils(object): def __init__(self, qemu_id=1): self._qemu_id = qemu_id - # Path to QEMU binary - self._qemu_bin = '/usr/bin/qemu-system-x86_64' + # Path to QEMU binary. Use x86_64 by default + self._qemu_path = '/usr/bin/' + self._qemu_bin = 'qemu-system-x86_64' # QEMU Machine Protocol socket self._qmp_sock = '/tmp/qmp{0}.sock'.format(self._qemu_id) # QEMU Guest Agent socket @@ -71,13 +72,13 @@ class QemuUtils(object): self._node = None self._socks = [self._qmp_sock, self._qga_sock] - def qemu_set_bin(self, path): + def qemu_set_path(self, path): """Set binary path for QEMU. :param path: Absolute path in filesystem. :type path: str """ - self._qemu_bin = path + self._qemu_path = path def qemu_set_smp(self, cpus, cores, threads, sockets): """Set SMP option for QEMU. @@ -165,7 +166,7 @@ class QemuUtils(object): """Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU processes. - :raises RuntimeError: Set scheduler policy failed. + :raises RuntimeError: Set scheduler policy failed. """ qemu_cpus = self._qemu_qmp_exec('query-cpus')['return'] @@ -188,16 +189,22 @@ class QemuUtils(object): self._ssh.connect(node) self._vm_info['host'] = node['host'] - def qemu_add_vhost_user_if(self, socket, server=True, mac=None): + arch = Topology.get_node_arch(node) + self._qemu_bin = 'qemu-system-{arch}'.format(arch=arch) + + def qemu_add_vhost_user_if(self, socket, server=True, mac=None, + jumbo_frames=False): """Add Vhost-user interface. :param socket: Path of the unix socket. :param server: If True the socket shall be a listening socket. :param mac: Vhost-user interface MAC address (optional, otherwise is used auto-generated MAC 52:54:00:00:xx:yy). + :param jumbo_frames: Set True if jumbo frames are used in the test. :type socket: str :type server: bool :type mac: str + :type jumbo_frames: bool """ self._vhost_id += 1 # Create unix socket character device. @@ -217,7 +224,11 @@ class QemuUtils(object): mac = '52:54:00:00:{0:02x}:{1:02x}'.\ format(self._qemu_id, self._vhost_id) extend_options = 'mq=on,csum=off,gso=off,guest_tso4=off,'\ - 'guest_tso6=off,guest_ecn=off,mrg_rxbuf=off' + 'guest_tso6=off,guest_ecn=off' + if jumbo_frames: + extend_options += ",mrg_rxbuf=on" + else: + extend_options += ",mrg_rxbuf=off" # Create Virtio network device. device = ' -device virtio-net-pci,netdev=vhost{0},mac={1},{2}'.format( self._vhost_id, mac, extend_options) @@ -236,7 +247,7 @@ class QemuUtils(object): :param cmd: QMP command to execute. :type cmd: str - :return: Command output in python representation of JSON format. The + :returns: Command output in python representation of JSON format. The { "return": {} } response is QMP's success response. An error response will contain the "error" keyword instead of "return". """ @@ -300,7 +311,8 @@ class QemuUtils(object): def _wait_until_vm_boot(self, timeout=60): """Wait until QEMU VM is booted. - Ping QEMU guest agent each 5s until VM booted or timeout. + First try to flush qga until there is output. + Then ping QEMU guest agent each 5s until VM booted or timeout. :param timeout: Waiting timeout in seconds (optional, default 60s). :type timeout: int @@ -312,7 +324,20 @@ class QemuUtils(object): self._qemu_opt['disk_image'], self._node['host'])) out = None try: - self._qemu_qga_flush() + out = self._qemu_qga_flush() + except ValueError: + logger.trace('QGA qga flush unexpected output {}'.format(out)) + # Empty output - VM not booted yet + if not out: + sleep(5) + else: + break + while True: + if time() - start > timeout: + raise RuntimeError('timeout, VM {0} not booted on {1}'.format( + self._qemu_opt['disk_image'], self._node['host'])) + out = None + try: out = self._qemu_qga_exec('guest-ping') except ValueError: logger.trace('QGA guest-ping unexpected output {}'.format(out)) @@ -424,7 +449,7 @@ class QemuUtils(object): :returns: Default size of free huge pages in system. :rtype: int - :raises: RuntimeError if reading failed for three times. + :raises RuntimeError: If reading failed for three times. """ # TODO: remove to dedicated library cmd_huge_size = "grep Hugepagesize /proc/meminfo | awk '{ print $2 }'" @@ -448,7 +473,7 @@ class QemuUtils(object): :type huge_size: int :returns: Number of free huge pages in system. :rtype: int - :raises: RuntimeError if reading failed for three times. + :raises RuntimeError: If reading failed for three times. """ # TODO: add numa aware option # TODO: remove to dedicated library @@ -474,7 +499,7 @@ class QemuUtils(object): :type huge_size: int :returns: Total number of huge pages in system. :rtype: int - :raises: RuntimeError if reading failed for three times. + :raises RuntimeError: If reading failed for three times. """ # TODO: add numa aware option # TODO: remove to dedicated library @@ -496,11 +521,15 @@ class QemuUtils(object): def qemu_start(self): """Start QEMU and wait until VM boot. - :return: VM node info. - :rtype: dict .. note:: First set at least node to run QEMU on. .. warning:: Starts only one VM on the node. + + :returns: VM node info. + :rtype: dict """ + # Qemu binary path + bin_path = '{0}{1}'.format(self._qemu_path, self._qemu_bin) + # SSH forwarding ssh_fwd = '-net user,hostfwd=tcp::{0}-:22'.format( self._qemu_opt.get('ssh_fwd_port')) @@ -531,23 +560,22 @@ class QemuUtils(object): pid = '-pidfile {}'.format(self._pid_file) # Run QEMU - cmd = '{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'.format( - self._qemu_bin, self._qemu_opt.get('smp'), mem, ssh_fwd, - self._qemu_opt.get('options'), - drive, qmp, serial, qga, graphic, pid) - (ret_code, _, stderr) = self._ssh.exec_command_sudo(cmd, timeout=300) - if int(ret_code) != 0: - logger.debug('QEMU start failed {0}'.format(stderr)) - raise RuntimeError('QEMU start failed on {0}'.format( - self._node['host'])) - logger.trace('QEMU running') - # Wait until VM boot + cmd = '{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'.format(bin_path, + self._qemu_opt.get('smp'), mem, ssh_fwd, + self._qemu_opt.get('options'), drive, qmp, serial, qga, graphic, + pid) try: + (ret_code, _, _) = self._ssh.exec_command_sudo(cmd, timeout=300) + if int(ret_code) != 0: + raise RuntimeError('QEMU start failed on {0}'.format( + self._node['host'])) + # Wait until VM boot self._wait_until_vm_boot() except (RuntimeError, SSHTimeout): - self.qemu_kill() + self.qemu_kill_all() self.qemu_clear_socks() raise + logger.trace('QEMU started successfully.') # Update interface names in VM node dict self._update_vm_interfaces() # Return VM node dict @@ -592,6 +620,11 @@ class QemuUtils(object): self._ssh.exec_command_sudo(cmd) def qemu_kill_all(self, node=None): + """Kill all qemu processes on DUT node if specified. + + :param node: Node to kill all QEMU processes on. + :type node: dict + """ if node: self.qemu_set_node(node) self._ssh.exec_command_sudo('pkill -SIGKILL qemu') @@ -627,7 +660,7 @@ class QemuUtils(object): - watchdog: watchdog action has been triggered - guest-panicked: panicked as a result of guest OS panic - :return: VM status. + :returns: VM status. :rtype: str """ out = self._qemu_qmp_exec('query-status') @@ -650,21 +683,27 @@ class QemuUtils(object): :type node: dict :type force_install: bool :type apply_patch: bool - :raises: RuntimeError if building QEMU failed. + :raises RuntimeError: If building QEMU failed. """ ssh = SSH() ssh.connect(node) directory = ' --directory={0}'.format(Constants.QEMU_INSTALL_DIR) + if apply_patch: + directory += '-patch' + else: + directory += '-base' version = ' --version={0}'.format(Constants.QEMU_INSTALL_VERSION) force = ' --force' if force_install else '' patch = ' --patch' if apply_patch else '' + arch = Topology.get_node_arch(node) + target_list = ' --target-list={0}-softmmu'.format(arch) (ret_code, stdout, stderr) = \ ssh.exec_command( - "sudo -E sh -c '{0}/{1}/qemu_build.sh{2}{3}{4}{5}'"\ + "sudo -E sh -c '{0}/{1}/qemu_build.sh{2}{3}{4}{5}{6}'"\ .format(Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH, - version, directory, force, patch), 1000) + version, directory, force, patch, target_list), 1000) if int(ret_code) != 0: logger.debug('QEMU build failed {0}'.format(stdout + stderr))