From: "Jiajia, Sun" <sunx.jiajia@intel.com>
To: dts@dpdk.org
Subject: [dts] [PATCH v2 03/19] Add QEMU KVM module based on virt_base module for KVM test cases
Date: Fri, 22 May 2015 17:03:56 +0800 [thread overview]
Message-ID: <1432285452-14286-4-git-send-email-sunx.jiajia@intel.com> (raw)
In-Reply-To: <1432285452-14286-1-git-send-email-sunx.jiajia@intel.com>
From: sjiajiax <sunx.jiajia@intel.com>
Added module: framework/qemu_kvm.py
Signed-off-by: sjiajiax <sunx.jiajia@intel.com>
---
framework/qemu_kvm.py | 972 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 972 insertions(+)
create mode 100644 framework/qemu_kvm.py
diff --git a/framework/qemu_kvm.py b/framework/qemu_kvm.py
new file mode 100644
index 0000000..e762695
--- /dev/null
+++ b/framework/qemu_kvm.py
@@ -0,0 +1,972 @@
+# BSD LICENSE
+#
+# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import time
+import re
+import os
+
+from virt_base import VirtBase
+from exception import StartVMFailedException
+
+# This name is derictly defined in the qemu guest serivce
+# So you can not change it except it is changed by the service
+QGA_DEV_NAME = 'org.qemu.guest_agent.0'
+# This path defines an socket path on the host connected with
+# a specified VM
+QGA_SOCK_PATH_TEMPLATE = '/tmp/%(vm_name)s_qga0.sock'
+
+
+class QEMUKvm(VirtBase):
+
+ DEFAULT_BRIDGE = 'br0'
+ QEMU_IFUP = "#!/bin/sh\n\n" + \
+ "set -x\n\n" + \
+ "switch=%(switch)s\n\n" + \
+ "if [ -n '$1' ];then\n" + \
+ " tunctl -t $1\n" + \
+ " ip link set $1 up\n" + \
+ " sleep 0.5s\n" + \
+ " brctl addif $switch $1\n" + \
+ " exit 0\n" + \
+ "else\n" + \
+ " echo 'Error: no interface specified'\n" + \
+ " exit 1\n" + \
+ "fi"
+
+ QEMU_IFUP_PATH = '/etc/qemu-ifup'
+
+ def __init__(self, dut, vm_name, suite_name):
+ super(QEMUKvm, self).__init__(dut, vm_name, suite_name)
+
+ # set some default values for vm,
+ # if there is not the values of the specified options
+ self.set_vm_default()
+
+ # initialize qemu emulator, example: qemu-system-x86_64
+ self.qemu_emulator = self.get_qemu_emulator()
+
+ # initialize qemu boot command line
+ # example: qemu-system-x86_64 -name vm1 -m 2048 -vnc :1 -daemonize
+ self.qemu_boot_line = ''
+
+ # initialize some resource used by guest.
+ self.init_vm_request_resource()
+
+ QGA_CLI_PATH = '-r dep/QMP/'
+ self.host_session.copy_file_to(QGA_CLI_PATH)
+
+ def set_vm_default(self):
+ self.set_vm_name(self.vm_name)
+ self.set_vm_enable_kvm()
+ self.set_vm_qga()
+ self.set_vm_daemon()
+
+ def init_vm_request_resource(self):
+ """
+ initialize some resource used by VM.
+ examples: CPU, PCIs, so on.
+ CPU:
+ initialize vcpus what will be pinned to the VM.
+ If specify this param, the specified vcpus will
+ be pinned to VM by the command 'taskset' when
+ starting the VM.
+ example:
+ vcpus_pinned_to_vm = '1 2 3 4'
+ taskset -c 1,2,3,4 qemu-boot-command-line
+ """
+ self.vcpus_pinned_to_vm = ''
+
+ # initialize assigned PCI
+ self.assigned_pcis = []
+
+ def get_virt_type(self):
+ """
+ Get the virtual type.
+ """
+ return 'KVM'
+
+ def get_qemu_emulator(self):
+ """
+ Get the qemu emulator based on the crb.
+ """
+ arch = self.host_session.send_expect('uname -m', '# ')
+ return 'qemu-system-' + arch
+
+ def set_qemu_emulator(self, qemu_emulator_path):
+ """
+ Set the qemu emulator in the specified path explicitly.
+ """
+ qemu_emulator_path = str(qemu_emulator_path)
+ out = self.host_session.send_expect(
+ 'ls %s' % qemu_emulator_path, '[.*')
+ if 'No such file or directory' in out:
+ self.host_logger.error("No emulator [ %s ] on the DUT [ %s ]" % \
+ (qemu_emulator, self.host_dut.get_ip_address()))
+ return None
+ out = self.host_session.send_expect("[ -x %s ];echo $?" % qemu_emulator_path, '# ')
+ if out == '1':
+ self.host_logger.error("Emulator [ %s ] not executable on the DUT [ %s ]" % \
+ (qemu_emulator, self.host_dut.get_ip_address()))
+ return None
+ self.qemu_emulator = qemu_emulator
+
+ def has_virtual_ability(self):
+ """
+ Check if host has the virtual ability.
+ """
+ out = self.host_session.send_expect('cat /proc/cpuinfo | grep flags', '# ')
+ rgx = re.search(' vmx ', out)
+ if rgx:
+ pass
+ else:
+ self.host_logger.warning("Hardware virtualization disabled on host!!!")
+ return False
+
+ out = self.host_session.send_expect('lsmod | grep kvm', '# ')
+ if 'kvm' in out and 'kvm_intel' in out:
+ return True
+ else:
+ self.host_logger.warning("kvm or kvm_intel not insmod!!!")
+ return False
+
+ def enable_virtual_ability(self):
+ """
+ Load the virutal module of kernel to enable the virutal ability.
+ """
+ self.host_session.send_expect('modprobe kvm', '# ')
+ self.host_session.send_expect('modprobe kvm_intel', '# ')
+ return True
+
+ def disk_image_is_ok(self, image):
+ """
+ Check if the image is OK and no error.
+ """
+ pass
+
+ def image_is_used(self, image_path):
+ """
+ Check if the image has been used on the host.
+ """
+ qemu_cmd_lines = self.host_session.send_expect(
+ "ps aux | grep qemu | grep -v grep", "# ")
+
+ image_name_flag = '/' + image_path.strip().split('/')[-1] + ' '
+ if image_path in qemu_cmd_lines or \
+ image_name_flag in qemu_cmd_lines:
+ return True
+ return False
+
+ def __add_boot_line(self, option_boot_line):
+ """
+ Add boot option into the boot line.
+ """
+ separator = ' '
+ self.qemu_boot_line += separator + option_boot_line
+
+ def set_vm_enable_kvm(self, enable='yes'):
+ """
+ Set VM boot option to enable the option 'enable-kvm'.
+ """
+ self.params.append({'enable_kvm': [{'enable': '%s' % enable}]})
+
+ def add_vm_enable_kvm(self, **options):
+ """
+ 'enable': 'yes'
+ """
+ if 'enable' in options.keys() and \
+ options['enable'] == 'yes':
+ enable_kvm_boot_line = '-enable-kvm'
+ self.__add_boot_line(enable_kvm_boot_line)
+
+ def set_vm_name(self, vm_name):
+ """
+ Set VM name.
+ """
+ self.params.append({'name': [{'name': '%s' % vm_name}]})
+
+ def add_vm_name(self, **options):
+ """
+ name: vm1
+ """
+ if 'name' in options.keys() and \
+ options['name']:
+ name_boot_line = '-name %s' % options['name']
+ self.__add_boot_line(name_boot_line)
+
+ def add_vm_cpu(self, **options):
+ """
+ model: [host | core2duo | ...]
+ usage:
+ choose model value from the command
+ qemu-system-x86_64 -cpu help
+ number: '4' #number of vcpus
+ cpupin: '3 4 5 6' # host cpu list
+ """
+ if 'model' in options.keys() and \
+ options['model']:
+ cpu_boot_line = '-cpu %s' % options['model']
+ self.__add_boot_line(cpu_boot_line)
+ if 'number' in options.keys() and \
+ options['number']:
+ smp_cmd_line = '-smp %d' % int(options['number'])
+ self.__add_boot_line(smp_cmd_line)
+ if 'cpupin' in options.keys() and \
+ options['cpupin']:
+ self.vcpus_pinned_to_vm = str(options['cpupin'])
+
+ def add_vm_mem(self, **options):
+ """
+ size: 1024
+ """
+ if 'size' in options.keys():
+ mem_boot_line = '-m %s' % options['size']
+ self.__add_boot_line(mem_boot_line)
+
+ def add_vm_disk(self, **options):
+ """
+ file: /home/image/test.img
+ """
+ if 'file' in options.keys():
+ disk_boot_line = '-drive file=%s' % options['file']
+ self.__add_boot_line(disk_boot_line)
+
+ def add_vm_net(self, **options):
+ """
+ Add VM net device.
+ type: [nic | user | tap | bridge | ...]
+ opt_[vlan | fd | br | mac | ...]
+ note:the sub-option will be decided according to the net type.
+ """
+ if 'type' in options.keys():
+ if 'opt_vlan' not in options.keys():
+ options['opt_vlan'] = '0'
+ if options['type'] == 'nic':
+ self.__add_vm_net_nic(**options)
+ if options['type'] == 'user':
+ self.__add_vm_net_user(**options)
+ if options['type'] == 'tap':
+ self.__add_vm_net_tap(**options)
+
+ if options['type'] == 'user':
+ self.net_type = 'hostfwd'
+ elif options['type'] in ['tap', 'bridge']:
+ self.net_type = 'bridge'
+
+ def __add_vm_net_nic(self, **options):
+ """
+ type: nic
+ opt_vlan: 0
+ note: Default is 0.
+ opt_macaddr: 00:00:00:00:01:01
+ note: if creating a nic, it`s better to specify a MAC,
+ else it will get a random number.
+ opt_model:["e1000" | "virtio" | "i82551" | ...]
+ note: Default is e1000.
+ opt_name: 'nic1'
+ opt_addr: ''
+ note: PCI cards only.
+ opt_vectors:
+ note: This option currently only affects virtio cards.
+ """
+ net_boot_line = '-net nic'
+ separator = ','
+ if 'opt_vlan' in options.keys() and \
+ options['opt_vlan']:
+ net_boot_line += separator + 'vlan=%s' % options['opt_vlan']
+
+ # add MAC info
+ if 'opt_macaddr' in options.keys() and \
+ options['opt_macaddr']:
+ mac = options['opt_macaddr']
+ else:
+ mac = self.generate_unique_mac()
+ net_boot_line += separator + 'macaddr=%s' % mac
+
+ if 'opt_model' in options.keys() and \
+ options['opt_model']:
+ net_boot_line += separator + 'model=%s' % options['opt_model']
+ if 'opt_name' in options.keys() and \
+ options['opt_name']:
+ net_boot_line += separator + 'name=%s' % options['opt_name']
+ if 'opt_addr' in options.keys() and \
+ options['opt_addr']:
+ net_boot_line += separator + 'addr=%s' % options['opt_addr']
+ if 'opt_vectors' in options.keys() and \
+ options['opt_vectors']:
+ net_boot_line += separator + 'vectors=%s' % options['opt_vectors']
+
+ if self.__string_has_multi_fields(net_boot_line, separator):
+ self.__add_boot_line(net_boot_line)
+
+ def __add_vm_net_user(self, **options):
+ """
+ type: user
+ opt_vlan: 0
+ note: default is 0.
+ opt_hostfwd: [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport
+ """
+ net_boot_line = '-net user'
+ separator = ','
+ if 'opt_vlan' in options.keys() and \
+ options['opt_vlan']:
+ net_boot_line += separator + 'vlan=%s' % options['opt_vlan']
+ if 'opt_hostfwd' in options.keys() and \
+ options['opt_hostfwd']:
+ self.__check_net_user_opt_hostfwd(options['opt_hostfwd'])
+ opt_hostfwd = options['opt_hostfwd']
+ else:
+ opt_hostfwd = '::-:'
+ hostfwd_line = self.__parse_net_user_opt_hostfwd(opt_hostfwd)
+ net_boot_line += separator + 'hostfwd=%s' % hostfwd_line
+
+ if self.__string_has_multi_fields(net_boot_line, separator):
+ self.__add_boot_line(net_boot_line)
+
+ def __check_net_user_opt_hostfwd(self, opt_hostfwd):
+ """
+ Use regular expression to check if hostfwd value format is correct.
+ """
+ regx_ip = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
+ regx_hostfwd = r'["tcp" | "udp"]?:%s?:\d+-%s?:\d+' % (regx_ip, regx_ip)
+ if not re.match(regx_hostfwd, opt_hostfwd):
+ raise Exception("Option opt_hostfwd format is not correct,\n" +
+ "it is %s,\n " % opt_hostfwd +
+ "it should be [tcp|udp]:[hostaddr]:hostport-" +
+ "[guestaddr]:guestport.\n")
+
+ def __parse_net_user_opt_hostfwd(self, opt_hostfwd):
+ """
+ Parse the boot option 'hostfwd'.
+ """
+ separator = ':'
+ field = lambda option, index, separator=':': \
+ option.split(separator)[index]
+
+ # get the forword type
+ fwd_type = field(opt_hostfwd, 0)
+ if not fwd_type:
+ fwd_type = 'tcp'
+
+ # get the host addr
+ host_addr = field(opt_hostfwd, 1)
+ if not host_addr:
+ host_addr = str(self.host_dut.get_ip_address())
+
+ # get the host port in the option
+ host_port = field(opt_hostfwd, 2).split('-')[0]
+ if not host_port:
+ host_port = str(self.virt_pool.alloc_port(self.vm_name))
+ self.redir_port = host_port
+
+ # get the guest addr
+ try:
+ guest_addr = str(field(opt_hostfwd, 2).split('-')[1])
+ except IndexError as e:
+ guest_addr = ''
+
+ # get the guest port in the option
+ guest_port = str(field(opt_hostfwd, 3))
+ if not guest_port:
+ guest_port = '22'
+
+ hostfwd_line = fwd_type + separator + \
+ host_addr + separator + \
+ host_port + \
+ '-' + \
+ guest_addr + separator + \
+ guest_port
+
+ # init the redirect incoming TCP or UDP connections
+ # just combine host address and host port, it is enough
+ # for using ssh to connect with VM
+ self.hostfwd_addr = host_addr + separator + host_port
+
+ return hostfwd_line
+
+ def __add_vm_net_tap(self, **options):
+ """
+ type: tap
+ opt_vlan: 0
+ note: default is 0.
+ opt_br: br0
+ note: if choosing tap, need to specify bridge name,
+ else it will be br0.
+ opt_script: QEMU_IFUP_PATH
+ note: if not specified, default is self.QEMU_IFUP_PATH.
+ opt_downscript: QEMU_IFDOWN_PATH
+ note: if not specified, default is self.QEMU_IFDOWN_PATH.
+ """
+ net_boot_line = '-net tap'
+ separator = ','
+
+ # add bridge info
+ if 'opt_br' in options.keys() and \
+ options['opt_br']:
+ bridge = options['opt_br']
+ else:
+ bridge = self.DEFAULT_BRIDGE
+ self.__generate_net_config_script(str(bridge))
+
+ if 'opt_vlan' in options.keys() and \
+ options['opt_vlan']:
+ net_boot_line += separator + 'vlan=%s' % options['opt_vlan']
+
+ # add network configure script path
+ if 'opt_script' in options.keys() and \
+ options['opt_script']:
+ script_path = options['opt_script']
+ else:
+ script_path = self.QEMU_IFUP_PATH
+ net_boot_line += separator + 'script=%s' % script_path
+
+ # add network configure downscript path
+ if 'opt_downscript' in options.keys() and \
+ options['opt_downscript']:
+ net_boot_line += separator + \
+ 'downscript=%s' % options['opt_downscript']
+
+ if self.__string_has_multi_fields(net_boot_line, separator):
+ self.__add_boot_line(net_boot_line)
+
+ def __generate_net_config_script(self, switch=DEFAULT_BRIDGE):
+ """
+ Generate a script for qemu emulator to build a tap device
+ between host and guest.
+ """
+ qemu_ifup = self.QEMU_IFUP % {'switch': switch}
+ file_name = os.path.basename(self.QEMU_IFUP_PATH)
+ tmp_file_path = '/tmp/%s' % file_name
+ self.host_dut.create_file(qemu_ifup, tmp_file_path)
+ self.host_session.send_expect('mv -f ~/%s %s' % (file_name,
+ self.QEMU_IFUP_PATH), '# ')
+ self.host_session.send_expect(
+ 'chmod +x %s' % self.QEMU_IFUP_PATH, '# ')
+
+ def set_vm_device(self, driver='pci-assign', **props):
+ """
+ Set VM device with specified driver.
+ """
+ props['driver'] = driver
+ index = self.find_option_index('device')
+ if index:
+ self.params[index]['device'].append(props)
+ else:
+ self.params.append({'device': [props]})
+
+ def add_vm_device(self, **options):
+ """
+ driver: [pci-assign | virtio-net-pci | ...]
+ prop_[host | addr | ...]: value
+ note:the sub-property will be decided according to the driver.
+ """
+ if 'driver' in options.keys() and \
+ options['driver']:
+ if options['driver'] == 'pci-assign':
+ self.__add_vm_pci_assign(**options)
+ elif options['driver'] == 'virtio-net-pci':
+ self.__add_vm_virtio_net_pci(**options)
+
+ def __add_vm_pci_assign(self, **options):
+ """
+ driver: pci-assign
+ prop_host: 08:00.0
+ prop_addr: 00:00:00:00:01:02
+ """
+ dev_boot_line = '-device pci-assign'
+ separator = ','
+ if 'prop_host' in options.keys() and \
+ options['prop_host']:
+ dev_boot_line += separator + 'host=%s' % options['prop_host']
+ if 'prop_addr' in options.keys() and \
+ options['prop_addr']:
+ dev_boot_line += separator + 'addr=%s' % options['prop_addr']
+ self.assigned_pcis.append(options['prop_addr'])
+
+ if self.__string_has_multi_fields(dev_boot_line, separator):
+ self.__add_boot_line(dev_boot_line)
+
+ def __add_vm_virtio_net_pci(self, **options):
+ """
+ driver: virtio-net-pci
+ prop_netdev: mynet1
+ prop_id: net1
+ prop_mac: 00:00:00:00:01:03
+ prop_bus: pci.0
+ prop_addr: 0x3
+ """
+ dev_boot_line = '-device virtio-net-pci'
+ separator = ','
+ if 'prop_netdev' in options.keys() and \
+ options['prop_netdev']:
+ dev_boot_line += separator + 'netdev=%s' % options['prop_netdev']
+ if 'prop_id' in options.keys() and \
+ options['prop_id']:
+ dev_boot_line += separator + 'id=%s' % options['prop_id']
+ if 'prop_mac' in options.keys() and \
+ options['prop_mac']:
+ dev_boot_line += separator + 'mac=%s' % options['prop_mac']
+ if 'prop_bus' in options.keys() and \
+ options['prop_bus']:
+ dev_boot_line += separator + 'bus=%s' % options['prop_bus']
+ if 'prop_addr' in options.keys() and \
+ options['prop_addr']:
+ dev_boot_line += separator + 'addr=%s' % options['prop_addr']
+
+ if self.__string_has_multi_fields(self, string, separator):
+ self.__add_boot_line(dev_boot_line)
+
+ def __string_has_multi_fields(self, string, separator, field_num=2):
+ """
+ Check if string has multiple fields which is splited with
+ specified separator.
+ """
+ fields = string.split(separator)
+ number = 0
+ for field in fields:
+ if field:
+ number += 1
+ if number >= field_num:
+ return True
+ else:
+ return False
+
+ def add_vm_monitor(self, **options):
+ """
+ port: 6061 # if adding monitor to vm, need to specicy
+ this port, else it will get a free port
+ on the host machine.
+ """
+ if 'port' in options.keys():
+ if options['port']:
+ port = options['port']
+ else:
+ port = self.virt_pool.alloc_port(self.vm_name)
+
+ monitor_boot_line = '-monitor tcp::%d,server,nowait' % int(port)
+ self.__add_boot_line(monitor_boot_line)
+
+ def set_vm_qga(self, enable='yes'):
+ """
+ Set VM qemu-guest-agent.
+ """
+ index = self.find_option_index('qga')
+ if index:
+ self.params[index] = {'qga': [{'enable': '%s' % enable}]}
+ else:
+ self.params.append({'qga': [{'enable': '%s' % enable}]})
+ QGA_SOCK_PATH = QGA_SOCK_PATH_TEMPLATE % {'vm_name': self.vm_name}
+ self.qga_sock_path = QGA_SOCK_PATH
+
+ def add_vm_qga(self, **options):
+ """
+ enable: 'yes'
+ """
+ QGA_DEV_ID = '%(vm_name)s_qga0' % {'vm_name': self.vm_name}
+ QGA_SOCK_PATH = QGA_SOCK_PATH_TEMPLATE % {'vm_name': self.vm_name}
+
+ separator = ' '
+
+ if 'enable' in options.keys():
+ if options['enable'] == 'yes':
+ qga_boot_block = '-chardev socket,path=%(SOCK_PATH)s,server,nowait,id=%(ID)s' + \
+ separator + '-device virtio-serial' + separator + \
+ '-device virtserialport,chardev=%(ID)s,name=%(DEV_NAME)s'
+ qga_boot_line = qga_boot_block % {'SOCK_PATH': QGA_SOCK_PATH,
+ 'DEV_NAME': QGA_DEV_NAME,
+ 'ID': QGA_DEV_ID}
+ self.__add_boot_line(qga_boot_line)
+ self.qga_sock_path = QGA_SOCK_PATH
+ else:
+ self.qga_sock_path = ''
+
+ def add_vm_serial_port(self, **options):
+ """
+ enable: 'yes'
+ """
+ SERAIL_SOCK_PATH = "/tmp/%s_serial.sock" % self.vm_name
+ if 'enable' in options.keys():
+ if options['enable'] == 'yes':
+ serial_boot_line = '-serial unix:%s,server,nowait' % SERIAL_SOCK_PATH
+ self.__add_boot_line(serial_boot_line)
+ else:
+ pass
+
+ def add_vm_vnc(self, **options):
+ """
+ displayNum: 1
+ """
+ if 'displayNum' in options.keys() and \
+ options['displayNum']:
+ display_num = options['displayNum']
+ else:
+ display_num = self.virt_pool.alloc_vnc_num(self.vm_name)
+
+ vnc_boot_line = '-vnc :%d' % int(display_num)
+ self.__add_boot_line(vnc_boot_line)
+
+ def set_vm_daemon(self, enable='yes'):
+ """
+ Set VM daemon option.
+ """
+ index = self.find_option_index('daemon')
+ if index:
+ self.params[index] = {'daemon': [{'enable': '%s' % enable}]}
+ else:
+ self.params.append({'daemon': [{'enable': '%s' % enable}]})
+
+ def add_vm_daemon(self, **options):
+ """
+ enable: 'yes'
+ note:
+ By default VM will start with the daemonize status.
+ Not support starting it on the stdin now.
+ """
+ if 'daemon' in options.keys() and \
+ options['enable'] == 'no':
+ pass
+ else:
+ daemon_boot_line = '-daemonize'
+ self.__add_boot_line(daemon_boot_line)
+
+ def _start_vm(self):
+ """
+ Start VM.
+ """
+ self.__alloc_assigned_pcis()
+
+ qemu_boot_line = self.generate_qemu_boot_line()
+
+ # Start VM using the qemu command
+ ret = self.host_session.send_expect(qemu_boot_line, '# ', verify=True)
+ time.sleep(30)
+ if type(ret) is int and ret != 0:
+ raise StartVMFailedException('Start VM failed!!!')
+
+ def generate_qemu_boot_line(self):
+ """
+ Generate the whole QEMU boot line.
+ """
+ qemu_emulator = self.qemu_emulator
+
+ if self.vcpus_pinned_to_vm.strip():
+ vcpus = self.__alloc_vcpus()
+
+ if vcpus.strip():
+ qemu_boot_line = 'taskset -c %s ' % vcpus + \
+ qemu_emulator + ' ' + \
+ self.qemu_boot_line
+ else:
+ qemu_boot_line = qemu_emulator + ' ' + \
+ self.qemu_boot_line
+
+ return qemu_boot_line
+
+ def __alloc_vcpus(self):
+ """
+ Allocate virtual CPUs for VM.
+ """
+ req_cpus = self.vcpus_pinned_to_vm.split()
+ cpus = self.virt_pool.alloc_cpu(vm=self.vm_name, corelist=req_cpus)
+
+ if len(req_cpus) != len(cpus):
+ self.host_logger.warn("VCPUs not enough, required [ %s ], just [ %s ]" % \
+ (req_cpus, cpus))
+ raise Exception("No enough required vcpus!!!")
+
+ vcpus_pinned_to_vm = ''
+ for cpu in cpus:
+ vcpus_pinned_to_vm += ',' + cpu
+ vcpus_pinned_to_vm = vcpus_pinned_to_vm.lstrip(',')
+
+ return vcpus_pinned_to_vm
+
+ def __alloc_assigned_pcis(self):
+ """
+ Record the PCI device info
+ Struct: {dev pci: {'is_vf': [True | False],
+ 'pf_pci': pci}}
+ example:
+ {'08:10.0':{'is_vf':True, 'pf_pci': 08:00.0}}
+ """
+ assigned_pcis_info = {}
+ for pci in self.assigned_pcis:
+ assigned_pcis_info[pci] = {}
+ if self.__is_vf_pci(pci):
+ assigned_pcis_info[pci]['is_vf'] = True
+ pf_pci = self.__map_vf_to_pf(pci)
+ assgined_pcis_info[pci]['pf_pci'] = pf_pci
+ if self.virt_pool.alloc_vf_from_pf(vm=self.vm_name,
+ pf_pci=pf_pci,
+ *[pci]):
+ port = self.__get_vf_port(pci)
+ port.unbind_driver()
+ port.bind_driver('pci-stub')
+ else:
+ # check that if any VF of specified PF has been
+ # used, raise exception
+ vf_pci = self.__vf_has_been_assinged(pci, **assinged_pcis_info)
+ if vf_pci:
+ raise Exception(
+ "Error: A VF [%s] generated by PF [%s] has " %
+ (vf_pci, pci) +
+ "been assigned to VM, so this PF can not be " +
+ "assigned to VM again!")
+ # get the port instance of PF
+ port = self.__get_net_device_by_pci(pci)
+
+ if self.virt_pool.alloc_pf(vm=self.vm_name,
+ *[pci]):
+ port.unbind_driver()
+
+ def __is_vf_pci(self, dev_pci):
+ """
+ Check if the specified PCI dev is a VF.
+ """
+ for port_info in self.host_dut.ports_info:
+ if 'sriov_vfs_pci' in port_info.keys():
+ if dev_pci in port_info['sriov_vfs_pci']:
+ return True
+ return False
+
+ def __map_vf_to_pf(self, dev_pci):
+ """
+ Map the specified VF to PF.
+ """
+ for port_info in self.host_dut.ports_info:
+ if 'sriov_vfs_pci' in port_info.keys():
+ if dev_pci in port_info['sriov_vfs_pci']:
+ return port_info['pci']
+ return None
+
+ def __get_vf_port(self, dev_pci):
+ """
+ Get the NetDevice instance of specified VF.
+ """
+ for port_info in self.host_dut.ports_info:
+ if 'vfs_port' in port_info.keys():
+ for port in port_info['vfs_port']:
+ if dev_pci == port.pci:
+ return port
+ return None
+
+ def __vf_has_been_assigned(self, pf_pci, **assigned_pcis_info):
+ """
+ Check if the specified VF has been used.
+ """
+ for pci in assigned_pcis_info.keys():
+ if assigned_pcis_info[pci]['is_vf'] and \
+ assigned_pcis_info[pci]['pf_pci'] == pf_pci:
+ return pci
+ return False
+
+ def __get_net_device_by_pci(self, net_device_pci):
+ """
+ Get NetDevice instance by the specified PCI bus number.
+ """
+ port_info = self.host_dut.get_port_info(net_device_pci)
+ return port_info['port']
+
+ def get_vm_ip(self):
+ """
+ Get VM IP.
+ """
+ get_vm_ip = getattr(self, "get_vm_ip_%s" % self.net_type)
+ return get_vm_ip()
+
+ def get_vm_ip_hostfwd(self):
+ """
+ Get IP which VM is connected by hostfwd.
+ """
+ return self.hostfwd_addr
+
+ def get_vm_ip_bridge(self):
+ """
+ Get IP which VM is connected by bridge.
+ """
+ out = self.__control_session('ping', '60')
+ if not out:
+ time.sleep(10)
+ out = self.__control_session('ifconfig')
+ ips = re.findall(r'inet (\d+\.\d+\.\d+\.\d+)', out)
+
+ if '127.0.0.1' in ips:
+ ips.remove('127.0.0.1')
+
+ num = 3
+ for ip in ips:
+ out = self.host_session.send_expect(
+ 'ping -c %d %s' % (num, ip), '# ')
+ if '0% packet loss' in out:
+ return ip
+ return ''
+
+ def __control_session(self, command, *args):
+ """
+ Use the qemu guest agent service to control VM.
+ Note:
+ :command: there are these commands as below:
+ cat, fsfreeze, fstrim, halt, ifconfig, info,\
+ ping, powerdown, reboot, shutdown, suspend
+ :args: give different args by the different commands.
+ """
+ if not self.qga_sock_path:
+ self.host_logger.info(
+ "No QGA service between host [ %s ] and guest [ %s ]" %
+ (self.host_dut.Name, self.vm_name))
+ return None
+
+ cmd_head = '~/QMP/' + \
+ "qemu-ga-client " + \
+ "--address=%s %s" % \
+ (self.qga_sock_path, command)
+
+ cmd = cmd_head
+ for arg in args:
+ cmd = cmd_head + ' ' + str(arg)
+
+ out = self.host_session.send_expect(cmd, '# ')
+
+ return out
+
+ def stop(self):
+ """
+ Stop VM.
+ """
+ self.__control_session('powerdown')
+ time.sleep(5)
+ self.virt_pool.free_all_resource(self.vm_name)
+
+
+if __name__ == "__main__":
+ import subprocess
+ import sys
+ from serializer import Serializer
+ from crbs import crbs
+ from tester import Tester
+ from dut import Dut
+ import dts
+ from virt_proxy import VirtProxy
+
+ command = "ifconfig br0"
+ subp = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
+ subp.wait()
+
+ intf_info = subp.stdout.readlines()
+ for line_info in intf_info:
+ regx = re.search(r'inet (\d+\.\d+\.\d+\.\d+)', line_info)
+ if regx:
+ dutIP = regx.group(1)
+ break
+
+ print "DEBUG: dutIp: ", dutIP
+
+ # look up in crbs - to find the matching IP
+ crbInst = None
+ for crb in crbs:
+ if crb['IP'] == dutIP:
+ crbInst = crb
+ break
+
+ # only run on the dut in known crbs
+ if crbInst is None:
+ raise Exception("No available crb instance!!!")
+
+ # initialize the dut and tester
+ serializer = Serializer()
+ serializer.set_serialized_filename('../.%s.cache' % crbInst['IP'])
+ serializer.load_from_file()
+
+ read_cache = None
+ skip_setup = None
+
+ project = "dpdk"
+ dts.Package = 'dep/dpdk.tar.gz'
+ dut = dts.get_project_obj(project, Dut, crbInst, serializer)
+ tester = dts.get_project_obj(project, Tester, crbInst, serializer)
+ dut.tester = tester
+ dut.base_dir = 'dpdk'
+ dut.set_nic_type('niantic')
+ tester.dut = dut
+
+ tester.set_test_types(True, False)
+ dut.set_test_types(True, False)
+
+ tester.set_speedup_options(read_cache, skip_setup)
+ tester.tester_prerequisites()
+ dut.set_speedup_options(read_cache, skip_setup)
+ dut.dut_prerequisites()
+
+ # test that generating and destroying VF
+ port0 = dut.ports_info[0]['port']
+ dut.generate_sriov_vfs_by_port(0, 4)
+ print "port 0 sriov vfs: ", dut.ports_info[0]
+
+ dut.destroy_sriov_vfs_by_port(0)
+
+ time.sleep(2)
+
+ # test that binding and unbing the NIC
+ port0_pci = dut.ports_info[0]['pci']
+ port0.unbind_driver()
+
+ dut.logger.info("JUST TESTING!!!")
+
+ # Start VM by the qemu kvm config file
+ vm1 = QEMUKvm(dut, 'vm1', 'pmd_sriov')
+ print "VM config params:"
+ print vm1.params
+ vm1_dut = vm1.start()
+
+ try:
+ host_ip = vm1.session.send_expect("ifconfig", '# ')
+ print "Host IP:"
+ print host_ip
+
+ vm1_ip = vm1.get_vm_ip()
+ print "VM1 IP:"
+ print vm1_ip
+
+ print "VM1 PCI device:"
+ print vm_dut.session.send_expect('lspci -nn | grep -i eth', '# ')
+ except Exception as e:
+ print e
+ vm1_dut.stop()
+ port0.bind_driver()
+ # Stop VM
+ vm1.stop()
+ port0.bind_driver()
+
+ dut.host_logger.logger_exit()
+ dut.logger.logger_exit()
+ tester.logger.logger_exit()
+
+ print "Start and stop VM over!"
--
1.9.3
next prev parent reply other threads:[~2015-05-22 9:04 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-05-22 9:03 [dts] [PATCH v2 00/19] *** Enable virtualization test for dts framework *** Jiajia, Sun
2015-05-22 9:03 ` [dts] [PATCH v2 01/19] Abstract the NIC device as the single class NetDevice Jiajia, Sun
2015-05-22 9:03 ` [dts] [PATCH v2 02/19] Add a base module for virtual test Jiajia, Sun
2015-05-22 9:03 ` Jiajia, Sun [this message]
2015-05-22 9:03 ` [dts] [PATCH v2 04/19] Add a module to manage the host resource Jiajia, Sun
2015-05-22 9:03 ` [dts] [PATCH v2 05/19] Add a module to instantiate the VM Jiajia, Sun
2015-05-25 6:10 ` Qiu, Michael
2015-05-25 9:14 ` Jiajia, SunX
2015-05-26 9:07 ` Qiu, Michael
2015-05-27 1:36 ` Jiajia, SunX
2015-05-22 9:03 ` [dts] [PATCH v2 06/19] Add a third-party module of qemu-guest-agent to manage VM Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 07/19] Move some general functions from dts.py to utils.py and settings.py Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 08/19] Add and move some functions because of the virtual tests and network device instantiation Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 09/19] Change and add some functions to support virtual test Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 10/19] add some exceptions to support framwork to handle virtual test exceptions Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 11/19] Add some codes to support virtual test log Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 12/19] Add some codes to make session to support virtual test Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 13/19] Add some base functions to get the device info in the testpmd Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 14/19] Change some codes to support network device instantiation and virtualization test Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 15/19] Add some codes to support network instantiation in the tester module Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 16/19] Make test_case know its suite name Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 17/19] Add a global virtualization config and a config related to SRIOV KVM suite Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 18/19] Add a test plan of how to test SRIOV on the KVM ENV Jiajia, Sun
2015-05-22 9:04 ` [dts] [PATCH v2 19/19] Add a test suite to verify the SRIOV feature " Jiajia, Sun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1432285452-14286-4-git-send-email-sunx.jiajia@intel.com \
--to=sunx.jiajia@intel.com \
--cc=dts@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).