mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
pv-qemu 9/10: XenD device model re-factoring
This patches adapts XenD so that it is capable of starting a qemu-dm device model for both paravirt and fullyvirt guests. A paravirt guest will only be given a device model if it has a VFB configured, or the user explicitly include the device_model option in the config config. This avoids unnecessary overhead for those wanting a minimal paravirt guest. The bulk of this patch involves moving code from the HVMImageHandler into the base ImageHandler class. The HVMImageHandler and LinuxImageHandler subclasses now merely containing a couple of overrides to set some specific command line flags. The most important is -M xenpv, vs -M xenfv. The XenConfig class has a minor refactoring to add a has_rfb() method to avoid duplicating code in a couple of places. Instead of hardcoding DEFAULT_DM it now uses the xen.util.auxbin APIs to locate it - this works on platforms where qemu-dm is in /usr/lib64 instead of /usr/lib. As before paravirt only gets a default qemu-dm if using a VFB. The vfbif.py class is trimmed out since it no longer needs to spawn a daemon. A few other misc fixes deal with qemu-dm interactions when saving/restoring, and in particular recovering from save failures (or checkpointing). Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
1 parent
36a48e9d87
commit
f8ffd3b21f
5 files changed
+285
-336
No files matched your search
@@ -6,6 +6,7 @@
|
||||
# this archive for more details.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import string
|
||||
import threading
|
||||
@@ -108,7 +109,7 @@ def save(fd, dominfo, network, live, dst, checkpoint=False):
|
||||
forkHelper(cmd, fd, saveInputHandler, False)
|
||||
|
||||
# put qemu device model state
|
||||
if hvm:
|
||||
if os.path.exists("/var/lib/xen/qemu-save.%d" % dominfo.getDomid()):
|
||||
write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
|
||||
qemu_fd = os.open("/var/lib/xen/qemu-save.%d" % dominfo.getDomid(),
|
||||
os.O_RDONLY)
|
||||
@@ -245,6 +246,8 @@ def restore(xd, fd, dominfo = None, paused = False):
|
||||
raise XendError('Could not read console MFN')
|
||||
|
||||
# get qemu state and create a tmp file for dm restore
|
||||
# Even PV guests may have QEMU stat, but its not currently
|
||||
# used so only bother with HVM currently.
|
||||
if is_hvm:
|
||||
qemu_signature = read_exact(fd, len(QEMU_SIGNATURE),
|
||||
"invalid device model signature read")
|
||||
|
||||
@@ -32,6 +32,7 @@ from xen.xend.server.BlktapController import blktap_disk_types
|
||||
from xen.xend.server.netif import randomMAC
|
||||
from xen.util.blkif import blkdev_name_to_number
|
||||
from xen.util import xsconstants
|
||||
import xen.util.auxbin
|
||||
|
||||
log = logging.getLogger("xend.XendConfig")
|
||||
log.setLevel(logging.WARN)
|
||||
@@ -234,8 +235,6 @@ LEGACY_XENSTORE_VM_PARAMS = [
|
||||
'on_xend_stop',
|
||||
]
|
||||
|
||||
DEFAULT_DM = '/usr/lib/xen/bin/qemu-dm'
|
||||
|
||||
##
|
||||
## Config Choices
|
||||
##
|
||||
@@ -393,13 +392,14 @@ class XendConfig(dict):
|
||||
self['name_label'] = 'Domain-' + self['uuid']
|
||||
|
||||
def _platform_sanity_check(self):
|
||||
if self.is_hvm():
|
||||
if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
|
||||
self['platform']['keymap'] = XendOptions.instance().get_keymap()
|
||||
if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
|
||||
self['platform']['keymap'] = XendOptions.instance().get_keymap()
|
||||
|
||||
if self.is_hvm() or self.has_rfb():
|
||||
if 'device_model' not in self['platform']:
|
||||
self['platform']['device_model'] = DEFAULT_DM
|
||||
self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
|
||||
|
||||
if self.is_hvm():
|
||||
# Compatibility hack, can go away soon.
|
||||
if 'soundhw' not in self['platform'] and \
|
||||
self['platform'].get('enable_audio'):
|
||||
@@ -744,16 +744,7 @@ class XendConfig(dict):
|
||||
# coalesce hvm vnc frame buffer with vfb config
|
||||
if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
|
||||
# add vfb device if it isn't there already
|
||||
has_rfb = False
|
||||
for console_uuid in self['console_refs']:
|
||||
if self['devices'][console_uuid][1].get('protocol') == 'rfb':
|
||||
has_rfb = True
|
||||
break
|
||||
if self['devices'][console_uuid][0] == 'vfb':
|
||||
has_rfb = True
|
||||
break
|
||||
|
||||
if not has_rfb:
|
||||
if not self.has_rfb():
|
||||
dev_config = ['vfb']
|
||||
dev_config.append(['type', 'vnc'])
|
||||
# copy VNC related params from platform config to vfb dev conf
|
||||
@@ -765,6 +756,14 @@ class XendConfig(dict):
|
||||
self.device_add('vfb', cfg_sxp = dev_config)
|
||||
|
||||
|
||||
def has_rfb(self):
|
||||
for console_uuid in self['console_refs']:
|
||||
if self['devices'][console_uuid][1].get('protocol') == 'rfb':
|
||||
return True
|
||||
if self['devices'][console_uuid][0] == 'vfb':
|
||||
return True
|
||||
return False
|
||||
|
||||
def _sxp_to_xapi_unsupported(self, sxp_cfg):
|
||||
"""Read in an SXP configuration object and populate
|
||||
values are that not related directly supported in
|
||||
|
||||
@@ -1455,10 +1455,16 @@ class XendDomainInfo:
|
||||
|
||||
def _releaseDevices(self, suspend = False):
|
||||
"""Release all domain's devices. Nothrow guarantee."""
|
||||
if suspend and self.image:
|
||||
self.image.destroy(suspend)
|
||||
return
|
||||
if self.image:
|
||||
try:
|
||||
log.debug("Destroying device model")
|
||||
self.image.destroyDeviceModel()
|
||||
except Exception, e:
|
||||
log.exception("Device model destroy failed %s" % str(e))
|
||||
else:
|
||||
log.debug("No device model")
|
||||
|
||||
log.debug("Releasing devices")
|
||||
t = xstransact("%s/device" % self.dompath)
|
||||
for devclass in XendDevices.valid_devices():
|
||||
for dev in t.list(devclass):
|
||||
@@ -1709,11 +1715,6 @@ class XendDomainInfo:
|
||||
bootloader_tidy(self)
|
||||
|
||||
if self.image:
|
||||
try:
|
||||
self.image.destroy()
|
||||
except:
|
||||
log.exception(
|
||||
"XendDomainInfo.cleanup: image.destroy() failed.")
|
||||
self.image = None
|
||||
|
||||
try:
|
||||
@@ -1881,8 +1882,8 @@ class XendDomainInfo:
|
||||
ResumeDomain(self.domid)
|
||||
except:
|
||||
log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
|
||||
if self.is_hvm():
|
||||
self.image.resumeDeviceModel()
|
||||
self.image.resumeDeviceModel()
|
||||
log.debug("XendDomainInfo.resumeDomain: completed")
|
||||
|
||||
|
||||
#
|
||||
|
||||
+238
-218
@@ -56,10 +56,9 @@ class ImageHandler:
|
||||
defining in a subclass.
|
||||
|
||||
The method createDeviceModel() is called to create the domain device
|
||||
model if it needs one. The default is to do nothing.
|
||||
model.
|
||||
|
||||
The method destroy() is called when the domain is destroyed.
|
||||
The default is to do nothing.
|
||||
The method destroyDeviceModel() is called to reap the device model
|
||||
"""
|
||||
|
||||
ostype = None
|
||||
@@ -91,6 +90,15 @@ class ImageHandler:
|
||||
("image/cmdline", self.cmdline),
|
||||
("image/ramdisk", self.ramdisk))
|
||||
|
||||
self.dmargs = self.parseDeviceModelArgs(vmConfig)
|
||||
self.device_model = vmConfig['platform'].get('device_model')
|
||||
|
||||
self.display = vmConfig['platform'].get('display')
|
||||
self.xauthority = vmConfig['platform'].get('xauthority')
|
||||
self.vncconsole = vmConfig['platform'].get('vncconsole')
|
||||
self.pid = None
|
||||
|
||||
|
||||
|
||||
def cleanupBootloading(self):
|
||||
if self.bootloader:
|
||||
@@ -173,216 +181,13 @@ class ImageHandler:
|
||||
"""Build the domain. Define in subclass."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def createDeviceModel(self, restore = False):
|
||||
"""Create device model for the domain (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
def saveDeviceModel(self):
|
||||
"""Save device model for the domain (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
def resumeDeviceModel(self):
|
||||
"""Unpause device model for the domain (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
"""Extra cleanup on domain destroy (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
|
||||
def recreate(self):
|
||||
pass
|
||||
|
||||
|
||||
class LinuxImageHandler(ImageHandler):
|
||||
|
||||
ostype = "linux"
|
||||
flags = 0
|
||||
vhpt = 0
|
||||
|
||||
def buildDomain(self):
|
||||
store_evtchn = self.vm.getStorePort()
|
||||
console_evtchn = self.vm.getConsolePort()
|
||||
|
||||
mem_mb = self.getRequiredInitialReservation() / 1024
|
||||
|
||||
log.debug("domid = %d", self.vm.getDomid())
|
||||
log.debug("memsize = %d", mem_mb)
|
||||
log.debug("image = %s", self.kernel)
|
||||
log.debug("store_evtchn = %d", store_evtchn)
|
||||
log.debug("console_evtchn = %d", console_evtchn)
|
||||
log.debug("cmdline = %s", self.cmdline)
|
||||
log.debug("ramdisk = %s", self.ramdisk)
|
||||
log.debug("vcpus = %d", self.vm.getVCpuCount())
|
||||
log.debug("features = %s", self.vm.getFeatures())
|
||||
if arch.type == "ia64":
|
||||
log.debug("vhpt = %d", self.flags)
|
||||
|
||||
return xc.linux_build(domid = self.vm.getDomid(),
|
||||
memsize = mem_mb,
|
||||
image = self.kernel,
|
||||
store_evtchn = store_evtchn,
|
||||
console_evtchn = console_evtchn,
|
||||
cmdline = self.cmdline,
|
||||
ramdisk = self.ramdisk,
|
||||
features = self.vm.getFeatures(),
|
||||
flags = self.flags,
|
||||
vhpt = self.vhpt)
|
||||
|
||||
class PPC_LinuxImageHandler(LinuxImageHandler):
|
||||
|
||||
ostype = "linux"
|
||||
|
||||
def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
|
||||
"""@param shadow_mem_kb The configured shadow memory, in KiB.
|
||||
@param maxmem_kb The configured maxmem, in KiB.
|
||||
@return The corresponding required amount of shadow memory, also in
|
||||
KiB.
|
||||
PowerPC currently uses "shadow memory" to refer to the hash table."""
|
||||
return max(maxmem_kb / 64, shadow_mem_kb)
|
||||
|
||||
|
||||
|
||||
class HVMImageHandler(ImageHandler):
|
||||
|
||||
ostype = "hvm"
|
||||
|
||||
def __init__(self, vm, vmConfig):
|
||||
ImageHandler.__init__(self, vm, vmConfig)
|
||||
self.shutdownWatch = None
|
||||
self.rebootFeatureWatch = None
|
||||
|
||||
def configure(self, vmConfig):
|
||||
ImageHandler.configure(self, vmConfig)
|
||||
|
||||
if not self.kernel:
|
||||
self.kernel = '/usr/lib/xen/boot/hvmloader'
|
||||
|
||||
info = xc.xeninfo()
|
||||
if 'hvm' not in info['xen_caps']:
|
||||
raise HVMRequired()
|
||||
|
||||
self.dmargs = self.parseDeviceModelArgs(vmConfig)
|
||||
self.device_model = vmConfig['platform'].get('device_model')
|
||||
if not self.device_model:
|
||||
raise VmError("hvm: missing device model")
|
||||
|
||||
self.display = vmConfig['platform'].get('display')
|
||||
self.xauthority = vmConfig['platform'].get('xauthority')
|
||||
self.vncconsole = vmConfig['platform'].get('vncconsole')
|
||||
|
||||
rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
|
||||
|
||||
self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
|
||||
("image/device-model", self.device_model),
|
||||
("image/display", self.display))
|
||||
self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
|
||||
|
||||
self.pid = None
|
||||
|
||||
self.apic = int(vmConfig['platform'].get('apic', 0))
|
||||
self.acpi = int(vmConfig['platform'].get('acpi', 0))
|
||||
|
||||
|
||||
def buildDomain(self):
|
||||
store_evtchn = self.vm.getStorePort()
|
||||
|
||||
mem_mb = self.getRequiredInitialReservation() / 1024
|
||||
|
||||
log.debug("domid = %d", self.vm.getDomid())
|
||||
log.debug("image = %s", self.kernel)
|
||||
log.debug("store_evtchn = %d", store_evtchn)
|
||||
log.debug("memsize = %d", mem_mb)
|
||||
log.debug("vcpus = %d", self.vm.getVCpuCount())
|
||||
log.debug("acpi = %d", self.acpi)
|
||||
log.debug("apic = %d", self.apic)
|
||||
|
||||
rc = xc.hvm_build(domid = self.vm.getDomid(),
|
||||
image = self.kernel,
|
||||
memsize = mem_mb,
|
||||
vcpus = self.vm.getVCpuCount(),
|
||||
acpi = self.acpi,
|
||||
apic = self.apic)
|
||||
|
||||
rc['notes'] = { 'SUSPEND_CANCEL': 1 }
|
||||
|
||||
rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
|
||||
HVM_PARAM_STORE_PFN)
|
||||
xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
|
||||
store_evtchn)
|
||||
|
||||
return rc
|
||||
|
||||
# Return a list of cmd line args to the device models based on the
|
||||
# xm config file
|
||||
def parseDeviceModelArgs(self, vmConfig):
|
||||
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
|
||||
'localtime', 'serial', 'stdvga', 'isa',
|
||||
'acpi', 'usb', 'usbdevice', 'keymap', 'pci' ]
|
||||
|
||||
ret = ['-vcpus', str(self.vm.getVCpuCount())]
|
||||
ret = ["-domain-name", str(self.vm.info['name_label'])]
|
||||
|
||||
for a in dmargs:
|
||||
v = vmConfig['platform'].get(a)
|
||||
|
||||
# python doesn't allow '-' in variable names
|
||||
if a == 'stdvga': a = 'std-vga'
|
||||
if a == 'keymap': a = 'k'
|
||||
|
||||
# Handle booleans gracefully
|
||||
if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
|
||||
try:
|
||||
if v != None: v = int(v)
|
||||
if v: ret.append("-%s" % a)
|
||||
except (ValueError, TypeError):
|
||||
pass # if we can't convert it to a sane type, ignore it
|
||||
else:
|
||||
if v:
|
||||
ret.append("-%s" % a)
|
||||
ret.append("%s" % v)
|
||||
|
||||
if a in ['fda', 'fdb']:
|
||||
if v:
|
||||
if not os.path.isabs(v):
|
||||
raise VmError("Floppy file %s does not exist." % v)
|
||||
log.debug("args: %s, val: %s" % (a,v))
|
||||
|
||||
# Handle disk/network related options
|
||||
mac = None
|
||||
ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
|
||||
nics = 0
|
||||
|
||||
for devuuid in vmConfig['vbd_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
uname = devinfo.get('uname')
|
||||
if uname is not None and 'file:' in uname:
|
||||
(_, vbdparam) = string.split(uname, ':', 1)
|
||||
if not os.path.isfile(vbdparam):
|
||||
raise VmError('Disk image does not exist: %s' %
|
||||
vbdparam)
|
||||
|
||||
for devuuid in vmConfig['vif_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
dtype = devinfo.get('type', 'ioemu')
|
||||
if dtype != 'ioemu':
|
||||
continue
|
||||
nics += 1
|
||||
mac = devinfo.get('mac')
|
||||
if mac is None:
|
||||
raise VmError("MAC address not specified or generated.")
|
||||
bridge = devinfo.get('bridge', 'xenbr0')
|
||||
model = devinfo.get('model', 'rtl8139')
|
||||
ret.append("-net")
|
||||
ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
|
||||
(nics, mac, model))
|
||||
ret.append("-net")
|
||||
ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
|
||||
|
||||
|
||||
#
|
||||
# Find RFB console device, and if it exists, make QEMU enable
|
||||
# the VNC console.
|
||||
#
|
||||
if int(vmConfig['platform'].get('nographic', 0)) != 0:
|
||||
# skip vnc init if nographic is set
|
||||
ret.append('-nographic')
|
||||
@@ -398,6 +203,11 @@ class HVMImageHandler(ImageHandler):
|
||||
has_vnc = True
|
||||
break
|
||||
|
||||
keymap = vmConfig['platform'].get("keymap")
|
||||
if keymap:
|
||||
ret.append("-k")
|
||||
ret.append(keymap)
|
||||
|
||||
if has_vnc:
|
||||
if not vnc_config:
|
||||
for key in ('vncunused', 'vnclisten', 'vncdisplay',
|
||||
@@ -435,20 +245,20 @@ class HVMImageHandler(ImageHandler):
|
||||
ret = ret + ['-monitor', 'vc']
|
||||
return ret
|
||||
|
||||
def getDeviceModelArgs(self, restore = False):
|
||||
args = [self.device_model]
|
||||
args = args + ([ "-d", "%d" % self.vm.getDomid() ])
|
||||
args = args + self.dmargs
|
||||
return args
|
||||
|
||||
def createDeviceModel(self, restore = False):
|
||||
if self.device_model is None:
|
||||
return
|
||||
if self.pid:
|
||||
return
|
||||
# Execute device model.
|
||||
#todo: Error handling
|
||||
args = [self.device_model]
|
||||
args = args + ([ "-d", "%d" % self.vm.getDomid() ])
|
||||
if arch.type == "ia64":
|
||||
args = args + ([ "-m", "%s" %
|
||||
(self.getRequiredInitialReservation() / 1024) ])
|
||||
args = args + self.dmargs
|
||||
if restore:
|
||||
args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
|
||||
self.vm.getDomid() ])
|
||||
args = self.getDeviceModelArgs(restore)
|
||||
env = dict(os.environ)
|
||||
if self.display:
|
||||
env['DISPLAY'] = self.display
|
||||
@@ -463,6 +273,8 @@ class HVMImageHandler(ImageHandler):
|
||||
log.info("device model pid: %d", self.pid)
|
||||
|
||||
def saveDeviceModel(self):
|
||||
if self.device_model is None:
|
||||
return
|
||||
# Signal the device model to pause itself and save its state
|
||||
xstransact.Store("/local/domain/0/device-model/%i"
|
||||
% self.vm.getDomid(), ('command', 'save'))
|
||||
@@ -479,15 +291,21 @@ class HVMImageHandler(ImageHandler):
|
||||
raise VmError('Timed out waiting for device model to save')
|
||||
|
||||
def resumeDeviceModel(self):
|
||||
if self.device_model is None:
|
||||
return
|
||||
# Signal the device model to resume activity after pausing to save.
|
||||
xstransact.Store("/local/domain/0/device-model/%i"
|
||||
% self.vm.getDomid(), ('command', 'continue'))
|
||||
|
||||
def recreate(self):
|
||||
if self.device_model is None:
|
||||
return
|
||||
self.pid = self.vm.gatherDom(('image/device-model-pid', int))
|
||||
|
||||
def destroy(self, suspend = False):
|
||||
if self.pid and not suspend:
|
||||
def destroyDeviceModel(self):
|
||||
if self.device_model is None:
|
||||
return
|
||||
if self.pid:
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
except OSError, exn:
|
||||
@@ -504,6 +322,201 @@ class HVMImageHandler(ImageHandler):
|
||||
% self.vm.getDomid())
|
||||
|
||||
|
||||
class LinuxImageHandler(ImageHandler):
|
||||
|
||||
ostype = "linux"
|
||||
flags = 0
|
||||
vhpt = 0
|
||||
|
||||
def buildDomain(self):
|
||||
store_evtchn = self.vm.getStorePort()
|
||||
console_evtchn = self.vm.getConsolePort()
|
||||
|
||||
mem_mb = self.getRequiredInitialReservation() / 1024
|
||||
|
||||
log.debug("domid = %d", self.vm.getDomid())
|
||||
log.debug("memsize = %d", mem_mb)
|
||||
log.debug("image = %s", self.kernel)
|
||||
log.debug("store_evtchn = %d", store_evtchn)
|
||||
log.debug("console_evtchn = %d", console_evtchn)
|
||||
log.debug("cmdline = %s", self.cmdline)
|
||||
log.debug("ramdisk = %s", self.ramdisk)
|
||||
log.debug("vcpus = %d", self.vm.getVCpuCount())
|
||||
log.debug("features = %s", self.vm.getFeatures())
|
||||
if arch.type == "ia64":
|
||||
log.debug("vhpt = %d", self.flags)
|
||||
|
||||
return xc.linux_build(domid = self.vm.getDomid(),
|
||||
memsize = mem_mb,
|
||||
image = self.kernel,
|
||||
store_evtchn = store_evtchn,
|
||||
console_evtchn = console_evtchn,
|
||||
cmdline = self.cmdline,
|
||||
ramdisk = self.ramdisk,
|
||||
features = self.vm.getFeatures(),
|
||||
flags = self.flags,
|
||||
vhpt = self.vhpt)
|
||||
|
||||
def parseDeviceModelArgs(self, vmConfig):
|
||||
ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
|
||||
# Equivalent to old xenconsoled behaviour. Should make
|
||||
# it configurable in future
|
||||
ret = ret + ["-serial", "pty"]
|
||||
return ret
|
||||
|
||||
def getDeviceModelArgs(self, restore = False):
|
||||
args = ImageHandler.getDeviceModelArgs(self, restore)
|
||||
args = args + ([ "-M", "xenpv"])
|
||||
return args
|
||||
|
||||
|
||||
class PPC_LinuxImageHandler(LinuxImageHandler):
|
||||
|
||||
ostype = "linux"
|
||||
|
||||
def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
|
||||
"""@param shadow_mem_kb The configured shadow memory, in KiB.
|
||||
@param maxmem_kb The configured maxmem, in KiB.
|
||||
@return The corresponding required amount of shadow memory, also in
|
||||
KiB.
|
||||
PowerPC currently uses "shadow memory" to refer to the hash table."""
|
||||
return max(maxmem_kb / 64, shadow_mem_kb)
|
||||
|
||||
|
||||
|
||||
class HVMImageHandler(ImageHandler):
|
||||
|
||||
ostype = "hvm"
|
||||
|
||||
def __init__(self, vm, vmConfig):
|
||||
ImageHandler.__init__(self, vm, vmConfig)
|
||||
self.shutdownWatch = None
|
||||
self.rebootFeatureWatch = None
|
||||
|
||||
def configure(self, vmConfig):
|
||||
ImageHandler.configure(self, vmConfig)
|
||||
|
||||
if not self.kernel:
|
||||
self.kernel = '/usr/lib/xen/boot/hvmloader'
|
||||
|
||||
info = xc.xeninfo()
|
||||
if 'hvm' not in info['xen_caps']:
|
||||
raise HVMRequired()
|
||||
|
||||
rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
|
||||
|
||||
self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
|
||||
("image/device-model", self.device_model),
|
||||
("image/display", self.display))
|
||||
self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
|
||||
|
||||
self.apic = int(vmConfig['platform'].get('apic', 0))
|
||||
self.acpi = int(vmConfig['platform'].get('acpi', 0))
|
||||
|
||||
# Return a list of cmd line args to the device models based on the
|
||||
# xm config file
|
||||
def parseDeviceModelArgs(self, vmConfig):
|
||||
ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
|
||||
ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
|
||||
|
||||
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
|
||||
'localtime', 'serial', 'stdvga', 'isa',
|
||||
'acpi', 'usb', 'usbdevice', 'pci' ]
|
||||
|
||||
for a in dmargs:
|
||||
v = vmConfig['platform'].get(a)
|
||||
|
||||
# python doesn't allow '-' in variable names
|
||||
if a == 'stdvga': a = 'std-vga'
|
||||
if a == 'keymap': a = 'k'
|
||||
|
||||
# Handle booleans gracefully
|
||||
if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
|
||||
try:
|
||||
if v != None: v = int(v)
|
||||
if v: ret.append("-%s" % a)
|
||||
except (ValueError, TypeError):
|
||||
pass # if we can't convert it to a sane type, ignore it
|
||||
else:
|
||||
if v:
|
||||
ret.append("-%s" % a)
|
||||
ret.append("%s" % v)
|
||||
|
||||
if a in ['fda', 'fdb']:
|
||||
if v:
|
||||
if not os.path.isabs(v):
|
||||
raise VmError("Floppy file %s does not exist." % v)
|
||||
log.debug("args: %s, val: %s" % (a,v))
|
||||
|
||||
# Handle disk/network related options
|
||||
mac = None
|
||||
nics = 0
|
||||
|
||||
for devuuid in vmConfig['vbd_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
uname = devinfo.get('uname')
|
||||
if uname is not None and 'file:' in uname:
|
||||
(_, vbdparam) = string.split(uname, ':', 1)
|
||||
if not os.path.isfile(vbdparam):
|
||||
raise VmError('Disk image does not exist: %s' %
|
||||
vbdparam)
|
||||
|
||||
for devuuid in vmConfig['vif_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
dtype = devinfo.get('type', 'ioemu')
|
||||
if dtype != 'ioemu':
|
||||
continue
|
||||
nics += 1
|
||||
mac = devinfo.get('mac')
|
||||
if mac is None:
|
||||
raise VmError("MAC address not specified or generated.")
|
||||
bridge = devinfo.get('bridge', 'xenbr0')
|
||||
model = devinfo.get('model', 'rtl8139')
|
||||
ret.append("-net")
|
||||
ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
|
||||
(nics, mac, model))
|
||||
ret.append("-net")
|
||||
ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
|
||||
|
||||
return ret
|
||||
|
||||
def getDeviceModelArgs(self, restore = False):
|
||||
args = ImageHandler.getDeviceModelArgs(self, restore)
|
||||
args = args + ([ "-M", "xenfv"])
|
||||
if restore:
|
||||
args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
|
||||
self.vm.getDomid() ])
|
||||
return args
|
||||
|
||||
def buildDomain(self):
|
||||
store_evtchn = self.vm.getStorePort()
|
||||
|
||||
mem_mb = self.getRequiredInitialReservation() / 1024
|
||||
|
||||
log.debug("domid = %d", self.vm.getDomid())
|
||||
log.debug("image = %s", self.kernel)
|
||||
log.debug("store_evtchn = %d", store_evtchn)
|
||||
log.debug("memsize = %d", mem_mb)
|
||||
log.debug("vcpus = %d", self.vm.getVCpuCount())
|
||||
log.debug("acpi = %d", self.acpi)
|
||||
log.debug("apic = %d", self.apic)
|
||||
|
||||
rc = xc.hvm_build(domid = self.vm.getDomid(),
|
||||
image = self.kernel,
|
||||
memsize = mem_mb,
|
||||
vcpus = self.vm.getVCpuCount(),
|
||||
acpi = self.acpi,
|
||||
apic = self.apic)
|
||||
rc['notes'] = { 'SUSPEND_CANCEL': 1 }
|
||||
|
||||
rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
|
||||
HVM_PARAM_STORE_PFN)
|
||||
xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
|
||||
store_evtchn)
|
||||
|
||||
return rc
|
||||
|
||||
|
||||
class IA64_HVM_ImageHandler(HVMImageHandler):
|
||||
|
||||
def configure(self, vmConfig):
|
||||
@@ -529,6 +542,13 @@ class IA64_HVM_ImageHandler(HVMImageHandler):
|
||||
# Explicit shadow memory is not a concept
|
||||
return 0
|
||||
|
||||
def getDeviceModelArgs(self, restore = False):
|
||||
args = HVMImageHandler.getDeviceModelArgs(self, restore)
|
||||
args = args + ([ "-m", "%s" %
|
||||
(self.getRequiredInitialReservation() / 1024) ])
|
||||
return args
|
||||
|
||||
|
||||
class IA64_Linux_ImageHandler(LinuxImageHandler):
|
||||
|
||||
def configure(self, vmConfig):
|
||||
|
||||
@@ -5,14 +5,6 @@ from xen.xend.XendError import VmError
|
||||
import xen.xend
|
||||
import os
|
||||
|
||||
def spawn_detached(path, args, env):
|
||||
p = os.fork()
|
||||
if p == 0:
|
||||
os.spawnve(os.P_NOWAIT, path, args, env)
|
||||
os._exit(0)
|
||||
else:
|
||||
os.waitpid(p, 0)
|
||||
|
||||
CONFIG_ENTRIES = ['type', 'vncdisplay', 'vnclisten', 'vncpasswd', 'vncunused',
|
||||
'display', 'xauthority', 'keymap',
|
||||
'uuid', 'location', 'protocol']
|
||||
@@ -43,65 +35,9 @@ class VfbifController(DevController):
|
||||
for i in range(len(CONFIG_ENTRIES))
|
||||
if devinfo[i] is not None])
|
||||
|
||||
|
||||
def createDevice(self, config):
|
||||
DevController.createDevice(self, config)
|
||||
if self.vm.info.is_hvm():
|
||||
# is HVM, so qemu-dm will handle the vfb.
|
||||
return
|
||||
|
||||
args = [ xen.util.auxbin.pathTo("qemu-dm"),
|
||||
"-M", "xenpv",
|
||||
"-d", "%d" % self.vm.getDomid(),
|
||||
"-domain-name", self.vm.getName() ]
|
||||
t = config.get("type", None)
|
||||
if t == "vnc":
|
||||
passwd = None
|
||||
if config.has_key("vncpasswd"):
|
||||
passwd = config["vncpasswd"]
|
||||
else:
|
||||
passwd = xen.xend.XendOptions.instance().get_vncpasswd_default()
|
||||
if passwd:
|
||||
self.vm.storeVm("vncpasswd", passwd)
|
||||
log.debug("Stored a VNC password for vfb access")
|
||||
else:
|
||||
log.debug("No VNC passwd configured for vfb access")
|
||||
|
||||
vnclisten = config.get('vnclisten',
|
||||
xen.xend.XendOptions.instance().get_vnclisten_address())
|
||||
vncdisplay = config.get('vncdisplay', 0)
|
||||
args += ['-vnc', "%s:%d" % (vnclisten, vncdisplay)]
|
||||
|
||||
if config.get('vncunused', 0):
|
||||
args += ['-vncunused']
|
||||
|
||||
if config.has_key("keymap"):
|
||||
args += ["-k", "%s" % config["keymap"]]
|
||||
else:
|
||||
xoptions = xen.xend.XendOptions.instance()
|
||||
if xoptions.get_keymap():
|
||||
args += ["-k", "%s" % xoptions.get_keymap()]
|
||||
|
||||
spawn_detached(args[0], args, os.environ)
|
||||
elif t == "sdl":
|
||||
env = dict(os.environ)
|
||||
if config.has_key("display"):
|
||||
env['DISPLAY'] = config["display"]
|
||||
if config.has_key("xauthority"):
|
||||
env['XAUTHORITY'] = config["xauthority"]
|
||||
spawn_detached(args[0], args, env)
|
||||
else:
|
||||
raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
|
||||
|
||||
|
||||
def waitForDevice(self, devid):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
log.debug('skip waiting for HVM vfb')
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
DevController.waitForDevice(self, devid)
|
||||
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
def reconfigureDevice(self, _, config):
|
||||
""" Only allow appending location information of vnc port into
|
||||
@@ -115,19 +51,16 @@ class VfbifController(DevController):
|
||||
raise VmError('Refusing to reconfigure device vfb:%d' % devid)
|
||||
|
||||
def destroyDevice(self, devid, force):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# remove the backend xenstore entries for HVM guests no matter
|
||||
# what
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
else:
|
||||
DevController.destroyDevice(self, devid, force)
|
||||
# remove the backend xenstore entries no matter what
|
||||
# because we kill qemu-dm with extreme prejudice
|
||||
# not giving it a chance to remove them itself
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
|
||||
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
return 0
|
||||
return DevController.migrate(self, deviceConfig, network, dst, step,
|
||||
domName)
|
||||
# Handled by qemu-dm so no action needed
|
||||
return 0
|
||||
|
||||
|
||||
class VkbdifController(DevController):
|
||||
"""Virtual keyboard controller. Handles all vkbd devices for a domain.
|
||||
@@ -141,22 +74,15 @@ class VkbdifController(DevController):
|
||||
return (devid, back, front)
|
||||
|
||||
def waitForDevice(self, config):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
DevController.waitForDevice(self, config)
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
def destroyDevice(self, devid, force):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# remove the backend xenstore entries for HVM guests no matter
|
||||
# what
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
else:
|
||||
DevController.destroyDevice(self, devid, force)
|
||||
# remove the backend xenstore entries no matter what
|
||||
# because we kill qemu-dm with extreme prejudice
|
||||
# not giving it a chance to remove them itself
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
return 0
|
||||
return DevController.migrate(self, deviceConfig, network, dst, step,
|
||||
domName)
|
||||
# Handled by qemu-dm so no action needed
|
||||
return 0
|
||||