Optimize Qemu installation to speed up vhost tests
[csit.git] / resources / libraries / python / QemuUtils.py
index 1664b0b..42ccb8c 100644 (file)
@@ -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.
@@ -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).
+        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)
@@ -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))
@@ -501,6 +526,9 @@ class QemuUtils(object):
         .. note:: First set at least node to run QEMU on.
         .. warning:: Starts only one VM on the node.
         """
+        # 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 +559,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 +619,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')
@@ -656,15 +688,21 @@ class QemuUtils(object):
         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))