Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/109307/?format=api
http://patches.dpdk.org/api/patches/109307/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/patch/20220406151903.2916254-9-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-9-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20220406151903.2916254-9-juraj.linkes@pantheon.tech", "date": "2022-04-06T15:18:48", "name": "[RFC,v1,08/23] dts: merge DTS framework/virt_base.py to DPDK", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": true, "hash": "786d144747ae65c06e39b6e50b5be0268d6d0fb9", "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-9-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/109307/comments/", "check": "warning", "checks": "http://patches.dpdk.org/api/patches/109307/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 3BEB7A0509;\n\tWed, 6 Apr 2022 17:20:11 +0200 (CEST)", "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 59E2D42917;\n\tWed, 6 Apr 2022 17:19:19 +0200 (CEST)", "from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20])\n by mails.dpdk.org (Postfix) with ESMTP id 1DAD8428B6\n for <dev@dpdk.org>; Wed, 6 Apr 2022 17:19:17 +0200 (CEST)", "from localhost (localhost [127.0.0.1])\n by lb.pantheon.sk (Postfix) with ESMTP id 0AA7A19E0DF;\n Wed, 6 Apr 2022 17:19:15 +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 ld7D3pmXFANC; Wed, 6 Apr 2022 17:19:14 +0200 (CEST)", "from entguard.lab.pantheon.local (unknown [46.229.239.141])\n by lb.pantheon.sk (Postfix) with ESMTP id A5735184FEE;\n Wed, 6 Apr 2022 17:19:08 +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 08/23] dts: merge DTS framework/virt_base.py to DPDK", "Date": "Wed, 6 Apr 2022 15:18:48 +0000", "Message-Id": "<20220406151903.2916254-9-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/virt_base.py | 553 +++++++++++++++++++++++++++++++++++++\n 1 file changed, 553 insertions(+)\n create mode 100644 dts/framework/virt_base.py", "diff": "diff --git a/dts/framework/virt_base.py b/dts/framework/virt_base.py\nnew file mode 100644\nindex 0000000000..d4af8b985f\n--- /dev/null\n+++ b/dts/framework/virt_base.py\n@@ -0,0 +1,553 @@\n+# BSD LICENSE\n+#\n+# Copyright(c) 2010-2014 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+import os\n+import sys\n+import threading\n+import traceback\n+from random import randint\n+\n+import framework.exception as exception\n+import framework.utils as utils\n+\n+from .config import VIRTCONF, VirtConf\n+from .dut import Dut\n+from .logger import getLogger\n+from .settings import CONFIG_ROOT_PATH\n+from .virt_dut import VirtDut\n+\n+ST_NOTSTART = \"NOTSTART\"\n+ST_PAUSE = \"PAUSE\"\n+ST_RUNNING = \"RUNNING\"\n+ST_UNKNOWN = \"UNKNOWN\"\n+VM_IMG_LIST = []\n+mutex_vm_list = threading.Lock()\n+\n+\n+class VirtBase(object):\n+ \"\"\"\n+ Basic module for customer special virtual type. This module implement\n+ functions configured and composed in the VM boot command. With these\n+ function, we can get and set the VM boot command, and instantiate the VM.\n+ \"\"\"\n+\n+ def __init__(self, dut, vm_name, suite_name):\n+ \"\"\"\n+ Initialize the VirtBase.\n+ dut: the instance of Dut\n+ vm_name: the name of VM which you have configured in the configure\n+ suite_name: the name of test suite\n+ \"\"\"\n+ self.host_dut = dut\n+ self.vm_name = vm_name\n+ self.suite = suite_name\n+ # indicate whether the current vm is migration vm\n+ self.migration_vm = False\n+\n+ # create self used host session, need close it later\n+ self.host_session = self.host_dut.new_session(self.vm_name)\n+\n+ self.host_logger = self.host_dut.logger\n+ # base_dir existed for host dut has prepared it\n+ self.host_session.send_expect(\"cd %s\" % self.host_dut.base_dir, \"# \")\n+\n+ # init the host resource pool for VM\n+ self.virt_pool = self.host_dut.virt_pool\n+\n+ if not self.has_virtual_ability():\n+ if not self.enable_virtual_ability():\n+ raise Exception(\"Dut [ %s ] cannot have the virtual ability!!!\")\n+\n+ self.virt_type = self.get_virt_type()\n+\n+ self.params = []\n+ self.local_conf = []\n+\n+ # default call back function is None\n+ self.callback = None\n+\n+ # vm status is running by default, only be changed in internal module\n+ self.vm_status = ST_RUNNING\n+\n+ # by default no special kernel module is required\n+ self.def_driver = \"\"\n+ self.driver_mode = \"\"\n+\n+ def get_virt_type(self):\n+ \"\"\"\n+ Get the virtual type, such as KVM, XEN or LIBVIRT.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def has_virtual_ability(self):\n+ \"\"\"\n+ Check if the host have the ability of virtualization.\n+ \"\"\"\n+ NotImplemented\n+\n+ def enable_virtual_ability(self):\n+ \"\"\"\n+ Enable the virtual ability on the DUT.\n+ \"\"\"\n+ NotImplemented\n+\n+ def get_vm_login(self):\n+ \"\"\"\n+ Get VM credentials.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def add_vm_login(self):\n+ \"\"\"\n+ Add VM credentials.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def _attach_vm(self):\n+ \"\"\"\n+ Attach VM.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def _quick_start_vm(self):\n+ \"\"\"\n+ Quick start VM.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def load_global_config(self):\n+ \"\"\"\n+ Load global configure in the path CONFIG_ROOT_PATH.\n+ \"\"\"\n+ conf = VirtConf(VIRTCONF)\n+ conf.load_virt_config(self.virt_type)\n+ global_conf = conf.get_virt_config()\n+ for param in global_conf:\n+ for key in list(param.keys()):\n+ if self.find_option_index(key) is None:\n+ self.__save_local_config(key, param[key])\n+\n+ def set_local_config(self, local_conf):\n+ \"\"\"\n+ Configure VM configuration from user input\n+ \"\"\"\n+ self.local_conf = local_conf\n+\n+ def load_local_config(self, suite_name):\n+ \"\"\"\n+ Load local configure in the path CONFIG_ROOT_PATH ('DTS_ROOT_PATH/$DTS_CFG_FOLDER/' by default).\n+ \"\"\"\n+ # load local configuration by suite and vm name\n+ try:\n+ conf = VirtConf(CONFIG_ROOT_PATH + os.sep + suite_name + \".cfg\")\n+ conf.load_virt_config(self.vm_name)\n+ self.local_conf = conf.get_virt_config()\n+ except:\n+ # when met exception in load VM config\n+ # just leave local conf untouched\n+ pass\n+\n+ # replace global configurations with local configurations\n+ for param in self.local_conf:\n+ if \"virt_type\" in list(param.keys()):\n+ # param 'virt_type' is for virt_base only\n+ continue\n+ # save local configurations\n+ for key in list(param.keys()):\n+ self.__save_local_config(key, param[key])\n+\n+ def __save_local_config(self, key, value):\n+ \"\"\"\n+ Save the local config into the global dict self.param.\n+ \"\"\"\n+ for param in self.params:\n+ if key in list(param.keys()):\n+ param[key] = value\n+ return\n+\n+ self.params.append({key: value})\n+\n+ def compose_boot_param(self):\n+ \"\"\"\n+ Compose all boot param for starting the VM.\n+ \"\"\"\n+ for param in self.params:\n+ key = list(param.keys())[0]\n+ value = param[key]\n+ try:\n+ param_func = getattr(self, \"add_vm_\" + key)\n+ if callable(param_func):\n+ if type(value) is list:\n+ for option in value:\n+ param_func(**option)\n+ else:\n+ print(utils.RED(\"Virt %s function not callable!!!\" % key))\n+ except AttributeError:\n+ self.host_logger.error(traceback.print_exception(*sys.exc_info()))\n+ print(utils.RED(\"Virt %s function not implemented!!!\" % key))\n+ except Exception:\n+ self.host_logger.error(traceback.print_exception(*sys.exc_info()))\n+ raise exception.VirtConfigParamException(key)\n+\n+ def add_vm_def_driver(self, **options):\n+ \"\"\"\n+ Set default driver which may required when setup VM\n+ \"\"\"\n+ if \"driver_name\" in list(options.keys()):\n+ self.def_driver = options[\"driver_name\"]\n+ if \"driver_mode\" in list(options.keys()):\n+ self.driver_mode = options[\"driver_mode\"]\n+\n+ def find_option_index(self, option):\n+ \"\"\"\n+ Find the boot option in the params which is generated from\n+ the global and local configures, and this function will\n+ return the index by which option can be indexed in the\n+ param list.\n+ \"\"\"\n+ index = 0\n+ for param in self.params:\n+ key = list(param.keys())[0]\n+ if key.strip() == option.strip():\n+ return index\n+ index += 1\n+\n+ return None\n+\n+ def generate_unique_mac(self):\n+ \"\"\"\n+ Generate a unique MAC based on the DUT.\n+ \"\"\"\n+ mac_head = \"00:00:00:\"\n+ mac_tail = \":\".join(\n+ [\"%02x\" % x for x in map(lambda x: randint(0, 255), list(range(3)))]\n+ )\n+ return mac_head + mac_tail\n+\n+ def get_vm_ip(self):\n+ \"\"\"\n+ Get the VM IP.\n+ \"\"\"\n+ raise NotImplementedError\n+\n+ def get_pci_mappings(self):\n+ \"\"\"\n+ Get host and VM pass-through device mapping\n+ \"\"\"\n+ NotImplemented\n+\n+ def isalive(self):\n+ \"\"\"\n+ Check whether VM existed.\n+ \"\"\"\n+ vm_status = self.host_session.send_expect(\n+ \"ps aux | grep qemu | grep 'name %s '| grep -v grep\" % self.vm_name, \"# \"\n+ )\n+\n+ if self.vm_name in vm_status:\n+ return True\n+ else:\n+ return False\n+\n+ def load_config(self):\n+ \"\"\"\n+ Load configurations for VM\n+ \"\"\"\n+ # load global and suite configuration file\n+ self.load_global_config()\n+ self.load_local_config(self.suite)\n+\n+ def attach(self):\n+ # load configuration\n+ self.load_config()\n+\n+ # change login user/password\n+ index = self.find_option_index(\"login\")\n+ if index:\n+ value = self.params[index][\"login\"]\n+ for option in value:\n+ self.add_vm_login(**option)\n+\n+ # attach real vm\n+ self._attach_vm()\n+ return None\n+\n+ def start(self, load_config=True, set_target=True, cpu_topo=\"\", bind_dev=True):\n+ \"\"\"\n+ Start VM and instantiate the VM with VirtDut.\n+ \"\"\"\n+ try:\n+ if load_config is True:\n+ self.load_config()\n+ # compose boot command for different hypervisors\n+ self.compose_boot_param()\n+\n+ # start virtual machine\n+ self._start_vm()\n+\n+ if self.vm_status is ST_RUNNING:\n+ # connect vm dut and init running environment\n+ vm_dut = self.instantiate_vm_dut(\n+ set_target, cpu_topo, bind_dev=bind_dev, autodetect_topo=True\n+ )\n+ else:\n+ vm_dut = None\n+\n+ except Exception as vm_except:\n+ if self.handle_exception(vm_except):\n+ print(utils.RED(\"Handled exception \" + str(type(vm_except))))\n+ else:\n+ print(utils.RED(\"Unhandled exception \" + str(type(vm_except))))\n+\n+ if callable(self.callback):\n+ self.callback()\n+\n+ return None\n+ return vm_dut\n+\n+ def quick_start(self, load_config=True, set_target=True, cpu_topo=\"\"):\n+ \"\"\"\n+ Only Start VM and not do anything else, will be helpful in multiple VMs\n+ \"\"\"\n+ try:\n+ if load_config is True:\n+ self.load_config()\n+ # compose boot command for different hypervisors\n+ self.compose_boot_param()\n+\n+ # start virtual machine\n+ self._quick_start_vm()\n+\n+ except Exception as vm_except:\n+ if self.handle_exception(vm_except):\n+ print(utils.RED(\"Handled exception \" + str(type(vm_except))))\n+ else:\n+ print(utils.RED(\"Unhandled exception \" + str(type(vm_except))))\n+\n+ if callable(self.callback):\n+ self.callback()\n+\n+ def migrated_start(self, set_target=True, cpu_topo=\"\"):\n+ \"\"\"\n+ Instantiate the VM after migration done\n+ There's no need to load param and start VM because VM has been started\n+ \"\"\"\n+ try:\n+ if self.vm_status is ST_PAUSE:\n+ # flag current vm is migration vm\n+ self.migration_vm = True\n+ # connect backup vm dut and it just inherited from host\n+ vm_dut = self.instantiate_vm_dut(\n+ set_target, cpu_topo, bind_dev=False, autodetect_topo=False\n+ )\n+ except Exception as vm_except:\n+ if self.handle_exception(vm_except):\n+ print(utils.RED(\"Handled exception \" + str(type(vm_except))))\n+ else:\n+ print(utils.RED(\"Unhandled exception \" + str(type(vm_except))))\n+\n+ return None\n+\n+ return vm_dut\n+\n+ def handle_exception(self, vm_except):\n+ # show exception back trace\n+ exc_type, exc_value, exc_traceback = sys.exc_info()\n+ traceback.print_exception(\n+ exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout\n+ )\n+ if type(vm_except) is exception.ConfigParseException:\n+ # nothing to handle just return True\n+ return True\n+ elif type(vm_except) is exception.VirtConfigParseException:\n+ # nothing to handle just return True\n+ return True\n+ elif type(vm_except) is exception.VirtConfigParamException:\n+ # nothing to handle just return True\n+ return True\n+ elif type(vm_except) is exception.StartVMFailedException:\n+ # start vm failure\n+ return True\n+ elif type(vm_except) is exception.VirtDutConnectException:\n+ # need stop vm\n+ self._stop_vm()\n+ return True\n+ elif type(vm_except) is exception.VirtDutInitException:\n+ # need close session\n+ vm_except.vm_dut.close()\n+ # need stop vm\n+ self.stop()\n+ return True\n+ else:\n+ return False\n+\n+ def _start_vm(self):\n+ \"\"\"\n+ Start VM.\n+ \"\"\"\n+ NotImplemented\n+\n+ def _stop_vm(self):\n+ \"\"\"\n+ Stop VM.\n+ \"\"\"\n+ NotImplemented\n+\n+ def get_vm_img(self):\n+ \"\"\"\n+ get current vm img name from params\n+ get format like: 10.67.110.11:TestVhostMultiQueueQemu:/home/img/Ub1604.img\n+ \"\"\"\n+ param_len = len(self.params)\n+ for i in range(param_len):\n+ if \"disk\" in list(self.params[i].keys()):\n+ value = self.params[i][\"disk\"][0]\n+ if \"file\" in list(value.keys()):\n+ host_ip = self.host_dut.get_ip_address()\n+ return (\n+ host_ip\n+ + \":\"\n+ + self.host_dut.test_classname\n+ + \":\"\n+ + value[\"file\"]\n+ )\n+ return None\n+\n+ def instantiate_vm_dut(\n+ self, set_target=True, cpu_topo=\"\", bind_dev=True, autodetect_topo=True\n+ ):\n+ \"\"\"\n+ Instantiate the Dut class for VM.\n+ \"\"\"\n+ crb = self.host_dut.crb.copy()\n+ crb[\"bypass core0\"] = False\n+ vm_ip = self.get_vm_ip()\n+ crb[\"IP\"] = vm_ip\n+ crb[\"My IP\"] = vm_ip\n+ username, password = self.get_vm_login()\n+ crb[\"user\"] = username\n+ crb[\"pass\"] = password\n+\n+ serializer = self.host_dut.serializer\n+\n+ try:\n+ vm_dut = VirtDut(\n+ self,\n+ crb,\n+ serializer,\n+ self.virt_type,\n+ self.vm_name,\n+ self.suite,\n+ cpu_topo,\n+ dut_id=self.host_dut.dut_id,\n+ )\n+ except Exception as vm_except:\n+ self.handle_exception(vm_except)\n+ raise exception.VirtDutConnectException\n+ return None\n+\n+ vm_dut.nic_type = \"any\"\n+ vm_dut.tester = self.host_dut.tester\n+ vm_dut.host_dut = self.host_dut\n+ vm_dut.host_session = self.host_session\n+ vm_dut.init_log()\n+ vm_dut.migration_vm = self.migration_vm\n+\n+ read_cache = False\n+ skip_setup = self.host_dut.skip_setup\n+ vm_img = self.get_vm_img()\n+ # if current vm is migration vm, skip compile dpdk\n+ # if VM_IMG_list include the vm_img, it means the vm have complie the dpdk ok, skip it\n+ if self.migration_vm or vm_img in VM_IMG_LIST:\n+ skip_setup = True\n+ base_dir = self.host_dut.base_dir\n+ vm_dut.set_speedup_options(read_cache, skip_setup)\n+\n+ # package and patch should be set before prerequisites\n+ vm_dut.set_package(self.host_dut.package, self.host_dut.patches)\n+\n+ # base_dir should be set before prerequisites\n+ vm_dut.set_directory(base_dir)\n+\n+ try:\n+ # setting up dpdk in vm, must call at last\n+ vm_dut.target = self.host_dut.target\n+ vm_dut.prerequisites(\n+ self.host_dut.package, self.host_dut.patches, autodetect_topo\n+ )\n+ if set_target:\n+ target = self.host_dut.target\n+ vm_dut.set_target(target, bind_dev, self.def_driver, self.driver_mode)\n+ except:\n+ raise exception.VirtDutInitException(vm_dut)\n+ return None\n+\n+ # after prerequisites and set_target, the dpdk compile is ok, add this vm img to list\n+ if vm_img not in VM_IMG_LIST:\n+ mutex_vm_list.acquire()\n+ VM_IMG_LIST.append(vm_img)\n+ mutex_vm_list.release()\n+\n+ self.vm_dut = vm_dut\n+ return vm_dut\n+\n+ def stop(self):\n+ \"\"\"\n+ Stop the VM.\n+ \"\"\"\n+ self._stop_vm()\n+ self.quit()\n+\n+ self.virt_pool.free_all_resource(self.vm_name)\n+\n+ def quit(self):\n+ \"\"\"\n+ Just quit connection to the VM\n+ \"\"\"\n+ if getattr(self, \"host_session\", None):\n+ self.host_session.close()\n+ self.host_session = None\n+\n+ # vm_dut may not init in migration case\n+ if getattr(self, \"vm_dut\", None):\n+ if self.vm_status is ST_RUNNING:\n+ self.vm_dut.close()\n+ else:\n+ # when vm is not running, not close session forcely\n+ self.vm_dut.close(force=True)\n+\n+ self.vm_dut.logger.logger_exit()\n+ self.vm_dut = None\n+\n+ def register_exit_callback(self, callback):\n+ \"\"\"\n+ Call register exit call back function\n+ \"\"\"\n+ self.callback = callback\n", "prefixes": [ "RFC", "v1", "08/23" ] }{ "id": 109307, "url": "