xmlbuilder: Allow classes to specify order of certain xml elements

This will save us some test case churn. As an example, we now
do auto building of disk <target> XML and it doesn't alter things.
Without this bus and target are often swapped.
This commit is contained in:
Cole Robinson 2013-07-14 15:06:40 -04:00
parent 154bad0184
commit 2cea517823
4 changed files with 65 additions and 12 deletions

View File

@ -23,17 +23,17 @@
<devices>
<emulator>/usr/bin/test-hv</emulator>
<disk type="file" device="disk">
<driver cache="writeback" io="threads"/>
<source file="/default-pool/UPPER"/>
<target dev="hda" bus="ide"/>
<shareable/>
<serial>WD-WMAP9A966149</serial>
<driver cache="writeback" io="threads"/>
</disk>
<disk type="file" device="disk">
<driver error_policy="enospace"/>
<source file="/tmp/__virtinst_cli_new1.img"/>
<target dev="hdb" bus="ide"/>
<readonly/>
<driver error_policy="enospace"/>
</disk>
<disk type="block" device="cdrom">
<target dev="sda" bus="sata"/>
@ -110,17 +110,17 @@
<devices>
<emulator>/usr/bin/test-hv</emulator>
<disk type="file" device="disk">
<driver cache="writeback" io="threads"/>
<source file="/default-pool/UPPER"/>
<target dev="hda" bus="ide"/>
<shareable/>
<serial>WD-WMAP9A966149</serial>
<driver cache="writeback" io="threads"/>
</disk>
<disk type="file" device="disk">
<driver error_policy="enospace"/>
<source file="/tmp/__virtinst_cli_new1.img"/>
<target dev="hdb" bus="ide"/>
<readonly/>
<driver error_policy="enospace"/>
</disk>
<disk type="block" device="cdrom">
<target dev="sda" bus="sata"/>

View File

@ -157,6 +157,7 @@ def read_file(filename):
def diff_compare(actual_out, filename=None, expect_out=None):
"""Compare passed string output to contents of filename"""
if not expect_out:
#file(filename, "w").write(actual_out)
expect_out = read_file(filename)
diff = "".join(difflib.unified_diff(expect_out.splitlines(1),

View File

@ -388,10 +388,14 @@ class VirtualDisk(VirtualDevice):
except Exception, e:
raise ValueError(_("Couldn't lookup volume object: %s" % str(e)))
_DEFAULT_SENTINEL = -1234
_XMLELEMENTORDER = ["driver", "source", "target"]
_XMLPROPORDER = ["target", "bus"]
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._DEFAULT_SENTINEL = -1234
self._device = self.DEVICE_DISK
self._type = self._DEFAULT_SENTINEL
self._driverName = self._DEFAULT_SENTINEL
@ -756,8 +760,6 @@ class VirtualDisk(VirtualDevice):
if path is not None:
ret += " <source %s='%s'/>\n" % (typeattr, path)
ret += " <target dev='%s'/>\n" % (self.target)
addr = self.indent(self.address.get_xml_config(), 6)
if addr:
ret += addr

View File

@ -521,6 +521,12 @@ class XMLBuilder(object):
xml += " " * level + l + "\n"
return xml
# Specify a list of tag values here and we will arrange them when
# outputing XML. They will be put before every other element. This
# is strictly to keep test suite happy.
_XMLELEMENTORDER = []
_XMLPROPORDER = []
_dumpxml_xpath = "."
def __init__(self, conn, parsexml=None, parsexmlnode=None):
"""
@ -596,10 +602,20 @@ class XMLBuilder(object):
if not self._propstore or self._is_parse():
return xml
do_order = []
proporder = self._proporder[:]
propstore = self._propstore.copy()
for key in self._XMLPROPORDER:
if key in proporder:
proporder.remove(key)
do_order.append(key)
proporder = do_order + proporder
try:
self._parsexml(xml, None)
for key in self._proporder[:]:
setattr(self, key, self._propstore[key])
for key in proporder:
setattr(self, key, propstore[key])
ret = self.get_xml_config()
for c in xml:
if c != " ":
@ -627,6 +643,37 @@ class XMLBuilder(object):
"""
raise NotImplementedError()
def _order_xml_elements(self, xml):
# This whole thing is reeeally hacky but it saves us some
# unittest churn.
if not self._XMLELEMENTORDER:
return xml
split = xml.splitlines()
if len(split) < 3:
return xml
head = split[0]
end = split[-1]
split = split[1:-1]
baseindent = 0
for i in head:
if i != " ":
break
baseindent += 1
neworder = []
for prio in reversed(self._XMLELEMENTORDER):
tag = "%s<%s " % ((baseindent + 2) * " ", prio)
for idx in range(len(split)):
if split[idx].startswith(tag):
neworder.insert(0, split.pop(idx))
break
neworder += split
return "\n".join([head] + neworder + [end])
def get_xml_config(self, *args, **kwargs):
"""
Construct and return object xml
@ -637,7 +684,10 @@ class XMLBuilder(object):
if self._xml_ctx:
node = _get_xpath_node(self._xml_ctx, self._dumpxml_xpath)
if not node:
return ""
return _sanitize_libxml_xml(node.serialize())
ret = ""
else:
ret = _sanitize_libxml_xml(node.serialize())
else:
ret = self._get_xml_config(*args, **kwargs)
return self._get_xml_config(*args, **kwargs)
return self._order_xml_elements(ret)