-# 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:
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):
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
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.
"""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']
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.
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)
: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".
"""
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
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))
: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 }'"
: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
: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
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'))
# 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
+ 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_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
- 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')
: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))