xmlbuilder: Always keep around an xml node, even if building from scratch

This further unifies the 'parse existing' vs. 'build new' case.
This commit is contained in:
Cole Robinson 2013-07-24 14:37:07 -04:00
parent 170595698e
commit bba3c93508
27 changed files with 206 additions and 204 deletions

View File

@ -20,10 +20,10 @@
<devices>
<emulator>/usr/lib/xen/bin/qemu-dm</emulator>
<disk type="block" device="floppy">
<source dev="/disk-pool/diskvol1-clone"/>
<target dev="fda" bus="fdc"/>
<address type="drive" controller="0" bus="0" target="0" unit="0"/>
<driver type="vmdk"/>
<source dev="/disk-pool/diskvol1-clone"/>
</disk>
<disk type="block" device="disk">
<source dev="/disk-pool/diskvol2"/>
@ -45,14 +45,14 @@
<address type="drive" controller="0" bus="0" target="0" unit="2"/>
</disk>
<disk type="file" device="disk">
<source file="/default-pool/default-vol-clone"/>
<target dev="hda" bus="ide"/>
<address type="drive" controller="0" bus="0" target="0" unit="0"/>
<source file="/default-pool/default-vol-clone"/>
</disk>
<disk type="file" device="disk">
<source file="/default-pool/testvol2-clone.img"/>
<target dev="hdb" bus="ide"/>
<address type="drive" controller="0" bus="0" target="0" unit="1"/>
<source file="/default-pool/testvol2-clone.img"/>
</disk>
<controller type="scsi" index="0"/>
<controller type="fdc" index="0"/>

View File

@ -20,9 +20,9 @@
<devices>
<emulator>/usr/lib/xen/bin/qemu-dm</emulator>
<disk type="file" device="disk">
<source file="/default-pool/newvm.img"/>
<target dev="hda" bus="ide"/>
<address type="drive" controller="0" bus="0" target="0" unit="0"/>
<source file="/default-pool/newvm.img"/>
</disk>
<controller type="ide" index="0"/>
<interface type="user">

View File

@ -18,12 +18,12 @@
<devices>
<emulator>/usr/bin/qemu-kvm</emulator>
<disk type="file" device="disk">
<target dev="hda" bus="ide"/>
<source file="/cross-pool/new1.img"/>
<target dev="hda" bus="ide"/>
</disk>
<disk type="file" device="disk">
<target dev="hdb" bus="ide"/>
<source file="/cross-pool/new2.img"/>
<target dev="hdb" bus="ide"/>
</disk>
<interface type="network">
<mac address="22:23:45:67:89:00"/>

View File

@ -29,8 +29,8 @@
<readonly/>
</disk>
<disk type="file" device="disk">
<target dev="sdb" bus="scsi"/>
<source file="/tmp/clone2.img"/>
<target dev="sdb" bus="scsi"/>
</disk>
<interface type="network">
<mac address="22:23:45:67:89:00"/>

View File

@ -18,8 +18,8 @@
<devices>
<emulator>/usr/bin/qemu-kvm</emulator>
<disk type="file" device="disk">
<target dev="hda" bus="ide"/>
<source file="/default-pool/1234.img"/>
<target dev="hda" bus="ide"/>
</disk>
<disk type="block" device="floppy">
<target dev="fdb" bus="fdc"/>
@ -29,8 +29,8 @@
<readonly/>
</disk>
<disk type="file" device="disk">
<target dev="sdb" bus="scsi"/>
<source file="/tmp/clone2.img"/>
<target dev="sdb" bus="scsi"/>
</disk>
<interface type="network">
<mac address="22:23:45:67:89:00"/>

View File

@ -18,13 +18,13 @@
<devices>
<emulator>/usr/bin/qemu-kvm</emulator>
<disk type="file" device="disk">
<source file="/default-pool/new1.img"/>
<target dev="hda" bus="ide"/>
<driver name="qemu" type="vmdk"/>
<source file="/default-pool/new1.img"/>
</disk>
<disk type="block" device="disk">
<target dev="hdb" bus="ide"/>
<source dev="/disk-pool/new2.img"/>
<target dev="hdb" bus="ide"/>
</disk>
<interface type="network">
<mac address="22:23:45:67:89:00"/>

View File

@ -27,8 +27,8 @@
<readonly/>
</disk>
<disk type="file" device="disk">
<target dev="sda" bus="scsi"/>
<source file="/tmp/clone2.img"/>
<target dev="sda" bus="scsi"/>
</disk>
<disk type="block" device="cdrom">
<target dev="sdb" bus="scsi"/>

View File

@ -29,8 +29,8 @@
<readonly/>
</disk>
<disk type="file" device="disk">
<target dev="sdb" bus="scsi"/>
<source file="/default-pool/1234.img"/>
<target dev="sdb" bus="scsi"/>
</disk>
<interface type="network">
<mac address="22:23:45:67:89:00"/>

View File

@ -1,5 +1,6 @@
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0" bus="21" slot="0" function="4"/>
</source>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0" bus="21" slot="0" function="4"/>
</source>
</hostdev>

View File

@ -1,6 +1,7 @@
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x0781"/>
<product id="0x5151"/>
</source>
</hostdev>
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x0781"/>
<product id="0x5151"/>
</source>
</hostdev>

View File

@ -1,7 +1,8 @@
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x0781"/>
<product id="0x5151"/>
<address bus="1" device="4"/>
</source>
</hostdev>
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x0781"/>
<product id="0x5151"/>
<address bus="1" device="4"/>
</source>
</hostdev>

View File

@ -13,7 +13,7 @@
<acpi/>
<apic/>
</features>
<cpu match="minimum" mode="custom">
<cpu mode="custom" match="minimum">
<model>footest</model>
<vendor>Intel</vendor>
<topology sockets="4" cores="5" threads="2"/>

View File

@ -6,8 +6,8 @@
<vcpu>5</vcpu>
<os>
<type arch="i686">hvm</type>
<kernel>kernel</kernel>
<initrd>initrd</initrd>
<kernel>./virtinst-vmlinuz.</kernel>
<initrd>./virtinst-initrd.img.</initrd>
<cmdline>method=tests/cli-test-xml/fakerhel6tree</cmdline>
</os>
<features>

View File

@ -18,8 +18,6 @@ import unittest
import os
import logging
import urlgrabber.progress as progress
import virtinst
from virtinst import VirtualDisk
from virtinst import VirtualAudio
@ -67,7 +65,7 @@ class TestXMLConfig(unittest.TestCase):
do_create=True):
filename = filebase and build_xmlfile(filebase) or None
guest._prepare_install(progress.BaseMeter())
guest._prepare_install(None)
try:
actualXML = guest.get_install_xml(install=do_install,
disk_boot=do_disk_boot)
@ -93,11 +91,6 @@ class TestXMLConfig(unittest.TestCase):
guest.start_install(consolecb, meter, removeOld, wait)
guest.domain.destroy()
# Replace kernel/initrd with known info
if guest.installer._install_kernel:
guest.installer._install_kernel = "kernel"
guest.installer._install_initrd = "initrd"
xmlinst = guest.get_install_xml(True, False)
xmlboot = guest.get_install_xml(False, False)
xmlcont = guest.get_install_xml(True, True)
@ -320,10 +313,6 @@ class TestXMLConfig(unittest.TestCase):
# Call get_xml_config sets first round of defaults w/o os_variant set
g.get_install_xml(do_install)
g._prepare_install(None)
g.get_install_xml(do_install)
g._prepare_install(None)
g.get_install_xml(do_install)
g.os_variant = "fedora11"
self._compare(g, "install-f11", do_install)
@ -992,19 +981,19 @@ class TestXMLConfig(unittest.TestCase):
dev3.macaddr = "22:22:33:44:55:68"
utils.diff_compare(dev1.get_xml_config(), None,
" <interface type=\"bridge\">\n"
" <source bridge=\"bzz0\"/>\n"
" <mac address=\"22:22:33:44:55:66\"/>\n"
" </interface>")
"<interface type=\"bridge\">\n"
" <source bridge=\"bzz0\"/>\n"
" <mac address=\"22:22:33:44:55:66\"/>\n"
"</interface>\n")
utils.diff_compare(dev2.get_xml_config(), None,
"<interface type=\"bridge\">\n"
" <mac address=\"22:22:33:44:55:67\"/>\n"
" <source bridge=\"foobr0\"/>\n"
" </interface>\n")
" <source bridge=\"foobr0\"/>\n"
" <mac address=\"22:22:33:44:55:67\"/>\n"
"</interface>\n")
utils.diff_compare(dev3.get_xml_config(), None,
"<interface type=\"bridge\">\n"
" <mac address=\"22:22:33:44:55:68\"/>\n"
" </interface>\n")
" <mac address=\"22:22:33:44:55:68\"/>\n"
"</interface>\n")
finally:
if util and origfunc:
util.default_bridge = origfunc

View File

@ -38,11 +38,11 @@
</interface>
<graphics type="sdl" display=":3.4" xauth="/tmp/.Xauthority"/>
<console type="pty"/>
<watchdog model="i6300esb" action="reset"/>
<interface type="network">
<source network="default"/>
<mac address="1A:2A:3A:4A:5A:6A"/>
</interface>
<watchdog model="i6300esb" action="reset"/>
</devices>
<seclabel type="static" model="selinux">
<label>foolabel</label>

View File

@ -7,10 +7,10 @@
<type arch="i686">hvm</type>
<loader>/usr/lib/xen/boot/hvmloader</loader>
<bootmenu enable="yes"/>
<boot dev="cdrom"/>
<kernel>foo.img</kernel>
<initrd>bar.img</initrd>
<cmdline>ks=foo.ks</cmdline>
<boot dev="cdrom"/>
</os>
<features>
<acpi/><apic/>

View File

@ -37,8 +37,8 @@
</parallel>
<console type="pty"/>
<console type="file">
<target type="virtio"/>
<source path="/root/foo"/>
<target type="virtio"/>
</console>
<channel type="pty">
<target type="virtio" name="test.changed"/>

View File

@ -21,8 +21,8 @@
<disk type="block" device="disk">
<target dev="hda" bus="ide"/>
<serial>frob</serial>
<source dev="/dev/loop0"/>
<driver name="test" type="raw"/>
<source dev="/dev/loop0"/>
</disk>
<disk type="block" device="disk">
<source dev="/dev/loop0"/>
@ -50,9 +50,9 @@
<total_iops_sec>5</total_iops_sec>
<total_bytes_sec>6</total_bytes_sec>
</iotune>
<driver cache="writeback" io="native"/>
<source file="/default-pool/default-vol"/>
<shareable/>
<driver cache="writeback" io="native"/>
</disk>
<disk type="block" device="floppy">
<driver name="phy" cache="none"/>

View File

@ -6,12 +6,11 @@
<os>
<type machine="pc-0.11">xen</type>
<loader>/foo/loader</loader>
<init>/sbin/init</init>
<boot dev="fd"/>
<init>/sbin/init</init>
<bootmenu enable="no"/>
</os>
<features>
<apic/>
<features><apic/>
</features>
<numatune>
<memory nodeset="2,4,6"/>

View File

@ -19,12 +19,12 @@
<acpi/>
</features>
<clock offset="utc"/>
<seclabel model="testSecurity" type="static">
<seclabel type="static" model="testSecurity">
<label>frob</label>
</seclabel>
<cpu mode="custom" match="exact">
<model>foobar</model>
<topology cores="4" sockets="1" threads="1"/>
<topology sockets="1" cores="4" threads="1"/>
<feature name="x2apic" policy="forbid"/>
</cpu>
</domain>

View File

@ -20,13 +20,13 @@
<emulator>/usr/lib/xen/bin/qemu-dm</emulator>
<interface type="user">
<mac address="AA:AA:AA:AA:AA:AA"/>
<model type="testmodel"/>
<source bridge="br0" network="route"/>
<model type="testmodel"/>
</interface>
<interface type="bridge">
<source bridge="newbr0"/>
<mac address="22:22:33:44:55:66"/>
<model type="virtio"/>
<source bridge="newbr0"/>
</interface>
<interface type="bridge">
<source bridge="newfoo0" network="default"/>

View File

@ -744,8 +744,8 @@ class XMLParseTest(unittest.TestCase):
adddev.macaddr = "1A:2A:3A:4A:5A:6A"
guest.add_device(virtinst.VirtualWatchdog(conn))
guest.add_device(adddev)
guest.add_device(adddev)
guest.remove_device(adddev)
guest.add_device(adddev)

View File

@ -70,7 +70,7 @@ class CPU(XMLBuilder):
def _parsexml(self, xml, node):
XMLBuilder._parsexml(self, xml, node)
for node in self._xml_node.children:
for node in self._xml_node.children or []:
if node.name != "feature":
continue
if not node.prop("name"):

View File

@ -337,12 +337,9 @@ class Guest(XMLBuilder):
@param dev: VirtualDevice instance to attach to guest
@param set_defaults: Whether to set defaults for the device
"""
if self._is_parse():
self._add_child("./devices", dev)
self._track_device(dev)
if self._is_parse():
self._recalculate_device_xpaths()
self._add_child("./devices", dev)
self._recalculate_device_xpaths()
if set_defaults:
origdev = self._devices
@ -365,7 +362,6 @@ class Guest(XMLBuilder):
VirtualDevice.virtual_device_types)
"""
devlist = self._dev_build_list(devtype)
devlist.extend(self._install_devices)
return self._dev_build_list(devtype, devlist)
def get_all_devices(self):
@ -384,7 +380,7 @@ class Guest(XMLBuilder):
@param dev: VirtualDevice instance
"""
found = False
for devlist in [self._devices, self._install_devices]:
for devlist in [self._devices]:
if found:
break
@ -396,11 +392,13 @@ class Guest(XMLBuilder):
if not found:
raise ValueError(_("Did not find device %s") % str(dev))
if self._is_parse():
xpath = dev.get_root_xpath()
if xpath:
self._remove_child_xpath(xpath)
self._recalculate_device_xpaths()
xpath = dev.get_root_xpath()
xml = dev.get_xml_config()
dev.set_root_xpath(None)
dev._parsexml(xml, None)
if xpath:
self._remove_child_xpath(xpath)
self._recalculate_device_xpaths()
################################
@ -410,7 +408,7 @@ class Guest(XMLBuilder):
def _parsexml(self, xml, node):
XMLBuilder._parsexml(self, xml, node)
for node in self._xml_node.children:
for node in self._xml_node.children or []:
if node.name != "devices":
continue
@ -455,6 +453,8 @@ class Guest(XMLBuilder):
############################
def _prepare_install(self, meter, dry=False):
for dev in self._install_devices:
self.remove_device(dev)
self._install_devices = []
ignore = dry
@ -464,6 +464,7 @@ class Guest(XMLBuilder):
# Initialize install device list
for dev in self.installer.install_devices:
self.add_device(dev)
self._install_devices.append(dev)
def _cleanup_install(self):

View File

@ -139,7 +139,7 @@ class _VirtualCharDevice(VirtualDevice):
return hasattr(self, propname)
_XML_PROP_ORDER = ["type",
_XML_PROP_ORDER = ["type", "_has_mode_bind", "_has_mode_connect",
"bind_host", "bind_port",
"source_mode", "source_path",
"source_host", "source_port",

View File

@ -607,9 +607,12 @@ class VirtualDisk(VirtualDevice):
#
# This will update the XML value with the newly determined
# default value, but it won't edit propstore. This means
if self.is_build():
return
prop = self.all_xml_props()[propname]
val = getattr(prop, "_default_cb")(self)
prop.setter(self, val, call_fset=False)
candefault, val = getattr(prop, "_default_get_value")(self)
if candefault:
getattr(prop, "_set_xml")(self, val)
refresh_prop_xml("type")
refresh_prop_xml("driver_name")

View File

@ -351,19 +351,6 @@ class XMLProperty(property):
return xmlbuilder.fix_relative_xpath(ret)
def _convert_value_for_setter(self, xmlbuilder):
# Convert from API value to XML value
val = self._nonxml_fget(xmlbuilder)
if self._default_name and val == self._default_name:
val = self._default_cb(xmlbuilder)
elif self._is_yesno:
if val is not None:
val = bool(val) and "yes" or "no"
if self._convert_value_for_setter_cb:
val = self._convert_value_for_setter_cb(xmlbuilder, val)
return val
def _build_node_list(self, xmlbuilder, xpath):
"""
Build list of nodes that the passed xpaths reference
@ -408,11 +395,37 @@ class XMLProperty(property):
ret = val
return ret
def _convert_set_value(self, xmlbuilder, val):
if self._default_name and val == self._default_name:
val = self._default_cb(xmlbuilder)
elif self._is_yesno:
if val is not None:
val = bool(val) and "yes" or "no"
if self._convert_value_for_setter_cb:
val = self._convert_value_for_setter_cb(xmlbuilder, val)
return val
def _prop_is_unset(self, xmlbuilder):
propstore = getattr(xmlbuilder, "_propstore")
propname = self._findpropname(xmlbuilder)
return (propname not in propstore)
def _default_get_value(self, xmlbuilder):
"""
Return (can use default, default value)
"""
ret = (False, -1)
if not self._prop_is_unset(xmlbuilder):
return ret
if not self._default_cb:
return ret
if self._default_name:
return (True, self._default_name)
return (True, self._default_cb(xmlbuilder))
def _set_default(self, xmlbuilder):
"""
Encode the property default into the XML and propstore, but
@ -422,13 +435,10 @@ class XMLProperty(property):
This is called during the get_xml_config process and shouldn't
be called from outside this file.
"""
if not self._prop_is_unset(xmlbuilder):
candefault, val = self._default_get_value(xmlbuilder)
if not candefault:
return
if not self._default_cb:
return
if self._default_cb(xmlbuilder) is None:
return
self.setter(xmlbuilder, self.getter(xmlbuilder), validate=False)
self.setter(xmlbuilder, val, validate=False)
def _nonxml_fset(self, xmlbuilder, val):
"""
@ -453,17 +463,17 @@ class XMLProperty(property):
The flip side to nonxml_fset, fetch the value from
XMLBuilder._propstore
"""
candefault, val = self._default_get_value(xmlbuilder)
if candefault:
return val
propstore = getattr(xmlbuilder, "_propstore")
propname = self._findpropname(xmlbuilder)
unset = (propname not in propstore)
if unset and self._default_cb:
if self._default_name:
return self._default_name
return self._default_cb(xmlbuilder)
return propstore.get(propname, None)
def _clear(self, xmlbuilder):
self.setter(xmlbuilder, None)
self._set_xml(xmlbuilder, None)
##################################
@ -471,33 +481,38 @@ class XMLProperty(property):
##################################
def getter(self, xmlbuilder):
if xmlbuilder._xml_ctx is None:
fgetval = self._nonxml_fget(xmlbuilder)
return self._convert_get_value(fgetval)
if self._prop_is_unset(xmlbuilder) and not xmlbuilder.is_build():
val = self._get_xml(xmlbuilder)
else:
val = self._nonxml_fget(xmlbuilder)
ret = self._convert_get_value(val)
return ret
def _get_xml(self, xmlbuilder):
xpath = self._xpath_for_getter(xmlbuilder)
node = _get_xpath_node(xmlbuilder._xml_ctx, xpath)
if not node:
return self._convert_get_value(None)
return None
content = node.content
if self._is_bool:
content = True
return self._convert_get_value(content)
return content
def setter(self, xmlbuilder, val, call_fset=True, validate=True):
if call_fset:
if validate and self._validate_cb:
self._validate_cb(xmlbuilder, val)
self._nonxml_fset(xmlbuilder, val)
def setter(self, xmlbuilder, val, validate=True):
if validate and self._validate_cb:
self._validate_cb(xmlbuilder, val)
self._nonxml_fset(xmlbuilder,
self._convert_set_value(xmlbuilder, val))
root_node = getattr(xmlbuilder, "_xml_node")
if root_node is None:
if xmlbuilder.is_build():
return
self._convert_set_value(xmlbuilder, val)
def _set_xml(self, xmlbuilder, setval, root_node=None):
if root_node is None:
root_node = xmlbuilder._xml_node
xpath = self._xpath_for_setter(xmlbuilder)
setval = self._convert_value_for_setter(xmlbuilder)
node = _get_xpath_node(xmlbuilder._xml_ctx, xpath)
clearlist = self._build_clear_list(xmlbuilder, node)
@ -562,8 +577,11 @@ class XMLBuilder(object):
self._propstore = {}
self._proporder = []
if parsexml or parsexmlnode:
self._parsexml(parsexml, parsexmlnode)
self._is_build = False
if not (parsexml or parsexmlnode):
parsexml = self._make_xml_stub()
self._is_build = True
self._parsexml(parsexml, parsexmlnode)
##############
@ -580,7 +598,8 @@ class XMLBuilder(object):
self._parsexml(xml, None)
def set_root_xpath(self, xpath):
self._xml_root_xpath = xpath
self._xml_root_xpath = xpath or ""
self._xml_dump_xpath = xpath or self._XML_ROOT_XPATH
xmlprops = self.all_xml_props()
for propname in self._XML_PROP_ORDER:
@ -608,30 +627,12 @@ class XMLBuilder(object):
for prop in self.all_xml_props().values():
prop._clear(self)
def _do_get_xml_config(self):
"""
Construct and return object xml
@return: object xml representation as a string
@rtype: str
"""
if self._xml_ctx:
node = _get_xpath_node(self._xml_ctx, self._xml_dump_xpath)
if not node:
ret = ""
else:
ret = _sanitize_libxml_xml(node.serialize())
else:
xmlstub = self._make_xml_stub()
if xmlstub is None:
return None
ret = self._add_parse_bits(xmlstub)
if ret == xmlstub:
ret = ""
if ret and self._xml_root_name == "domain" and not ret.endswith("\n"):
ret += "\n"
def all_xml_props(self):
ret = {}
for c in reversed(type.mro(self.__class__)[:-1]):
for key, val in c.__dict__.items():
if val.__class__ is XMLProperty:
ret[key] = val
return ret
@ -639,8 +640,8 @@ class XMLBuilder(object):
# Internal helper API #
#######################
def _is_parse(self):
return bool(self._xml_node or self._xml_ctx)
def is_build(self):
return bool(self._is_build)
###################
@ -663,6 +664,36 @@ class XMLBuilder(object):
# Internal XML parsers #
########################
def _get_node_xml(self, ctx=None):
if ctx is None:
ctx = self._xml_ctx
node = _get_xpath_node(ctx, self._xml_dump_xpath)
if not node:
return ""
return _sanitize_libxml_xml(node.serialize())
def _do_get_xml_config(self):
xmlstub = self._make_xml_stub()
try:
node = None
ctx = None
if self.is_build():
node = self._xml_node.docCopyNodeList(self._xml_node.doc)
ctx = self._make_xml_context(node)
ret = self._add_parse_bits(node, ctx)
finally:
if node:
node.freeNode()
if ret == xmlstub:
ret = ""
if ret and self._xml_root_name == "domain" and not ret.endswith("\n"):
ret += "\n"
return ret
def _make_xml_stub(self):
return _indent("<%s/>" % (self._xml_root_name), self._xml_indent)
@ -671,26 +702,30 @@ class XMLBuilder(object):
Insert the passed XMLBuilder object into our XML document at the
specified path
"""
dev.set_new_xml(dev.get_xml_config())
newnode = dev._xml_node
ret = _build_xpath_node(self._xml_ctx, parent_xpath, newnode)
return ret
if not dev.is_build():
newnode = libxml2.parseDoc(dev.get_xml_config()).children
_build_xpath_node(self._xml_ctx, parent_xpath, newnode)
dev._parsexml(None, self._xml_node)
def _remove_child_xpath(self, xpath):
_remove_xpath_node(self._xml_ctx, xpath, dofree=False)
self._set_xml_context()
def _set_xml_context(self):
doc = self._xml_node.doc
def _make_xml_context(self, node):
doc = node.doc
ctx = _CtxCleanupWrapper(doc.xpathNewContext())
ctx.setContextNode(self._xml_node)
self._xml_ctx = ctx
ctx.setContextNode(node)
return ctx
def _set_xml_context(self):
self._xml_ctx = self._make_xml_context(self._xml_node)
def _parsexml(self, xml, node):
if xml:
doc = libxml2.parseDoc(xml)
self._xml_root_doc = _DocCleanupWrapper(doc)
self._xml_node = doc.children
self._xml_node.virtinst_is_build = self._is_build
self._xml_dump_xpath = "."
# This just stores a reference to our root doc wrapper in
@ -699,41 +734,19 @@ class XMLBuilder(object):
self._xml_node.virtinst_root_doc = self._xml_root_doc
else:
self._xml_node = node
self._is_build = (getattr(node, "virtinst_is_build", False) or
self._is_build)
self._xml_dump_xpath = self._XML_ROOT_XPATH
self._set_xml_context()
def all_xml_props(self):
ret = {}
for c in reversed(type.mro(self.__class__)[:-1]):
for key, val in c.__dict__.items():
if val.__class__ is XMLProperty:
ret[key] = val
return ret
def _do_add_parse_bits(self, xml, node):
def _do_add_parse_bits(self, node, ctx):
# Set all defaults if the properties have one registered
xmlprops = self.all_xml_props()
for prop in xmlprops.values():
prop._set_default(self)
# Default props alter our _propstore. But at this point _propstore
# is empty, there's nothing for us to do, so exit early
if not self._propstore:
return xml
# Unindent XML
indent = 0
if xml:
for c in xml:
if c != " ":
break
indent += 1
xml = "\n".join([l[indent:] for l in xml.splitlines()])
# Parse the XML into our internal state. Use the raw
# _parsexml so we don't hit Guest parsing into its internal state
XMLBuilder._parsexml(self, xml, node)
if self.is_build():
for prop in xmlprops.values():
prop._set_default(self)
# Set up preferred XML ordering
do_order = self._proporder[:]
@ -747,32 +760,26 @@ class XMLBuilder(object):
# Alter the XML
for key in do_order:
if key in xmlprops:
xmlprops[key].setter(self, self._propstore[key],
validate=False)
else:
for obj in util.listify(getattr(self, key)):
if self._xml_root_xpath and not obj._xml_root_xpath:
obj._xml_root_xpath = self._xml_root_xpath
obj._add_parse_bits(xml=None, node=self._xml_node)
xmlprops[key]._set_xml(self, self._propstore[key], node)
continue
return _indent(self._do_get_xml_config(), indent)
for obj in util.listify(getattr(self, key)):
if self._xml_root_xpath and not obj._xml_root_xpath:
obj._xml_root_xpath = self._xml_root_xpath
obj._add_parse_bits(node, ctx)
def _add_parse_bits(self, xml, node=None):
return self._get_node_xml(ctx)
def _add_parse_bits(self, node, ctx):
"""
Callback that adds the implicitly tracked XML properties to
the manually generated xml. This should only exist until all
classes are converted to all parsing all the time
"""
if self._is_parse():
return xml
origproporder = self._proporder[:]
origpropstore = self._propstore.copy()
try:
return self._do_add_parse_bits(xml, node)
return self._do_add_parse_bits(node, ctx)
finally:
self._xml_root_doc = None
self._xml_node = None
self._xml_ctx = None
self._proporder = origproporder
self._propstore = origpropstore