XMLBuilder: Provide default fget and fset impls
This is the first step towards unifying the XML handling split between parsing and creating from scratch. Demonstrate it with VirtualDisk.iotune bits.
This commit is contained in:
parent
f620b317b9
commit
dcd632da80
|
@ -43,11 +43,21 @@
|
|||
</disk>
|
||||
<disk type="block" device="floppy">
|
||||
<target dev="fda" bus="fdc"/>
|
||||
<iotune>
|
||||
<total_bytes_sec>1</total_bytes_sec>
|
||||
<total_iops_sec>2</total_iops_sec>
|
||||
</iotune>
|
||||
</disk>
|
||||
<disk type="block" device="floppy">
|
||||
<driver name="phy" cache="none"/>
|
||||
<source dev="/dev/loop0"/>
|
||||
<target dev="fdb" bus="fdc"/>
|
||||
<iotune>
|
||||
<read_bytes_sec>5555</read_bytes_sec>
|
||||
<read_iops_sec>1234</read_iops_sec>
|
||||
<write_bytes_sec>3</write_bytes_sec>
|
||||
<write_iops_sec>4</write_iops_sec>
|
||||
</iotune>
|
||||
</disk>
|
||||
<disk type="block" device="disk">
|
||||
<driver name="qemu" type="qcow2" cache="none" io="threads"/>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -45,6 +45,12 @@
|
|||
</disk>
|
||||
<disk type="block" device="floppy">
|
||||
<target dev="fda" bus="fdc"/>
|
||||
<iotune>
|
||||
<read_iops_sec>1</read_iops_sec>
|
||||
<read_bytes_sec>2</read_bytes_sec>
|
||||
<write_iops_sec>3</write_iops_sec>
|
||||
<write_bytes_sec>4</write_bytes_sec>
|
||||
</iotune>
|
||||
</disk>
|
||||
<disk type="block" device="floppy">
|
||||
<driver name="phy" cache="none"/>
|
||||
|
|
|
@ -42,6 +42,10 @@
|
|||
</disk>
|
||||
<disk type="file" device="floppy">
|
||||
<target dev="fda" bus="fdc"/>
|
||||
<iotune>
|
||||
<total_iops_sec>5</total_iops_sec>
|
||||
<total_bytes_sec>6</total_bytes_sec>
|
||||
</iotune>
|
||||
<source file="/default-pool/default-vol"/>
|
||||
<shareable/>
|
||||
<driver cache="writeback" io="native"/>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 += (" <serial>%s</serial>\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 += " <iotune>\n"
|
||||
if self.iotune_rbs:
|
||||
ret += " <read_bytes_sec>%s</read_bytes_sec>\n" % (self.iotune_rbs)
|
||||
if self.iotune_ris:
|
||||
ret += " <read_iops_sec>%s</read_iops_sec>\n" % (self.iotune_ris)
|
||||
if self.iotune_tbs:
|
||||
ret += " <total_bytes_sec>%s</total_bytes_sec>\n" % (self.iotune_tbs)
|
||||
if self.iotune_tis:
|
||||
ret += " <total_iops_sec>%s</total_iops_sec>\n" % (self.iotune_tis)
|
||||
if self.iotune_wbs:
|
||||
ret += " <write_bytes_sec>%s</write_bytes_sec>\n" % (self.iotune_wbs)
|
||||
if self.iotune_wis:
|
||||
ret += " <write_iops_sec>%s</write_iops_sec>\n" % (self.iotune_wis)
|
||||
ret += " </iotune>\n"
|
||||
|
||||
addr = self.indent(self.address.get_xml_config(), 6)
|
||||
if addr:
|
||||
ret += addr
|
||||
ret += " </disk>"
|
||||
return ret
|
||||
return self._add_parse_bits(ret)
|
||||
|
||||
def is_size_conflict(self):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue