From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 12155A0613 for ; Wed, 28 Aug 2019 12:29:11 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 5C82E1C123; Wed, 28 Aug 2019 12:29:10 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by dpdk.org (Postfix) with ESMTP id B721C1C123 for ; Wed, 28 Aug 2019 12:29:07 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 Aug 2019 03:29:06 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,440,1559545200"; d="scan'208";a="380353125" Received: from fmsmsx104.amr.corp.intel.com ([10.18.124.202]) by fmsmga005.fm.intel.com with ESMTP; 28 Aug 2019 03:29:06 -0700 Received: from fmsmsx123.amr.corp.intel.com (10.18.125.38) by fmsmsx104.amr.corp.intel.com (10.18.124.202) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 28 Aug 2019 03:29:05 -0700 Received: from shsmsx152.ccr.corp.intel.com (10.239.6.52) by fmsmsx123.amr.corp.intel.com (10.18.125.38) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 28 Aug 2019 03:29:05 -0700 Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.80]) by SHSMSX152.ccr.corp.intel.com ([169.254.6.62]) with mapi id 14.03.0439.000; Wed, 28 Aug 2019 18:29:03 +0800 From: "Tu, Lijuan" To: "Mo, YufengX" , "dts@dpdk.org" , "Wang, Yinan" Thread-Topic: [dts][PATCH V2 1/1] framework/qemu_libvirt: add new features Thread-Index: AQHVXXTyyWVLuuifXEmaA88HnUktCKcQW98A Date: Wed, 28 Aug 2019 10:29:02 +0000 Message-ID: <8CE3E05A3F976642AAB0F4675D0AD20E0BB06C1A@SHSMSX101.ccr.corp.intel.com> References: <20190828074756.16519-1-yufengx.mo@intel.com> <20190828074756.16519-2-yufengx.mo@intel.com> In-Reply-To: <20190828074756.16519-2-yufengx.mo@intel.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-ctpclassification: CTP_NT x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiNDZkMjQxYTktODgxMS00OWY5LWE1MWUtYjljNzFiYzA1YzEwIiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoiczNSUkxGQ2RuSmp1c0Zabk1BeWFcL0thK085OE9oOWxQQkhoQUNBOExQa2sremdnZEIyS1BUOHFuUFwvSXRiYXJJIn0= x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V2 1/1] framework/qemu_libvirt: add new features X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" Applied, thanks > -----Original Message----- > From: Mo, YufengX > Sent: Wednesday, August 28, 2019 3:48 PM > To: dts@dpdk.org; Wang, Yinan ; Tu, Lijuan > > Cc: Mo, YufengX > Subject: [dts][PATCH V2 1/1] framework/qemu_libvirt: add new features >=20 >=20 > *. support bridge setting in xml. > *. support vhost-user setting in xml. support to add driver attribute by = set > option opt_queue and opt_setting. > *. add __add_vm_net_tap method to add tap&bridge net device. > *. add __generate_net_config_script method to set default br0 using scrip= t. > *. add __write_config method to make recursion write config in xml config > file. > *. use dut logger to take the place of print. > *. remove graphics vnc config in set_vm_default method, add add_vm_vnc > method to do graphics vnc setting. This is aimed to keep the same as > qemu_kvm method. > *. add add_vm_daemon method to keep the same as qemu_kvm method. > libvirt run virtual machine with daemon status by default. >=20 > Signed-off-by: yufengmx > --- > framework/qemu_libvirt.py | 275 +++++++++++++++++++++++++++++++++---- > - > 1 file changed, 241 insertions(+), 34 deletions(-) >=20 > diff --git a/framework/qemu_libvirt.py b/framework/qemu_libvirt.py index > ab62ab8..d2edd31 100644 > --- a/framework/qemu_libvirt.py > +++ b/framework/qemu_libvirt.py > @@ -48,6 +48,21 @@ from xml.dom import minidom >=20 >=20 > class LibvirtKvm(VirtBase): > + DEFAULT_BRIDGE =3D 'br0' > + QEMU_IFUP =3D "#!/bin/sh\n\n" + \ > + "set -x\n\n" + \ > + "switch=3D%(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 =3D '/etc/qemu-ifup' >=20 > def __init__(self, dut, name, suite): > # initialize virtualization base module @@ -56,6 +71,7 @@ class > LibvirtKvm(VirtBase): > # initialize qemu emulator, example: qemu-system-x86_64 > self.qemu_emulator =3D self.get_qemu_emulator() >=20 > + self.logger =3D dut.logger > # disk and pci device default index > self.diskindex =3D 'a' > self.controllerindex =3D 0 > @@ -258,9 +274,6 @@ class LibvirtKvm(VirtBase): > device =3D ET.SubElement(self.domain, 'devices') > ET.SubElement(device, 'emulator').text =3D self.qemu_emulator >=20 > - # graphic device > - ET.SubElement(device, 'graphics', { > - 'type': 'vnc', 'port': '-1', 'autoport': 'yes'}) > # qemu guest agent > self.add_vm_qga(None) >=20 > @@ -348,6 +361,38 @@ class LibvirtKvm(VirtBase): > 'slot': '0x00', 'function': '0x00'}) > self.pciindex +=3D 1 >=20 > + def add_vm_daemon(self, **options): > + pass > + > + def add_vm_vnc(self, **options): > + """ > + Add VM display option > + """ > + disable =3D options.get('disable') > + if disable and disable =3D=3D 'True': > + return > + else: > + displayNum =3D options.get('displayNum') > + port =3D \ > + displayNum if displayNum else \ > + self.virt_pool.alloc_port(self.vm_name, port_type=3D"dis= play") > + ip =3D self.host_dut.get_ip_address() > + # set main block > + graphics =3D { > + 'type': 'vnc', > + 'port': port, > + 'autoport': 'yes', > + 'listen': ip, > + 'keymap': 'en-us', } > + > + devices =3D self.domain.find('devices') > + graphics =3D ET.SubElement(devices, 'graphics', graphics) > + # set sub block > + listen =3D { > + 'type': 'address', > + 'address': ip, } > + ET.SubElement(graphics, 'listen', listen) > + > def add_vm_serial_port(self, **options): > if 'enable' in options.keys(): > if options['enable'].lower() =3D=3D 'yes': > @@ -370,8 +415,8 @@ class LibvirtKvm(VirtBase): > {'mode': 'bind', 'path': self.serial_path}) > ET.SubElement(serial, 'target', {'port': '0'}) > else: > - print utils.RED( > - "Serial type %s is not supported!" % serial_type= ) > + msg =3D "Serial type %s is not supported!" % serial_= type > + self.logger.error(msg) > return False > console =3D ET.SubElement( > devices, 'console', {'type': serial_type}) @@ -417,5= 4 +462,189 > @@ class LibvirtKvm(VirtBase): > return None >=20 > def set_vm_device(self, driver=3D'pci-assign', **opts): > + opts['driver'] =3D driver > self.add_vm_device(**opts) >=20 > - def add_vm_device(self, **options): > + def __generate_net_config_script(self, switch=3DDEFAULT_BRIDGE): > """ > - options: > - pf_idx: device index of pass-through device > - guestpci: assigned pci address in vm > + Generate a script for qemu emulator to build a tap device > + between host and guest. > """ > - devices =3D self.domain.find('devices') > - hostdevice =3D ET.SubElement(devices, 'hostdev', { > - 'mode': 'subsystem', 'type': 'pci', > - 'managed': 'yes'}) > + qemu_ifup =3D self.QEMU_IFUP % {'switch': switch} > + file_name =3D os.path.basename(self.QEMU_IFUP_PATH) > + tmp_file_path =3D '/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, '# ') >=20 > - if 'opt_host' in options.keys(): > - pci_addr =3D options['opt_host'] > - else: > - print utils.RED("Missing opt_host for device option!!!") > + def __parse_opt_setting(self, opt_settings): > + if '=3D' not in opt_settings: > + msg =3D 'wrong opt_settings setting' > + raise Exception(msg) > + setting =3D [item.split('=3D') for item in opt_settings.split(',= ')] > + return dict(setting) > + > + def __get_pci_addr_config(self, pci): > + pci =3D self.__parse_pci(pci) > + if pci is None: > + msg =3D 'Invalid guestpci for host device pass-through !!!' > + self.logger.error(msg) > return False > + bus, slot, func, dom =3D pci > + config =3D { > + 'type': 'pci', 'domain': '0x%s' % dom, 'bus': '0x%s' % bus, > + 'slot': '0x%s' % slot, 'function': '0x%s' % func} > + return config > + > + def __write_config(self, parent, configs): > + for config in configs: > + node_name =3D config[0] > + opt =3D config[1] > + node =3D ET.SubElement(parent, node_name, opt) > + if len(config) =3D=3D 3: > + self.__write_config(node, config[2]) > + > + def __set_vm_bridge_interface(self, **options): > + mac =3D options.get('opt_mac') > + opt_br =3D options.get('opt_br') > + if not mac or not opt_br: > + msg =3D "Missing some bridge device option !!!" > + self.logger.error(msg) > + return False > + _config =3D [ > + ['mac', {'address': mac}], > + ['source', {'bridge': opt_br, }], > + ['model', {'type': 'virtio', }]] > + config =3D [['interface', {'type': 'bridge'}, _config]] > + # set xml file > + parent =3D self.domain.find('devices') > + self.__write_config(parent, config) > + > + def __add_vm_virtio_user_pci(self, **options): > + mac =3D options.get('opt_mac') > + mode =3D options.get('opt_server') or 'client' > + # unix socket path of character device > + sock_path =3D options.get('opt_path') > + queue =3D options.get('opt_queue') > + settings =3D options.get('opt_settings') > + # pci address in virtual machine > + pci =3D options.get('opt_host') > + if not mac or not sock_path: > + msg =3D "Missing some vhostuser device option !!!" > + self.logger.error(msg) > + return False > + node_name =3D 'interface' > + # basic options > + _config =3D [ > + ['mac', {'address': mac}], > + ['source', {'type': 'unix', > + 'path': sock_path, > + 'mode': mode, }], > + ['model', {'type': 'virtio', }]] > + # append pci address > + if pci: > + _config.append(['address', self.__get_pci_addr_config(pci)]) > + if queue or settings: > + drv_config =3D {'name': 'vhost'} > + if settings: > + _sub_opt =3D self.__parse_opt_setting(settings) > + drv_opt =3D {} > + guest_opt =3D {} > + host_opt =3D {} > + for key, value in _sub_opt.iteritems(): > + if key.startswith('host_'): > + host_opt[key[5:]] =3D value > + continue > + if key.startswith('guest_'): > + guest_opt[key[6:]] =3D value > + continue > + drv_opt[key] =3D value > + drv_config.update(drv_opt) > + sub_drv_config =3D [] > + if host_opt: > + sub_drv_config.append(['host', host_opt]) > + if guest_opt: > + sub_drv_config.append(['guest', guest_opt]) > + # The optional queues attribute controls the number of queue= s to > be > + # used for either Multiqueue virtio-net or vhost-user networ= k > + # interfaces. Each queue will potentially be handled by a di= fferent > + # processor, resulting in much higher throughput. virtio-net= since > + # 1.0.6 (QEMU and KVM only) vhost-user since 1.2.17(QEMU and > KVM > + # only). > + if queue: > + drv_config.update({'queues': queue, }) > + # set driver config > + if sub_drv_config: > + _config.append(['driver', drv_config, sub_drv_config]) > + else: > + _config.append(['driver', drv_config]) > + config =3D [[node_name, {'type': 'vhostuser'}, _config]] > + # set xml file > + parent =3D self.domain.find('devices') > + self.__write_config(parent, config) >=20 > + def __add_vm_pci_assign(self, **options): > + devices =3D self.domain.find('devices') > + # add hostdev config block > + config =3D { > + 'mode': 'subsystem', > + 'type': 'pci', > + 'managed': 'yes'} > + hostdevice =3D ET.SubElement(devices, 'hostdev', config) > + # add hostdev/source config block > + pci_addr =3D options.get('opt_host') > + if not pci_addr: > + msg =3D "Missing opt_host for device option!!!" > + self.logger.error(msg) > + return False > pci =3D self.__parse_pci(pci_addr) > if pci is None: > return False > bus, slot, func, dom =3D pci > - > source =3D ET.SubElement(hostdevice, 'source') > - ET.SubElement(source, 'address', { > - 'domain': '0x%s' % dom, 'bus': '0x%s' % bus, > - 'slot': '0x%s' % slot, > - 'function': '0x%s' % func}) > - if 'guestpci' in options.keys(): > - guest_pci_addr =3D options['guestpci'] > - else: > + config =3D { > + 'domain': '0x%s' % dom, > + 'bus': '0x%s' % bus, > + 'slot': '0x%s' % slot, > + 'function': '0x%s' % func} > + ET.SubElement(source, 'address', config) > + # add hostdev/source/address config block > + guest_pci_addr =3D options.get('guestpci') > + if not guest_pci_addr: > guest_pci_addr =3D '0000:%s:00.0' % hex(self.pciindex)[2:] > self.pciindex +=3D 1 > - pci =3D self.__parse_pci(guest_pci_addr) > - if pci is None: > - print utils.RED('Invalid guestpci for host device pass-throu= gh!!!') > - return False > - bus, slot, func, dom =3D pci > - ET.SubElement(hostdevice, 'address', { > - 'type': 'pci', 'domain': '0x%s' % dom, 'bus': '0x%s' % bus, > - 'slot': '0x%s' % slot, 'function': '0x%s' % func}) > + config =3D self.__get_pci_addr_config(guest_pci_addr) > + ET.SubElement(hostdevice, 'address', config) > # save host and guest pci address mapping > pci_map =3D {} > pci_map['hostpci'] =3D pci_addr > pci_map['guestpci'] =3D guest_pci_addr > self.pci_maps.append(pci_map) >=20 > + def add_vm_device(self, **options): > + """ > + options: > + pf_idx: device index of pass-through device > + guestpci: assigned pci address in vm > + """ > + driver_table =3D { > + 'vhost-user': > + self.__add_vm_virtio_user_pci, > + 'bridge': > + self.__set_vm_bridge_interface, > + 'pci-assign': > + self.__add_vm_pci_assign, > + } > + driver =3D options.get('driver') > + if not driver or driver not in driver_table.keys(): > + driver =3D 'pci-assign' > + msg =3D 'use {0} configuration as default driver'.format(dri= ver) > + self.logger.warning(msg) > + func =3D driver_table.get(driver) > + func(**options) > + > def add_vm_net(self, **options): > """ > Options: > @@ -473,6 +653,8 @@ class LibvirtKvm(VirtBase): > if 'type' in options.keys(): > if options['type'] =3D=3D 'nic': > self.__add_vm_net_nic(**options) > + elif options['type'] =3D=3D 'tap': > + self.__add_vm_net_tap(**options) >=20 > def __add_vm_net_nic(self, **options): > """ > @@ -514,6 +696,30 @@ class LibvirtKvm(VirtBase): > ET.SubElement(qemu, 'qemu:arg', {'value': 'user,hostfwd=3D' > 'tcp:%s:%d-:22' % (dut_ip, = port)}) >=20 > + def __add_vm_net_tap(self, **options): > + """ > + type: tap > + 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. > + """ > + _config =3D [['target', {'dev': 'tap0'}]] > + # add bridge info > + opt_br =3D options.get('opt_br') > + bridge =3D opt_br if opt_br else self.DEFAULT_BRIDGE > + _config.append(['source', {'bridge': bridge}]) > + self.__generate_net_config_script(str(bridge)) > + # add network configure script path > + opt_script =3D options.get('opt_script') > + script_path =3D opt_script if opt_script else self.QEMU_IFUP_PAT= H > + _config.append(['script', {'path': script_path}]) > + config =3D [['interface', {'type': 'bridge'}, _config]] > + # set xml file > + parent =3D self.domain.find('devices') > + self.__write_config(parent, config) > + > def add_vm_virtio_serial_channel(self, **options): > """ > Options: > @@ -524,7 +730,8 @@ class LibvirtKvm(VirtBase): > channel =3D ET.SubElement(devices, 'channel', {'type': 'unix'}) > for opt in ['path', 'name']: > if opt not in options.keys(): > - print "invalid virtio serial channel setting" > + msg =3D "invalid virtio serial channel setting" > + self.logger.error(msg) > return >=20 > ET.SubElement( > -- > 2.21.0