diff --git a/tests/xmlconfig-xml/boot-many-disks2.xml b/tests/xmlconfig-xml/boot-many-disks2.xml index e218dc6c3..3dabf3cdd 100644 --- a/tests/xmlconfig-xml/boot-many-disks2.xml +++ b/tests/xmlconfig-xml/boot-many-disks2.xml @@ -43,11 +43,21 @@ + + 1 + 2 + + + 5555 + 1234 + 3 + 4 + diff --git a/tests/xmlconfig.py b/tests/xmlconfig.py index c0b2c96c6..d5fbfccef 100644 --- a/tests/xmlconfig.py +++ b/tests/xmlconfig.py @@ -550,19 +550,28 @@ class TestXMLConfig(unittest.TestCase): g.add_device(VirtualDisk(g.conn, path=None, device=VirtualDisk.DEVICE_CDROM, bus="scsi")) - g.add_device(VirtualDisk(g.conn, path=None, - device=VirtualDisk.DEVICE_FLOPPY)) - d1 = VirtualDisk(g.conn, path="/dev/loop0", - device=VirtualDisk.DEVICE_FLOPPY, - driverName="phy") - d1.driver_cache = "none" - g.add_device(d1) - d2 = VirtualDisk(g.conn, path="/dev/loop0", - bus="virtio", driverName="qemu", - driverType="qcow2") - d2.driver_cache = "none" - d2.driver_io = "threads" - g.add_device(d2) + d = VirtualDisk(g.conn, path=None, + device=VirtualDisk.DEVICE_FLOPPY) + d.iotune_tbs = 1 + d.iotune_tis = 2 + g.add_device(d) + + d = VirtualDisk(g.conn, path="/dev/loop0", + device=VirtualDisk.DEVICE_FLOPPY, + driverName="phy") + d.driver_cache = "none" + d.iotune_rbs = 5555 + d.iotune_ris = 1234 + d.iotune_wbs = 3 + d.iotune_wis = 4 + g.add_device(d) + + d = VirtualDisk(g.conn, path="/dev/loop0", + bus="virtio", driverName="qemu", + driverType="qcow2") + d.driver_cache = "none" + d.driver_io = "threads" + g.add_device(d) self._compare(g, "boot-many-disks2", False) diff --git a/tests/xmlparse-xml/change-disk-in.xml b/tests/xmlparse-xml/change-disk-in.xml index b2ebfab63..720f7309a 100644 --- a/tests/xmlparse-xml/change-disk-in.xml +++ b/tests/xmlparse-xml/change-disk-in.xml @@ -45,6 +45,12 @@ + + 1 + 2 + 3 + 4 + diff --git a/tests/xmlparse-xml/change-disk-out.xml b/tests/xmlparse-xml/change-disk-out.xml index 73f0764b6..736ccbb51 100644 --- a/tests/xmlparse-xml/change-disk-out.xml +++ b/tests/xmlparse-xml/change-disk-out.xml @@ -42,6 +42,10 @@ + + 5 + 6 + diff --git a/tests/xmlparse.py b/tests/xmlparse.py index 67d8a8456..c4488d6f8 100644 --- a/tests/xmlparse.py +++ b/tests/xmlparse.py @@ -249,10 +249,10 @@ class XMLParseTest(unittest.TestCase): disks = guest.get_devices("disk") disk1 = disks[0] disk1.size = 1 - disk2 = disks[2] - disk2.size = 1 - disk3 = disks[5] + disk3 = disks[2] disk3.size = 1 + disk6 = disks[5] + disk6.size = 1 check = self._make_checker(disk1) check("path", "/tmp/test.img", "/dev/loop0") @@ -260,7 +260,7 @@ class XMLParseTest(unittest.TestCase): check("driver_type", None, "raw") check("serial", "WD-WMAP9A966149", "frob") - check = self._make_checker(disk2) + check = self._make_checker(disk3) check("path", "/dev/loop0", None) check("device", "cdrom", "floppy") check("read_only", True, False) @@ -268,12 +268,19 @@ class XMLParseTest(unittest.TestCase): check("bus", None, "fdc") check("error_policy", "stop", None) - check = self._make_checker(disk3) + check = self._make_checker(disk6) check("path", None, "/default-pool/default-vol") check("shareable", False, True) check("driver_cache", None, "writeback") check("driver_io", None, "threads") check("driver_io", "threads", "native") + check("iotune_ris", 1, 0) + check("iotune_rbs", 2, 0) + check("iotune_wis", 3, 0) + check("iotune_wbs", 4, 0) + check("iotune_tis", 0, 5) + check("iotune_tbs", 0, 6) + self._alter_compare(guest.get_xml_config(), outfile) diff --git a/virtinst/VirtualDisk.py b/virtinst/VirtualDisk.py index ff5b99278..7343286d9 100644 --- a/virtinst/VirtualDisk.py +++ b/virtinst/VirtualDisk.py @@ -579,12 +579,6 @@ class VirtualDisk(VirtualDevice): self._error_policy = None self._serial = None self._target = None - self._iotune_rbs = None - self._iotune_ris = None - self._iotune_tbs = None - self._iotune_tis = None - self._iotune_wbs = None - self._iotune_wis = None self._validate = validate self._nomanaged = nomanaged @@ -833,81 +827,22 @@ class VirtualDisk(VirtualDevice): serial = _xml_property(_get_serial, _set_serial, xpath="./serial") - def _get_iotune_rbs(self): - return self._iotune_rbs - def _set_iotune_rbs(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune read bytes per second value must be an " - "integer")) - self._iotune_rbs = val - iotune_rbs = _xml_property(_get_iotune_rbs, - _set_iotune_rbs, - xpath="./iotune/read_bytes_sec", + iotune_rbs = _xml_property(xpath="./iotune/read_bytes_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) - - def _get_iotune_ris(self): - return self._iotune_ris - def _set_iotune_ris(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune read iops per second value must be an " - "integer")) - self._iotune_ris = val - iotune_ris = _xml_property(_get_iotune_ris, - _set_iotune_ris, - xpath="./iotune/read_iops_sec", + iotune_ris = _xml_property(xpath="./iotune/read_iops_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) - - def _get_iotune_tbs(self): - return self._iotune_tbs - def _set_iotune_tbs(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune total bytes per second value must be an " - "integer")) - self._iotune_tbs = val - iotune_tbs = _xml_property(_get_iotune_tbs, - _set_iotune_tbs, - xpath="./iotune/total_bytes_sec", + iotune_tbs = _xml_property(xpath="./iotune/total_bytes_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) - - def _get_iotune_tis(self): - return self._iotune_tis - def _set_iotune_tis(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune total iops per second value must be an " - "integer")) - self._iotune_tis = val - iotune_tis = _xml_property(_get_iotune_tis, - _set_iotune_tis, - xpath="./iotune/total_iops_sec", + iotune_tis = _xml_property(xpath="./iotune/total_iops_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) - - def _get_iotune_wbs(self): - return self._iotune_wbs - def _set_iotune_wbs(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune write bytes per second value must be an " - "integer")) - self._iotune_wbs = val - iotune_wbs = _xml_property(_get_iotune_wbs, - _set_iotune_wbs, - xpath="./iotune/write_bytes_sec", + iotune_wbs = _xml_property(xpath="./iotune/write_bytes_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) - - def _get_iotune_wis(self): - return self._iotune_wis - def _set_iotune_wis(self, val): - if not isinstance(val, int) or val < 0: - raise ValueError(_("IOTune write iops per second value must be an " - "integer")) - self._iotune_wis = val - iotune_wis = _xml_property(_get_iotune_wis, - _set_iotune_wis, - xpath="./iotune/write_iops_sec", + iotune_wis = _xml_property(xpath="./iotune/write_iops_sec", get_converter=lambda s, x: int(x or 0), set_converter=lambda s, x: int(x)) @@ -1445,29 +1380,11 @@ class VirtualDisk(VirtualDevice): ret += (" %s\n" % util.xml_escape(self.serial)) - if (self.iotune_rbs or self.iotune_ris or - self.iotune_tbs or self.iotune_tis or - self.iotune_wbs or self.iotune_wis): - ret += " \n" - if self.iotune_rbs: - ret += " %s\n" % (self.iotune_rbs) - if self.iotune_ris: - ret += " %s\n" % (self.iotune_ris) - if self.iotune_tbs: - ret += " %s\n" % (self.iotune_tbs) - if self.iotune_tis: - ret += " %s\n" % (self.iotune_tis) - if self.iotune_wbs: - ret += " %s\n" % (self.iotune_wbs) - if self.iotune_wis: - ret += " %s\n" % (self.iotune_wis) - ret += " \n" - addr = self.indent(self.address.get_xml_config(), 6) if addr: ret += addr ret += " " - return ret + return self._add_parse_bits(ret) def is_size_conflict(self): """ diff --git a/virtinst/XMLBuilderDomain.py b/virtinst/XMLBuilderDomain.py index af57141d4..ea1c76506 100644 --- a/virtinst/XMLBuilderDomain.py +++ b/virtinst/XMLBuilderDomain.py @@ -244,10 +244,33 @@ def _xml_property(fget=None, fset=None, fdel=None, doc=None, @param default_converter: If the virtinst value is "default", use this function to get the actual XML value """ - # pylint: disable=W0212 # Accessing _xml vals of self. This should be a class method, but # we break it out to be more readable + def _findpropname(self): + for key, val in self.__class__.__dict__.items(): + if val is retprop: + return key + raise RuntimeError("Didn't find expected property") + + def _default_fset(self, val, *args, **kwargs): + ignore = args + ignore = kwargs + propname = _findpropname(self) + self._propstore[propname] = val + if propname in self._proporder: + self._proporder.remove(propname) + self._proporder.append(propname) + + def _default_fget(self, *args, **kwargs): + ignore = args + ignore = kwargs + return self._propstore.get(_findpropname(self), None) + + if not fget: + fget = _default_fget + if not fset: + fset = _default_fset def new_getter(self, *args, **kwargs): val = None @@ -258,15 +281,14 @@ def _xml_property(fget=None, fset=None, fdel=None, doc=None, if default_converter and getval == "default": return getval - usexpath = xpath + node_xpath = xpath if xml_get_xpath: - usexpath = xml_get_xpath(self) - - if usexpath is None: + node_xpath = xml_get_xpath(self) + if node_xpath is None: return getval nodes = util.listify(_get_xpath_node(self._xml_ctx, - usexpath, is_multi)) + node_xpath, is_multi)) if nodes: ret = [] for node in nodes: @@ -292,7 +314,6 @@ def _xml_property(fget=None, fset=None, fdel=None, doc=None, def new_setter(self, val, *args, **kwargs): # Do this regardless, for validation purposes fset(self, val, *args, **kwargs) - if not self._xml_node: return @@ -303,28 +324,28 @@ def _xml_property(fget=None, fset=None, fdel=None, doc=None, elif default_converter and val == "default": val = default_converter(self) - nodexpath = xpath + node_xpath = xpath if xml_set_xpath: - nodexpath = xml_set_xpath(self) + node_xpath = xml_set_xpath(self) - if nodexpath is None: + if node_xpath is None: return nodes = util.listify(_get_xpath_node(self._xml_ctx, - nodexpath, is_multi)) + node_xpath, is_multi)) - xpath_list = nodexpath + xpath_list = node_xpath if xml_set_list: xpath_list = xml_set_list(self) node_map = _tuplify_lists(nodes, val, xpath_list) - for node, val, usexpath in node_map: + for node, val, use_xpath in node_map: if node: - usexpath = node.nodePath() + use_xpath = node.nodePath() if val not in [None, False]: if not node: - node = _build_xpath_node(self._xml_node, usexpath) + node = _build_xpath_node(self._xml_node, use_xpath) if val is True: # Boolean property, creating the node is enough @@ -332,20 +353,30 @@ def _xml_property(fget=None, fset=None, fdel=None, doc=None, else: node.setContent(util.xml_escape(str(val))) else: - _remove_xpath_node(self._xml_node, usexpath) + _remove_xpath_node(self._xml_node, use_xpath) if fdel: # Not tested raise RuntimeError("XML deleter not yet supported.") - return property(fget=new_getter, fset=new_setter, doc=doc) + retprop = property(fget=new_getter, fset=new_setter, doc=doc) + return retprop class XMLBuilderDomain(object): """ Base for all classes which build or parse domain XML """ + @staticmethod + def indent(xmlstr, level): + xml = "" + if not xmlstr: + return xml + + for l in iter(xmlstr.splitlines()): + xml += " " * level + l + "\n" + return xml _dumpxml_xpath = "." def __init__(self, conn, parsexml=None, parsexmlnode=None): @@ -363,6 +394,8 @@ class XMLBuilderDomain(object): self._xml_node = None self._xml_ctx = None self._xml_root_doc = None + self._propstore = {} + self._proporder = [] if parsexml or parsexmlnode: self._parsexml(parsexml, parsexmlnode) @@ -429,6 +462,27 @@ class XMLBuilderDomain(object): self._set_xml_context() + def _add_parse_bits(self, xml): + if not self._propstore or self._is_parse(): + return xml + + try: + self._parsexml(xml, None) + for key in self._proporder[:]: + setattr(self, key, self._propstore[key]) + ret = self.get_xml_config() + for c in xml: + if c != " ": + break + ret = " " + ret + return ret.strip("\n") + finally: + self._xml_root_doc = None + self._xml_node = None + if self._xml_ctx: + self._xml_ctx.xpathFreeContext() + self._xml_ctx = None + def _get_xml_config(self): """ Internal XML building function. Must be overwritten by subclass @@ -449,13 +503,3 @@ class XMLBuilderDomain(object): return _sanitize_libxml_xml(node.serialize()) return self._get_xml_config(*args, **kwargs) - - @staticmethod - def indent(xmlstr, level): - xml = "" - if not xmlstr: - return xml - - for l in iter(xmlstr.splitlines()): - xml += " " * level + l + "\n" - return xml