XMLBuilder: Drop some is_parse checking

This commit is contained in:
Cole Robinson 2013-07-24 08:46:55 -04:00
parent 2ba3ec2684
commit e0190f7b5e
18 changed files with 177 additions and 142 deletions

View File

@ -33,8 +33,8 @@ from virtinst import util
from virtinst import support
from virtinst.osxml import OSXML
from virtinst.xmlbuilder import XMLBuilder, XMLProperty
from virtinst.VirtualDevice import VirtualDevice
from virtinst.VirtualDisk import VirtualDisk
from virtinst.VirtualDevice import VirtualDevice
from virtinst.Clock import Clock
from virtinst.Seclabel import Seclabel
from virtinst.CPU import CPU
@ -191,19 +191,18 @@ class Guest(XMLBuilder):
self.domain = None
self._consolechild = None
# Since we overwrite _parsexml handling, need to set up some
# internal state before calling __init__
XMLBuilder.__init__(self, conn, parsexml)
if self._is_parse():
return
self.installer = virtinst.DistroInstaller(conn)
# Need to do this after all parameter init
self.os = OSXML(self.conn, parsexml, parsexmlnode)
self.features = DomainFeatures(self.conn)
self.clock = Clock(self.conn)
self.seclabel = Seclabel(self.conn)
self.cpu = CPU(self.conn)
self.numatune = DomainNumatune(self.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)
######################
@ -340,16 +339,13 @@ class Guest(XMLBuilder):
@param dev: VirtualDevice instance to attach to guest
@param set_defaults: Whether to set defaults for the device
"""
if not isinstance(dev, VirtualDevice):
raise ValueError(_("Must pass a VirtualDevice instance."))
if self._is_parse():
xml = dev.get_xml_config()
node = libxml2.parseDoc(xml).children
dev.set_xml_node(node)
self._add_child_node("./devices", node)
self._add_child("./devices", dev)
self._track_device(dev)
if self._is_parse():
self._recalculate_device_xpaths()
if set_defaults:
origdev = self._devices
try:
@ -358,10 +354,10 @@ class Guest(XMLBuilder):
except:
self._devices = origdev
def _track_device(self, dev):
self._devices.append(dev)
def get_devices(self, devtype):
"""
Return a list of devices of type 'devtype' that will installed on
@ -403,9 +399,10 @@ class Guest(XMLBuilder):
raise ValueError(_("Did not find device %s") % str(dev))
if self._is_parse():
xpath = dev.get_xml_node_path()
xpath = dev.get_root_xpath()
if xpath:
self._remove_child_xpath(xpath)
self._recalculate_device_xpaths()
################################
@ -415,51 +412,32 @@ class Guest(XMLBuilder):
def _parsexml(self, xml, node):
XMLBuilder._parsexml(self, xml, node)
device_mappings = {
"disk" : virtinst.VirtualDisk,
"interface" : virtinst.VirtualNetworkInterface,
"sound" : virtinst.VirtualAudio,
"hostdev" : virtinst.VirtualHostDevice,
"input" : virtinst.VirtualInputDevice,
"serial" : virtinst.VirtualSerialDevice,
"parallel" : virtinst.VirtualParallelDevice,
"console" : virtinst.VirtualConsoleDevice,
"channel" : virtinst.VirtualChannelDevice,
"graphics" : virtinst.VirtualGraphics,
"video" : virtinst.VirtualVideoDevice,
"watchdog" : virtinst.VirtualWatchdog,
"controller": virtinst.VirtualController,
"filesystem": virtinst.VirtualFilesystem,
"smartcard" : virtinst.VirtualSmartCardDevice,
"redirdev" : virtinst.VirtualRedirDevice,
"memballoon": virtinst.VirtualMemballoon,
"tpm" : virtinst.VirtualTPMDevice,
}
# Hand off all child element parsing to relevant classes
for node in self._xml_node.children:
if node.name != "devices":
continue
children = [x for x in node.children if
(x.name in device_mappings and
x.parent == node)]
for devnode in children:
devnode.virtinst_root_doc = self._xml_root_doc
objclass = device_mappings.get(devnode.name)
dev = objclass(self.conn, parsexmlnode=devnode)
devnodes = [
x for x in node.children if
(x.name in VirtualDevice.virtual_device_classes and
x.parent == node)
]
for devnode in devnodes:
objclass = VirtualDevice.virtual_device_classes[devnode.name]
dev = objclass(self.conn, parsexmlnode=self._xml_node)
self._track_device(dev)
self._xml_node.virtinst_root_doc = self._xml_root_doc
self.os = OSXML(self.conn, parsexmlnode=self._xml_node)
self.features = DomainFeatures(self.conn,
parsexmlnode=self._xml_node)
self.clock = Clock(self.conn, parsexmlnode=self._xml_node)
self.seclabel = Seclabel(self.conn, parsexmlnode=self._xml_node)
self.cpu = CPU(self.conn, parsexmlnode=self._xml_node)
self.numatune = DomainNumatune(self.conn,
parsexmlnode=self._xml_node)
self._recalculate_device_xpaths()
def _recalculate_device_xpaths(self):
count = {}
for dev in self.get_all_devices():
devtype = dev.virtual_device_type
if devtype not in count:
count[devtype] = 1
newpath = "./devices/%s[%d]" % (devtype, count[devtype])
dev.set_root_xpath(newpath)
count[devtype] += 1
def add_default_input_device(self):
if self.os.is_container():
@ -573,15 +551,7 @@ class Guest(XMLBuilder):
self.bootloader = "/usr/bin/pygrub"
self.os.clear()
count = {}
for dev in self.get_all_devices():
devtype = dev.virtual_device_type
if devtype not in count:
count[devtype] = 1
newpath = "./devices/%s[%d]" % (devtype, count[devtype])
setattr(dev, "_XML_NEW_ROOT_PATH", newpath)
count[devtype] += 1
self._recalculate_device_xpaths()
return self._make_xml_stub()
def get_continue_inst(self):
@ -875,7 +845,7 @@ class Guest(XMLBuilder):
# Keep cdrom around, but with no media attached,
# But only if we are a distro that doesn't have a multi
# stage install (aka not Windows)
return (d.virtual_device_type == VirtualDevice.VIRTUAL_DEV_DISK and
return (d.virtual_device_type == "disk" and
d.device == VirtualDisk.DEVICE_CDROM
and d.transient
and not install and
@ -883,7 +853,7 @@ class Guest(XMLBuilder):
def do_skip_disk(d):
# Skip transient labeled non-media disks
return (d.virtual_device_type == VirtualDevice.VIRTUAL_DEV_DISK and
return (d.virtual_device_type == "disk" and
d.device == VirtualDisk.DEVICE_DISK
and d.transient
and not install)
@ -942,17 +912,15 @@ class Guest(XMLBuilder):
self.os.bootorder = []
def _set_hvm_defaults(self):
disktype = VirtualDevice.VIRTUAL_DEV_DISK
nettype = VirtualDevice.VIRTUAL_DEV_NET
disk_bus = self._lookup_device_param(disktype, "bus")
net_model = self._lookup_device_param(nettype, "model")
disk_bus = self._lookup_device_param("disk", "bus")
net_model = self._lookup_device_param("interface", "model")
# Only overwrite params if they weren't already specified
for net in self.get_devices(nettype):
for net in self.get_devices("interface"):
if net_model and not net.model:
net.model = net_model
for disk in self.get_devices(disktype):
for disk in self.get_devices("disk"):
if (disk_bus and not disk.bus and
disk.device == VirtualDisk.DEVICE_DISK):
disk.bus = disk_bus
@ -966,13 +934,13 @@ class Guest(XMLBuilder):
def _set_pv_defaults(self):
# Default file backed PV guests to tap driver
for d in self.get_devices(VirtualDevice.VIRTUAL_DEV_DISK):
for d in self.get_devices("disk"):
if (d.type == VirtualDisk.TYPE_FILE
and d.driver_name is None
and util.is_blktap_capable(self.conn)):
d.driver_name = VirtualDisk.DRIVER_TAP
for d in self.get_devices(VirtualDevice.VIRTUAL_DEV_INPUT):
for d in self.get_devices("input"):
if d.type == d.TYPE_DEFAULT:
d.type = d.TYPE_MOUSE
if d.bus == d.BUS_DEFAULT:
@ -1001,7 +969,7 @@ class Guest(XMLBuilder):
# Add spapr-vio controller if needed
if (dev.address.type == "spapr-vio" and
dev.virtual_device_type == VirtualDevice.VIRTUAL_DEV_DISK and
dev.virtual_device_type == "disk" and
not any([cont.address.type == "spapr-vio" for cont in
self.get_devices("controller")])):
ctrl = virtinst.VirtualController(self.conn)
@ -1015,16 +983,10 @@ class Guest(XMLBuilder):
if self.os.is_xenpv():
self._set_pv_defaults()
soundtype = VirtualDevice.VIRTUAL_DEV_AUDIO
videotype = VirtualDevice.VIRTUAL_DEV_VIDEO
inputtype = VirtualDevice.VIRTUAL_DEV_INPUT
gfxtype = VirtualDevice.VIRTUAL_DEV_GRAPHICS
channeltype = VirtualDevice.VIRTUAL_DEV_CHANNEL
# Set default input values
input_type = self._lookup_device_param(inputtype, "type")
input_bus = self._lookup_device_param(inputtype, "bus")
for inp in self.get_devices(inputtype):
input_type = self._lookup_device_param("input", "type")
input_bus = self._lookup_device_param("input", "bus")
for inp in self.get_devices("input"):
if (inp.type == inp.TYPE_DEFAULT and
inp.bus == inp.BUS_DEFAULT):
inp.type = input_type
@ -1032,7 +994,7 @@ class Guest(XMLBuilder):
# Generate disk targets, and set preferred disk bus
used_targets = []
for disk in self.get_devices(VirtualDevice.VIRTUAL_DEV_DISK):
for disk in self.get_devices("disk"):
if not disk.bus:
if disk.device == disk.DEVICE_FLOPPY:
disk.bus = "fdc"
@ -1051,29 +1013,29 @@ class Guest(XMLBuilder):
used_targets.append(disk.generate_target(used_targets))
# Set sound device model
sound_model = self._lookup_device_param(soundtype, "model")
for sound in self.get_devices(soundtype):
sound_model = self._lookup_device_param("sound", "model")
for sound in self.get_devices("sound"):
if sound.model == sound.MODEL_DEFAULT:
sound.model = sound_model
# Set video device model
# QXL device (only if we use spice) - safe even if guest is VGA only
def has_spice():
for gfx in self.get_devices(gfxtype):
for gfx in self.get_devices("graphics"):
if gfx.type == gfx.TYPE_SPICE:
return True
if has_spice():
video_model = "qxl"
video_model = "qxl"
else:
video_model = self._lookup_device_param(videotype, "model")
video_model = self._lookup_device_param("video", "model")
for video in self.get_devices(videotype):
for video in self.get_devices("video"):
if video.model == video.MODEL_DEFAULT:
video.model = video_model
# Spice agent channel (only if we use spice)
def has_spice_agent():
for chn in self.get_devices(channeltype):
for chn in self.get_devices("channel"):
if chn.type == chn.TYPE_SPICEVMC:
return True

View File

@ -30,3 +30,5 @@ class VirtualAudio(VirtualDevice):
model = XMLProperty(xpath="./@model",
default_cb=lambda s: "es1370",
default_name=MODEL_DEFAULT)
VirtualAudio.register_type()

View File

@ -150,7 +150,12 @@ class _VirtualCharDevice(VirtualDevice):
xpath="./@type")
def _sourcepath_get_xpath(self):
return "./source/@path | ./@tty"
ret = "./source/@path"
for xpath in [ret, "./@tty"]:
if self._xml_ctx.xpathEval(self.fix_relative_xpath(xpath)):
ret = xpath
break
return ret
source_path = XMLProperty(make_getter_xpath_cb=_sourcepath_get_xpath,
doc=_("Host input path to attach to the guest."),
xpath="./source/@path")
@ -263,3 +268,9 @@ class VirtualParallelDevice(_VirtualCharDevice):
class VirtualChannelDevice(_VirtualCharDevice):
virtual_device_type = "channel"
TYPES = [_VirtualCharDevice.TYPE_SPICEVMC]
VirtualConsoleDevice.register_type()
VirtualSerialDevice.register_type()
VirtualParallelDevice.register_type()
VirtualChannelDevice.register_type()

View File

@ -91,3 +91,5 @@ class VirtualController(VirtualDevice):
master_startport = XMLProperty(xpath="./master/@startport", is_int=True)
index = XMLProperty(xpath="./@index", is_int=True, default_cb=lambda s: 0)
VirtualController.register_type()

View File

@ -19,6 +19,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
import virtinst
from virtinst.xmlbuilder import XMLBuilder, XMLProperty
@ -67,6 +68,12 @@ class VirtualDevice(XMLBuilder):
VIRTUAL_DEV_MEMBALLOON,
VIRTUAL_DEV_TPM]
virtual_device_classes = {}
@classmethod
def register_type(c):
VirtualDevice.virtual_device_classes[c.virtual_device_type] = c
# General device type (disk, interface, etc.)
virtual_device_type = None
_XML_INDENT = 4

View File

@ -398,9 +398,7 @@ class VirtualDisk(VirtualDevice):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._storage_backend = diskbackend.StorageBackend(self.conn,
self._xmlpath,
None, None)
self.__storage_backend = None
self._storage_creator = None
self.nomanaged = False
@ -487,7 +485,7 @@ class VirtualDisk(VirtualDevice):
ret = "./source/@file"
for prop in _TARGET_PROPS:
xpath = "./source/@" + prop
if self._xml_ctx.xpathEval(xpath):
if self._xml_ctx.xpathEval(self.fix_relative_xpath(xpath)):
ret = xpath
break
return ret
@ -531,6 +529,16 @@ class VirtualDisk(VirtualDevice):
# Validation assistance methods #
#################################
def _get_storage_backend(self):
if self.__storage_backend is None:
self.__storage_backend = diskbackend.StorageBackend(self.conn,
self._xmlpath,
None, None)
return self.__storage_backend
def _set_storage_backend(self, val):
self.__storage_backend = val
_storage_backend = property(_get_storage_backend, _set_storage_backend)
def set_create_storage(self, size=None, sparse=True,
fmt=None, vol_install=None, clone_path=None,
fake=False):
@ -543,8 +551,6 @@ class VirtualDisk(VirtualDevice):
@fake: If true, make like we are creating storage but fail
if we ever asked to do so.
"""
if self._is_parse():
raise ValueError("Cannot create storage for a parsed disk.")
path = self.path
# Validate clone_path
@ -841,3 +847,5 @@ class VirtualDisk(VirtualDevice):
self.target = t
return self.target
raise ValueError(_("No more space for disks of type '%s'" % prefix))
VirtualDisk.register_type()

View File

@ -91,9 +91,8 @@ class VirtualFilesystem(VirtualDevice):
ret = "./source/@dir"
for prop in self._target_props:
xpath = "./source/@" + prop
if self._xml_ctx.xpathEval(xpath):
if self._xml_ctx.xpathEval(self.fix_relative_xpath(xpath)):
ret = xpath
return ret
def _xml_set_source_xpath(self):
ret = "./source/@" + self.type_to_source_prop(self.type)
@ -116,3 +115,6 @@ class VirtualFilesystem(VirtualDevice):
return val
target = XMLProperty(xpath="./target/@dir",
set_converter=_validate_set_target)
VirtualFilesystem.register_type()

View File

@ -180,3 +180,6 @@ class VirtualGraphics(VirtualDevice):
passwd = XMLProperty(xpath="./@passwd")
passwdValidTo = XMLProperty(xpath="./@passwdValidTo")
socket = XMLProperty(xpath="./@socket")
VirtualGraphics.register_type()

View File

@ -105,3 +105,6 @@ class VirtualHostDevice(VirtualDevice):
default_cb=_get_default_domain)
function = XMLProperty(xpath="./source/address/@function")
slot = XMLProperty(xpath="./source/address/@slot")
VirtualHostDevice.register_type()

View File

@ -41,3 +41,6 @@ class VirtualInputDevice(VirtualDevice):
bus = XMLProperty(xpath="./@bus",
default_cb=lambda s: s.BUS_XEN,
default_name=BUS_DEFAULT)
VirtualInputDevice.register_type()

View File

@ -28,3 +28,6 @@ class VirtualMemballoon(VirtualDevice):
MODELS = ["xen", "none", MODEL_DEFAULT]
model = XMLProperty(xpath="./@model", default_cb=lambda s: s.MODEL_DEFAULT)
VirtualMemballoon.register_type()

View File

@ -202,8 +202,6 @@ class VirtualNetworkInterface(VirtualDevice):
default_cb=lambda s: s.TYPE_BRIDGE)
def _get_default_mac(self):
if self._is_parse():
return None
if not self._random_mac:
self._random_mac = self.generate_mac(self.conn)
return self._random_mac
@ -233,3 +231,6 @@ class VirtualNetworkInterface(VirtualDevice):
default_cb=_default_source_mode)
model = XMLProperty(xpath="./model/@type")
target_dev = XMLProperty(xpath="./target/@dev")
VirtualNetworkInterface.register_type()

View File

@ -52,3 +52,6 @@ class VirtualRedirDevice(VirtualDevice):
host = XMLProperty(xpath="./source/@host")
service = XMLProperty(xpath="./source/@service", is_int=True)
VirtualRedirDevice.register_type()

View File

@ -48,3 +48,6 @@ class VirtualSmartCardDevice(VirtualDevice):
type = XMLProperty(xpath="./@type",
default_cb=_default_type,
default_name=TYPE_DEFAULT)
VirtualSmartCardDevice.register_type()

View File

@ -63,3 +63,6 @@ class VirtualTPMDevice(VirtualDevice):
default_cb=lambda s: s.MODEL_TIS)
device_path = XMLProperty(xpath="./backend/device/@path",
default_cb=lambda s: "/dev/tpm0")
VirtualTPMDevice.register_type()

View File

@ -43,3 +43,6 @@ class VirtualVideoDevice(VirtualDevice):
vram = XMLProperty(xpath="./model/@vram", is_int=True)
ram = XMLProperty(xpath="./model/@ram", is_int=True)
heads = XMLProperty(xpath="./model/@heads", is_int=True)
VirtualVideoDevice.register_type()

View File

@ -63,3 +63,6 @@ class VirtualWatchdog(VirtualDevice):
action = XMLProperty(xpath="./@action",
default_name=ACTION_DEFAULT,
default_cb=lambda s: s.ACTION_RESET)
VirtualWatchdog.register_type()

View File

@ -349,18 +349,14 @@ class XMLProperty(property):
ret = self._xpath_for_getter_cb(xmlbuilder)
if ret is None:
raise RuntimeError("%s: didn't generate any setter xpath." % self)
return self._xpath_fix_relative(xmlbuilder, ret)
return xmlbuilder.fix_relative_xpath(ret)
def _xpath_for_setter(self, xmlbuilder):
ret = self._xpath
if self._xpath_for_setter_cb:
ret = self._xpath_for_setter_cb(xmlbuilder)
if ret is None:
raise RuntimeError("%s: didn't generate any setter xpath." % self)
return self._xpath_fix_relative(xmlbuilder, ret)
def _xpath_fix_relative(self, xmlbuilder, xpath):
if not xmlbuilder._XML_NEW_ROOT_PATH:
return xpath
return "./%s%s" % (xmlbuilder._XML_NEW_ROOT_PATH, xpath.strip("."))
return xmlbuilder.fix_relative_xpath(ret)
def _xpath_list_for_setter(self, xpath, setval, nodelist):
@ -411,6 +407,7 @@ class XMLProperty(property):
clear_nodes = []
for cpath in self._setter_clear_these_first:
cpath = xmlbuilder.fix_relative_xpath(cpath)
cnode = _get_xpath_node(root_ctx, cpath, False)
if not cnode:
continue
@ -506,10 +503,9 @@ class XMLProperty(property):
##################################
def getter(self, xmlbuilder):
fgetval = self._nonxml_fget(xmlbuilder)
root_node = getattr(xmlbuilder, "_xml_node")
if root_node is None:
fgetval = self._nonxml_fget(xmlbuilder)
return self._convert_get_value(fgetval, initial=True)
xpath = self._xpath_for_getter(xmlbuilder)
@ -585,9 +581,6 @@ class XMLBuilder(object):
# Integer indentation level for generated XML.
_XML_INDENT = None
# This is only used to make device XML work for guest XML generating
_XML_NEW_ROOT_PATH = ""
_dumpxml_xpath = "."
def __init__(self, conn, parsexml=None, parsexmlnode=None):
@ -600,8 +593,9 @@ class XMLBuilder(object):
@type parsexml: C{str}
@param parsexmlnode: Option xpathNode to use
"""
self._conn = conn
self.conn = conn
self._xml_root_xpath = ""
self._xml_node = None
self._xml_ctx = None
self._xml_root_doc = None
@ -622,17 +616,38 @@ class XMLBuilder(object):
ret._proporder = ret._proporder[:]
return ret
def _get_conn(self):
return self._conn
conn = property(_get_conn)
def set_new_xml(self, xml):
self._parsexml(xml, None)
def set_xml_node(self, node):
self._parsexml(None, node)
def set_root_xpath(self, xpath):
self._xml_root_xpath = xpath
def get_xml_node_path(self):
if self._xml_node:
return self._xml_node.nodePath()
return None
xmlprops = self.all_xml_props()
for propname in self._XML_PROP_ORDER:
if propname in self.all_xml_props():
continue
for prop in util.listify(getattr(self, propname)):
prop.set_root_xpath(xpath)
def get_root_xpath(self):
return self._xml_root_xpath
def fix_relative_xpath(self, xpath):
if not self._xml_root_xpath:
return xpath
return "%s%s" % (self._xml_root_xpath, xpath.strip("."))
def get_xml_config(self, *args, **kwargs):
data = self._prepare_get_xml()
try:
return self._do_get_xml_config(self._dumpxml_xpath, True,
*args, **kwargs)
finally:
self._finish_get_xml(data)
def clear(self):
for prop in self.all_xml_props().values():
prop._clear(self)
def _do_get_xml_config(self, dumpxml_xpath, clean, *args, **kwargs):
"""
@ -661,18 +676,6 @@ class XMLBuilder(object):
ret = self._cleanup_xml(ret)
return ret
def get_xml_config(self, *args, **kwargs):
data = self._prepare_get_xml()
try:
return self._do_get_xml_config(self._dumpxml_xpath, True,
*args, **kwargs)
finally:
self._finish_get_xml(data)
def clear(self):
for prop in self.all_xml_props().values():
prop._clear(self)
#######################
# Internal helper API #
@ -727,7 +730,13 @@ class XMLBuilder(object):
return ""
return _indent("<%s/>" % (self._XML_ROOT_NAME), self._XML_INDENT)
def _add_child_node(self, parent_xpath, newnode):
def _add_child(self, parent_xpath, dev):
"""
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
@ -746,6 +755,10 @@ class XMLBuilder(object):
doc = libxml2.parseDoc(xml)
self._xml_root_doc = _DocCleanupWrapper(doc)
self._xml_node = doc.children
# This just stores a reference to our root doc wrapper in
# the root node, so when the node goes away it triggers
# auto free'ing of the doc
self._xml_node.virtinst_root_doc = self._xml_root_doc
else:
self._xml_node = node
@ -800,8 +813,8 @@ class XMLBuilder(object):
validate=False)
else:
for obj in util.listify(getattr(self, key)):
if self._XML_NEW_ROOT_PATH and not obj._XML_NEW_ROOT_PATH:
obj._XML_NEW_ROOT_PATH = self._XML_NEW_ROOT_PATH
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)
xml = self._do_get_xml_config(".", clean).strip("\n")