direct-io.hg

view tools/python/xen/xend/image.py @ 12803:df5fa63490f4

[XEN] Implement XENMEM_set_memory_map, which specifies memory map to
be returned by XENMEM_memory_map. Hook this into the domain builder.

Based on a patch by Glauber de Oliveira Costa <gcosta@redhat.com>

Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Fri Dec 08 11:30:30 2006 +0000 (2006-12-08)
parents 3f0ca90351e2
children 749c399d73df
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 class HVMImageHandler(ImageHandler):
240 ostype = "hvm"
242 def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
243 ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig)
244 self.shutdownWatch = None
245 self.rebootFeatureWatch = None
247 def configure(self, vmConfig, imageConfig, deviceConfig):
248 ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
250 info = xc.xeninfo()
251 if 'hvm' not in info['xen_caps']:
252 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
253 "supported by your CPU and enabled in your BIOS?")
255 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
256 self.device_model = imageConfig['hvm'].get('device_model')
257 if not self.device_model:
258 raise VmError("hvm: missing device model")
260 self.display = imageConfig['hvm'].get('display')
261 self.xauthority = imageConfig['hvm'].get('xauthority')
262 self.vncconsole = imageConfig['hvm'].get('vncconsole')
264 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
265 ("image/device-model", self.device_model),
266 ("image/display", self.display))
268 self.pid = None
270 self.dmargs += self.configVNC(imageConfig)
272 self.pae = imageConfig['hvm'].get('pae', 0)
273 self.apic = imageConfig['hvm'].get('apic', 0)
274 self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
277 def buildDomain(self):
278 store_evtchn = self.vm.getStorePort()
280 mem_mb = self.getRequiredInitialReservation() / 1024
282 log.debug("domid = %d", self.vm.getDomid())
283 log.debug("image = %s", self.kernel)
284 log.debug("store_evtchn = %d", store_evtchn)
285 log.debug("memsize = %d", mem_mb)
286 log.debug("vcpus = %d", self.vm.getVCpuCount())
287 log.debug("pae = %d", self.pae)
288 log.debug("acpi = %d", self.acpi)
289 log.debug("apic = %d", self.apic)
291 self.register_shutdown_watch()
292 self.register_reboot_feature_watch()
294 return xc.hvm_build(domid = self.vm.getDomid(),
295 image = self.kernel,
296 store_evtchn = store_evtchn,
297 memsize = mem_mb,
298 vcpus = self.vm.getVCpuCount(),
299 pae = self.pae,
300 acpi = self.acpi,
301 apic = self.apic)
303 # Return a list of cmd line args to the device models based on the
304 # xm config file
305 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
306 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
307 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
308 'acpi', 'usb', 'usbdevice', 'keymap' ]
309 ret = []
310 hvmDeviceConfig = imageConfig['hvm']['devices']
312 for a in dmargs:
313 v = hvmDeviceConfig.get(a)
315 # python doesn't allow '-' in variable names
316 if a == 'stdvga': a = 'std-vga'
317 if a == 'keymap': a = 'k'
319 # Handle booleans gracefully
320 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
321 if v != None: v = int(v)
322 if v: ret.append("-%s" % a)
323 else:
324 if v:
325 ret.append("-%s" % a)
326 ret.append("%s" % v)
328 if a in ['fda', 'fdb']:
329 if v:
330 if not os.path.isabs(v):
331 raise VmError("Floppy file %s does not exist." % v)
332 log.debug("args: %s, val: %s" % (a,v))
334 # Handle disk/network related options
335 mac = None
336 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
337 nics = 0
339 for devuuid, (devtype, devinfo) in deviceConfig.items():
340 if devtype == 'vbd':
341 uname = devinfo.get('uname')
342 if uname is not None and 'file:' in uname:
343 (_, vbdparam) = string.split(uname, ':', 1)
344 if not os.path.isfile(vbdparam):
345 raise VmError('Disk image does not exist: %s' %
346 vbdparam)
347 if devtype == 'vif':
348 dtype = devinfo.get('type', 'ioemu')
349 if dtype != 'ioemu':
350 continue
351 nics += 1
352 mac = devinfo.get('mac')
353 if mac == None:
354 mac = randomMAC()
355 bridge = devinfo.get('bridge', 'xenbr0')
356 model = devinfo.get('model', 'rtl8139')
357 ret.append("-net")
358 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
359 (nics, mac, model))
360 ret.append("-net")
361 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
362 return ret
364 def configVNC(self, imageConfig):
365 # Handle graphics library related options
366 vnc = imageConfig.get('vnc')
367 sdl = imageConfig.get('sdl')
368 ret = []
369 nographic = imageConfig.get('nographic')
371 # get password from VM config (if password omitted, None)
372 vncpasswd_vmconfig = imageConfig.get('vncpasswd')
374 if nographic:
375 ret.append('-nographic')
376 return ret
378 if vnc:
379 vncdisplay = imageConfig.get('vncdisplay',
380 int(self.vm.getDomid()))
381 vncunused = imageConfig.get('vncunused')
383 if vncunused:
384 ret += ['-vncunused']
385 else:
386 ret += ['-vnc', '%d' % vncdisplay]
388 vnclisten = imageConfig.get('vnclisten')
390 if not(vnclisten):
391 vnclisten = (xen.xend.XendRoot.instance().
392 get_vnclisten_address())
393 if vnclisten:
394 ret += ['-vnclisten', vnclisten]
396 vncpasswd = vncpasswd_vmconfig
397 if vncpasswd is None:
398 vncpasswd = (xen.xend.XendRoot.instance().
399 get_vncpasswd_default())
400 if vncpasswd is None:
401 raise VmError('vncpasswd is not set up in ' +
402 'VMconfig and xend-config.')
403 if vncpasswd != '':
404 self.vm.storeVm("vncpasswd", vncpasswd)
406 return ret
408 def createDeviceModel(self):
409 if self.pid:
410 return
411 # Execute device model.
412 #todo: Error handling
413 args = [self.device_model]
414 args = args + ([ "-d", "%d" % self.vm.getDomid(),
415 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
416 args = args + self.dmargs
417 env = dict(os.environ)
418 if self.display:
419 env['DISPLAY'] = self.display
420 if self.xauthority:
421 env['XAUTHORITY'] = self.xauthority
422 if self.vncconsole:
423 args = args + ([ "-vncviewer" ])
424 log.info("spawning device models: %s %s", self.device_model, args)
425 # keep track of pid and spawned options to kill it later
426 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
427 self.vm.storeDom("image/device-model-pid", self.pid)
428 log.info("device model pid: %d", self.pid)
430 def recreate(self):
431 self.register_shutdown_watch()
432 self.register_reboot_feature_watch()
433 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
435 def destroy(self):
436 self.unregister_shutdown_watch()
437 self.unregister_reboot_feature_watch();
438 if self.pid:
439 try:
440 os.kill(self.pid, signal.SIGKILL)
441 except OSError, exn:
442 log.exception(exn)
443 try:
444 os.waitpid(self.pid, 0)
445 except OSError, exn:
446 # This is expected if Xend has been restarted within the
447 # life of this domain. In this case, we can kill the process,
448 # but we can't wait for it because it's not our child.
449 pass
450 self.pid = None
452 def register_shutdown_watch(self):
453 """ add xen store watch on control/shutdown """
454 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown",
455 self.hvm_shutdown)
456 log.debug("hvm shutdown watch registered")
458 def unregister_shutdown_watch(self):
459 """Remove the watch on the control/shutdown, if any. Nothrow
460 guarantee."""
462 try:
463 if self.shutdownWatch:
464 self.shutdownWatch.unwatch()
465 except:
466 log.exception("Unwatching hvm shutdown watch failed.")
467 self.shutdownWatch = None
468 log.debug("hvm shutdown watch unregistered")
470 def hvm_shutdown(self, _):
471 """ watch call back on node control/shutdown,
472 if node changed, this function will be called
473 """
474 xd = xen.xend.XendDomain.instance()
475 try:
476 vm = xd.domain_lookup( self.vm.getDomid() )
477 except XendError:
478 # domain isn't registered, no need to clean it up.
479 return False
481 reason = vm.getShutdownReason()
482 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
483 if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS:
484 vm.info['shutdown'] = 1
485 vm.info['shutdown_reason'] = \
486 REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
487 vm.refreshShutdown(vm.info)
489 return True # Keep watching
491 def register_reboot_feature_watch(self):
492 """ add xen store watch on control/feature-reboot """
493 self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
494 self.hvm_reboot_feature)
495 log.debug("hvm reboot feature watch registered")
497 def unregister_reboot_feature_watch(self):
498 """Remove the watch on the control/feature-reboot, if any. Nothrow
499 guarantee."""
501 try:
502 if self.rebootFeatureWatch:
503 self.rebootFeatureWatch.unwatch()
504 except:
505 log.exception("Unwatching hvm reboot feature watch failed.")
506 self.rebootFeatureWatch = None
507 log.debug("hvm reboot feature watch unregistered")
509 def hvm_reboot_feature(self, _):
510 """ watch call back on node control/feature-reboot,
511 if node changed, this function will be called
512 """
513 status = self.vm.readDom('control/feature-reboot')
514 log.debug("hvm_reboot_feature fired, module status=%s", status)
515 if status == '1':
516 self.unregister_shutdown_watch()
518 return True # Keep watching
521 class IA64_HVM_ImageHandler(HVMImageHandler):
523 def getRequiredAvailableMemory(self, mem_kb):
524 page_kb = 16
525 # ROM size for guest firmware, ioreq page and xenstore page
526 extra_pages = 1024 + 3
527 return mem_kb + extra_pages * page_kb
529 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
530 # Explicit shadow memory is not a concept
531 return 0
533 class X86_HVM_ImageHandler(HVMImageHandler):
535 def getRequiredAvailableMemory(self, mem_kb):
536 # Add 8 MiB overhead for QEMU's video RAM.
537 return mem_kb + 8192
539 def getRequiredInitialReservation(self):
540 return self.vm.getMemoryTarget()
542 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
543 # 256 pages (1MB) per vcpu,
544 # plus 1 page per MiB of RAM for the P2M map,
545 # plus 1 page per MiB of RAM to shadow the resident processes.
546 # This is higher than the minimum that Xen would allocate if no value
547 # were given (but the Xen minimum is for safety, not performance).
548 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
549 shadow_mem_kb)
551 class X86_Linux_ImageHandler(LinuxImageHandler):
553 def buildDomain(self):
554 # set physical mapping limit
555 # add an 8MB slack to balance backend allocations.
556 mem_kb = self.getRequiredInitialReservation() + (8 * 1024)
557 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
558 return LinuxImageHandler.buildDomain(self)
560 _handlers = {
561 "powerpc": {
562 "linux": PPC_LinuxImageHandler,
563 },
564 "ia64": {
565 "linux": LinuxImageHandler,
566 "hvm": IA64_HVM_ImageHandler,
567 },
568 "x86": {
569 "linux": X86_Linux_ImageHandler,
570 "hvm": X86_HVM_ImageHandler,
571 },
572 }
574 def findImageHandlerClass(image):
575 """Find the image handler class for an image config.
577 @param image config
578 @return ImageHandler subclass or None
579 """
580 image_type = image['type']
581 if image_type is None:
582 raise VmError('missing image type')
583 try:
584 return _handlers[arch.type][image_type]
585 except KeyError:
586 raise VmError('unknown image type: ' + image_type)