diff --git a/virtinst/clock.py b/virtinst/clock.py
index 62f6c0be..46571680 100644
--- a/virtinst/clock.py
+++ b/virtinst/clock.py
@@ -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")
diff --git a/virtinst/cpu.py b/virtinst/cpu.py
index b2fe1e40..d651d7b5 100644
--- a/virtinst/cpu.py
+++ b/virtinst/cpu.py
@@ -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)
diff --git a/virtinst/device.py b/virtinst/device.py
index 656a3101..11004853 100644
--- a/virtinst/device.py
+++ b/virtinst/device.py
@@ -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 = "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 = "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)
diff --git a/virtinst/devicedisk.py b/virtinst/devicedisk.py
index 5beb7f8c..4348dcfd 100644
--- a/virtinst/devicedisk.py
+++ b/virtinst/devicedisk.py
@@ -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
diff --git a/virtinst/devicegraphics.py b/virtinst/devicegraphics.py
index 63ca633a..781d97dd 100644
--- a/virtinst/devicegraphics.py
+++ b/virtinst/devicegraphics.py
@@ -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
diff --git a/virtinst/deviceinterface.py b/virtinst/deviceinterface.py
index 8412b830..69016d74 100644
--- a/virtinst/deviceinterface.py
+++ b/virtinst/deviceinterface.py
@@ -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)
diff --git a/virtinst/domainfeatures.py b/virtinst/domainfeatures.py
index b2934b8f..8ca9c386 100644
--- a/virtinst/domainfeatures.py
+++ b/virtinst/domainfeatures.py
@@ -24,16 +24,16 @@ class DomainFeatures(XMLBuilder):
"""
Class for generating 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)
diff --git a/virtinst/domainnumatune.py b/virtinst/domainnumatune.py
index 61caca17..cebdcc39 100644
--- a/virtinst/domainnumatune.py
+++ b/virtinst/domainnumatune.py
@@ -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")
diff --git a/virtinst/guest.py b/virtinst/guest.py
index 3b7a3c66..757330ec 100644
--- a/virtinst/guest.py
+++ b/virtinst/guest.py
@@ -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):
diff --git a/virtinst/osxml.py b/virtinst/osxml.py
index 9a324611..8d429ed5 100644
--- a/virtinst/osxml.py
+++ b/virtinst/osxml.py
@@ -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")
diff --git a/virtinst/seclabel.py b/virtinst/seclabel.py
index 43e5c117..2d4083fe 100644
--- a/virtinst/seclabel.py
+++ b/virtinst/seclabel.py
@@ -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)
diff --git a/virtinst/snapshot.py b/virtinst/snapshot.py
index befb31b9..be6b10e2 100644
--- a/virtinst/snapshot.py
+++ b/virtinst/snapshot.py
@@ -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")
diff --git a/virtinst/xmlbuilder.py b/virtinst/xmlbuilder.py
index 25ff4241..148e32e3 100644
--- a/virtinst/xmlbuilder.py
+++ b/virtinst/xmlbuilder.py
@@ -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
# this is empty, but if the disk is the forth one in a
# 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 , this
# is empty, but if the is part of a ,
# 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()