ia64/xen-unstable

view tools/python/xen/xend/image.py @ 13939:09b3fd488726

[XEND][POWERPC] Remove FlatDeviceTree.py, eliminate devtree from prose builder.

Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Signed-off-by: Jimi Xenidis <jimix@watson.ibm.com>
author Jimi Xenidis <jimix@watson.ibm.com>
date Sun Jan 21 08:15:39 2007 -0500 (2007-01-21)
parents 7bf078335342
children 54530b945067
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
33 xc = xen.lowlevel.xc.xc()
35 MAX_GUEST_CMDLINE = 1024
38 def create(vm, vmConfig, imageConfig, deviceConfig):
39 """Create an image handler for a vm.
41 @return ImageHandler instance
42 """
43 return findImageHandlerClass(imageConfig)(vm, vmConfig, imageConfig,
44 deviceConfig)
47 class ImageHandler:
48 """Abstract base class for image handlers.
50 createImage() is called to configure and build the domain from its
51 kernel image and ramdisk etc.
53 The method buildDomain() is used to build the domain, and must be
54 defined in a subclass. Usually this is the only method that needs
55 defining in a subclass.
57 The method createDeviceModel() is called to create the domain device
58 model if it needs one. The default is to do nothing.
60 The method destroy() is called when the domain is destroyed.
61 The default is to do nothing.
62 """
64 ostype = None
67 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
68 self.vm = vm
70 self.bootloader = False
71 self.kernel = None
72 self.ramdisk = None
73 self.cmdline = None
75 self.configure(vmConfig, imageConfig, deviceConfig)
77 def configure(self, vmConfig, imageConfig, _):
78 """Config actions common to all unix-like domains."""
79 if '_temp_using_bootloader' in vmConfig:
80 self.bootloader = True
81 self.kernel = vmConfig['_temp_kernel']
82 self.cmdline = vmConfig['_temp_args']
83 self.ramdisk = vmConfig['_temp_ramdisk']
84 else:
85 self.kernel = vmConfig['PV_kernel']
86 self.cmdline = vmConfig['PV_args']
87 self.ramdisk = vmConfig['PV_ramdisk']
88 self.vm.storeVm(("image/ostype", self.ostype),
89 ("image/kernel", self.kernel),
90 ("image/cmdline", self.cmdline),
91 ("image/ramdisk", self.ramdisk))
94 def cleanupBootloading(self):
95 if self.bootloader:
96 self.unlink(self.kernel)
97 self.unlink(self.ramdisk)
100 def unlink(self, f):
101 if not f: return
102 try:
103 os.unlink(f)
104 except OSError, ex:
105 log.warning("error removing bootloader file '%s': %s", f, ex)
108 def createImage(self):
109 """Entry point to create domain memory image.
110 Override in subclass if needed.
111 """
112 return self.createDomain()
115 def createDomain(self):
116 """Build the domain boot image.
117 """
118 # Set params and call buildDomain().
120 if not os.path.isfile(self.kernel):
121 raise VmError('Kernel image does not exist: %s' % self.kernel)
122 if self.ramdisk and not os.path.isfile(self.ramdisk):
123 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
124 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
125 log.warning('kernel cmdline too long, domain %d',
126 self.vm.getDomid())
128 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
129 self.vm.getDomid(), self.vm.getVCpuCount())
131 result = self.buildDomain()
133 if isinstance(result, dict):
134 return result
135 else:
136 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
137 % (self.ostype, self.vm.getDomid(), str(result)))
139 def getRequiredAvailableMemory(self, mem_kb):
140 """@param mem_kb The configured maxmem or memory, in KiB.
141 @return The corresponding required amount of memory for the domain,
142 also in KiB. This is normally the given mem_kb, but architecture- or
143 image-specific code may override this to add headroom where
144 necessary."""
145 return mem_kb
147 def getRequiredInitialReservation(self):
148 """@param mem_kb The configured memory, in KiB.
149 @return The corresponding required amount of memory to be free, also
150 in KiB. This is normally the same as getRequiredAvailableMemory, but
151 architecture- or image-specific code may override this to
152 add headroom where necessary."""
153 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
155 def getRequiredMaximumReservation(self):
156 """@param mem_kb The maximum possible memory, in KiB.
157 @return The corresponding required amount of memory to be free, also
158 in KiB. This is normally the same as getRequiredAvailableMemory, but
159 architecture- or image-specific code may override this to
160 add headroom where necessary."""
161 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
163 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
164 """@param shadow_mem_kb The configured shadow memory, in KiB.
165 @param maxmem_kb The configured maxmem, in KiB.
166 @return The corresponding required amount of shadow memory, also in
167 KiB."""
168 # PV domains don't need any shadow memory
169 return 0
171 def buildDomain(self):
172 """Build the domain. Define in subclass."""
173 raise NotImplementedError()
175 def createDeviceModel(self):
176 """Create device model for the domain (define in subclass if needed)."""
177 pass
179 def destroy(self):
180 """Extra cleanup on domain destroy (define in subclass if needed)."""
181 pass
184 def recreate(self):
185 pass
188 class LinuxImageHandler(ImageHandler):
190 ostype = "linux"
192 def buildDomain(self):
193 store_evtchn = self.vm.getStorePort()
194 console_evtchn = self.vm.getConsolePort()
196 mem_mb = self.getRequiredInitialReservation() / 1024
198 log.debug("domid = %d", self.vm.getDomid())
199 log.debug("memsize = %d", mem_mb)
200 log.debug("image = %s", self.kernel)
201 log.debug("store_evtchn = %d", store_evtchn)
202 log.debug("console_evtchn = %d", console_evtchn)
203 log.debug("cmdline = %s", self.cmdline)
204 log.debug("ramdisk = %s", self.ramdisk)
205 log.debug("vcpus = %d", self.vm.getVCpuCount())
206 log.debug("features = %s", self.vm.getFeatures())
208 return xc.linux_build(domid = self.vm.getDomid(),
209 memsize = mem_mb,
210 image = self.kernel,
211 store_evtchn = store_evtchn,
212 console_evtchn = console_evtchn,
213 cmdline = self.cmdline,
214 ramdisk = self.ramdisk,
215 features = self.vm.getFeatures())
219 class PPC_ProseImageHandler(LinuxImageHandler):
221 ostype = "prose"
223 def configure(self, vmConfig, imageConfig, deviceConfig):
224 LinuxImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
225 self.imageConfig = imageConfig
227 def buildDomain(self):
228 store_evtchn = self.vm.getStorePort()
229 console_evtchn = self.vm.getConsolePort()
231 mem_mb = self.getRequiredInitialReservation() / 1024
233 log.debug("dom = %d", self.vm.getDomid())
234 log.debug("memsize = %d", mem_mb)
235 log.debug("image = %s", self.kernel)
236 log.debug("store_evtchn = %d", store_evtchn)
237 log.debug("console_evtchn = %d", console_evtchn)
238 log.debug("cmdline = %s", self.cmdline)
239 log.debug("ramdisk = %s", self.ramdisk)
240 log.debug("vcpus = %d", self.vm.getVCpuCount())
241 log.debug("features = %s", self.vm.getFeatures())
243 return xc.arch_prose_build(dom = self.vm.getDomid(),
244 memsize = mem_mb,
245 image = self.kernel,
246 store_evtchn = store_evtchn,
247 console_evtchn = console_evtchn,
248 cmdline = self.cmdline,
249 ramdisk = self.ramdisk,
250 features = self.vm.getFeatures())
252 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
253 """@param shadow_mem_kb The configured shadow memory, in KiB.
254 @param maxmem_kb The configured maxmem, in KiB.
255 @return The corresponding required amount of shadow memory, also in
256 KiB.
257 PowerPC currently uses "shadow memory" to refer to the hash table."""
258 return max(maxmem_kb / 64, shadow_mem_kb)
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(imageConfig, deviceConfig)
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.dmargs += self.configVNC(imageConfig)
298 self.pae = imageConfig['hvm'].get('pae', 0)
299 self.apic = imageConfig['hvm'].get('apic', 0)
300 self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
303 def buildDomain(self):
304 store_evtchn = self.vm.getStorePort()
306 mem_mb = self.getRequiredInitialReservation() / 1024
308 log.debug("domid = %d", self.vm.getDomid())
309 log.debug("image = %s", self.kernel)
310 log.debug("store_evtchn = %d", store_evtchn)
311 log.debug("memsize = %d", mem_mb)
312 log.debug("vcpus = %d", self.vm.getVCpuCount())
313 log.debug("pae = %d", self.pae)
314 log.debug("acpi = %d", self.acpi)
315 log.debug("apic = %d", self.apic)
317 self.register_shutdown_watch()
318 self.register_reboot_feature_watch()
320 return xc.hvm_build(domid = self.vm.getDomid(),
321 image = self.kernel,
322 store_evtchn = store_evtchn,
323 memsize = mem_mb,
324 vcpus = self.vm.getVCpuCount(),
325 pae = self.pae,
326 acpi = self.acpi,
327 apic = self.apic)
329 # Return a list of cmd line args to the device models based on the
330 # xm config file
331 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
332 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
333 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
334 'acpi', 'usb', 'usbdevice', 'keymap' ]
335 ret = []
336 hvmDeviceConfig = imageConfig['hvm']['devices']
338 for a in dmargs:
339 v = hvmDeviceConfig.get(a)
341 # python doesn't allow '-' in variable names
342 if a == 'stdvga': a = 'std-vga'
343 if a == 'keymap': a = 'k'
345 # Handle booleans gracefully
346 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
347 if v != None: v = int(v)
348 if v: ret.append("-%s" % a)
349 else:
350 if v:
351 ret.append("-%s" % a)
352 ret.append("%s" % v)
354 if a in ['fda', 'fdb']:
355 if v:
356 if not os.path.isabs(v):
357 raise VmError("Floppy file %s does not exist." % v)
358 log.debug("args: %s, val: %s" % (a,v))
360 # Handle disk/network related options
361 mac = None
362 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
363 nics = 0
365 for devuuid, (devtype, devinfo) in deviceConfig.items():
366 if devtype == 'vbd':
367 uname = devinfo.get('uname')
368 if uname is not None and 'file:' in uname:
369 (_, vbdparam) = string.split(uname, ':', 1)
370 if not os.path.isfile(vbdparam):
371 raise VmError('Disk image does not exist: %s' %
372 vbdparam)
373 if devtype == 'vif':
374 dtype = devinfo.get('type', 'ioemu')
375 if dtype != 'ioemu':
376 continue
377 nics += 1
378 mac = devinfo.get('mac')
379 if mac == None:
380 mac = randomMAC()
381 bridge = devinfo.get('bridge', 'xenbr0')
382 model = devinfo.get('model', 'rtl8139')
383 ret.append("-net")
384 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
385 (nics, mac, model))
386 ret.append("-net")
387 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
388 return ret
390 def configVNC(self, imageConfig):
391 # Handle graphics library related options
392 vnc = imageConfig.get('vnc')
393 sdl = imageConfig.get('sdl')
394 ret = []
395 nographic = imageConfig.get('nographic')
397 # get password from VM config (if password omitted, None)
398 vncpasswd_vmconfig = imageConfig.get('vncpasswd')
400 if nographic:
401 ret.append('-nographic')
402 return ret
404 if vnc:
405 vncdisplay = imageConfig.get('vncdisplay',
406 int(self.vm.getDomid()))
407 vncunused = imageConfig.get('vncunused')
409 if vncunused:
410 ret += ['-vncunused']
411 else:
412 ret += ['-vnc', '%d' % vncdisplay]
414 vnclisten = imageConfig.get('vnclisten')
416 if not(vnclisten):
417 vnclisten = (xen.xend.XendRoot.instance().
418 get_vnclisten_address())
419 if vnclisten:
420 ret += ['-vnclisten', vnclisten]
422 vncpasswd = vncpasswd_vmconfig
423 if vncpasswd is None:
424 vncpasswd = (xen.xend.XendRoot.instance().
425 get_vncpasswd_default())
426 if vncpasswd is None:
427 raise VmError('vncpasswd is not set up in ' +
428 'VMconfig and xend-config.')
429 if vncpasswd != '':
430 self.vm.storeVm("vncpasswd", vncpasswd)
432 return ret
434 def createDeviceModel(self):
435 if self.pid:
436 return
437 # Execute device model.
438 #todo: Error handling
439 args = [self.device_model]
440 args = args + ([ "-d", "%d" % self.vm.getDomid(),
441 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
442 args = args + self.dmargs
443 env = dict(os.environ)
444 if self.display:
445 env['DISPLAY'] = self.display
446 if self.xauthority:
447 env['XAUTHORITY'] = self.xauthority
448 if self.vncconsole:
449 args = args + ([ "-vncviewer" ])
450 log.info("spawning device models: %s %s", self.device_model, args)
451 # keep track of pid and spawned options to kill it later
452 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
453 self.vm.storeDom("image/device-model-pid", self.pid)
454 log.info("device model pid: %d", self.pid)
456 def recreate(self):
457 self.register_shutdown_watch()
458 self.register_reboot_feature_watch()
459 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
461 def destroy(self):
462 self.unregister_shutdown_watch()
463 self.unregister_reboot_feature_watch();
464 if self.pid:
465 try:
466 os.kill(self.pid, signal.SIGKILL)
467 except OSError, exn:
468 log.exception(exn)
469 try:
470 os.waitpid(self.pid, 0)
471 except OSError, exn:
472 # This is expected if Xend has been restarted within the
473 # life of this domain. In this case, we can kill the process,
474 # but we can't wait for it because it's not our child.
475 pass
476 self.pid = None
478 def register_shutdown_watch(self):
479 """ add xen store watch on control/shutdown """
480 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown",
481 self.hvm_shutdown)
482 log.debug("hvm shutdown watch registered")
484 def unregister_shutdown_watch(self):
485 """Remove the watch on the control/shutdown, if any. Nothrow
486 guarantee."""
488 try:
489 if self.shutdownWatch:
490 self.shutdownWatch.unwatch()
491 except:
492 log.exception("Unwatching hvm shutdown watch failed.")
493 self.shutdownWatch = None
494 log.debug("hvm shutdown watch unregistered")
496 def hvm_shutdown(self, _):
497 """ watch call back on node control/shutdown,
498 if node changed, this function will be called
499 """
500 xd = xen.xend.XendDomain.instance()
501 try:
502 vm = xd.domain_lookup( self.vm.getDomid() )
503 except XendError:
504 # domain isn't registered, no need to clean it up.
505 return False
507 reason = vm.getShutdownReason()
508 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
509 if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
510 vm.info['shutdown'] = 1
511 vm.info['shutdown_reason'] = \
512 REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
513 vm.refreshShutdown(vm.info)
515 return True # Keep watching
517 def register_reboot_feature_watch(self):
518 """ add xen store watch on control/feature-reboot """
519 self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
520 self.hvm_reboot_feature)
521 log.debug("hvm reboot feature watch registered")
523 def unregister_reboot_feature_watch(self):
524 """Remove the watch on the control/feature-reboot, if any. Nothrow
525 guarantee."""
527 try:
528 if self.rebootFeatureWatch:
529 self.rebootFeatureWatch.unwatch()
530 except:
531 log.exception("Unwatching hvm reboot feature watch failed.")
532 self.rebootFeatureWatch = None
533 log.debug("hvm reboot feature watch unregistered")
535 def hvm_reboot_feature(self, _):
536 """ watch call back on node control/feature-reboot,
537 if node changed, this function will be called
538 """
539 status = self.vm.readDom('control/feature-reboot')
540 log.debug("hvm_reboot_feature fired, module status=%s", status)
541 if status == '1':
542 self.unregister_shutdown_watch()
544 return True # Keep watching
547 class IA64_HVM_ImageHandler(HVMImageHandler):
549 def getRequiredAvailableMemory(self, mem_kb):
550 page_kb = 16
551 # ROM size for guest firmware, ioreq page and xenstore page
552 extra_pages = 1024 + 3
553 return mem_kb + extra_pages * page_kb
555 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
556 # Explicit shadow memory is not a concept
557 return 0
559 class X86_HVM_ImageHandler(HVMImageHandler):
561 def getRequiredAvailableMemory(self, mem_kb):
562 # Add 8 MiB overhead for QEMU's video RAM.
563 return mem_kb + 8192
565 def getRequiredInitialReservation(self):
566 return self.vm.getMemoryTarget()
568 def getRequiredMaximumReservation(self):
569 return self.vm.getMemoryMaximum()
571 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
572 # 256 pages (1MB) per vcpu,
573 # plus 1 page per MiB of RAM for the P2M map,
574 # plus 1 page per MiB of RAM to shadow the resident processes.
575 # This is higher than the minimum that Xen would allocate if no value
576 # were given (but the Xen minimum is for safety, not performance).
577 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
578 shadow_mem_kb)
580 class X86_Linux_ImageHandler(LinuxImageHandler):
582 def buildDomain(self):
583 # set physical mapping limit
584 # add an 8MB slack to balance backend allocations.
585 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
586 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
587 return LinuxImageHandler.buildDomain(self)
589 _handlers = {
590 "powerpc": {
591 "linux": LinuxImageHandler,
592 "prose": PPC_ProseImageHandler,
593 },
594 "ia64": {
595 "linux": LinuxImageHandler,
596 "hvm": IA64_HVM_ImageHandler,
597 },
598 "x86": {
599 "linux": X86_Linux_ImageHandler,
600 "hvm": X86_HVM_ImageHandler,
601 },
602 }
604 def findImageHandlerClass(image):
605 """Find the image handler class for an image config.
607 @param image config
608 @return ImageHandler subclass or None
609 """
610 image_type = image['type']
611 if image_type is None:
612 raise VmError('missing image type')
613 try:
614 return _handlers[arch.type][image_type]
615 except KeyError:
616 raise VmError('unknown image type: ' + image_type)