xmlbuilder: Track singleton child properties explicitly

Things like Guest.Seclabel, VirtualDevice.VirtualAlias, etc.

Now we don't need to track a full xpath with each class, just its root
name and we build its hierarchy depending on its parent.
This commit is contained in:
Cole Robinson 2013-09-11 11:47:09 -04:00
parent 38545e3af8
commit 0be727e6bb
13 changed files with 226 additions and 199 deletions

View File

@ -21,6 +21,6 @@ from virtinst.xmlbuilder import XMLBuilder, XMLProperty
class Clock(XMLBuilder):
_XML_ROOT_XPATH = "/domain/clock"
_XML_ROOT_NAME = "clock"
offset = XMLProperty(xpath="./clock/@offset")
offset = XMLProperty(xpath="./@offset")

View File

@ -27,7 +27,7 @@ class CPUFeature(XMLBuilder):
POLICIES = ["force", "require", "optional", "disable", "forbid"]
_XML_ROOT_XPATH = "/domain/cpu/feature"
_XML_ROOT_NAME = "feature"
_XML_PROP_ORDER = ["name", "policy"]
name = XMLProperty("./@name")
@ -41,7 +41,7 @@ class CPU(XMLBuilder):
MATCHS = ["minimum", "exact", "strict"]
_XML_ROOT_XPATH = "/domain/cpu"
_XML_ROOT_NAME = "cpu"
_XML_PROP_ORDER = ["mode", "match", "model", "vendor",
"sockets", "cores", "threads", "features"]
@ -131,12 +131,12 @@ class CPU(XMLBuilder):
if not self.match:
self.match = "exact"
return val
model = XMLProperty(xpath="./cpu/model", set_converter=_set_model)
model = XMLProperty(xpath="./model", set_converter=_set_model)
match = XMLProperty(xpath="./cpu/@match")
vendor = XMLProperty(xpath="./cpu/vendor")
mode = XMLProperty(xpath="./cpu/@mode")
match = XMLProperty(xpath="./@match")
vendor = XMLProperty(xpath="./vendor")
mode = XMLProperty(xpath="./@mode")
sockets = XMLProperty(xpath="./cpu/topology/@sockets", is_int=True)
cores = XMLProperty(xpath="./cpu/topology/@cores", is_int=True)
threads = XMLProperty(xpath="./cpu/topology/@threads", is_int=True)
sockets = XMLProperty(xpath="./topology/@sockets", is_int=True)
cores = XMLProperty(xpath="./topology/@cores", is_int=True)
threads = XMLProperty(xpath="./topology/@threads", is_int=True)

View File

@ -19,7 +19,65 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
from virtinst.xmlbuilder import XMLBuilder, XMLProperty
from virtinst.xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
class VirtualDeviceAlias(XMLBuilder):
_XML_ROOT_NAME = "alias"
name = XMLProperty(xpath="./@name")
class VirtualDeviceAddress(XMLBuilder):
"""
Examples:
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
<address type='drive' controller='0' bus='0' unit='0'/>
<address type='ccid' controller='0' slot='0'/>
<address type='virtio-serial' controller='1' bus='0' port='4'/>
"""
ADDRESS_TYPE_PCI = "pci"
ADDRESS_TYPE_DRIVE = "drive"
ADDRESS_TYPE_VIRTIO_SERIAL = "virtio-serial"
ADDRESS_TYPE_CCID = "ccid"
ADDRESS_TYPE_SPAPR_VIO = "spapr-vio"
TYPES = [ADDRESS_TYPE_PCI, ADDRESS_TYPE_DRIVE,
ADDRESS_TYPE_VIRTIO_SERIAL, ADDRESS_TYPE_CCID,
ADDRESS_TYPE_SPAPR_VIO]
_XML_ROOT_NAME = "address"
_XML_PROP_ORDER = ["type", "domain", "controller", "bus", "slot",
"function", "target", "unit", "multifunction"]
def set_addrstr(self, addrstr):
if addrstr is None:
return
if addrstr.count(":") in [1, 2] and addrstr.count("."):
self.type = self.ADDRESS_TYPE_PCI
addrstr, self.function = addrstr.split(".", 1)
addrstr, self.slot = addrstr.rsplit(":", 1)
self.domain = "0"
if addrstr.count(":"):
self.domain, self.bus = addrstr.split(":", 1)
elif addrstr == "spapr-vio":
self.type = self.ADDRESS_TYPE_SPAPR_VIO
else:
raise ValueError(_("Could not determine or unsupported "
"format of '%s'") % addrstr)
type = XMLProperty(xpath="./@type")
domain = XMLProperty(xpath="./@domain", is_int=True)
bus = XMLProperty(xpath="./@bus", is_int=True)
slot = XMLProperty(xpath="./@slot", is_int=True)
function = XMLProperty(xpath="./@function", is_int=True)
controller = XMLProperty(xpath="./@controller", is_int=True)
unit = XMLProperty(xpath="./@unit", is_int=True)
port = XMLProperty(xpath="./@port", is_int=True)
target = XMLProperty(xpath="./@target", is_int=True)
multifunction = XMLProperty(xpath="./@multifunction", is_onoff=True)
class VirtualDevice(XMLBuilder):
@ -71,23 +129,19 @@ class VirtualDevice(XMLBuilder):
@classmethod
def register_type(cls):
cls._XML_ROOT_XPATH = "/domain/devices/%s" % cls.virtual_device_type
cls._XML_ROOT_NAME = cls.virtual_device_type
VirtualDevice.virtual_device_classes[cls.virtual_device_type] = cls
# General device type (disk, interface, etc.)
virtual_device_type = None
def __init__(self, conn, parsexml=None, parsexmlnode=None):
def __init__(self, *args, **kwargs):
"""
Initialize device state
@param conn: libvirt connection to validate device against
"""
XMLBuilder.__init__(self, conn, parsexml, parsexmlnode)
self.alias = VirtualDeviceAlias(conn, parsexmlnode=parsexmlnode)
self.address = VirtualDeviceAddress(conn, parsexmlnode=parsexmlnode)
XMLBuilder.__init__(self, *args, **kwargs)
self._XML_PROP_ORDER = self._XML_PROP_ORDER + ["alias", "address"]
if not self.virtual_device_type:
@ -97,6 +151,9 @@ class VirtualDevice(XMLBuilder):
raise ValueError(_("Unknown virtual device type '%s'.") %
self.virtual_device_type)
alias = XMLChildProperty(VirtualDeviceAlias, is_single=True)
address = XMLChildProperty(VirtualDeviceAddress, is_single=True)
def setup(self, meter=None):
"""
@ -108,61 +165,3 @@ class VirtualDevice(XMLBuilder):
# Will be overwritten by subclasses if necessary.
ignore = meter
return
class VirtualDeviceAlias(XMLBuilder):
_XML_ROOT_XPATH = "/alias"
name = XMLProperty(xpath="./alias/@name")
class VirtualDeviceAddress(XMLBuilder):
"""
Examples:
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
<address type='drive' controller='0' bus='0' unit='0'/>
<address type='ccid' controller='0' slot='0'/>
<address type='virtio-serial' controller='1' bus='0' port='4'/>
"""
ADDRESS_TYPE_PCI = "pci"
ADDRESS_TYPE_DRIVE = "drive"
ADDRESS_TYPE_VIRTIO_SERIAL = "virtio-serial"
ADDRESS_TYPE_CCID = "ccid"
ADDRESS_TYPE_SPAPR_VIO = "spapr-vio"
TYPES = [ADDRESS_TYPE_PCI, ADDRESS_TYPE_DRIVE,
ADDRESS_TYPE_VIRTIO_SERIAL, ADDRESS_TYPE_CCID,
ADDRESS_TYPE_SPAPR_VIO]
_XML_ROOT_XPATH = "/address"
_XML_PROP_ORDER = ["type", "domain", "controller", "bus", "slot",
"function", "target", "unit", "multifunction"]
def set_addrstr(self, addrstr):
if addrstr is None:
return
if addrstr.count(":") in [1, 2] and addrstr.count("."):
self.type = self.ADDRESS_TYPE_PCI
addrstr, self.function = addrstr.split(".", 1)
addrstr, self.slot = addrstr.rsplit(":", 1)
self.domain = "0"
if addrstr.count(":"):
self.domain, self.bus = addrstr.split(":", 1)
elif addrstr == "spapr-vio":
self.type = self.ADDRESS_TYPE_SPAPR_VIO
else:
raise ValueError(_("Could not determine or unsupported "
"format of '%s'") % addrstr)
type = XMLProperty(xpath="./address/@type")
domain = XMLProperty(xpath="./address/@domain", is_int=True)
bus = XMLProperty(xpath="./address/@bus", is_int=True)
slot = XMLProperty(xpath="./address/@slot", is_int=True)
function = XMLProperty(xpath="./address/@function", is_int=True)
controller = XMLProperty(xpath="./address/@controller", is_int=True)
unit = XMLProperty(xpath="./address/@unit", is_int=True)
port = XMLProperty(xpath="./address/@port", is_int=True)
target = XMLProperty(xpath="./address/@target", is_int=True)
multifunction = XMLProperty(xpath="./address/@multifunction", is_onoff=True)

View File

@ -397,8 +397,8 @@ class VirtualDisk(VirtualDevice):
"_xmlpath", "target", "bus",
]
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
def __init__(self, *args, **kwargs):
VirtualDevice.__init__(self, *args, **kwargs)
self.__storage_backend = None
self._storage_creator = None

View File

@ -94,8 +94,8 @@ class VirtualGraphics(VirtualDevice):
return str(gtype).capitalize()
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
def __init__(self, *args, **kwargs):
VirtualDevice.__init__(self, *args, **kwargs)
self._local_keymap = -1

View File

@ -22,7 +22,7 @@ import random
from virtinst import util
from virtinst import VirtualDevice
from virtinst.xmlbuilder import XMLBuilder, XMLProperty
from virtinst.xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
def _random_mac(conn):
@ -53,15 +53,13 @@ def _random_mac(conn):
class VirtualPort(XMLBuilder):
_XML_ROOT_XPATH = "/domain/devices/interface/virtualport"
_XML_ROOT_NAME = "virtualport"
type = XMLProperty(xpath="./virtualport/@type")
managerid = XMLProperty(xpath="./virtualport/parameters/@managerid",
is_int=True)
typeid = XMLProperty(xpath="./virtualport/parameters/@typeid", is_int=True)
typeidversion = XMLProperty(
xpath="./virtualport/parameters/@typeidversion", is_int=True)
instanceid = XMLProperty(xpath="./virtualport/parameters/@instanceid")
type = XMLProperty("./@type")
managerid = XMLProperty("./parameters/@managerid", is_int=True)
typeid = XMLProperty("./parameters/@typeid", is_int=True)
typeidversion = XMLProperty("./parameters/@typeidversion", is_int=True)
instanceid = XMLProperty("./parameters/@instanceid")
class VirtualNetworkInterface(VirtualDevice):
@ -133,15 +131,12 @@ class VirtualNetworkInterface(VirtualDevice):
return (False, None)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self.virtualport = VirtualPort(conn, parsexml, parsexmlnode)
def __init__(self, *args, **kwargs):
VirtualDevice.__init__(self, *args, **kwargs)
self._random_mac = None
self._default_bridge = None
def _generate_default_bridge(self):
ret = self._default_bridge
if ret is None:
@ -201,6 +196,7 @@ class VirtualNetworkInterface(VirtualDevice):
"macaddr", "target_dev", "model", "virtualport",
"filterref"]
virtualport = XMLChildProperty(VirtualPort, is_single=True)
type = XMLProperty(xpath="./@type",
default_cb=lambda s: s.TYPE_BRIDGE)

View File

@ -24,16 +24,16 @@ class DomainFeatures(XMLBuilder):
"""
Class for generating <features> XML
"""
_XML_ROOT_XPATH = "/domain/features"
_XML_ROOT_NAME = "features"
_XML_PROP_ORDER = ["acpi", "apic", "pae"]
acpi = XMLProperty(xpath="./features/acpi", is_bool=True,
acpi = XMLProperty(xpath="./acpi", is_bool=True,
default_name="default",
default_cb=lambda s: False)
apic = XMLProperty(xpath="./features/apic", is_bool=True,
apic = XMLProperty(xpath="./apic", is_bool=True,
default_name="default",
default_cb=lambda s: False)
pae = XMLProperty(xpath="./features/pae", is_bool=True,
pae = XMLProperty(xpath="./pae", is_bool=True,
default_name="default",
default_cb=lambda s: False)

View File

@ -146,8 +146,8 @@ class DomainNumatune(XMLBuilder):
MEMORY_MODES = ["interleave", "strict", "preferred"]
_XML_ROOT_XPATH = "/domain/numatune"
_XML_ROOT_NAME = "numatune"
_XML_PROP_ORDER = ["memory_mode", "memory_nodeset"]
memory_nodeset = XMLProperty(xpath="./numatune/memory/@nodeset")
memory_mode = XMLProperty(xpath="./numatune/memory/@mode")
memory_nodeset = XMLProperty(xpath="./memory/@nodeset")
memory_mode = XMLProperty(xpath="./memory/@mode")

View File

@ -84,15 +84,15 @@ class Guest(XMLBuilder):
raise ValueError(_("Guest name '%s' is already in use.") % name)
_XML_ROOT_XPATH = "/domain"
_XML_ROOT_NAME = "domain"
_XML_PROP_ORDER = ["type", "name", "uuid", "description",
"maxmemory", "memory", "hugepage", "vcpus", "curvcpus",
"numatune", "bootloader", "os", "features", "cpu", "clock",
"on_poweroff", "on_reboot", "on_crash", "emulator", "all_devices",
"seclabel"]
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilder.__init__(self, conn, parsexml, parsexmlnode)
def __init__(self, *args, **kwargs):
XMLBuilder.__init__(self, *args, **kwargs)
self.autostart = False
self.replace = False
@ -105,13 +105,7 @@ class Guest(XMLBuilder):
# The libvirt virDomain object we 'Create'
self.domain = None
self.installer = virtinst.DistroInstaller(conn)
self.os = OSXML(self.conn, None, self._xml_node)
self.features = DomainFeatures(self.conn, None, self._xml_node)
self.clock = Clock(self.conn, None, self._xml_node)
self.seclabel = Seclabel(self.conn, None, self._xml_node)
self.cpu = CPU(self.conn, None, self._xml_node)
self.numatune = DomainNumatune(self.conn, None, self._xml_node)
self.installer = virtinst.DistroInstaller(self.conn)
######################
@ -173,6 +167,13 @@ class Guest(XMLBuilder):
on_reboot = XMLProperty(xpath="./on_reboot")
on_crash = XMLProperty(xpath="./on_crash")
os = XMLChildProperty(OSXML, is_single=True)
features = XMLChildProperty(DomainFeatures, is_single=True)
clock = XMLChildProperty(Clock, is_single=True)
seclabel = XMLChildProperty(Seclabel, is_single=True)
cpu = XMLChildProperty(CPU, is_single=True)
numatune = XMLChildProperty(DomainNumatune, is_single=True)
###############################
# Distro detection properties #
@ -225,7 +226,8 @@ class Guest(XMLBuilder):
return newlist
_devices = XMLChildProperty(
[VirtualDevice.virtual_device_classes[_n]
for _n in VirtualDevice.virtual_device_types])
for _n in VirtualDevice.virtual_device_types],
relative_xpath="./devices")
def get_all_devices(self):
"""
@ -266,11 +268,14 @@ class Guest(XMLBuilder):
# We do a shallow copy of the device list here, and set the defaults.
# This way, default changes aren't persistent, and we don't need
# to worry about when to call set_defaults
#
# XXX: this is hacky, we should find a way to use xmlbuilder.copy(),
# but need to make sure it's not a massive performance hit
data = (self._devices[:], self.features, self.os)
try:
self._propstore["_devices"] = [dev.copy() for dev in self._devices]
self.features = self.features.copy()
self.os = self.os.copy()
self._propstore["features"] = self.features.copy()
self._propstore["os"] = self.os.copy()
support.set_rhel6(self._is_rhel6())
except:
self._finish_get_xml(data)
@ -278,7 +283,9 @@ class Guest(XMLBuilder):
return data
def _finish_get_xml(self, data):
self._propstore["_devices"], self.features, self.os = data
(self._propstore["_devices"],
self._propstore["features"],
self._propstore["os"]) = data
support.set_rhel6(False)
def get_install_xml(self, *args, **kwargs):

View File

@ -21,7 +21,7 @@ from virtinst.xmlbuilder import XMLBuilder, XMLProperty, XMLChildProperty
class _BootDevice(XMLBuilder):
_XML_ROOT_XPATH = "/domain/os/boot"
_XML_ROOT_NAME = "boot"
dev = XMLProperty("./@dev")
@ -54,7 +54,7 @@ class OSXML(XMLBuilder):
def is_pseries(self):
return self.is_ppc64 and self.machine == "pseries"
_XML_ROOT_XPATH = "/domain/os"
_XML_ROOT_NAME = "os"
_XML_PROP_ORDER = ["arch", "os_type", "loader",
"kernel", "initrd", "kernel_args", "dtb",
"_bootdevs"]
@ -72,17 +72,17 @@ class OSXML(XMLBuilder):
_bootdevs = XMLChildProperty(_BootDevice)
bootorder = property(_get_bootorder, _set_bootorder)
enable_bootmenu = XMLProperty(xpath="./os/bootmenu/@enable", is_yesno=True)
useserial = XMLProperty(xpath="./os/bios/@useserial", is_yesno=True)
enable_bootmenu = XMLProperty(xpath="./bootmenu/@enable", is_yesno=True)
useserial = XMLProperty(xpath="./bios/@useserial", is_yesno=True)
kernel = XMLProperty(xpath="./os/kernel")
initrd = XMLProperty(xpath="./os/initrd")
kernel_args = XMLProperty(xpath="./os/cmdline")
dtb = XMLProperty(xpath="./os/dtb")
kernel = XMLProperty(xpath="./kernel")
initrd = XMLProperty(xpath="./initrd")
kernel_args = XMLProperty(xpath="./cmdline")
dtb = XMLProperty(xpath="./dtb")
init = XMLProperty(xpath="./os/init")
loader = XMLProperty(xpath="./os/loader")
arch = XMLProperty(xpath="./os/type/@arch",
init = XMLProperty(xpath="./init")
loader = XMLProperty(xpath="./loader")
arch = XMLProperty(xpath="./type/@arch",
default_cb=lambda s: s.conn.caps.host.arch)
machine = XMLProperty(xpath="./os/type/@machine")
os_type = XMLProperty(xpath="./os/type", default_cb=lambda s: "xen")
machine = XMLProperty(xpath="./type/@machine")
os_type = XMLProperty(xpath="./type", default_cb=lambda s: "xen")

View File

@ -38,7 +38,7 @@ class Seclabel(XMLBuilder):
MODEL_NONE = "none"
MODELS = [MODEL_SELINUX, MODEL_DAC, MODEL_NONE]
_XML_ROOT_XPATH = "/domain/seclabel"
_XML_ROOT_NAME = "seclabel"
_XML_PROP_ORDER = ["type", "model", "relabel", "label", "imagelabel"]
def _guess_secmodel(self):
@ -75,7 +75,7 @@ class Seclabel(XMLBuilder):
if self.type is None or self.type == self.TYPE_DEFAULT:
return None
return self._guess_secmodel()
model = XMLProperty(xpath="./seclabel/@model",
model = XMLProperty(xpath="./@model",
default_cb=_get_default_model,
default_name=MODEL_DEFAULT)
@ -83,10 +83,10 @@ class Seclabel(XMLBuilder):
if self.model is None or self.model == self.MODEL_DEFAULT:
return None
return self.TYPE_DYNAMIC
type = XMLProperty(xpath="./seclabel/@type",
type = XMLProperty(xpath="./@type",
default_cb=_get_default_type,
default_name=TYPE_DEFAULT)
label = XMLProperty(xpath="./seclabel/label")
imagelabel = XMLProperty(xpath="./seclabel/imagelabel")
relabel = XMLProperty(xpath="./seclabel/@relabel", is_yesno=True)
label = XMLProperty(xpath="./label")
imagelabel = XMLProperty(xpath="./imagelabel")
relabel = XMLProperty(xpath="./@relabel", is_yesno=True)

View File

@ -21,7 +21,7 @@ from virtinst.xmlbuilder import XMLBuilder, XMLProperty
class DomainSnapshot(XMLBuilder):
_XML_ROOT_XPATH = "/domainsnapshot"
_XML_ROOT_NAME = "domainsnapshot"
_XML_PROP_ORDER = ["name", "description", "creationTime"]
name = XMLProperty(xpath="./name")

View File

@ -259,9 +259,15 @@ class XMLChildProperty(property):
interfaces rooted at /interface/bridge/interface, so we pass
./bridge/interface here for example.
"""
def __init__(self, child_classes, relative_xpath="."):
def __init__(self, child_classes, relative_xpath=".", is_single=False):
self.child_classes = util.listify(child_classes)
self.relative_xpath = relative_xpath
self.is_single = is_single
if self.is_single and len(self.child_classes) > 1:
raise RuntimeError("programming error: Can't specify multiple "
"child_classes with is_single")
property.__init__(self, self._fget)
def __repr__(self):
@ -273,23 +279,33 @@ class XMLChildProperty(property):
return key
raise RuntimeError("Didn't find expected property=%s" % self)
def _get_list(self, xmlbuilder):
def _get(self, xmlbuilder):
propname = self._findpropname(xmlbuilder)
if propname not in xmlbuilder._propstore:
if propname not in xmlbuilder._propstore and not self.is_single:
xmlbuilder._propstore[propname] = []
return xmlbuilder._propstore[propname]
def _fget(self, xmlbuilder):
return self._get_list(xmlbuilder)[:]
if self.is_single:
return self._get(xmlbuilder)
return self._get(xmlbuilder)[:]
def clear(self, xmlbuilder):
if self.is_single:
self._get(xmlbuilder).clear()
else:
for obj in self._get(xmlbuilder)[:]:
xmlbuilder._remove_child(obj)
def append(self, xmlbuilder, obj):
self._get_list(xmlbuilder).append(obj)
self._get(xmlbuilder).append(obj)
def remove(self, xmlbuilder, obj):
self._get_list(xmlbuilder).remove(obj)
self._get(xmlbuilder).remove(obj)
def set(self, xmlbuilder, obj):
xmlbuilder._propstore[self._findpropname(xmlbuilder)] = obj
def get_prop_xpath(self, xmlbuilder, child_class):
child_root = child_class._XML_ROOT_XPATH
relative_xpath = self.relative_xpath
def get_prop_xpath(self, xmlbuilder, obj):
relative_xpath = self.relative_xpath + "/" + obj._XML_ROOT_NAME
match = re.search("%\((.*)\)", self.relative_xpath)
if match:
@ -297,8 +313,8 @@ class XMLChildProperty(property):
for paramname in match.groups():
valuedict[paramname] = getattr(xmlbuilder, paramname)
relative_xpath = relative_xpath % valuedict
return child_root.replace(xmlbuilder._xmlstate.get_node_top_xpath(),
relative_xpath)
return relative_xpath
class XMLProperty(property):
@ -543,7 +559,7 @@ class XMLProperty(property):
propname = self._findpropname(xmlbuilder)
return xmlbuilder._propstore.get(propname, None)
def _clear(self, xmlbuilder):
def clear(self, xmlbuilder):
self.setter(xmlbuilder, None)
self._set_xml(xmlbuilder, None)
@ -628,13 +644,10 @@ class XMLProperty(property):
class _XMLState(object):
def __init__(self, xpath, parsexml, parsexmlnode):
if xpath is None or not xpath.startswith("/"):
raise RuntimeError("xpath=%s must start with /" % xpath)
self.orig_root_xpath = xpath
self.root_name = xpath.split("/")[-1]
self.indent = (xpath.count("/") - 1) * 2
def __init__(self, root_name, parsexml, parsexmlnode,
parent_xpath, relative_object_xpath):
self.root_name = root_name
self.stub_path = "/%s" % self.root_name
self.xml_ctx = None
self.xml_node = None
@ -643,12 +656,12 @@ class _XMLState(object):
# xpath of this object relative to its parent. So for a standalone
# <disk> this is empty, but if the disk is the forth one in a <domain>
# it will be set to ./devices/disk[4]
self._relative_object_xpath = ""
self._relative_object_xpath = relative_object_xpath or ""
# xpath of the parent. For a disk in a standalone <domain>, this
# is empty, but if the <domain> is part of a <domainsnapshot>,
# it will be "./domain"
self._parent_xpath = ""
self._parent_xpath = parent_xpath or ""
self.is_build = False
if not parsexml and not parsexmlnode:
@ -668,7 +681,7 @@ class _XMLState(object):
self.xml_root_doc = _DocCleanupWrapper(doc)
self.xml_node = doc.children
self.xml_node.virtinst_is_build = self.is_build
self.xml_node.virtinst_node_top_xpath = self.orig_root_xpath
self.xml_node.virtinst_node_top_xpath = self.stub_path
# This just stores a reference to our root doc wrapper in
# the root node, so when the node goes away it triggers
@ -678,29 +691,24 @@ class _XMLState(object):
self.xml_ctx = _make_xml_context(self.xml_node)
def make_xml_stub(self):
return _indent(("<%s/>" % self.root_name), self.indent)
return "<%s/>" % self.root_name
def set_relative_object_xpath(self, xpath):
self._relative_object_xpath = xpath or ""
def get_relative_object_xpath(self):
return self._relative_object_xpath
def set_parent_xpath(self, xpath):
self._parent_xpath = xpath or ""
def get_parent_xpath(self):
return self._parent_xpath
def get_root_xpath(self):
fullpath = self._parent_xpath or ""
if not fullpath:
return self._relative_object_xpath
if self._relative_object_xpath:
fullpath += "/" + self._relative_object_xpath[2:]
return fullpath
relpath = self._relative_object_xpath
if not self._parent_xpath:
return relpath
return self._parent_xpath + (relpath.startswith(".") and
relpath[1:] or relpath)
def fix_relative_xpath(self, xpath):
fullpath = self.get_root_xpath()
if not fullpath:
if not fullpath or fullpath == self.stub_path:
return xpath
if xpath.startswith("."):
return "%s%s" % (fullpath, xpath.strip("."))
@ -714,16 +722,11 @@ class _XMLState(object):
"""
return self.xml_node.virtinst_node_top_xpath
def _get_dump_xpath(self):
if self.xml_root_doc or self.get_root_xpath():
return self.fix_relative_xpath(".")
return self.orig_root_xpath
def get_node_xml(self, ctx=None):
if ctx is None:
ctx = self.xml_ctx
node = _get_xpath_node(ctx, self._get_dump_xpath())
node = _get_xpath_node(ctx, self.fix_relative_xpath("."))
if not node:
return ""
return _sanitize_libxml_xml(node.serialize())
@ -737,10 +740,11 @@ class XMLBuilder(object):
# consistent with what the test suite expects.
_XML_PROP_ORDER = []
# Absolute xpath this object is rooted at
_XML_ROOT_XPATH = None
# Name of the root XML element
_XML_ROOT_NAME = None
def __init__(self, conn, parsexml=None, parsexmlnode=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None,
parent_xpath=None, relative_object_xpath=None):
"""
Initialize state
@ -748,26 +752,45 @@ class XMLBuilder(object):
@type conn: VirtualConnection
@param parsexml: Optional XML string to parse
@type parsexml: C{str}
@param parsexmlnode: Option xpathNode to use
The rest of the parameters are for internal use only
"""
self.conn = conn
self._propstore = {}
self._proporder = []
self._xmlstate = _XMLState(self._XML_ROOT_XPATH,
parsexml, parsexmlnode)
self._xmlstate = _XMLState(self._XML_ROOT_NAME,
parsexml, parsexmlnode,
parent_xpath, relative_object_xpath)
# Walk the XML tree and hand of parsing to any registered
# child classes
for xmlprop in self._all_child_props().values():
if xmlprop.is_single:
child_class = xmlprop.child_classes[0]
prop_path = xmlprop.get_prop_xpath(self, child_class)
obj = child_class(self.conn,
parsexmlnode=self._xml_node,
parent_xpath=self.get_root_xpath(),
relative_object_xpath=prop_path)
xmlprop.set(self, obj)
continue
if self._xmlstate.is_build:
continue
for child_class in xmlprop.child_classes:
prop_path = xmlprop.get_prop_xpath(self, child_class)
nodecount = int(self._xml_node.xpathEval(
"count(%s)" % prop_path))
for ignore in range(nodecount):
xmlprop.append(self,
child_class(self.conn, parsexmlnode=self._xml_node))
"count(%s)" % self.fix_relative_xpath(prop_path)))
for idx in range(nodecount):
idxstr = "[%d]" % (idx + 1)
obj = child_class(self.conn,
parsexmlnode=self._xml_node,
parent_xpath=self.get_root_xpath(),
relative_object_xpath=(prop_path + idxstr))
xmlprop.append(self, obj)
self._set_child_xpaths()
@ -817,11 +840,10 @@ class XMLBuilder(object):
"""
Wipe out all properties of the object
"""
for prop in self._all_xml_props().values():
prop._clear(self)
for prop in self._all_child_props().values():
for obj in prop._get_list(self)[:]:
self._remove_child(obj)
props = self._all_xml_props().values()
props += self._all_child_props().values()
for prop in props:
prop.clear(self)
def validate(self):
"""
@ -897,6 +919,8 @@ class XMLBuilder(object):
def _find_child_prop(self, child_class):
xmlprops = self._all_child_props()
for xmlprop in xmlprops.values():
if xmlprop.is_single:
continue
if child_class in xmlprop.child_classes:
return xmlprop
raise RuntimeError("programming error: "
@ -968,17 +992,18 @@ class XMLBuilder(object):
"""
typecount = {}
for propname, xmlprop in self._all_child_props().items():
for obj in getattr(self, propname):
class_type = obj.__class__
if class_type not in typecount:
typecount[class_type] = 0
idx = typecount[class_type]
for obj in util.listify(getattr(self, propname)):
idxstr = ""
if not xmlprop.is_single:
class_type = obj.__class__
if class_type not in typecount:
typecount[class_type] = 0
typecount[class_type] += 1
idxstr = "[%d]" % typecount[class_type]
prop_path = xmlprop.get_prop_xpath(self, obj)
obj._set_parent_xpath(self.get_root_xpath())
obj._set_relative_object_xpath(prop_path + ("[%d]" % (idx + 1)))
typecount[class_type] += 1
obj._set_relative_object_xpath(prop_path + idxstr)
def _do_get_xml_config(self):
xmlstub = self._xmlstate.make_xml_stub()