diff --git a/virttest/qemu_devices/qdevice_format.py b/virttest/qemu_devices/qdevice_format.py new file mode 100644 index 0000000000..fdeec85c8d --- /dev/null +++ b/virttest/qemu_devices/qdevice_format.py @@ -0,0 +1,549 @@ +import logging +import re + +from avocado.utils import process + +from virttest import utils_numeric + +LOG = logging.getLogger("avocado." + __name__) + + +class _QDeviceFormatManagement(object): + """ + This class is designed to manage the expression( including json format + and raw format) of classes in qdevices.py file. + """ + + def __init__(self): + self._format_func = {"json": self._json_format, "raw": self._raw_format} + # _special_args_in_json stores the common qemu device type. + # For examples: + # -blockdev xxx,xxx + # -device xxx,xxx + # -object xxx,xxx + # Those qemu device types except -device have the following structure: + # "${qemu_device_type}": { + # "${argument#01}": ${type_in_json_format}, + # "${argument#02}": ${type_in_json_format}, + # ... + # } + # The -device has the following structure: + # "${qemu_device_type}": { + # "${driver#01}": { + # "${argument#01}": ${type_in_json_format}, + # "${argument#02}": ${type_in_json_format}, + # ... + # }, + # "${driver#02}": { + # "${argument#01}": ${type_in_json_format}, + # "${argument#02}": ${type_in_json_format}, + # ... + # }, + # ... + # } + # NOTE: + # 1. The records are from collections at qdevices.py + # 2. For arguments type conversion from string( on/off, yes/on, + # true/false ) to bool( True/False ), they are NOT listed here + # since they are too many. + self._special_args_in_json = { + "blockdev": { + "detect-zeroes": self._unchanged, + "offset": self._str_to_dec, + "size": self._str_to_dec, + "cache-size": self._str_to_dec, + "timeout": self._str_to_dec, + }, + "device": { + # "general" is NOT a kind of driver. + # The arguments in "general" are needed by + # at least 2 drivers. + "general": { + "write-cache": self._unchanged, + "disable-legacy": self._unchanged, + "intremap": self._unchanged, + "serial": self._unchanged, + "eim": self._unchanged, + "wwn": self._hex_in_str_to_dec, + }, + "virtio-scsi-pci": { + "virtqueue_size": self._str_to_dec, + "num_queues": self._str_to_dec, + "max_sectors": self._str_to_dec, + }, + "virtio-rng-pci": { + "period": self._str_to_dec, + "max-bytes": self._str_to_dec, + }, + "scsi-hd": { + "discard_granularity": self._str_to_dec, + "physical_block_size": self._str_to_dec, + "logical_block_size": self._str_to_dec, + "bootindex": self._str_to_dec, + }, + "virtio-blk-pci": { + "max-write-zeroes-sectors": self._str_to_dec, + "queue-size": self._str_to_dec, + "max-discard-sectors": self._str_to_dec, + "num-queues": self._str_to_dec, + "discard_granularity": self._str_to_dec, + }, + "usb_driver": { + "port": self._str, + "serial": self._on, + # min_io_size, opt_io_size from device ("driver": "usb-storage") + "min_io_size": self._str_to_dec, + "opt_io_size": self._str_to_dec, + }, + "pcie-root-port": { + "port": self._hex_in_str_to_dec, + }, + "nvdimm": { + "label-size": self._normalize_data_size, + }, + "pc-dimm": { + "node": self._str_to_dec, + }, + "virtio-mem-pci": { + "requested-size": self._normalize_data_size, + }, + "intel-iommu": { + "aw-bits": self._str_to_dec, + }, + "virtio-net-pci": { + "acpi-index": self._str_to_dec, + "host_mtu": self._str_to_dec, + "speed": self._str_to_dec, + "vectors": self._str_to_dec, + }, + "virtio-balloon-ccw": { + "guest-stats-polling-interval": self._str_to_dec, + }, + "pvpanic": { + "events": self._str_to_dec, + }, + }, + "object": { + "size": self._normalize_data_size, + "align": self._normalize_data_size, + "host-nodes": self._int_in_list, + "prealloc-threads": self._str_to_dec, + }, + "netdev": { + "fd": self._unchanged, + "vhostfd": self._unchanged, + "dnssearch": self._dict_in_list, + "hostfwd": self._dict_in_list, + "guestfwd": self._dict_in_list, + "sndbuf": self._normalize_data_size, + }, + } + self._device_driver_checked = [] + # _skip_args stores those arguments which are NOT expected to be + # converted while updating the arguments type from qemu on machine. + # The reason is here: + # About "addr" in qemu output, + # addr= - Slot and optional function number, example: 06.0 or 06 (default: -1) + # In fact: The "addr" is accepted by hexadecimal in string instead of + # hexadecimal in int. + # The following is qemu output: + # qemu-kvm: -device {"id": "pcie-root-port-0", "driver": "pcie-root-port", + # "multifunction": true, "bus": "pcie.0", "addr": 1, + # "chassis": 1}: PCI: single function device can't be populated in function 0.1 + self._skip_args = ("addr",) + # _mandatory_assignment_args_type stores those arguments whose types + # are assigned. The _mandatory_assignment_args_type structure is as + # same as the _special_args_in_json structure. + self._mandatory_assignment_args_type = { + "device": { + "pvpanic": { + # About "ioport" in qemu output: + # ioport= - (default: 1285) + # In fact: The "ioport" is accepted by function + # _hex_in_str_to_dec instead of function _str_to_dec. + "ioport": self._hex_in_str_to_dec, + }, + "vhost-vsock-pci": { + # About "guest-cid" in qemu output: + # guest-cid= - (default: 0) + # In fact: The "guest-cid" is accepted by function + # _str_to_dec instead of function _hex_in_str_to_dec. + "guest-cid": self._str_to_dec, + }, + } + } + self._type_func_mapping = { + "": self._str_to_dec, + "": self._str_to_dec, + "": self._str_to_dec, + "": self._str_to_dec, + "": self._bool_in_string_to_bool, + "": self._hex_in_str_to_dec, + "": self._hex_in_str_to_dec, + "": self._str, + } + + def format(self, format_type, params, dev_type): + """ + Convert the params format based on format_type and dev_type. + + :param format_type: The expected format. + :type format_type: String. + :param params: The params. + :type params: Dict. + :param dev_type: The device type. + :type dev_type: String. + + :return: The params in the expected format. + :rtype: Dict. + """ + return self._format_func[format_type](params, dev_type) + + def _json_format(self, params, dev_type): + """ + Convert the params to json format based on dev_type. + + :param params: The params. + :type params: Dict. + :param dev_type: The device type. + :type dev_type: String. + + :return: The params in the json format. + :rtype: Dict. + """ + return eval("self._" + dev_type + "_json_format")(params) + + def _raw_format(self, params, dev_type): + """ + Convert the params to raw format based on dev_type. + + :param params: The params. + :type params: Dict. + :param dev_type: The device type. + :type dev_type: String. + + :return: The params in the raw format. + :rtype: Dict. + """ + # TODO: + # Implement this function and replace the related functions in + # qdevices.py file + pass + + def _netdev_json_format(self, params): + """ + Convert the params to json format based on the netdev. + + :param params: The params. + :type params: Dict. + + :return: The params in the json format. + :rtype: Dict. + """ + dev_type = "netdev" + args_in_json = self._special_args_in_json[dev_type] + new_args = dict() + for key, value in params.items(): + if key in args_in_json.keys(): + new_args[key] = args_in_json[key](value) + elif isinstance(value, str) and value.isdigit(): + new_args[key] = int(value) + else: + new_args[key] = self._bool_in_string_to_bool(value) + + if "." in key: + subs = key.split(".") + curr = new_args + for subk in subs[:-1]: + try: + int(subk) + subv = list() + except ValueError: + subv = dict() + curr.setdefault(subk, subv) + curr = curr[subk] + curr[subs[-1]] = value + + return new_args + + def _object_json_format(self, params): + """ + Convert the params to json format based on the object. + + :param params: The params. + :type params: Dict. + + :return: The params in the json format. + :rtype: Dict. + """ + dev_type = "object" + if "backend" in params: + params["qom-type"] = params.pop("backend") + args_in_json = self._special_args_in_json[dev_type] + new_args = dict() + for key, value in params.items(): + if key in args_in_json.keys(): + new_args[key] = args_in_json[key](value) + else: + new_args[key] = self._bool_in_string_to_bool(value) + + return new_args + + def _device_json_format(self, params): + """ + Convert the params to json format based on the device. + + :param params: The params. + :type params: Dict. + + :return: The params in the json format. + :rtype: Dict. + """ + dev_type = "device" + driver = params.get("driver") + driver = "usb_driver" if driver.startswith("usb-") else driver + if driver not in self._device_driver_checked: + self._device_driver_checked.append(driver) + self._update_args_type_from_qemu(driver) + device_args = self._special_args_in_json[dev_type] + new_args = dict() + # convert type + for key, value in params.items(): + if key in device_args[driver]: + value = device_args[driver][key](value) + new_args[key] = value + if key in device_args["general"]: + new_args[key] = device_args["general"][key](value) + else: + new_args[key] = self._bool_in_string_to_bool(value) + + return new_args + + def _blockdev_json_format(self, params): + """ + Convert the params to json format based on the blockdev. + + :param params: The params. + :type params: Dict. + + :return: The params in the json format. + :rtype: Dict. + """ + dev_type = "blockdev" + args_in_json = self._special_args_in_json[dev_type] + new_args = dict() + for key, value in params.items(): + new_args[key] = ( + args_in_json[key](value) + if key in args_in_json.keys() + else self._bool_in_string_to_bool(value) + ) + new_args = self._flat_to_dict(key, new_args) + + return new_args + + @staticmethod + def _unchanged(val): + """ + Do NOT change anything, just return the val. + + :param val: The value. + :type val: Any + + :return: The value. + :rtype: Any + """ + return val + + @staticmethod + def _str_to_dec(val): + """ + Convert decimal in string to int. + + :param val: The value. + :type val: String + + :return: The value. + :rtype: int + """ + return int(val) + + @staticmethod + def _str(val): + """ + Convert any type to string. + + :param val: The value. + :type val: Any + + :return: The value. + :rtype: String + """ + return str(val) + + @staticmethod + def _on(val): + """ + Convert "NO_EQUAL_STRING" to "on". + + :param val: The value. + :type val: String + + :return: The value. + :rtype: String + """ + return "on" if val == "NO_EQUAL_STRING" else val + + @staticmethod + def _hex_in_str_to_dec(val): + """ + Convert hexadecimal in string to int. + + :param val: The value. + :type val: String + + :return: The value. + :rtype: int + """ + if isinstance(val, str) and val.startswith("0x"): + val = val[2:] + return int(val, 16) + + @staticmethod + def _normalize_data_size(val): + """ + Normalize a data size based on the magnitude bytes. + + :param val: The value. + :type val: String + + :return: The value. + :rtype: int + """ + if isinstance(val, str): + return int(utils_numeric.normalize_data_size(val, "B")) + return val + + @staticmethod + def _int_in_list(val): + """ + Converted string to int in list. + + :param val: The value. + :type val: String + + :return: The value. + :rtype: list + """ + return list(map(int, val.split())) + + @staticmethod + def _dict_in_list(val): + """ + Converted list to dict in list. + + :param val: The value. + :type val: list + + :return: The value. + :rtype: list + """ + if isinstance(val, list): + return [{"str": v} for v in val] + return val + + @staticmethod + def _bool_in_string_to_bool(val): + """ + Convert the "on""off""yes""no""true""false" to boolean. + Note: If the val is a string except "on""off""yes""no""true""false", + just return val without any operations. + + :param val: The value. + :type val: String. + + :return: The value converted or original val. + :rtype: Boolean or the original type. + """ + if isinstance(val, str) and val.lower() in ( + "on", + "off", + "yes", + "no", + "true", + "false", + ): + return val in ( + "on", + "yes", + "true", + ) + return val + + @staticmethod + def _flat_to_dict(key, args): + """ + Convert the flat expression to multi-level expression. + + :param key: The flat key. + :type key: String. + :param args: The structure including flat key. + :type args: Dict. + + :return: The structure converted. + :rtype: Dict. + """ + val = args[key] + parts = key.split(".") + if parts: + args.pop(key) + p = re.compile(r"server\.(?P\d+)\.(?P.+)") + m = p.match(key) + if m: + # convert 'server.0.host', 'server.1.host' + # to {'server': [{'host':xx}, {'host':xx}]} + servers = args["server"] if "server" in args else [] + servers.append({m.group("opt"): val}) + args["server"] = servers.sort(key=lambda k: k[1]) + else: + # convert 'cache.direct': 'true' to {'cache': {'direct': 'true'}} + tmp = args + for part in parts[:-1]: + if part not in tmp: + tmp[part] = dict() + tmp = tmp[part] + tmp[parts[-1]] = val + + return args + + def _update_args_type_from_qemu(self, driver): + """ + Update the args type from qemu. + Only update the following type: int16, int32, int64, bool, str. + + :param driver: The driver of -device. + :type driver: String. + """ + if driver not in self._special_args_in_json["device"]: + self._special_args_in_json["device"][driver] = dict() + cmd = "/usr/libexec/qemu-kvm --device %s,\\?" % driver + output = process.run(cmd, shell=True, verbose=False).stdout_text.strip() + args_list = re.findall("(.+)=(<[^>]+>+)", output) + for arg, arg_type in args_list: + arg = arg.strip() + if arg in self._skip_args: + continue + arg_type = arg_type.strip() + if driver not in self._mandatory_assignment_args_type["device"]: + self._mandatory_assignment_args_type["device"][driver] = dict() + if arg in self._mandatory_assignment_args_type["device"][driver]: + self._special_args_in_json["device"][driver][ + arg + ] = self._mandatory_assignment_args_type["device"][driver][arg] + elif arg_type in self._type_func_mapping: + self._special_args_in_json["device"][driver][ + arg + ] = self._type_func_mapping[arg_type] + + +qdevice_format = _QDeviceFormatManagement() diff --git a/virttest/qemu_devices/qdevices.py b/virttest/qemu_devices/qdevices.py index 1df7b963fa..205d0d5e5c 100644 --- a/virttest/qemu_devices/qdevices.py +++ b/virttest/qemu_devices/qdevices.py @@ -24,10 +24,12 @@ from avocado.utils import process from six.moves import xrange -from virttest import qemu_monitor, utils_logfile, utils_misc, utils_numeric +from virttest import qemu_monitor, utils_logfile, utils_misc from virttest.qemu_devices.utils import DeviceError, none_or_int from virttest.utils_version import VersionInterval +from .qdevice_format import qdevice_format + LOG = logging.getLogger("avocado." + __name__) @@ -779,41 +781,6 @@ def clear_child_nodes(self): """Delete all child blockdev nodes.""" self._child_nodes.clear() - @staticmethod - def _convert_blkdev_args(args): - """ - Convert string type of 'on' and 'off' to boolean, and create new dict - (e.g: 'cache': {'direct': 'true'}) from key which include symbol '.' - (e.g: 'cache.direct': 'true') to adhere to the blockdev qmp syntax. - - :param args: Dictionary with the qmp parameters. - :type args: dict - :return: Converted args. - :rtype: dict - """ - new_args = dict() - keep_original_type = ("detect-zeroes",) - int_opts = ("offset", "size") - for key, value in six.iteritems(args): - if key not in keep_original_type: - if value in ("on", "yes"): - value = True - elif value in ("off", "no"): - value = False - - if key in int_opts: - value = int(value) - - parts = key.split(".") - d = new_args - for part in parts[:-1]: - if part not in d: - d[part] = dict() - d = d[part] - d[parts[-1]] = value - - return new_args - def hotplug_qmp(self): """ Hot plug this blockdev node by qmp. @@ -821,7 +788,7 @@ def hotplug_qmp(self): :return: Hot plug qemu command and arguments. :rtype: tuple """ - return "blockdev-add", self._convert_blkdev_args(self.params) + return "blockdev-add", qdevice_format.format("json", self.params, self.type) def unplug_qmp(self): """ @@ -907,7 +874,7 @@ def get_qid(self): def _cmdline_json(self): params = self.params.copy() out = "-%s " % self.type - new_args = self._convert_blkdev_args(params) + new_args = qdevice_format.format(self.cmdline_format, params, self.type) return out + "'" + json.dumps(new_args) + "'" def get_children(self): @@ -948,13 +915,6 @@ class QBlockdevFormatQcow2(QBlockdevFormatNode): TYPE = "qcow2" - def _convert_blkdev_args(self, args): - for key, val in args.items(): - # "cache-size" is from device( "driver": "qcow2" ) - if key == "cache-size": - args[key] = int(val) - return super(QBlockdevFormatQcow2, self)._convert_blkdev_args(args) - class QBlockdevFormatRaw(QBlockdevFormatNode): """New a format raw blockdev node.""" @@ -1046,28 +1006,6 @@ class QBlockdevProtocolGluster(QBlockdevProtocol): TYPE = "gluster" - def hotplug_qmp(self): - # TODO: design a new _convert_blkdev_args to handle list - # of dicts, e.g. convert 'server.0.host', 'server.1.host' - # to {'server': [{'host':xx}, {'host':xx}]} - servers = {} - args = OrderedDict() - p = re.compile(r"server\.(?P\d+)\.(?P.+)") - - for key, value in six.iteritems(self.params): - m = p.match(key) - if m is not None: - index = int(m.group("index")) - servers.setdefault(index, {}) - servers[index].update({m.group("opt"): value}) - else: - args[key] = value - - params = self._convert_blkdev_args(args) - params["server"] = [servers[i] for i in sorted(servers)] - - return "blockdev-add", params - class QBlockdevProtocolNBD(QBlockdevProtocol): """New a protocol nbd blockdev node.""" @@ -1098,12 +1036,6 @@ class QBlockdevProtocolHTTPS(QBlockdevProtocol): TYPE = "https" - def _convert_blkdev_args(self, args): - for key, val in args.items(): - if key == "timeout": - args[key] = int(val) - return super(QBlockdevProtocolHTTPS, self)._convert_blkdev_args(args) - class QBlockdevProtocolFTP(QBlockdevProtocol): """New a protocol ftp blockdev node.""" @@ -1152,7 +1084,7 @@ def hotplug_hmp(self): def hotplug_qmp(self): """:return: the hotplug monitor command""" - return "device_add", self.params + return "device_add", qdevice_format.format("json", self.params, "device") def hotplug_hmp_nd(self): """:return: the hotplug monitor command without dynamic parameters""" @@ -1214,97 +1146,10 @@ def verify_hotplug(self, out, monitor): return False def _cmdline_json(self): - command_dict = {} out = "-%s " % self.type - - usb_driver = self.get_param("driver", "").startswith("usb-") - - pcic = self.get_param("driver") in ("pcie-root-port") - - pvpanic = self.get_param("driver") in ("pvpanic") - - expect_string_val = ( - "write-cache", - "disable-legacy", - "intremap", - "serial", - "eim", + command_dict = qdevice_format.format( + self.cmdline_format, self.params, self.type ) - - for key, val in self.params.items(): - # wwn needs to be presented as hexadecimal - # port from device ( "driver": "pcie-root-port" ) - if ( - key in ("wwn") - or (key == "port" and pcic) - or (key == "ioport" and pvpanic) - ): - command_dict[key] = int(val, 16) - # physical_block_size from device ("driver": "scsi-hd") - # logical_block_size from device ("driver": "scsi-hd") - # bootindex from device ("driver": "scsi-hd") - # max_sectors from device ("driver": "virtio-scsi-pci") - # num_queues from device ("driver": "virtio-scsi-pci") - # virtqueue_size from device ("driver": "virtio-scsi-pci") - # period, max-bytes from device ("driver": "virtio-rng-pci") - # max-write-zeroes-sectors, queue-size, max-discard-sectors, - # num-queues from device ("driver": "virtio-blk-pci") - # host_mtu, speed, vectors from - # device ( "driver": "virtio-net-pci" ) - # node from device ("driver": "pc-dimm") - # events from device("driver": "pvpanic") - # min_io_size, opt_io_size from device ( "driver": "usb-storage" ) - # discard_granularity from device ("driver": "scsi-hd") and - # ("driver": "virtio-blk-pci") - # guest-stats-polling-interval from - # device ("driver": "virtio-balloon-ccw") - # acpi-index from device("driver": "virtio-net-pci") - # aw-bits from device("driver": "intel-iommu") - elif key in ( - "physical_block_size", - "logical_block_size", - "bootindex", - "max_sectors", - "num_queues", - "virtqueue_size", - "discard_granularity", - "period", - "max-bytes", - "max-write-zeroes-sectors", - "queue-size", - "max-discard-sectors", - "num-queues", - "host_mtu", - "speed", - "vectors", - "node", - "events", - "min_io_size", - "opt_io_size", - "guest-stats-polling-interval", - "acpi-index", - "aw-bits", - ): - command_dict[key] = int(val) - # port from usb related driver - elif key == "port" and usb_driver: - command_dict[key] = str(val) - elif val == "NO_EQUAL_STRING": - if usb_driver and key == "serial": - command_dict[key] = "on" - # disable-legacy from device ("driver": "virtio-scsi-pci") - # write-cache from device ("driver": "scsi-hd") - elif val in ("on", "yes", "true") and key not in expect_string_val: - command_dict[key] = True - elif val in ("off", "no", "false") and key not in expect_string_val: - command_dict[key] = False - # requested-size from device("driver": "virtio-mem-pci") - # label-size from device("driver": "nvdimm") - elif key in ("requested-size", "label-size"): - command_dict[key] = int(utils_numeric.normalize_data_size(val, "B")) - else: - command_dict[key] = val - return out + "'" + json.dumps(command_dict) + "'" @@ -1397,14 +1242,6 @@ def hotplug_hmp(self): out = "object_add %s" % _convert_args(self.params) return out - def _refresh_hotplug_props(self, params): - """ - Refresh hotplug optional props as per params. - - :return: A dict containing hotplug optional props. - """ - return params - def _hotplug_qmp_mapping(self, qemu_version): return ( self.hotplug_qmp_lt_600 @@ -1415,16 +1252,7 @@ def _hotplug_qmp_mapping(self, qemu_version): def hotplug_qmp(self): """:return: the object-add command (since 6.0.0)""" params = self.params.copy() - - # qom-type and id are mandatory - kwargs = {"qom-type": params.pop("backend"), "id": params.pop("id")} - - # optional params - params = self._refresh_hotplug_props(params) - if len(params) > 0: - kwargs.update(params) - - return "object-add", kwargs + return "object-add", qdevice_format.format("json", params, self.type) def hotplug_qmp_lt_600(self): """:return: the object-add command (before 6.0.0)""" @@ -1434,11 +1262,10 @@ def hotplug_qmp_lt_600(self): kwargs = {"qom-type": params.pop("backend"), "id": params.pop("id")} # props is optional - params = self._refresh_hotplug_props(params) if len(params) > 0: kwargs["props"] = params - return "object-add", kwargs + return "object-add", qdevice_format.format("json", kwargs, self.type) def hotplug_hmp_nd(self): """:return: the hotplug monitor command without dynamic parameters""" @@ -1462,7 +1289,7 @@ def hotplug_qmp_nd(self): params = self.params.copy() for key in self.dynamic_params: params[key] = "DYN" - return "object-add", params + return "object-add", qdevice_format.format("json", params, self.type) def unplug_hmp(self): """:return: the unplug monitor command""" @@ -1486,11 +1313,10 @@ def verify_hotplug(self, out, monitor): return len(out) == 0 def _cmdline_json(self): - command_dict = {} out = "-%s " % self.type params = self.params.copy() - command_dict["qom-type"] = params.pop("backend") - return out + "'" + json.dumps(dict(command_dict, **params)) + "'" + command_dict = qdevice_format.format(self.cmdline_format, params, self.type) + return out + "'" + json.dumps(command_dict) + "'" class QIOThread(QObject): @@ -1591,9 +1417,10 @@ def raw_limits(self, props): """Update raw throttle group properties.""" self._raw_limits.update(props) - def _refresh_hotplug_props(self, params): + def hotplug_qmp(self): + params = self.params.copy() params["limits"] = self.raw_limits - return params + return "object-add", qdevice_format.format("json", params, "object") def _query(self, monitor): """Check if throttle is in use by QEMU.""" @@ -1694,20 +1521,6 @@ class Memory(QObject): def __init__(self, backend, params=None): super(Memory, self).__init__(backend, params) - def _refresh_hotplug_props(self, params): - convert_size = utils_misc.normalize_data_size - args = (params["size"], "B", 1024) - params["size"] = int(float(convert_size(*args))) - if params.get("prealloc-threads"): - params["prealloc-threads"] = int(params["prealloc-threads"]) - if params.get("host-nodes"): - host_nodes = list(map(int, params["host-nodes"].split())) - params["host-nodes"] = host_nodes - for k in params: - params[k] = True if params[k] == "yes" else params[k] - params[k] = False if params[k] == "no" else params[k] - return params - def verify_unplug(self, out, monitor): """ :param out: Output of the unplug command @@ -1756,50 +1569,9 @@ def verify_hotplug(self, out, monitor): def _cmdline_json(self): out = "-%s " % self.type params = self.params.copy() - params["qom-type"] = params.pop("backend") - params = self._convert_memobj_args(params) + params = qdevice_format.format(self.cmdline_format, params, self.type) return out + "'" + json.dumps(params) + "'" - @staticmethod - def _convert_memobj_args(args): - """ - Type convert, such as string to uint64( "size": "14336M" to - "size": 15032385536 (bytes) ) - - :param args: Dictionary with the qmp parameters. - :type args: dict - :return: Converted args. - :rtype: dict - """ - command_dict = {} - for key, val in args.items(): - if key in ("size", "align"): - command_dict[key] = int(utils_numeric.normalize_data_size(val, "B")) - # "share", "reserve", "hugetlb" - # from object( "qom-type": "memory-backend-memfd" ) - # "prealloc", "dump", "merge" - # from -object ("qom-type": "memory-backend-ram") - # readonly from -object("qom-type": "memory-backend-file") - # pmem from -object("qom-type": "memory-backend-file") - # discard-data from object("qom-type": "memory-backend-file") - elif key in ( - "share", - "reserve", - "hugetlb", - "pmem", - "prealloc", - "dump", - "merge", - "readonly", - "discard-data", - ): - command_dict[key] = val in ("yes", "on") - elif key == "host-nodes": - command_dict[key] = list(map(int, val.split())) - else: - command_dict[key] = val - return command_dict - class Dimm(QDevice): """ @@ -2550,51 +2322,9 @@ def _cmdline_raw(self): def _cmdline_json(self): out = "-%s " % self.type params = self.params.copy() - params = self._convert_netdev_args(params) + params = qdevice_format.format(self.cmdline_format, params, self.type) return out + f" '{json.dumps(params)}'" - @staticmethod - def _convert_netdev_args(args): - """ - Convert string type of 'on' and 'off' to boolean, and create new dict - - :param args: Dictionary with the qmp parameters. - :type args: dict - :return: Converted args. - :rtype: dict - """ - new_args = dict() - keep_original_type = ("fd", "vhostfd") - for key, value in args.items(): - if key not in keep_original_type: - if key in ["dnssearch", "hostfwd", "guestfwd"] and isinstance( - value, list - ): - value = [{"str": v} for v in value] - # https://gitlab.com/qemu-project/qemu/-/blob/master/qapi/net.json#L242 - elif key in ("sndbuf",): - value = int(utils_numeric.normalize_data_size(value, "B")) - elif isinstance(value, str) and value.isdigit(): - value = int(value) - elif value in ("on", "yes", "true"): - value = True - elif value in ("off", "no", "false"): - value = False - - subs = key.split(".") - curr = new_args - for subk in subs[:-1]: - try: - int(subk) - subv = list() - except ValueError: - subv = dict() - curr.setdefault(subk, subv) - curr = curr[subk] - curr[subs[-1]] = value - - return new_args - def hotplug_hmp(self): """:return: the hotplug monitor command""" params = self.params.copy() @@ -2609,7 +2339,7 @@ def hotplug_hmp(self): def hotplug_qmp(self): """:return: the hotplug monitor command""" - return "netdev_add", self._convert_netdev_args(self.params) + return "netdev_add", qdevice_format.format("json", self.params, self.type) def hotplug_hmp_nd(self): """:return: the hotplug monitor command without dynamic parameters""" @@ -2630,7 +2360,7 @@ def hotplug_qmp_nd(self): params = self.params.copy() for key in self.dynamic_params: params[key] = "DYN" - return "netdev_add", self._convert_netdev_args(params) + return "netdev_add", qdevice_format.format("json", params, self.type) def unplug_hmp(self): """:return: the unplug monitor command"""