mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-11 05:17:59 +03:00
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:
parent
170595698e
commit
bba3c93508
@ -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"/>
|
||||
|
@ -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">
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
@ -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"/>
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"):
|
||||
|
@ -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):
|
||||
|
@ -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",
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user