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