ia64/xen-unstable

view tools/python/xen/xend/image.py @ 13931:3773af71a14b

[TOOLS][POWERPC] fixes for the prose builder.

Signed-off-by: Maria Butrico <butrico@watson.ibm.com>
Signed-off-by: Jimi Xenidis <jimix@watson.ibm.com>
author Jimi Xenidis <jimix@watson.ibm.com>
date Wed Jan 17 19:37:20 2007 -0500 (2007-01-17)
parents 5c268a24e44b
children 7bf078335342
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.server.netif import randomMAC
30 from xen.xend.xenstore.xswatch import xswatch
31 from xen.xend import arch
32 from xen.xend import FlatDeviceTree
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):
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 configure(self, vmConfig, imageConfig, deviceConfig):
223 LinuxImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
224 self.imageConfig = imageConfig
226 def buildDomain(self):
227 store_evtchn = self.vm.getStorePort()
228 console_evtchn = self.vm.getConsolePort()
230 mem_mb = self.getRequiredInitialReservation() / 1024
232 log.debug("domid = %d", self.vm.getDomid())
233 log.debug("memsize = %d", mem_mb)
234 log.debug("image = %s", self.kernel)
235 log.debug("store_evtchn = %d", store_evtchn)
236 log.debug("console_evtchn = %d", console_evtchn)
237 log.debug("cmdline = %s", self.cmdline)
238 log.debug("ramdisk = %s", self.ramdisk)
239 log.debug("vcpus = %d", self.vm.getVCpuCount())
240 log.debug("features = %s", self.vm.getFeatures())
242 devtree = FlatDeviceTree.build(self)
244 return xc.linux_build(domid = self.vm.getDomid(),
245 memsize = mem_mb,
246 image = self.kernel,
247 store_evtchn = store_evtchn,
248 console_evtchn = console_evtchn,
249 cmdline = self.cmdline,
250 ramdisk = self.ramdisk,
251 features = self.vm.getFeatures(),
252 arch_args = devtree.to_bin())
254 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
255 """@param shadow_mem_kb The configured shadow memory, in KiB.
256 @param maxmem_kb The configured maxmem, in KiB.
257 @return The corresponding required amount of shadow memory, also in
258 KiB.
259 PowerPC currently uses "shadow memory" to refer to the hash table."""
260 return max(maxmem_kb / 64, shadow_mem_kb)
263 class PPC_ProseImageHandler(PPC_LinuxImageHandler):
265 ostype = "prose"
267 def buildDomain(self):
268 store_evtchn = self.vm.getStorePort()
269 console_evtchn = self.vm.getConsolePort()
271 mem_mb = self.getRequiredInitialReservation() / 1024
273 log.debug("dom = %d", self.vm.getDomid())
274 log.debug("memsize = %d", mem_mb)
275 log.debug("image = %s", self.kernel)
276 log.debug("store_evtchn = %d", store_evtchn)
277 log.debug("console_evtchn = %d", console_evtchn)
278 log.debug("cmdline = %s", self.cmdline)
279 log.debug("ramdisk = %s", self.ramdisk)
280 log.debug("vcpus = %d", self.vm.getVCpuCount())
281 log.debug("features = %s", self.vm.getFeatures())
283 devtree = FlatDeviceTree.build(self)
285 return xc.arch_prose_build(dom = self.vm.getDomid(),
286 memsize = mem_mb,
287 image = self.kernel,
288 store_evtchn = store_evtchn,
289 console_evtchn = console_evtchn,
290 cmdline = self.cmdline,
291 ramdisk = self.ramdisk,
292 features = self.vm.getFeatures(),
293 arch_args = devtree.to_bin())
295 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
296 """@param shadow_mem_kb The configured shadow memory, in KiB.
297 @param maxmem_kb The configured maxmem, in KiB.
298 @return The corresponding required amount of shadow memory, also in
299 KiB.
300 PowerPC currently uses "shadow memory" to refer to the hash table."""
301 return max(maxmem_kb / 64, shadow_mem_kb)
304 class HVMImageHandler(ImageHandler):
306 ostype = "hvm"
308 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
309 ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig)
310 self.shutdownWatch = None
311 self.rebootFeatureWatch = None
313 def configure(self, vmConfig, imageConfig, deviceConfig):
314 ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
316 if not self.kernel:
317 self.kernel = '/usr/lib/xen/boot/hvmloader'
319 info = xc.xeninfo()
320 if 'hvm' not in info['xen_caps']:
321 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
322 "supported by your CPU and enabled in your BIOS?")
324 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
325 self.device_model = imageConfig['hvm'].get('device_model')
326 if not self.device_model:
327 raise VmError("hvm: missing device model")
329 self.display = imageConfig['hvm'].get('display')
330 self.xauthority = imageConfig['hvm'].get('xauthority')
331 self.vncconsole = imageConfig['hvm'].get('vncconsole')
333 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
334 ("image/device-model", self.device_model),
335 ("image/display", self.display))
337 self.pid = None
339 self.dmargs += self.configVNC(imageConfig)
341 self.pae = imageConfig['hvm'].get('pae', 0)
342 self.apic = imageConfig['hvm'].get('apic', 0)
343 self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
346 def buildDomain(self):
347 store_evtchn = self.vm.getStorePort()
349 mem_mb = self.getRequiredInitialReservation() / 1024
351 log.debug("domid = %d", self.vm.getDomid())
352 log.debug("image = %s", self.kernel)
353 log.debug("store_evtchn = %d", store_evtchn)
354 log.debug("memsize = %d", mem_mb)
355 log.debug("vcpus = %d", self.vm.getVCpuCount())
356 log.debug("pae = %d", self.pae)
357 log.debug("acpi = %d", self.acpi)
358 log.debug("apic = %d", self.apic)
360 self.register_shutdown_watch()
361 self.register_reboot_feature_watch()
363 return xc.hvm_build(domid = self.vm.getDomid(),
364 image = self.kernel,
365 store_evtchn = store_evtchn,
366 memsize = mem_mb,
367 vcpus = self.vm.getVCpuCount(),
368 pae = self.pae,
369 acpi = self.acpi,
370 apic = self.apic)
372 # Return a list of cmd line args to the device models based on the
373 # xm config file
374 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
375 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
376 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
377 'acpi', 'usb', 'usbdevice', 'keymap' ]
378 ret = []
379 hvmDeviceConfig = imageConfig['hvm']['devices']
381 for a in dmargs:
382 v = hvmDeviceConfig.get(a)
384 # python doesn't allow '-' in variable names
385 if a == 'stdvga': a = 'std-vga'
386 if a == 'keymap': a = 'k'
388 # Handle booleans gracefully
389 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
390 if v != None: v = int(v)
391 if v: ret.append("-%s" % a)
392 else:
393 if v:
394 ret.append("-%s" % a)
395 ret.append("%s" % v)
397 if a in ['fda', 'fdb']:
398 if v:
399 if not os.path.isabs(v):
400 raise VmError("Floppy file %s does not exist." % v)
401 log.debug("args: %s, val: %s" % (a,v))
403 # Handle disk/network related options
404 mac = None
405 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
406 nics = 0
408 for devuuid, (devtype, devinfo) in deviceConfig.items():
409 if devtype == 'vbd':
410 uname = devinfo.get('uname')
411 if uname is not None and 'file:' in uname:
412 (_, vbdparam) = string.split(uname, ':', 1)
413 if not os.path.isfile(vbdparam):
414 raise VmError('Disk image does not exist: %s' %
415 vbdparam)
416 if devtype == 'vif':
417 dtype = devinfo.get('type', 'ioemu')
418 if dtype != 'ioemu':
419 continue
420 nics += 1
421 mac = devinfo.get('mac')
422 if mac == None:
423 mac = randomMAC()
424 bridge = devinfo.get('bridge', 'xenbr0')
425 model = devinfo.get('model', 'rtl8139')
426 ret.append("-net")
427 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
428 (nics, mac, model))
429 ret.append("-net")
430 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
431 return ret
433 def configVNC(self, imageConfig):
434 # Handle graphics library related options
435 vnc = imageConfig.get('vnc')
436 sdl = imageConfig.get('sdl')
437 ret = []
438 nographic = imageConfig.get('nographic')
440 # get password from VM config (if password omitted, None)
441 vncpasswd_vmconfig = imageConfig.get('vncpasswd')
443 if nographic:
444 ret.append('-nographic')
445 return ret
447 if vnc:
448 vncdisplay = imageConfig.get('vncdisplay',
449 int(self.vm.getDomid()))
450 vncunused = imageConfig.get('vncunused')
452 if vncunused:
453 ret += ['-vncunused']
454 else:
455 ret += ['-vnc', '%d' % vncdisplay]
457 vnclisten = imageConfig.get('vnclisten')
459 if not(vnclisten):
460 vnclisten = (xen.xend.XendRoot.instance().
461 get_vnclisten_address())
462 if vnclisten:
463 ret += ['-vnclisten', vnclisten]
465 vncpasswd = vncpasswd_vmconfig
466 if vncpasswd is None:
467 vncpasswd = (xen.xend.XendRoot.instance().
468 get_vncpasswd_default())
469 if vncpasswd is None:
470 raise VmError('vncpasswd is not set up in ' +
471 'VMconfig and xend-config.')
472 if vncpasswd != '':
473 self.vm.storeVm("vncpasswd", vncpasswd)
475 return ret
477 def createDeviceModel(self):
478 if self.pid:
479 return
480 # Execute device model.
481 #todo: Error handling
482 args = [self.device_model]
483 args = args + ([ "-d", "%d" % self.vm.getDomid(),
484 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
485 args = args + self.dmargs
486 env = dict(os.environ)
487 if self.display:
488 env['DISPLAY'] = self.display
489 if self.xauthority:
490 env['XAUTHORITY'] = self.xauthority
491 if self.vncconsole:
492 args = args + ([ "-vncviewer" ])
493 log.info("spawning device models: %s %s", self.device_model, args)
494 # keep track of pid and spawned options to kill it later
495 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
496 self.vm.storeDom("image/device-model-pid", self.pid)
497 log.info("device model pid: %d", self.pid)
499 def recreate(self):
500 self.register_shutdown_watch()
501 self.register_reboot_feature_watch()
502 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
504 def destroy(self):
505 self.unregister_shutdown_watch()
506 self.unregister_reboot_feature_watch();
507 if self.pid:
508 try:
509 os.kill(self.pid, signal.SIGKILL)
510 except OSError, exn:
511 log.exception(exn)
512 try:
513 os.waitpid(self.pid, 0)
514 except OSError, exn:
515 # This is expected if Xend has been restarted within the
516 # life of this domain. In this case, we can kill the process,
517 # but we can't wait for it because it's not our child.
518 pass
519 self.pid = None
521 def register_shutdown_watch(self):
522 """ add xen store watch on control/shutdown """
523 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown",
524 self.hvm_shutdown)
525 log.debug("hvm shutdown watch registered")
527 def unregister_shutdown_watch(self):
528 """Remove the watch on the control/shutdown, if any. Nothrow
529 guarantee."""
531 try:
532 if self.shutdownWatch:
533 self.shutdownWatch.unwatch()
534 except:
535 log.exception("Unwatching hvm shutdown watch failed.")
536 self.shutdownWatch = None
537 log.debug("hvm shutdown watch unregistered")
539 def hvm_shutdown(self, _):
540 """ watch call back on node control/shutdown,
541 if node changed, this function will be called
542 """
543 xd = xen.xend.XendDomain.instance()
544 try:
545 vm = xd.domain_lookup( self.vm.getDomid() )
546 except XendError:
547 # domain isn't registered, no need to clean it up.
548 return False
550 reason = vm.getShutdownReason()
551 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
552 if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
553 vm.info['shutdown'] = 1
554 vm.info['shutdown_reason'] = \
555 REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
556 vm.refreshShutdown(vm.info)
558 return True # Keep watching
560 def register_reboot_feature_watch(self):
561 """ add xen store watch on control/feature-reboot """
562 self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
563 self.hvm_reboot_feature)
564 log.debug("hvm reboot feature watch registered")
566 def unregister_reboot_feature_watch(self):
567 """Remove the watch on the control/feature-reboot, if any. Nothrow
568 guarantee."""
570 try:
571 if self.rebootFeatureWatch:
572 self.rebootFeatureWatch.unwatch()
573 except:
574 log.exception("Unwatching hvm reboot feature watch failed.")
575 self.rebootFeatureWatch = None
576 log.debug("hvm reboot feature watch unregistered")
578 def hvm_reboot_feature(self, _):
579 """ watch call back on node control/feature-reboot,
580 if node changed, this function will be called
581 """
582 status = self.vm.readDom('control/feature-reboot')
583 log.debug("hvm_reboot_feature fired, module status=%s", status)
584 if status == '1':
585 self.unregister_shutdown_watch()
587 return True # Keep watching
590 class IA64_HVM_ImageHandler(HVMImageHandler):
592 def getRequiredAvailableMemory(self, mem_kb):
593 page_kb = 16
594 # ROM size for guest firmware, ioreq page and xenstore page
595 extra_pages = 1024 + 3
596 return mem_kb + extra_pages * page_kb
598 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
599 # Explicit shadow memory is not a concept
600 return 0
602 class X86_HVM_ImageHandler(HVMImageHandler):
604 def getRequiredAvailableMemory(self, mem_kb):
605 # Add 8 MiB overhead for QEMU's video RAM.
606 return mem_kb + 8192
608 def getRequiredInitialReservation(self):
609 return self.vm.getMemoryTarget()
611 def getRequiredMaximumReservation(self):
612 return self.vm.getMemoryMaximum()
614 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
615 # 256 pages (1MB) per vcpu,
616 # plus 1 page per MiB of RAM for the P2M map,
617 # plus 1 page per MiB of RAM to shadow the resident processes.
618 # This is higher than the minimum that Xen would allocate if no value
619 # were given (but the Xen minimum is for safety, not performance).
620 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
621 shadow_mem_kb)
623 class X86_Linux_ImageHandler(LinuxImageHandler):
625 def buildDomain(self):
626 # set physical mapping limit
627 # add an 8MB slack to balance backend allocations.
628 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
629 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
630 return LinuxImageHandler.buildDomain(self)
632 _handlers = {
633 "powerpc": {
634 "linux": PPC_LinuxImageHandler,
635 "prose": PPC_ProseImageHandler,
636 },
637 "ia64": {
638 "linux": LinuxImageHandler,
639 "hvm": IA64_HVM_ImageHandler,
640 },
641 "x86": {
642 "linux": X86_Linux_ImageHandler,
643 "hvm": X86_HVM_ImageHandler,
644 },
645 }
647 def findImageHandlerClass(image):
648 """Find the image handler class for an image config.
650 @param image config
651 @return ImageHandler subclass or None
652 """
653 image_type = image['type']
654 if image_type is None:
655 raise VmError('missing image type')
656 try:
657 return _handlers[arch.type][image_type]
658 except KeyError:
659 raise VmError('unknown image type: ' + image_type)