ia64/xen-unstable

view tools/python/xen/xend/image.py @ 13990:cd03d7c23aa7

Fix vncdisplay config option for HVM guests.
Signed-off-by: Takanori Kasai <kasai.takanori@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Fri Feb 16 09:57:25 2007 +0000 (2007-02-16)
parents 978ff6fad81f
children e450a3b90aba
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005 XenSource Ltd
17 #============================================================================
20 import os, string
21 import re
22 import math
23 import signal
25 import xen.lowlevel.xc
26 from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS
27 from xen.xend.XendError import VmError, XendError
28 from xen.xend.XendLogging import log
29 from xen.xend.XendOptions import instance as xenopts
30 from xen.xend.server.netif import randomMAC
31 from xen.xend.xenstore.xswatch import xswatch
32 from xen.xend import arch
34 xc = xen.lowlevel.xc.xc()
36 MAX_GUEST_CMDLINE = 1024
39 def create(vm, vmConfig, imageConfig, deviceConfig):
40 """Create an image handler for a vm.
42 @return ImageHandler instance
43 """
44 return findImageHandlerClass(imageConfig)(vm, vmConfig, imageConfig,
45 deviceConfig)
48 class ImageHandler:
49 """Abstract base class for image handlers.
51 createImage() is called to configure and build the domain from its
52 kernel image and ramdisk etc.
54 The method buildDomain() is used to build the domain, and must be
55 defined in a subclass. Usually this is the only method that needs
56 defining in a subclass.
58 The method createDeviceModel() is called to create the domain device
59 model if it needs one. The default is to do nothing.
61 The method destroy() is called when the domain is destroyed.
62 The default is to do nothing.
63 """
65 ostype = None
68 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
69 self.vm = vm
71 self.bootloader = False
72 self.kernel = None
73 self.ramdisk = None
74 self.cmdline = None
76 self.configure(vmConfig, imageConfig, deviceConfig)
78 def configure(self, vmConfig, imageConfig, _):
79 """Config actions common to all unix-like domains."""
80 if '_temp_using_bootloader' in vmConfig:
81 self.bootloader = True
82 self.kernel = vmConfig['_temp_kernel']
83 self.cmdline = vmConfig['_temp_args']
84 self.ramdisk = vmConfig['_temp_ramdisk']
85 else:
86 self.kernel = vmConfig['PV_kernel']
87 self.cmdline = vmConfig['PV_args']
88 self.ramdisk = vmConfig['PV_ramdisk']
89 self.vm.storeVm(("image/ostype", self.ostype),
90 ("image/kernel", self.kernel),
91 ("image/cmdline", self.cmdline),
92 ("image/ramdisk", self.ramdisk))
95 def cleanupBootloading(self):
96 if self.bootloader:
97 self.unlink(self.kernel)
98 self.unlink(self.ramdisk)
101 def unlink(self, f):
102 if not f: return
103 try:
104 os.unlink(f)
105 except OSError, ex:
106 log.warning("error removing bootloader file '%s': %s", f, ex)
109 def createImage(self):
110 """Entry point to create domain memory image.
111 Override in subclass if needed.
112 """
113 return self.createDomain()
116 def createDomain(self):
117 """Build the domain boot image.
118 """
119 # Set params and call buildDomain().
121 if not os.path.isfile(self.kernel):
122 raise VmError('Kernel image does not exist: %s' % self.kernel)
123 if self.ramdisk and not os.path.isfile(self.ramdisk):
124 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
125 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
126 log.warning('kernel cmdline too long, domain %d',
127 self.vm.getDomid())
129 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
130 self.vm.getDomid(), self.vm.getVCpuCount())
132 result = self.buildDomain()
134 if isinstance(result, dict):
135 return result
136 else:
137 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
138 % (self.ostype, self.vm.getDomid(), str(result)))
140 def getRequiredAvailableMemory(self, mem_kb):
141 """@param mem_kb The configured maxmem or memory, in KiB.
142 @return The corresponding required amount of memory for the domain,
143 also in KiB. This is normally the given mem_kb, but architecture- or
144 image-specific code may override this to add headroom where
145 necessary."""
146 return mem_kb
148 def getRequiredInitialReservation(self):
149 """@param mem_kb The configured memory, in KiB.
150 @return The corresponding required amount of memory to be free, also
151 in KiB. This is normally the same as getRequiredAvailableMemory, but
152 architecture- or image-specific code may override this to
153 add headroom where necessary."""
154 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
156 def getRequiredMaximumReservation(self):
157 """@param mem_kb The maximum possible memory, in KiB.
158 @return The corresponding required amount of memory to be free, also
159 in KiB. This is normally the same as getRequiredAvailableMemory, but
160 architecture- or image-specific code may override this to
161 add headroom where necessary."""
162 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
164 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
165 """@param shadow_mem_kb The configured shadow memory, in KiB.
166 @param maxmem_kb The configured maxmem, in KiB.
167 @return The corresponding required amount of shadow memory, also in
168 KiB."""
169 # PV domains don't need any shadow memory
170 return 0
172 def buildDomain(self):
173 """Build the domain. Define in subclass."""
174 raise NotImplementedError()
176 def createDeviceModel(self, restore = False):
177 """Create device model for the domain (define in subclass if needed)."""
178 pass
180 def destroy(self):
181 """Extra cleanup on domain destroy (define in subclass if needed)."""
182 pass
185 def recreate(self):
186 pass
189 class LinuxImageHandler(ImageHandler):
191 ostype = "linux"
193 def buildDomain(self):
194 store_evtchn = self.vm.getStorePort()
195 console_evtchn = self.vm.getConsolePort()
197 mem_mb = self.getRequiredInitialReservation() / 1024
199 log.debug("domid = %d", self.vm.getDomid())
200 log.debug("memsize = %d", mem_mb)
201 log.debug("image = %s", self.kernel)
202 log.debug("store_evtchn = %d", store_evtchn)
203 log.debug("console_evtchn = %d", console_evtchn)
204 log.debug("cmdline = %s", self.cmdline)
205 log.debug("ramdisk = %s", self.ramdisk)
206 log.debug("vcpus = %d", self.vm.getVCpuCount())
207 log.debug("features = %s", self.vm.getFeatures())
209 return xc.linux_build(domid = self.vm.getDomid(),
210 memsize = mem_mb,
211 image = self.kernel,
212 store_evtchn = store_evtchn,
213 console_evtchn = console_evtchn,
214 cmdline = self.cmdline,
215 ramdisk = self.ramdisk,
216 features = self.vm.getFeatures())
218 class PPC_LinuxImageHandler(LinuxImageHandler):
220 ostype = "linux"
222 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
223 """@param shadow_mem_kb The configured shadow memory, in KiB.
224 @param maxmem_kb The configured maxmem, in KiB.
225 @return The corresponding required amount of shadow memory, also in
226 KiB.
227 PowerPC currently uses "shadow memory" to refer to the hash table."""
228 return max(maxmem_kb / 64, shadow_mem_kb)
232 class PPC_ProseImageHandler(PPC_LinuxImageHandler):
234 ostype = "prose"
236 def buildDomain(self):
237 store_evtchn = self.vm.getStorePort()
238 console_evtchn = self.vm.getConsolePort()
240 mem_mb = self.getRequiredInitialReservation() / 1024
242 log.debug("dom = %d", self.vm.getDomid())
243 log.debug("memsize = %d", mem_mb)
244 log.debug("image = %s", self.kernel)
245 log.debug("store_evtchn = %d", store_evtchn)
246 log.debug("console_evtchn = %d", console_evtchn)
247 log.debug("cmdline = %s", self.cmdline)
248 log.debug("ramdisk = %s", self.ramdisk)
249 log.debug("vcpus = %d", self.vm.getVCpuCount())
250 log.debug("features = %s", self.vm.getFeatures())
252 return xc.arch_prose_build(dom = self.vm.getDomid(),
253 memsize = mem_mb,
254 image = self.kernel,
255 store_evtchn = store_evtchn,
256 console_evtchn = console_evtchn,
257 cmdline = self.cmdline,
258 ramdisk = self.ramdisk,
259 features = self.vm.getFeatures())
261 class HVMImageHandler(ImageHandler):
263 ostype = "hvm"
265 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
266 ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig)
267 self.shutdownWatch = None
268 self.rebootFeatureWatch = None
270 def configure(self, vmConfig, imageConfig, deviceConfig):
271 ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
273 if not self.kernel:
274 self.kernel = '/usr/lib/xen/boot/hvmloader'
276 info = xc.xeninfo()
277 if 'hvm' not in info['xen_caps']:
278 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
279 "supported by your CPU and enabled in your BIOS?")
281 self.dmargs = self.parseDeviceModelArgs(vmConfig)
282 self.device_model = imageConfig['hvm'].get('device_model')
283 if not self.device_model:
284 raise VmError("hvm: missing device model")
286 self.display = imageConfig['hvm'].get('display')
287 self.xauthority = imageConfig['hvm'].get('xauthority')
288 self.vncconsole = imageConfig['hvm'].get('vncconsole')
290 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
291 ("image/device-model", self.device_model),
292 ("image/display", self.display))
294 self.pid = None
296 self.pae = imageConfig['hvm'].get('pae', 0)
297 self.apic = imageConfig['hvm'].get('apic', 0)
298 self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
301 def buildDomain(self):
302 store_evtchn = self.vm.getStorePort()
304 mem_mb = self.getRequiredInitialReservation() / 1024
306 log.debug("domid = %d", self.vm.getDomid())
307 log.debug("image = %s", self.kernel)
308 log.debug("store_evtchn = %d", store_evtchn)
309 log.debug("memsize = %d", mem_mb)
310 log.debug("vcpus = %d", self.vm.getVCpuCount())
311 log.debug("pae = %d", self.pae)
312 log.debug("acpi = %d", self.acpi)
313 log.debug("apic = %d", self.apic)
315 self.register_shutdown_watch()
316 self.register_reboot_feature_watch()
318 return xc.hvm_build(domid = self.vm.getDomid(),
319 image = self.kernel,
320 store_evtchn = store_evtchn,
321 memsize = mem_mb,
322 vcpus = self.vm.getVCpuCount(),
323 pae = self.pae,
324 acpi = self.acpi,
325 apic = self.apic)
327 # Return a list of cmd line args to the device models based on the
328 # xm config file
329 def parseDeviceModelArgs(self, vmConfig):
330 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
331 'localtime', 'serial', 'stdvga', 'isa',
332 'acpi', 'usb', 'usbdevice', 'keymap' ]
334 hvmDeviceConfig = vmConfig['image']['hvm']['devices']
335 ret = ['-vcpus', str(self.vm.getVCpuCount())]
337 for a in dmargs:
338 v = hvmDeviceConfig.get(a)
340 # python doesn't allow '-' in variable names
341 if a == 'stdvga': a = 'std-vga'
342 if a == 'keymap': a = 'k'
344 # Handle booleans gracefully
345 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
346 try:
347 if v != None: v = int(v)
348 if v: ret.append("-%s" % a)
349 except (ValueError, TypeError):
350 pass # if we can't convert it to a sane type, ignore it
351 else:
352 if v:
353 ret.append("-%s" % a)
354 ret.append("%s" % v)
356 if a in ['fda', 'fdb']:
357 if v:
358 if not os.path.isabs(v):
359 raise VmError("Floppy file %s does not exist." % v)
360 log.debug("args: %s, val: %s" % (a,v))
362 # Handle disk/network related options
363 mac = None
364 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
365 nics = 0
367 for devuuid in vmConfig['vbd_refs']:
368 devinfo = vmConfig['devices'][devuuid][1]
369 uname = devinfo.get('uname')
370 if uname is not None and 'file:' in uname:
371 (_, vbdparam) = string.split(uname, ':', 1)
372 if not os.path.isfile(vbdparam):
373 raise VmError('Disk image does not exist: %s' %
374 vbdparam)
376 for devuuid in vmConfig['vif_refs']:
377 devinfo = vmConfig['devices'][devuuid][1]
378 dtype = devinfo.get('type', 'ioemu')
379 if dtype != 'ioemu':
380 continue
381 nics += 1
382 mac = devinfo.get('mac')
383 if mac is None:
384 mac = randomMAC()
385 bridge = devinfo.get('bridge', 'xenbr0')
386 model = devinfo.get('model', 'rtl8139')
387 ret.append("-net")
388 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
389 (nics, mac, model))
390 ret.append("-net")
391 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
394 #
395 # Find RFB console device, and if it exists, make QEMU enable
396 # the VNC console.
397 #
398 if vmConfig['image'].get('nographic'):
399 # skip vnc init if nographic is set
400 ret.append('-nographic')
401 return ret
403 vnc_config = {}
404 has_vnc = int(vmConfig['image'].get('vnc', 0)) != 0
405 has_sdl = int(vmConfig['image'].get('sdl', 0)) != 0
406 for dev_uuid in vmConfig['console_refs']:
407 dev_type, dev_info = vmConfig['devices'][dev_uuid]
408 if dev_type == 'vfb':
409 vnc_config = dev_info.get('other_config', {})
410 has_vnc = True
411 break
413 if has_vnc:
414 if not vnc_config:
415 for key in ('vncunused', 'vnclisten', 'vncdisplay',
416 'vncpasswd'):
417 if key in vmConfig['image']:
418 vnc_config[key] = vmConfig['image'][key]
420 if not vnc_config.get('vncunused', 0) and \
421 vnc_config.get('vncdisplay', 0):
422 vncdisplay = vnc_config.get('vncdisplay')
423 ret.append('-vnc')
424 ret.append(str(vncdisplay))
425 else:
426 ret.append('-vncunused')
428 vnclisten = vnc_config.get('vnclisten',
429 xenopts().get_vnclisten_address())
430 ret.append('-vnclisten')
431 ret.append(str(vnclisten))
433 # Store vncpassword in xenstore
434 vncpasswd = vnc_config.get('vncpasswd')
435 if not vncpasswd:
436 vncpasswd = xenopts().get_vncpasswd_default()
438 if vncpasswd is None:
439 raise VmError('vncpasswd is not setup in vmconfig or '
440 'xend-config.sxp')
442 if vncpasswd != '':
443 self.vm.storeVm('vncpasswd', vncpasswd)
444 elif has_sdl:
445 # SDL is default in QEMU.
446 pass
447 else:
448 ret.append('-nographic')
450 return ret
452 def createDeviceModel(self, restore = False):
453 if self.pid:
454 return
455 # Execute device model.
456 #todo: Error handling
457 args = [self.device_model]
458 args = args + ([ "-d", "%d" % self.vm.getDomid(),
459 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
460 args = args + self.dmargs
461 if restore:
462 args = args + ([ "-loadvm", "/tmp/xen.qemu-dm.%d" % self.vm.getDomid() ])
463 env = dict(os.environ)
464 if self.display:
465 env['DISPLAY'] = self.display
466 if self.xauthority:
467 env['XAUTHORITY'] = self.xauthority
468 if self.vncconsole:
469 args = args + ([ "-vncviewer" ])
470 log.info("spawning device models: %s %s", self.device_model, args)
471 # keep track of pid and spawned options to kill it later
472 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
473 self.vm.storeDom("image/device-model-pid", self.pid)
474 log.info("device model pid: %d", self.pid)
476 def recreate(self):
477 self.register_shutdown_watch()
478 self.register_reboot_feature_watch()
479 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
481 def destroy(self, suspend = False):
482 self.unregister_shutdown_watch()
483 self.unregister_reboot_feature_watch();
484 if self.pid:
485 try:
486 sig = signal.SIGKILL
487 if suspend:
488 log.info("use sigusr1 to signal qemu %d", self.pid)
489 sig = signal.SIGUSR1
490 os.kill(self.pid, sig)
491 except OSError, exn:
492 log.exception(exn)
493 try:
494 os.waitpid(self.pid, 0)
495 except OSError, exn:
496 # This is expected if Xend has been restarted within the
497 # life of this domain. In this case, we can kill the process,
498 # but we can't wait for it because it's not our child.
499 pass
500 self.pid = None
502 def register_shutdown_watch(self):
503 """ add xen store watch on control/shutdown """
504 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown",
505 self.hvm_shutdown)
506 log.debug("hvm shutdown watch registered")
508 def unregister_shutdown_watch(self):
509 """Remove the watch on the control/shutdown, if any. Nothrow
510 guarantee."""
512 try:
513 if self.shutdownWatch:
514 self.shutdownWatch.unwatch()
515 except:
516 log.exception("Unwatching hvm shutdown watch failed.")
517 self.shutdownWatch = None
518 log.debug("hvm shutdown watch unregistered")
520 def hvm_shutdown(self, _):
521 """ watch call back on node control/shutdown,
522 if node changed, this function will be called
523 """
524 xd = xen.xend.XendDomain.instance()
525 try:
526 vm = xd.domain_lookup( self.vm.getDomid() )
527 except XendError:
528 # domain isn't registered, no need to clean it up.
529 return False
531 reason = vm.getShutdownReason()
532 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
533 if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
534 vm.info['shutdown'] = 1
535 vm.info['shutdown_reason'] = \
536 REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
537 vm.refreshShutdown(vm.info)
539 return True # Keep watching
541 def register_reboot_feature_watch(self):
542 """ add xen store watch on control/feature-reboot """
543 self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
544 self.hvm_reboot_feature)
545 log.debug("hvm reboot feature watch registered")
547 def unregister_reboot_feature_watch(self):
548 """Remove the watch on the control/feature-reboot, if any. Nothrow
549 guarantee."""
551 try:
552 if self.rebootFeatureWatch:
553 self.rebootFeatureWatch.unwatch()
554 except:
555 log.exception("Unwatching hvm reboot feature watch failed.")
556 self.rebootFeatureWatch = None
557 log.debug("hvm reboot feature watch unregistered")
559 def hvm_reboot_feature(self, _):
560 """ watch call back on node control/feature-reboot,
561 if node changed, this function will be called
562 """
563 status = self.vm.readDom('control/feature-reboot')
564 log.debug("hvm_reboot_feature fired, module status=%s", status)
565 if status == '1':
566 self.unregister_shutdown_watch()
568 return True # Keep watching
571 class IA64_HVM_ImageHandler(HVMImageHandler):
573 def getRequiredAvailableMemory(self, mem_kb):
574 page_kb = 16
575 # ROM size for guest firmware, ioreq page and xenstore page
576 extra_pages = 1024 + 3
577 return mem_kb + extra_pages * page_kb
579 def getRequiredInitialReservation(self):
580 return self.vm.getMemoryTarget()
582 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
583 # Explicit shadow memory is not a concept
584 return 0
586 class X86_HVM_ImageHandler(HVMImageHandler):
588 def getRequiredAvailableMemory(self, mem_kb):
589 # Add 8 MiB overhead for QEMU's video RAM.
590 return mem_kb + 8192
592 def getRequiredInitialReservation(self):
593 return self.vm.getMemoryTarget()
595 def getRequiredMaximumReservation(self):
596 return self.vm.getMemoryMaximum()
598 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
599 # 256 pages (1MB) per vcpu,
600 # plus 1 page per MiB of RAM for the P2M map,
601 # plus 1 page per MiB of RAM to shadow the resident processes.
602 # This is higher than the minimum that Xen would allocate if no value
603 # were given (but the Xen minimum is for safety, not performance).
604 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
605 shadow_mem_kb)
607 class X86_Linux_ImageHandler(LinuxImageHandler):
609 def buildDomain(self):
610 # set physical mapping limit
611 # add an 8MB slack to balance backend allocations.
612 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
613 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
614 return LinuxImageHandler.buildDomain(self)
616 _handlers = {
617 "powerpc": {
618 "linux": PPC_LinuxImageHandler,
619 "prose": PPC_ProseImageHandler,
620 },
621 "ia64": {
622 "linux": LinuxImageHandler,
623 "hvm": IA64_HVM_ImageHandler,
624 },
625 "x86": {
626 "linux": X86_Linux_ImageHandler,
627 "hvm": X86_HVM_ImageHandler,
628 },
629 }
631 def findImageHandlerClass(image):
632 """Find the image handler class for an image config.
634 @param image config
635 @return ImageHandler subclass or None
636 """
637 image_type = image['type']
638 if image_type is None:
639 raise VmError('missing image type')
640 try:
641 return _handlers[arch.type][image_type]
642 except KeyError:
643 raise VmError('unknown image type: ' + image_type)