direct-io.hg

view tools/python/xen/xend/image.py @ 12988:e080700efa56

[TOOLS] Fix the build. Clearly demarcate PPC-specific stuff.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Dec 13 10:23:53 2006 +0000 (2006-12-13)
parents 749c399d73df
children 73b59ad4dd42
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.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 self.kernel = vmConfig['kernel_kernel']
80 self.cmdline = vmConfig['kernel_args']
81 self.ramdisk = vmConfig['kernel_initrd']
82 self.vm.storeVm(("image/ostype", self.ostype),
83 ("image/kernel", self.kernel),
84 ("image/cmdline", self.cmdline),
85 ("image/ramdisk", self.ramdisk))
88 def cleanupBootloading(self):
89 self.unlink(self.kernel)
90 self.unlink(self.ramdisk)
93 def unlink(self, f):
94 if not f: return
95 try:
96 os.unlink(f)
97 except OSError, ex:
98 log.warning("error removing bootloader file '%s': %s", f, ex)
101 def createImage(self):
102 """Entry point to create domain memory image.
103 Override in subclass if needed.
104 """
105 return self.createDomain()
108 def createDomain(self):
109 """Build the domain boot image.
110 """
111 # Set params and call buildDomain().
113 if not os.path.isfile(self.kernel):
114 raise VmError('Kernel image does not exist: %s' % self.kernel)
115 if self.ramdisk and not os.path.isfile(self.ramdisk):
116 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
117 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
118 log.warning('kernel cmdline too long, domain %d',
119 self.vm.getDomid())
121 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
122 self.vm.getDomid(), self.vm.getVCpuCount())
124 result = self.buildDomain()
126 if isinstance(result, dict):
127 return result
128 else:
129 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
130 % (self.ostype, self.vm.getDomid(), str(result)))
132 def getRequiredAvailableMemory(self, mem_kb):
133 """@param mem_kb The configured maxmem or memory, in KiB.
134 @return The corresponding required amount of memory for the domain,
135 also in KiB. This is normally the given mem_kb, but architecture- or
136 image-specific code may override this to add headroom where
137 necessary."""
138 return mem_kb
140 def getRequiredInitialReservation(self):
141 """@param mem_kb The configured memory, in KiB.
142 @return The corresponding required amount of memory to be free, also
143 in KiB. This is normally the same as getRequiredAvailableMemory, but
144 architecture- or image-specific code may override this to
145 add headroom where necessary."""
146 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
148 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
149 """@param shadow_mem_kb The configured shadow memory, in KiB.
150 @param maxmem_kb The configured maxmem, in KiB.
151 @return The corresponding required amount of shadow memory, also in
152 KiB."""
153 # PV domains don't need any shadow memory
154 return 0
156 def buildDomain(self):
157 """Build the domain. Define in subclass."""
158 raise NotImplementedError()
160 def createDeviceModel(self):
161 """Create device model for the domain (define in subclass if needed)."""
162 pass
164 def destroy(self):
165 """Extra cleanup on domain destroy (define in subclass if needed)."""
166 pass
169 def recreate(self):
170 pass
173 class LinuxImageHandler(ImageHandler):
175 ostype = "linux"
177 def buildDomain(self):
178 store_evtchn = self.vm.getStorePort()
179 console_evtchn = self.vm.getConsolePort()
181 mem_mb = self.getRequiredInitialReservation() / 1024
183 log.debug("domid = %d", self.vm.getDomid())
184 log.debug("memsize = %d", mem_mb)
185 log.debug("image = %s", self.kernel)
186 log.debug("store_evtchn = %d", store_evtchn)
187 log.debug("console_evtchn = %d", console_evtchn)
188 log.debug("cmdline = %s", self.cmdline)
189 log.debug("ramdisk = %s", self.ramdisk)
190 log.debug("vcpus = %d", self.vm.getVCpuCount())
191 log.debug("features = %s", self.vm.getFeatures())
193 return xc.linux_build(domid = self.vm.getDomid(),
194 memsize = mem_mb,
195 image = self.kernel,
196 store_evtchn = store_evtchn,
197 console_evtchn = console_evtchn,
198 cmdline = self.cmdline,
199 ramdisk = self.ramdisk,
200 features = self.vm.getFeatures())
202 class PPC_LinuxImageHandler(LinuxImageHandler):
204 ostype = "linux"
206 def configure(self, vmConfig, imageConfig, deviceConfig):
207 LinuxImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
208 self.imageConfig = imageConfig
210 def buildDomain(self):
211 store_evtchn = self.vm.getStorePort()
212 console_evtchn = self.vm.getConsolePort()
214 mem_mb = self.getRequiredInitialReservation() / 1024
216 log.debug("domid = %d", self.vm.getDomid())
217 log.debug("memsize = %d", mem_mb)
218 log.debug("image = %s", self.kernel)
219 log.debug("store_evtchn = %d", store_evtchn)
220 log.debug("console_evtchn = %d", console_evtchn)
221 log.debug("cmdline = %s", self.cmdline)
222 log.debug("ramdisk = %s", self.ramdisk)
223 log.debug("vcpus = %d", self.vm.getVCpuCount())
224 log.debug("features = %s", self.vm.getFeatures())
226 devtree = FlatDeviceTree.build(self)
228 return xc.linux_build(domid = self.vm.getDomid(),
229 memsize = mem_mb,
230 image = self.kernel,
231 store_evtchn = store_evtchn,
232 console_evtchn = console_evtchn,
233 cmdline = self.cmdline,
234 ramdisk = self.ramdisk,
235 features = self.vm.getFeatures(),
236 arch_args = devtree.to_bin())
238 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
239 """@param shadow_mem_kb The configured shadow memory, in KiB.
240 @param maxmem_kb The configured maxmem, in KiB.
241 @return The corresponding required amount of shadow memory, also in
242 KiB.
243 PowerPC currently uses "shadow memory" to refer to the hash table."""
244 return max(maxmem_kb / 64, shadow_mem_kb)
247 class PPC_ProseImageHandler(LinuxImageHandler):
249 ostype = "prose"
251 def configure(self, imageConfig, deviceConfig):
252 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
253 self.imageConfig = imageConfig
255 def buildDomain(self):
256 store_evtchn = self.vm.getStorePort()
257 console_evtchn = self.vm.getConsolePort()
259 mem_mb = self.getRequiredInitialReservation() / 1024
261 log.debug("dom = %d", self.vm.getDomid())
262 log.debug("memsize = %d", mem_mb)
263 log.debug("image = %s", self.kernel)
264 log.debug("store_evtchn = %d", store_evtchn)
265 log.debug("console_evtchn = %d", console_evtchn)
266 log.debug("cmdline = %s", self.cmdline)
267 log.debug("ramdisk = %s", self.ramdisk)
268 log.debug("vcpus = %d", self.vm.getVCpuCount())
269 log.debug("features = %s", self.vm.getFeatures())
271 devtree = FlatDeviceTree.build(self)
273 return xc.arch_prose_build(dom = self.vm.getDomid(),
274 memsize = mem_mb,
275 image = self.kernel,
276 store_evtchn = store_evtchn,
277 console_evtchn = console_evtchn,
278 cmdline = self.cmdline,
279 ramdisk = self.ramdisk,
280 features = self.vm.getFeatures(),
281 arch_args = devtree.to_bin())
283 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
284 """@param shadow_mem_kb The configured shadow memory, in KiB.
285 @param maxmem_kb The configured maxmem, in KiB.
286 @return The corresponding required amount of shadow memory, also in
287 KiB.
288 PowerPC currently uses "shadow memory" to refer to the hash table."""
289 return max(maxmem_kb / 64, shadow_mem_kb)
292 class HVMImageHandler(ImageHandler):
294 ostype = "hvm"
296 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
297 ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig)
298 self.shutdownWatch = None
299 self.rebootFeatureWatch = None
301 def configure(self, vmConfig, imageConfig, deviceConfig):
302 ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
304 info = xc.xeninfo()
305 if 'hvm' not in info['xen_caps']:
306 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
307 "supported by your CPU and enabled in your BIOS?")
309 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
310 self.device_model = imageConfig['hvm'].get('device_model')
311 if not self.device_model:
312 raise VmError("hvm: missing device model")
314 self.display = imageConfig['hvm'].get('display')
315 self.xauthority = imageConfig['hvm'].get('xauthority')
316 self.vncconsole = imageConfig['hvm'].get('vncconsole')
318 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
319 ("image/device-model", self.device_model),
320 ("image/display", self.display))
322 self.pid = None
324 self.dmargs += self.configVNC(imageConfig)
326 self.pae = imageConfig['hvm'].get('pae', 0)
327 self.apic = imageConfig['hvm'].get('apic', 0)
328 self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
331 def buildDomain(self):
332 store_evtchn = self.vm.getStorePort()
334 mem_mb = self.getRequiredInitialReservation() / 1024
336 log.debug("domid = %d", self.vm.getDomid())
337 log.debug("image = %s", self.kernel)
338 log.debug("store_evtchn = %d", store_evtchn)
339 log.debug("memsize = %d", mem_mb)
340 log.debug("vcpus = %d", self.vm.getVCpuCount())
341 log.debug("pae = %d", self.pae)
342 log.debug("acpi = %d", self.acpi)
343 log.debug("apic = %d", self.apic)
345 self.register_shutdown_watch()
346 self.register_reboot_feature_watch()
348 return xc.hvm_build(domid = self.vm.getDomid(),
349 image = self.kernel,
350 store_evtchn = store_evtchn,
351 memsize = mem_mb,
352 vcpus = self.vm.getVCpuCount(),
353 pae = self.pae,
354 acpi = self.acpi,
355 apic = self.apic)
357 # Return a list of cmd line args to the device models based on the
358 # xm config file
359 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
360 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
361 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
362 'acpi', 'usb', 'usbdevice', 'keymap' ]
363 ret = []
364 hvmDeviceConfig = imageConfig['hvm']['devices']
366 for a in dmargs:
367 v = hvmDeviceConfig.get(a)
369 # python doesn't allow '-' in variable names
370 if a == 'stdvga': a = 'std-vga'
371 if a == 'keymap': a = 'k'
373 # Handle booleans gracefully
374 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
375 if v != None: v = int(v)
376 if v: ret.append("-%s" % a)
377 else:
378 if v:
379 ret.append("-%s" % a)
380 ret.append("%s" % v)
382 if a in ['fda', 'fdb']:
383 if v:
384 if not os.path.isabs(v):
385 raise VmError("Floppy file %s does not exist." % v)
386 log.debug("args: %s, val: %s" % (a,v))
388 # Handle disk/network related options
389 mac = None
390 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
391 nics = 0
393 for devuuid, (devtype, devinfo) in deviceConfig.items():
394 if devtype == 'vbd':
395 uname = devinfo.get('uname')
396 if uname is not None and 'file:' in uname:
397 (_, vbdparam) = string.split(uname, ':', 1)
398 if not os.path.isfile(vbdparam):
399 raise VmError('Disk image does not exist: %s' %
400 vbdparam)
401 if devtype == 'vif':
402 dtype = devinfo.get('type', 'ioemu')
403 if dtype != 'ioemu':
404 continue
405 nics += 1
406 mac = devinfo.get('mac')
407 if mac == None:
408 mac = randomMAC()
409 bridge = devinfo.get('bridge', 'xenbr0')
410 model = devinfo.get('model', 'rtl8139')
411 ret.append("-net")
412 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
413 (nics, mac, model))
414 ret.append("-net")
415 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
416 return ret
418 def configVNC(self, imageConfig):
419 # Handle graphics library related options
420 vnc = imageConfig.get('vnc')
421 sdl = imageConfig.get('sdl')
422 ret = []
423 nographic = imageConfig.get('nographic')
425 # get password from VM config (if password omitted, None)
426 vncpasswd_vmconfig = imageConfig.get('vncpasswd')
428 if nographic:
429 ret.append('-nographic')
430 return ret
432 if vnc:
433 vncdisplay = imageConfig.get('vncdisplay',
434 int(self.vm.getDomid()))
435 vncunused = imageConfig.get('vncunused')
437 if vncunused:
438 ret += ['-vncunused']
439 else:
440 ret += ['-vnc', '%d' % vncdisplay]
442 vnclisten = imageConfig.get('vnclisten')
444 if not(vnclisten):
445 vnclisten = (xen.xend.XendRoot.instance().
446 get_vnclisten_address())
447 if vnclisten:
448 ret += ['-vnclisten', vnclisten]
450 vncpasswd = vncpasswd_vmconfig
451 if vncpasswd is None:
452 vncpasswd = (xen.xend.XendRoot.instance().
453 get_vncpasswd_default())
454 if vncpasswd is None:
455 raise VmError('vncpasswd is not set up in ' +
456 'VMconfig and xend-config.')
457 if vncpasswd != '':
458 self.vm.storeVm("vncpasswd", vncpasswd)
460 return ret
462 def createDeviceModel(self):
463 if self.pid:
464 return
465 # Execute device model.
466 #todo: Error handling
467 args = [self.device_model]
468 args = args + ([ "-d", "%d" % self.vm.getDomid(),
469 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
470 args = args + self.dmargs
471 env = dict(os.environ)
472 if self.display:
473 env['DISPLAY'] = self.display
474 if self.xauthority:
475 env['XAUTHORITY'] = self.xauthority
476 if self.vncconsole:
477 args = args + ([ "-vncviewer" ])
478 log.info("spawning device models: %s %s", self.device_model, args)
479 # keep track of pid and spawned options to kill it later
480 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
481 self.vm.storeDom("image/device-model-pid", self.pid)
482 log.info("device model pid: %d", self.pid)
484 def recreate(self):
485 self.register_shutdown_watch()
486 self.register_reboot_feature_watch()
487 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
489 def destroy(self):
490 self.unregister_shutdown_watch()
491 self.unregister_reboot_feature_watch();
492 if self.pid:
493 try:
494 os.kill(self.pid, signal.SIGKILL)
495 except OSError, exn:
496 log.exception(exn)
497 try:
498 os.waitpid(self.pid, 0)
499 except OSError, exn:
500 # This is expected if Xend has been restarted within the
501 # life of this domain. In this case, we can kill the process,
502 # but we can't wait for it because it's not our child.
503 pass
504 self.pid = None
506 def register_shutdown_watch(self):
507 """ add xen store watch on control/shutdown """
508 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown",
509 self.hvm_shutdown)
510 log.debug("hvm shutdown watch registered")
512 def unregister_shutdown_watch(self):
513 """Remove the watch on the control/shutdown, if any. Nothrow
514 guarantee."""
516 try:
517 if self.shutdownWatch:
518 self.shutdownWatch.unwatch()
519 except:
520 log.exception("Unwatching hvm shutdown watch failed.")
521 self.shutdownWatch = None
522 log.debug("hvm shutdown watch unregistered")
524 def hvm_shutdown(self, _):
525 """ watch call back on node control/shutdown,
526 if node changed, this function will be called
527 """
528 xd = xen.xend.XendDomain.instance()
529 try:
530 vm = xd.domain_lookup( self.vm.getDomid() )
531 except XendError:
532 # domain isn't registered, no need to clean it up.
533 return False
535 reason = vm.getShutdownReason()
536 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
537 if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
538 vm.info['shutdown'] = 1
539 vm.info['shutdown_reason'] = \
540 REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
541 vm.refreshShutdown(vm.info)
543 return True # Keep watching
545 def register_reboot_feature_watch(self):
546 """ add xen store watch on control/feature-reboot """
547 self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
548 self.hvm_reboot_feature)
549 log.debug("hvm reboot feature watch registered")
551 def unregister_reboot_feature_watch(self):
552 """Remove the watch on the control/feature-reboot, if any. Nothrow
553 guarantee."""
555 try:
556 if self.rebootFeatureWatch:
557 self.rebootFeatureWatch.unwatch()
558 except:
559 log.exception("Unwatching hvm reboot feature watch failed.")
560 self.rebootFeatureWatch = None
561 log.debug("hvm reboot feature watch unregistered")
563 def hvm_reboot_feature(self, _):
564 """ watch call back on node control/feature-reboot,
565 if node changed, this function will be called
566 """
567 status = self.vm.readDom('control/feature-reboot')
568 log.debug("hvm_reboot_feature fired, module status=%s", status)
569 if status == '1':
570 self.unregister_shutdown_watch()
572 return True # Keep watching
575 class IA64_HVM_ImageHandler(HVMImageHandler):
577 def getRequiredAvailableMemory(self, mem_kb):
578 page_kb = 16
579 # ROM size for guest firmware, ioreq page and xenstore page
580 extra_pages = 1024 + 3
581 return mem_kb + extra_pages * page_kb
583 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
584 # Explicit shadow memory is not a concept
585 return 0
587 class X86_HVM_ImageHandler(HVMImageHandler):
589 def getRequiredAvailableMemory(self, mem_kb):
590 # Add 8 MiB overhead for QEMU's video RAM.
591 return mem_kb + 8192
593 def getRequiredInitialReservation(self):
594 return self.vm.getMemoryTarget()
596 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
597 # 256 pages (1MB) per vcpu,
598 # plus 1 page per MiB of RAM for the P2M map,
599 # plus 1 page per MiB of RAM to shadow the resident processes.
600 # This is higher than the minimum that Xen would allocate if no value
601 # were given (but the Xen minimum is for safety, not performance).
602 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
603 shadow_mem_kb)
605 class X86_Linux_ImageHandler(LinuxImageHandler):
607 def buildDomain(self):
608 # set physical mapping limit
609 # add an 8MB slack to balance backend allocations.
610 mem_kb = self.getRequiredInitialReservation() + (8 * 1024)
611 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
612 return LinuxImageHandler.buildDomain(self)
614 _handlers = {
615 "powerpc": {
616 "linux": PPC_LinuxImageHandler,
617 "prose": PPC_ProseImageHandler,
618 },
619 "ia64": {
620 "linux": LinuxImageHandler,
621 "hvm": IA64_HVM_ImageHandler,
622 },
623 "x86": {
624 "linux": X86_Linux_ImageHandler,
625 "hvm": X86_HVM_ImageHandler,
626 },
627 }
629 def findImageHandlerClass(image):
630 """Find the image handler class for an image config.
632 @param image config
633 @return ImageHandler subclass or None
634 """
635 image_type = image['type']
636 if image_type is None:
637 raise VmError('missing image type')
638 try:
639 return _handlers[arch.type][image_type]
640 except KeyError:
641 raise VmError('unknown image type: ' + image_type)