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()