Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/109304/?format=api
http://patches.dpdk.org/api/patches/109304/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/patch/20220406151903.2916254-6-juraj.linkes@pantheon.tech/", "project": { "id": 1, "url": "http://patches.dpdk.org/api/projects/1/?format=api", "name": "DPDK", "link_name": "dpdk", "list_id": "dev.dpdk.org", "list_email": "dev@dpdk.org", "web_url": "http://core.dpdk.org", "scm_url": "git://dpdk.org/dpdk", "webscm_url": "http://git.dpdk.org/dpdk", "list_archive_url": "https://inbox.dpdk.org/dev", "list_archive_url_format": "https://inbox.dpdk.org/dev/{}", "commit_url_format": "" }, "msgid": "<20220406151903.2916254-6-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20220406151903.2916254-6-juraj.linkes@pantheon.tech", "date": "2022-04-06T15:18:45", "name": "[RFC,v1,05/23] dts: merge DTS framework/qemu_libvirt.py to DPDK", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": true, "hash": "4b05bc93b78d9f6ca444263fdf4b15d60e1ccdea", "submitter": { "id": 1626, "url": "http://patches.dpdk.org/api/people/1626/?format=api", "name": "Juraj Linkeš", "email": "juraj.linkes@pantheon.tech" }, "delegate": { "id": 1, "url": "http://patches.dpdk.org/api/users/1/?format=api", "username": "tmonjalo", "first_name": "Thomas", "last_name": "Monjalon", "email": "thomas@monjalon.net" }, "mbox": "http://patches.dpdk.org/project/dpdk/patch/20220406151903.2916254-6-juraj.linkes@pantheon.tech/mbox/", "series": [ { "id": 22381, "url": "http://patches.dpdk.org/api/series/22381/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=22381", "date": "2022-04-06T15:18:40", "name": "merge DTS test resource files to DPDK", "version": 1, "mbox": "http://patches.dpdk.org/series/22381/mbox/" } ], "comments": "http://patches.dpdk.org/api/patches/109304/comments/", "check": "warning", "checks": "http://patches.dpdk.org/api/patches/109304/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<dev-bounces@dpdk.org>", "X-Original-To": "patchwork@inbox.dpdk.org", "Delivered-To": "patchwork@inbox.dpdk.org", "Received": [ "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id DFF09A0509;\n\tWed, 6 Apr 2022 17:19:45 +0200 (CEST)", "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 69B25428CF;\n\tWed, 6 Apr 2022 17:19:15 +0200 (CEST)", "from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20])\n by mails.dpdk.org (Postfix) with ESMTP id 33C0142880\n for <dev@dpdk.org>; Wed, 6 Apr 2022 17:19:14 +0200 (CEST)", "from localhost (localhost [127.0.0.1])\n by lb.pantheon.sk (Postfix) with ESMTP id 1FF00129C28;\n Wed, 6 Apr 2022 17:19:12 +0200 (CEST)", "from lb.pantheon.sk ([127.0.0.1])\n by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id CqTmaNByBp6q; Wed, 6 Apr 2022 17:19:09 +0200 (CEST)", "from entguard.lab.pantheon.local (unknown [46.229.239.141])\n by lb.pantheon.sk (Postfix) with ESMTP id 7BA8B184FF4;\n Wed, 6 Apr 2022 17:19:06 +0200 (CEST)" ], "X-Virus-Scanned": "amavisd-new at siecit.sk", "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "To": "thomas@monjalon.net, david.marchand@redhat.com,\n Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu, lijuan.tu@intel.com", "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "Subject": "[RFC PATCH v1 05/23] dts: merge DTS framework/qemu_libvirt.py to DPDK", "Date": "Wed, 6 Apr 2022 15:18:45 +0000", "Message-Id": "<20220406151903.2916254-6-juraj.linkes@pantheon.tech>", "X-Mailer": "git-send-email 2.25.1", "In-Reply-To": "<20220406151903.2916254-1-juraj.linkes@pantheon.tech>", "References": "<20220406151903.2916254-1-juraj.linkes@pantheon.tech>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "dev@dpdk.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "DPDK patches and discussions <dev.dpdk.org>", "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>", "List-Archive": "<http://mails.dpdk.org/archives/dev/>", "List-Post": "<mailto:dev@dpdk.org>", "List-Help": "<mailto:dev-request@dpdk.org?subject=help>", "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>", "Errors-To": "dev-bounces@dpdk.org" }, "content": "---\n dts/framework/qemu_libvirt.py | 884 ++++++++++++++++++++++++++++++++++\n 1 file changed, 884 insertions(+)\n create mode 100644 dts/framework/qemu_libvirt.py", "diff": "diff --git a/dts/framework/qemu_libvirt.py b/dts/framework/qemu_libvirt.py\nnew file mode 100644\nindex 0000000000..740b7bbc55\n--- /dev/null\n+++ b/dts/framework/qemu_libvirt.py\n@@ -0,0 +1,884 @@\n+# BSD LICENSE\n+#\n+# Copyright(c) 2010-2015 Intel Corporation. All rights reserved.\n+# All rights reserved.\n+#\n+# Redistribution and use in source and binary forms, with or without\n+# modification, are permitted provided that the following conditions\n+# are met:\n+#\n+# * Redistributions of source code must retain the above copyright\n+# notice, this list of conditions and the following disclaimer.\n+# * Redistributions in binary form must reproduce the above copyright\n+# notice, this list of conditions and the following disclaimer in\n+# the documentation and/or other materials provided with the\n+# distribution.\n+# * Neither the name of Intel Corporation nor the names of its\n+# contributors may be used to endorse or promote products derived\n+# from this software without specific prior written permission.\n+#\n+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+\n+import os\n+import re\n+import time\n+import xml.etree.ElementTree as ET\n+from xml.dom import minidom\n+from xml.etree.ElementTree import ElementTree\n+\n+import framework.utils as utils\n+\n+from .config import VIRTCONF, VirtConf\n+from .dut import Dut\n+from .exception import StartVMFailedException\n+from .logger import getLogger\n+from .ssh_connection import SSHConnection\n+from .virt_base import VirtBase\n+from .virt_resource import VirtResource\n+\n+\n+class LibvirtKvm(VirtBase):\n+ DEFAULT_BRIDGE = \"br0\"\n+ QEMU_IFUP = (\n+ \"#!/bin/sh\\n\\n\"\n+ + \"set -x\\n\\n\"\n+ + \"switch=%(switch)s\\n\\n\"\n+ + \"if [ -n '$1' ];then\\n\"\n+ + \" tunctl -t $1\\n\"\n+ + \" ip link set $1 up\\n\"\n+ + \" sleep 0.5s\\n\"\n+ + \" brctl addif $switch $1\\n\"\n+ + \" exit 0\\n\"\n+ + \"else\\n\"\n+ + \" echo 'Error: no interface specified'\\n\"\n+ + \" exit 1\\n\"\n+ + \"fi\"\n+ )\n+ QEMU_IFUP_PATH = \"/etc/qemu-ifup\"\n+\n+ def __init__(self, dut, name, suite):\n+ # initialize virtualization base module\n+ super(LibvirtKvm, self).__init__(dut, name, suite)\n+\n+ # initialize qemu emulator, example: qemu-system-x86_64\n+ self.qemu_emulator = self.get_qemu_emulator()\n+\n+ self.logger = dut.logger\n+ # disk and pci device default index\n+ self.diskindex = \"a\"\n+ self.controllerindex = 0\n+ self.pciindex = 10\n+\n+ # configure root element\n+ self.root = ElementTree()\n+ self.domain = ET.Element(\"domain\")\n+ # replace root element\n+ self.root._setroot(self.domain)\n+ # add xml header\n+ self.domain.set(\"type\", \"kvm\")\n+ self.domain.set(\"xmlns:qemu\", \"http://libvirt.org/schemas/domain/qemu/1.0\")\n+ ET.SubElement(self.domain, \"name\").text = name\n+\n+ # devices pass-through into vm\n+ self.pci_maps = []\n+\n+ # default login user,password\n+ self.username = self.host_dut.crb[\"user\"]\n+ self.password = self.host_dut.crb[\"pass\"]\n+\n+ # internal variable to track whether default nic has been added\n+ self.__default_nic = False\n+ self.__default_nic_pci = \"\"\n+\n+ # set some default values for vm,\n+ # if there is not the values of the specified options\n+ self.set_vm_default()\n+\n+ def get_qemu_emulator(self):\n+ \"\"\"\n+ Get the qemu emulator based on the crb.\n+ \"\"\"\n+ arch = self.host_session.send_expect(\"uname -m\", \"# \")\n+ return \"/usr/bin/qemu-system-\" + arch\n+\n+ def get_virt_type(self):\n+ return \"LIBVIRT\"\n+\n+ def has_virtual_ability(self):\n+ \"\"\"\n+ check and setup host virtual ability\n+ \"\"\"\n+ arch = self.host_session.send_expect(\"uname -m\", \"# \")\n+ if arch == \"aarch64\":\n+ out = self.host_session.send_expect(\"service libvirtd status\", \"# \")\n+ if \"active (running)\" not in out:\n+ return False\n+ return True\n+\n+ out = self.host_session.send_expect(\"cat /proc/cpuinfo | grep flags\", \"# \")\n+ rgx = re.search(\" vmx \", out)\n+ if rgx:\n+ pass\n+ else:\n+ self.host_logger.warning(\"Hardware virtualization \" \"disabled on host!!!\")\n+ return False\n+\n+ out = self.host_session.send_expect(\"lsmod | grep kvm\", \"# \")\n+ if \"kvm\" not in out or \"kvm_intel\" not in out:\n+ return False\n+\n+ out = self.host_session.send_expect(\"service libvirtd status\", \"# \")\n+ if \"active (running)\" not in out:\n+ return False\n+\n+ return True\n+\n+ def load_virtual_mod(self):\n+ self.host_session.send_expect(\"modprobe kvm\", \"# \")\n+ self.host_session.send_expect(\"modprobe kvm_intel\", \"# \")\n+\n+ def unload_virtual_mod(self):\n+ self.host_session.send_expect(\"rmmod kvm_intel\", \"# \")\n+ self.host_session.send_expect(\"rmmod kvm\", \"# \")\n+\n+ def disk_image_is_ok(self, image):\n+ \"\"\"\n+ Check if the image is OK and no error.\n+ \"\"\"\n+ pass\n+\n+ def add_vm_mem(self, **options):\n+ \"\"\"\n+ Options:\n+ size : memory size, measured in MB\n+ hugepage : guest memory allocated using hugepages\n+ \"\"\"\n+ if \"size\" in list(options.keys()):\n+ memory = ET.SubElement(self.domain, \"memory\", {\"unit\": \"MB\"})\n+ memory.text = options[\"size\"]\n+ if \"hugepage\" in list(options.keys()):\n+ memoryBacking = ET.SubElement(self.domain, \"memoryBacking\")\n+ ET.SubElement(memoryBacking, \"hugepages\")\n+\n+ def set_vm_cpu(self, **options):\n+ \"\"\"\n+ Set VM cpu.\n+ \"\"\"\n+ index = self.find_option_index(\"cpu\")\n+ if index:\n+ self.params[index] = {\"cpu\": [options]}\n+ else:\n+ self.params.append({\"cpu\": [options]})\n+\n+ def add_vm_cpu(self, **options):\n+ \"\"\"\n+ 'number' : '4' #number of vcpus\n+ 'cpupin' : '3 4 5 6' # host cpu list\n+ \"\"\"\n+ vcpu = 0\n+ if \"number\" in list(options.keys()):\n+ vmcpu = ET.SubElement(self.domain, \"vcpu\", {\"placement\": \"static\"})\n+ vmcpu.text = options[\"number\"]\n+ if \"cpupin\" in list(options.keys()):\n+ cputune = ET.SubElement(self.domain, \"cputune\")\n+ # cpu resource will be allocated\n+ req_cpus = options[\"cpupin\"].split()\n+ cpus = self.virt_pool.alloc_cpu(vm=self.vm_name, corelist=req_cpus)\n+ for cpu in cpus:\n+ ET.SubElement(cputune, \"vcpupin\", {\"vcpu\": \"%d\" % vcpu, \"cpuset\": cpu})\n+ vcpu += 1\n+ else: # request cpu from vm resource pool\n+ cpus = self.virt_pool.alloc_cpu(self.vm_name, number=int(options[\"number\"]))\n+ for cpu in cpus:\n+ ET.SubElement(cputune, \"vcpupin\", {\"vcpu\": \"%d\" % vcpu, \"cpuset\": cpu})\n+ vcpu += 1\n+\n+ def get_vm_cpu(self):\n+ cpus = self.virt_pool.get_cpu_on_vm(self.vm_name)\n+ return cpus\n+\n+ def add_vm_qga(self, options):\n+ qemu = ET.SubElement(self.domain, \"qemu:commandline\")\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"-chardev\"})\n+ ET.SubElement(\n+ qemu,\n+ \"qemu:arg\",\n+ {\n+ \"value\": \"socket,path=/tmp/\"\n+ + \"%s_qga0.sock,\" % self.vm_name\n+ + \"server,nowait,id=%s_qga0\" % self.vm_name\n+ },\n+ )\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"-device\"})\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"virtio-serial\"})\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"-device\"})\n+ ET.SubElement(\n+ qemu,\n+ \"qemu:arg\",\n+ {\n+ \"value\": \"virtserialport,\"\n+ + \"chardev=%s_qga0\" % self.vm_name\n+ + \",name=org.qemu.guest_agent.0\"\n+ },\n+ )\n+ self.qga_sock_path = \"/tmp/%s_qga0.sock\" % self.vm_name\n+\n+ def add_vm_os(self, **options):\n+ os = self.domain.find(\"os\")\n+ if \"loader\" in list(options.keys()):\n+ loader = ET.SubElement(os, \"loader\", {\"readonly\": \"yes\", \"type\": \"pflash\"})\n+ loader.text = options[\"loader\"]\n+ if \"nvram\" in list(options.keys()):\n+ nvram = ET.SubElement(os, \"nvram\")\n+ nvram.text = options[\"nvram\"]\n+\n+ def set_vm_default_aarch64(self):\n+ os = ET.SubElement(self.domain, \"os\")\n+ type = ET.SubElement(os, \"type\", {\"arch\": \"aarch64\", \"machine\": \"virt\"})\n+ type.text = \"hvm\"\n+ ET.SubElement(os, \"boot\", {\"dev\": \"hd\"})\n+ features = ET.SubElement(self.domain, \"features\")\n+ ET.SubElement(features, \"acpi\")\n+\n+ ET.SubElement(self.domain, \"cpu\", {\"mode\": \"host-passthrough\", \"check\": \"none\"})\n+\n+ def set_vm_default_x86_64(self):\n+ os = ET.SubElement(self.domain, \"os\")\n+ type = ET.SubElement(os, \"type\", {\"arch\": \"x86_64\", \"machine\": \"pc-i440fx-1.6\"})\n+ type.text = \"hvm\"\n+ ET.SubElement(os, \"boot\", {\"dev\": \"hd\"})\n+ features = ET.SubElement(self.domain, \"features\")\n+ ET.SubElement(features, \"acpi\")\n+ ET.SubElement(features, \"apic\")\n+ ET.SubElement(features, \"pae\")\n+\n+ ET.SubElement(self.domain, \"cpu\", {\"mode\": \"host-passthrough\"})\n+ self.__default_nic_pci = \"00:1f.0\"\n+\n+ def set_vm_default(self):\n+ arch = self.host_session.send_expect(\"uname -m\", \"# \")\n+ set_default_func = getattr(self, \"set_vm_default_\" + arch)\n+ if callable(set_default_func):\n+ set_default_func()\n+\n+ # qemu-kvm for emulator\n+ device = ET.SubElement(self.domain, \"devices\")\n+ ET.SubElement(device, \"emulator\").text = self.qemu_emulator\n+\n+ # qemu guest agent\n+ self.add_vm_qga(None)\n+\n+ # add default control interface\n+ if not self.__default_nic:\n+ if len(self.__default_nic_pci) > 0:\n+ def_nic = {\n+ \"type\": \"nic\",\n+ \"opt_hostfwd\": \"\",\n+ \"opt_addr\": self.__default_nic_pci,\n+ }\n+ else:\n+ def_nic = {\"type\": \"nic\", \"opt_hostfwd\": \"\"}\n+ self.add_vm_net(**def_nic)\n+ self.__default_nic = True\n+\n+ def set_qemu_emulator(self, qemu_emulator_path):\n+ \"\"\"\n+ Set the qemu emulator in the specified path explicitly.\n+ \"\"\"\n+ out = self.host_session.send_expect(\"ls %s\" % qemu_emulator_path, \"# \")\n+ if \"No such file or directory\" in out:\n+ self.host_logger.error(\n+ \"No emulator [ %s ] on the DUT\" % (qemu_emulator_path)\n+ )\n+ return None\n+ out = self.host_session.send_expect(\n+ \"[ -x %s ];echo $?\" % (qemu_emulator_path), \"# \"\n+ )\n+ if out != \"0\":\n+ self.host_logger.error(\n+ \"Emulator [ %s ] \" % qemu_emulator_path + \"not executable on the DUT\"\n+ )\n+ return None\n+ self.qemu_emulator = qemu_emulator_path\n+\n+ def add_vm_qemu(self, **options):\n+ \"\"\"\n+ Options:\n+ path: absolute path for qemu emulator\n+ \"\"\"\n+ if \"path\" in list(options.keys()):\n+ self.set_qemu_emulator(options[\"path\"])\n+ # update emulator config\n+ devices = self.domain.find(\"devices\")\n+ ET.SubElement(devices, \"emulator\").text = self.qemu_emulator\n+\n+ def add_vm_disk(self, **options):\n+ \"\"\"\n+ Options:\n+ file: absolute path of disk image file\n+ type: image file formats\n+ \"\"\"\n+ devices = self.domain.find(\"devices\")\n+ disk = ET.SubElement(devices, \"disk\", {\"type\": \"file\", \"device\": \"disk\"})\n+\n+ if \"file\" not in options:\n+ return False\n+\n+ ET.SubElement(disk, \"source\", {\"file\": options[\"file\"]})\n+ if \"opt_format\" not in options:\n+ disk_type = \"raw\"\n+ else:\n+ disk_type = options[\"opt_format\"]\n+\n+ ET.SubElement(disk, \"driver\", {\"name\": \"qemu\", \"type\": disk_type})\n+\n+ if \"opt_bus\" not in options:\n+ bus = \"virtio\"\n+ else:\n+ bus = options[\"opt_bus\"]\n+ if \"opt_dev\" not in options:\n+ dev = \"vd%c\" % self.diskindex\n+ self.diskindex = chr(ord(self.diskindex) + 1)\n+ else:\n+ dev = options[\"opt_dev\"]\n+ ET.SubElement(disk, \"target\", {\"dev\": dev, \"bus\": bus})\n+\n+ if \"opt_controller\" in options:\n+ controller = ET.SubElement(\n+ devices,\n+ \"controller\",\n+ {\n+ \"type\": bus,\n+ \"index\": hex(self.controllerindex)[2:],\n+ \"model\": options[\"opt_controller\"],\n+ },\n+ )\n+ self.controllerindex += 1\n+ ET.SubElement(\n+ controller,\n+ \"address\",\n+ {\n+ \"type\": \"pci\",\n+ \"domain\": \"0x0000\",\n+ \"bus\": hex(self.pciindex),\n+ \"slot\": \"0x00\",\n+ \"function\": \"0x00\",\n+ },\n+ )\n+ self.pciindex += 1\n+\n+ def add_vm_daemon(self, **options):\n+ pass\n+\n+ def add_vm_vnc(self, **options):\n+ \"\"\"\n+ Add VM display option\n+ \"\"\"\n+ disable = options.get(\"disable\")\n+ if disable and disable == \"True\":\n+ return\n+ else:\n+ displayNum = options.get(\"displayNum\")\n+ port = (\n+ displayNum\n+ if displayNum\n+ else self.virt_pool.alloc_port(self.vm_name, port_type=\"display\")\n+ )\n+ ip = self.host_dut.get_ip_address()\n+ # set main block\n+ graphics = {\n+ \"type\": \"vnc\",\n+ \"port\": port,\n+ \"autoport\": \"yes\",\n+ \"listen\": ip,\n+ \"keymap\": \"en-us\",\n+ }\n+\n+ devices = self.domain.find(\"devices\")\n+ graphics = ET.SubElement(devices, \"graphics\", graphics)\n+ # set sub block\n+ listen = {\n+ \"type\": \"address\",\n+ \"address\": ip,\n+ }\n+ ET.SubElement(graphics, \"listen\", listen)\n+\n+ def add_vm_serial_port(self, **options):\n+ if \"enable\" in list(options.keys()):\n+ if options[\"enable\"].lower() == \"yes\":\n+ devices = self.domain.find(\"devices\")\n+ if \"opt_type\" in list(options.keys()):\n+ serial_type = options[\"opt_type\"]\n+ else:\n+ serial_type = \"unix\"\n+ if serial_type == \"pty\":\n+ serial = ET.SubElement(devices, \"serial\", {\"type\": serial_type})\n+ ET.SubElement(serial, \"target\", {\"port\": \"0\"})\n+ elif serial_type == \"unix\":\n+ serial = ET.SubElement(devices, \"serial\", {\"type\": serial_type})\n+ self.serial_path = \"/tmp/%s_serial.sock\" % self.vm_name\n+ ET.SubElement(\n+ serial, \"source\", {\"mode\": \"bind\", \"path\": self.serial_path}\n+ )\n+ ET.SubElement(serial, \"target\", {\"port\": \"0\"})\n+ else:\n+ msg = \"Serial type %s is not supported!\" % serial_type\n+ self.logger.error(msg)\n+ return False\n+ console = ET.SubElement(devices, \"console\", {\"type\": serial_type})\n+ ET.SubElement(console, \"target\", {\"type\": \"serial\", \"port\": \"0\"})\n+\n+ def add_vm_login(self, **options):\n+ \"\"\"\n+ options:\n+ user: login username of virtual machine\n+ password: login password of virtual machine\n+ \"\"\"\n+ if \"user\" in list(options.keys()):\n+ user = options[\"user\"]\n+ self.username = user\n+\n+ if \"password\" in list(options.keys()):\n+ password = options[\"password\"]\n+ self.password = password\n+\n+ def get_vm_login(self):\n+ return (self.username, self.password)\n+\n+ def __parse_pci(self, pci_address):\n+ pci_regex = r\"([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2})\" + \".([0-9a-fA-F]{1,2})\"\n+ pci_regex_domain = (\n+ r\"([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,2}):\"\n+ + \"([0-9a-fA-F]{1,2}).([0-9a-fA-F]{1,2})\"\n+ )\n+ m = re.match(pci_regex, pci_address)\n+ if m is not None:\n+ bus = m.group(1)\n+ slot = m.group(2)\n+ func = m.group(3)\n+ dom = \"0\"\n+ return (bus, slot, func, dom)\n+ m = re.match(pci_regex_domain, pci_address)\n+ if m is not None:\n+ bus = m.group(2)\n+ slot = m.group(3)\n+ func = m.group(4)\n+ dom = m.group(1)\n+ return (bus, slot, func, dom)\n+ return None\n+\n+ def set_vm_device(self, driver=\"pci-assign\", **opts):\n+ opts[\"driver\"] = driver\n+ self.add_vm_device(**opts)\n+\n+ def __generate_net_config_script(self, switch=DEFAULT_BRIDGE):\n+ \"\"\"\n+ Generate a script for qemu emulator to build a tap device\n+ between host and guest.\n+ \"\"\"\n+ qemu_ifup = self.QEMU_IFUP % {\"switch\": switch}\n+ file_name = os.path.basename(self.QEMU_IFUP_PATH)\n+ tmp_file_path = \"/tmp/%s\" % file_name\n+ self.host_dut.create_file(qemu_ifup, tmp_file_path)\n+ self.host_session.send_expect(\n+ \"mv -f ~/%s %s\" % (file_name, self.QEMU_IFUP_PATH), \"# \"\n+ )\n+ self.host_session.send_expect(\"chmod +x %s\" % self.QEMU_IFUP_PATH, \"# \")\n+\n+ def __parse_opt_setting(self, opt_settings):\n+ if \"=\" not in opt_settings:\n+ msg = \"wrong opt_settings setting\"\n+ raise Exception(msg)\n+ setting = [item.split(\"=\") for item in opt_settings.split(\",\")]\n+ return dict(setting)\n+\n+ def __get_pci_addr_config(self, pci):\n+ pci = self.__parse_pci(pci)\n+ if pci is None:\n+ msg = \"Invalid guestpci for host device pass-through !!!\"\n+ self.logger.error(msg)\n+ return False\n+ bus, slot, func, dom = pci\n+ config = {\n+ \"type\": \"pci\",\n+ \"domain\": \"0x%s\" % dom,\n+ \"bus\": \"0x%s\" % bus,\n+ \"slot\": \"0x%s\" % slot,\n+ \"function\": \"0x%s\" % func,\n+ }\n+ return config\n+\n+ def __write_config(self, parent, configs):\n+ for config in configs:\n+ node_name = config[0]\n+ opt = config[1]\n+ node = ET.SubElement(parent, node_name, opt)\n+ if len(config) == 3:\n+ self.__write_config(node, config[2])\n+\n+ def __set_vm_bridge_interface(self, **options):\n+ mac = options.get(\"opt_mac\")\n+ opt_br = options.get(\"opt_br\")\n+ if not mac or not opt_br:\n+ msg = \"Missing some bridge device option !!!\"\n+ self.logger.error(msg)\n+ return False\n+ _config = [\n+ [\"mac\", {\"address\": mac}],\n+ [\n+ \"source\",\n+ {\n+ \"bridge\": opt_br,\n+ },\n+ ],\n+ [\n+ \"model\",\n+ {\n+ \"type\": \"virtio\",\n+ },\n+ ],\n+ ]\n+ config = [[\"interface\", {\"type\": \"bridge\"}, _config]]\n+ # set xml file\n+ parent = self.domain.find(\"devices\")\n+ self.__write_config(parent, config)\n+\n+ def __add_vm_virtio_user_pci(self, **options):\n+ mac = options.get(\"opt_mac\")\n+ mode = options.get(\"opt_server\") or \"client\"\n+ # unix socket path of character device\n+ sock_path = options.get(\"opt_path\")\n+ queue = options.get(\"opt_queue\")\n+ settings = options.get(\"opt_settings\")\n+ # pci address in virtual machine\n+ pci = options.get(\"opt_host\")\n+ if not mac or not sock_path:\n+ msg = \"Missing some vhostuser device option !!!\"\n+ self.logger.error(msg)\n+ return False\n+ node_name = \"interface\"\n+ # basic options\n+ _config = [\n+ [\"mac\", {\"address\": mac}],\n+ [\n+ \"source\",\n+ {\n+ \"type\": \"unix\",\n+ \"path\": sock_path,\n+ \"mode\": mode,\n+ },\n+ ],\n+ [\n+ \"model\",\n+ {\n+ \"type\": \"virtio\",\n+ },\n+ ],\n+ ]\n+ # append pci address\n+ if pci:\n+ _config.append([\"address\", self.__get_pci_addr_config(pci)])\n+ if queue or settings:\n+ drv_config = {\"name\": \"vhost\"}\n+ if settings:\n+ _sub_opt = self.__parse_opt_setting(settings)\n+ drv_opt = {}\n+ guest_opt = {}\n+ host_opt = {}\n+ for key, value in _sub_opt.items():\n+ if key.startswith(\"host_\"):\n+ host_opt[key[5:]] = value\n+ continue\n+ if key.startswith(\"guest_\"):\n+ guest_opt[key[6:]] = value\n+ continue\n+ drv_opt[key] = value\n+ drv_config.update(drv_opt)\n+ sub_drv_config = []\n+ if host_opt:\n+ sub_drv_config.append([\"host\", host_opt])\n+ if guest_opt:\n+ sub_drv_config.append([\"guest\", guest_opt])\n+ # The optional queues attribute controls the number of queues to be\n+ # used for either Multiqueue virtio-net or vhost-user network\n+ # interfaces. Each queue will potentially be handled by a different\n+ # processor, resulting in much higher throughput. virtio-net since\n+ # 1.0.6 (QEMU and KVM only) vhost-user since 1.2.17(QEMU and KVM\n+ # only).\n+ if queue:\n+ drv_config.update(\n+ {\n+ \"queues\": queue,\n+ }\n+ )\n+ # set driver config\n+ if sub_drv_config:\n+ _config.append([\"driver\", drv_config, sub_drv_config])\n+ else:\n+ _config.append([\"driver\", drv_config])\n+ config = [[node_name, {\"type\": \"vhostuser\"}, _config]]\n+ # set xml file\n+ parent = self.domain.find(\"devices\")\n+ self.__write_config(parent, config)\n+\n+ def __add_vm_pci_assign(self, **options):\n+ devices = self.domain.find(\"devices\")\n+ # add hostdev config block\n+ config = {\"mode\": \"subsystem\", \"type\": \"pci\", \"managed\": \"yes\"}\n+ hostdevice = ET.SubElement(devices, \"hostdev\", config)\n+ # add hostdev/source config block\n+ pci_addr = options.get(\"opt_host\")\n+ if not pci_addr:\n+ msg = \"Missing opt_host for device option!!!\"\n+ self.logger.error(msg)\n+ return False\n+ pci = self.__parse_pci(pci_addr)\n+ if pci is None:\n+ return False\n+ bus, slot, func, dom = pci\n+ source = ET.SubElement(hostdevice, \"source\")\n+ config = {\n+ \"domain\": \"0x%s\" % dom,\n+ \"bus\": \"0x%s\" % bus,\n+ \"slot\": \"0x%s\" % slot,\n+ \"function\": \"0x%s\" % func,\n+ }\n+ ET.SubElement(source, \"address\", config)\n+ # add hostdev/source/address config block\n+ guest_pci_addr = options.get(\"guestpci\")\n+ if not guest_pci_addr:\n+ guest_pci_addr = \"0000:%s:00.0\" % hex(self.pciindex)[2:]\n+ self.pciindex += 1\n+ config = self.__get_pci_addr_config(guest_pci_addr)\n+ ET.SubElement(hostdevice, \"address\", config)\n+ # save host and guest pci address mapping\n+ pci_map = {}\n+ pci_map[\"hostpci\"] = pci_addr\n+ pci_map[\"guestpci\"] = guest_pci_addr\n+ self.pci_maps.append(pci_map)\n+\n+ def add_vm_device(self, **options):\n+ \"\"\"\n+ options:\n+ pf_idx: device index of pass-through device\n+ guestpci: assigned pci address in vm\n+ \"\"\"\n+ driver_table = {\n+ \"vhost-user\": self.__add_vm_virtio_user_pci,\n+ \"bridge\": self.__set_vm_bridge_interface,\n+ \"pci-assign\": self.__add_vm_pci_assign,\n+ }\n+ driver = options.get(\"driver\")\n+ if not driver or driver not in list(driver_table.keys()):\n+ driver = \"pci-assign\"\n+ msg = \"use {0} configuration as default driver\".format(driver)\n+ self.logger.warning(msg)\n+ func = driver_table.get(driver)\n+ func(**options)\n+\n+ def add_vm_net(self, **options):\n+ \"\"\"\n+ Options:\n+ default: create e1000 netdev and redirect ssh port\n+ \"\"\"\n+ if \"type\" in list(options.keys()):\n+ if options[\"type\"] == \"nic\":\n+ self.__add_vm_net_nic(**options)\n+ elif options[\"type\"] == \"tap\":\n+ self.__add_vm_net_tap(**options)\n+\n+ def __add_vm_net_nic(self, **options):\n+ \"\"\"\n+ type: nic\n+ opt_model: [\"e1000\" | \"virtio\" | \"i82551\" | ...]\n+ Default is e1000.\n+ opt_addr: ''\n+ note: PCI cards only.\n+ \"\"\"\n+ if \"opt_model\" in list(options.keys()):\n+ model = options[\"opt_model\"]\n+ else:\n+ model = \"e1000\"\n+\n+ if \"opt_hostfwd\" in list(options.keys()):\n+ port = self.virt_pool.alloc_port(self.vm_name)\n+ if port is None:\n+ return\n+ dut_ip = self.host_dut.crb[\"IP\"]\n+ self.vm_ip = \"%s:%d\" % (dut_ip, port)\n+\n+ qemu = ET.SubElement(self.domain, \"qemu:commandline\")\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"-net\"})\n+ if \"opt_addr\" in list(options.keys()):\n+ pci = self.__parse_pci(options[\"opt_addr\"])\n+ if pci is None:\n+ return False\n+ bus, slot, func, dom = pci\n+ ET.SubElement(\n+ qemu, \"qemu:arg\", {\"value\": \"nic,model=e1000,addr=0x%s\" % slot}\n+ )\n+ else:\n+ ET.SubElement(\n+ qemu, \"qemu:arg\", {\"value\": \"nic,model=e1000,addr=0x%x\" % self.pciindex}\n+ )\n+ self.pciindex += 1\n+\n+ if \"opt_hostfwd\" in list(options.keys()):\n+ ET.SubElement(qemu, \"qemu:arg\", {\"value\": \"-net\"})\n+ ET.SubElement(\n+ qemu,\n+ \"qemu:arg\",\n+ {\"value\": \"user,hostfwd=\" \"tcp:%s:%d-:22\" % (dut_ip, port)},\n+ )\n+\n+ def __add_vm_net_tap(self, **options):\n+ \"\"\"\n+ type: tap\n+ opt_br: br0\n+ note: if choosing tap, need to specify bridge name,\n+ else it will be br0.\n+ opt_script: QEMU_IFUP_PATH\n+ note: if not specified, default is self.QEMU_IFUP_PATH.\n+ \"\"\"\n+ _config = [[\"target\", {\"dev\": \"tap0\"}]]\n+ # add bridge info\n+ opt_br = options.get(\"opt_br\")\n+ bridge = opt_br if opt_br else self.DEFAULT_BRIDGE\n+ _config.append([\"source\", {\"bridge\": bridge}])\n+ self.__generate_net_config_script(str(bridge))\n+ # add network configure script path\n+ opt_script = options.get(\"opt_script\")\n+ script_path = opt_script if opt_script else self.QEMU_IFUP_PATH\n+ _config.append([\"script\", {\"path\": script_path}])\n+ config = [[\"interface\", {\"type\": \"bridge\"}, _config]]\n+ # set xml file\n+ parent = self.domain.find(\"devices\")\n+ self.__write_config(parent, config)\n+\n+ def add_vm_virtio_serial_channel(self, **options):\n+ \"\"\"\n+ Options:\n+ path: virtio unix socket absolute path\n+ name: virtio serial name in vm\n+ \"\"\"\n+ devices = self.domain.find(\"devices\")\n+ channel = ET.SubElement(devices, \"channel\", {\"type\": \"unix\"})\n+ for opt in [\"path\", \"name\"]:\n+ if opt not in list(options.keys()):\n+ msg = \"invalid virtio serial channel setting\"\n+ self.logger.error(msg)\n+ return\n+\n+ ET.SubElement(channel, \"source\", {\"mode\": \"bind\", \"path\": options[\"path\"]})\n+ ET.SubElement(channel, \"target\", {\"type\": \"virtio\", \"name\": options[\"name\"]})\n+ ET.SubElement(\n+ channel,\n+ \"address\",\n+ {\n+ \"type\": \"virtio-serial\",\n+ \"controller\": \"0\",\n+ \"bus\": \"0\",\n+ \"port\": \"%d\" % self.pciindex,\n+ },\n+ )\n+ self.pciindex += 1\n+\n+ def get_vm_ip(self):\n+ return self.vm_ip\n+\n+ def get_pci_mappings(self):\n+ \"\"\"\n+ Return guest and host pci devices mapping structure\n+ \"\"\"\n+ return self.pci_maps\n+\n+ def __control_session(self, command, *args):\n+ \"\"\"\n+ Use the qemu guest agent service to control VM.\n+ Note:\n+ :command: there are these commands as below:\n+ cat, fsfreeze, fstrim, halt, ifconfig, info,\\\n+ ping, powerdown, reboot, shutdown, suspend\n+ :args: give different args by the different commands.\n+ \"\"\"\n+ if not self.qga_sock_path:\n+ self.host_logger.info(\n+ \"No QGA service between host [ %s ] and guest [ %s ]\"\n+ % (self.host_dut.Name, self.vm_name)\n+ )\n+ return None\n+\n+ cmd_head = (\n+ \"~/QMP/\"\n+ + \"qemu-ga-client \"\n+ + \"--address=%s %s\" % (self.qga_sock_path, command)\n+ )\n+\n+ cmd = cmd_head\n+ for arg in args:\n+ cmd = cmd_head + \" \" + str(arg)\n+\n+ if command is \"ping\":\n+ out = self.host_session.send_expect(cmd, \"# \", int(args[0]))\n+ else:\n+ out = self.host_session.send_expect(cmd, \"# \")\n+\n+ return out\n+\n+ def _start_vm(self):\n+ xml_file = \"/tmp/%s.xml\" % self.vm_name\n+ if os.path.exists(xml_file):\n+ os.remove(xml_file)\n+ self.root.write(xml_file)\n+ with open(xml_file, \"r\") as fp:\n+ content = fp.read()\n+ doc = minidom.parseString(content)\n+ vm_content = doc.toprettyxml(indent=\" \")\n+ with open(xml_file, \"w\") as fp:\n+ fp.write(vm_content)\n+ self.host_session.copy_file_to(xml_file)\n+ time.sleep(2)\n+\n+ self.host_session.send_expect(\"virsh\", \"virsh #\")\n+ self.host_session.send_expect(\"create /root/%s.xml\" % self.vm_name, \"virsh #\")\n+ self.host_session.send_expect(\"quit\", \"# \")\n+ out = self.__control_session(\"ping\", \"120\")\n+\n+ if \"Not responded\" in out:\n+ raise StartVMFailedException(\"Not response in 120 seconds!!!\")\n+\n+ self.__wait_vmnet_ready()\n+\n+ def __wait_vmnet_ready(self):\n+ \"\"\"\n+ wait for 120 seconds for vm net ready\n+ 10.0.2.* is the default ip address allocated by qemu\n+ \"\"\"\n+ count = 20\n+ while count:\n+ out = self.__control_session(\"ifconfig\")\n+ if \"10.0.2\" in out:\n+ pos = self.vm_ip.find(\":\")\n+ ssh_key = \"[\" + self.vm_ip[:pos] + \"]\" + self.vm_ip[pos:]\n+ os.system(\"ssh-keygen -R %s\" % ssh_key)\n+ return True\n+ time.sleep(6)\n+ count -= 1\n+\n+ raise StartVMFailedException(\n+ \"Virtual machine control net not ready \" + \"in 120 seconds!!!\"\n+ )\n+\n+ def stop(self):\n+ self.__control_session(\"shutdown\")\n+ time.sleep(5)\n", "prefixes": [ "RFC", "v1", "05/23" ] }{ "id": 109304, "url": "