get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/109304/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 109304,
    "url": "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"
    ]
}