direct-io.hg

view tools/python/xen/xend/image.py @ 12236:e5067f9b268f

[XEND] Fix HVM image shutdown.

Missed HVM image creation's hook into XendDomainInfo.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Nov 03 15:15:16 2006 +0000 (2006-11-03)
parents 2b36aeb896c6
children 3a0116aa7ff5
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 import sxp
27 from xen.xend.XendError import VmError
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
35 xc = xen.lowlevel.xc.xc()
38 MAX_GUEST_CMDLINE = 1024
41 def create(vm, imageConfig, deviceConfig):
42 """Create an image handler for a vm.
44 @return ImageHandler instance
45 """
46 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
49 class ImageHandler:
50 """Abstract base class for image handlers.
52 createImage() is called to configure and build the domain from its
53 kernel image and ramdisk etc.
55 The method buildDomain() is used to build the domain, and must be
56 defined in a subclass. Usually this is the only method that needs
57 defining in a subclass.
59 The method createDeviceModel() is called to create the domain device
60 model if it needs one. The default is to do nothing.
62 The method destroy() is called when the domain is destroyed.
63 The default is to do nothing.
64 """
66 ostype = None
69 def __init__(self, vm, imageConfig, deviceConfig):
70 self.vm = vm
72 self.kernel = None
73 self.ramdisk = None
74 self.cmdline = None
76 self.configure(imageConfig, deviceConfig)
78 def configure(self, imageConfig, _):
79 """Config actions common to all unix-like domains."""
81 def get_cfg(name, default = None):
82 return sxp.child_value(imageConfig, name, default)
84 self.kernel = get_cfg("kernel")
85 self.cmdline = ""
86 ip = get_cfg("ip")
87 if ip:
88 self.cmdline += " ip=" + ip
89 root = get_cfg("root")
90 if root:
91 self.cmdline += " root=" + root
92 args = get_cfg("args")
93 if args:
94 self.cmdline += " " + args
95 self.ramdisk = get_cfg("ramdisk", '')
97 self.vm.storeVm(("image/ostype", self.ostype),
98 ("image/kernel", self.kernel),
99 ("image/cmdline", self.cmdline),
100 ("image/ramdisk", self.ramdisk))
103 def cleanupBootloading(self):
104 self.unlink(self.kernel)
105 self.unlink(self.ramdisk)
108 def unlink(self, f):
109 if not f: return
110 try:
111 os.unlink(f)
112 except OSError, ex:
113 log.warning("error removing bootloader file '%s': %s", f, ex)
116 def createImage(self):
117 """Entry point to create domain memory image.
118 Override in subclass if needed.
119 """
120 return self.createDomain()
123 def createDomain(self):
124 """Build the domain boot image.
125 """
126 # Set params and call buildDomain().
128 if not os.path.isfile(self.kernel):
129 raise VmError('Kernel image does not exist: %s' % self.kernel)
130 if self.ramdisk and not os.path.isfile(self.ramdisk):
131 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
132 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
133 log.warning('kernel cmdline too long, domain %d',
134 self.vm.getDomid())
136 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
137 self.vm.getDomid(), self.vm.getVCpuCount())
139 result = self.buildDomain()
141 if isinstance(result, dict):
142 return result
143 else:
144 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
145 % (self.ostype, self.vm.getDomid(), str(result)))
147 def getRequiredAvailableMemory(self, mem_kb):
148 """@param mem_kb The configured maxmem or memory, in KiB.
149 @return The corresponding required amount of memory for the domain,
150 also in KiB. This is normally the given mem_kb, but architecture- or
151 image-specific code may override this to add headroom where
152 necessary."""
153 return mem_kb
155 def getRequiredInitialReservation(self):
156 """@param mem_kb The configured 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.getMemoryTarget())
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 class LinuxImageHandler(ImageHandler):
186 ostype = "linux"
188 def buildDomain(self):
189 store_evtchn = self.vm.getStorePort()
190 console_evtchn = self.vm.getConsolePort()
192 mem_mb = self.getRequiredInitialReservation() / 1024
194 log.debug("domid = %d", self.vm.getDomid())
195 log.debug("memsize = %d", mem_mb)
196 log.debug("image = %s", self.kernel)
197 log.debug("store_evtchn = %d", store_evtchn)
198 log.debug("console_evtchn = %d", console_evtchn)
199 log.debug("cmdline = %s", self.cmdline)
200 log.debug("ramdisk = %s", self.ramdisk)
201 log.debug("vcpus = %d", self.vm.getVCpuCount())
202 log.debug("features = %s", self.vm.getFeatures())
204 return xc.linux_build(domid = self.vm.getDomid(),
205 memsize = mem_mb,
206 image = self.kernel,
207 store_evtchn = store_evtchn,
208 console_evtchn = console_evtchn,
209 cmdline = self.cmdline,
210 ramdisk = self.ramdisk,
211 features = self.vm.getFeatures())
213 class PPC_LinuxImageHandler(LinuxImageHandler):
215 ostype = "linux"
217 def configure(self, imageConfig, deviceConfig):
218 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
219 self.imageConfig = imageConfig
221 def buildDomain(self):
222 store_evtchn = self.vm.getStorePort()
223 console_evtchn = self.vm.getConsolePort()
225 mem_mb = self.getRequiredInitialReservation() / 1024
227 log.debug("domid = %d", self.vm.getDomid())
228 log.debug("memsize = %d", mem_mb)
229 log.debug("image = %s", self.kernel)
230 log.debug("store_evtchn = %d", store_evtchn)
231 log.debug("console_evtchn = %d", console_evtchn)
232 log.debug("cmdline = %s", self.cmdline)
233 log.debug("ramdisk = %s", self.ramdisk)
234 log.debug("vcpus = %d", self.vm.getVCpuCount())
235 log.debug("features = %s", self.vm.getFeatures())
237 devtree = FlatDeviceTree.build(self)
239 return xc.linux_build(domid = self.vm.getDomid(),
240 memsize = mem_mb,
241 image = self.kernel,
242 store_evtchn = store_evtchn,
243 console_evtchn = console_evtchn,
244 cmdline = self.cmdline,
245 ramdisk = self.ramdisk,
246 features = self.vm.getFeatures(),
247 arch_args = devtree.to_bin())
249 class HVMImageHandler(ImageHandler):
251 def configure(self, imageConfig, deviceConfig):
252 ImageHandler.configure(self, imageConfig, deviceConfig)
254 info = xc.xeninfo()
255 if not 'hvm' in info['xen_caps']:
256 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
257 "supported by your CPU and enabled in your BIOS?")
259 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
260 self.device_model = sxp.child_value(imageConfig, 'device_model')
261 if not self.device_model:
262 raise VmError("hvm: missing device model")
263 self.display = sxp.child_value(imageConfig, 'display')
264 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
265 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
267 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
268 ("image/device-model", self.device_model),
269 ("image/display", self.display))
271 self.pid = 0
273 self.dmargs += self.configVNC(imageConfig)
275 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
277 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
278 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
280 def buildDomain(self):
281 store_evtchn = self.vm.getStorePort()
283 mem_mb = self.getRequiredInitialReservation() / 1024
285 log.debug("domid = %d", self.vm.getDomid())
286 log.debug("image = %s", self.kernel)
287 log.debug("store_evtchn = %d", store_evtchn)
288 log.debug("memsize = %d", mem_mb)
289 log.debug("vcpus = %d", self.vm.getVCpuCount())
290 log.debug("pae = %d", self.pae)
291 log.debug("acpi = %d", self.acpi)
292 log.debug("apic = %d", self.apic)
294 self.register_shutdown_watch()
296 return xc.hvm_build(domid = self.vm.getDomid(),
297 image = self.kernel,
298 store_evtchn = store_evtchn,
299 memsize = mem_mb,
300 vcpus = self.vm.getVCpuCount(),
301 pae = self.pae,
302 acpi = self.acpi,
303 apic = self.apic)
305 # Return a list of cmd line args to the device models based on the
306 # xm config file
307 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
308 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
309 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
310 'acpi', 'usb', 'usbdevice']
311 ret = []
312 for a in dmargs:
313 v = sxp.child_value(imageConfig, a)
315 # python doesn't allow '-' in variable names
316 if a == 'stdvga': a = 'std-vga'
318 # Handle booleans gracefully
319 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
320 if v != None: v = int(v)
321 if v: ret.append("-%s" % a)
322 else:
323 if v:
324 ret.append("-%s" % a)
325 ret.append("%s" % v)
327 if a in ['fda', 'fdb' ]:
328 if v:
329 if not os.path.isfile(v):
330 raise VmError("Floppy file %s does not exist." % v)
331 log.debug("args: %s, val: %s" % (a,v))
333 # Handle disk/network related options
334 mac = None
335 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
336 nics = 0
337 for (name, info) in deviceConfig:
338 if name == 'vbd':
339 uname = sxp.child_value(info, 'uname')
340 if uname is not None and 'file:' in uname:
341 (_, vbdparam) = string.split(uname, ':', 1)
342 if not os.path.isfile(vbdparam):
343 raise VmError('Disk image does not exist: %s' %
344 vbdparam)
345 if name == 'vif':
346 type = sxp.child_value(info, 'type')
347 if type != 'ioemu':
348 continue
349 nics += 1
350 mac = sxp.child_value(info, 'mac')
351 if mac == None:
352 mac = randomMAC()
353 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
354 model = sxp.child_value(info, 'model', 'rtl8139')
355 ret.append("-net")
356 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
357 (nics, mac, model))
358 ret.append("-net")
359 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
360 return ret
362 def configVNC(self, config):
363 # Handle graphics library related options
364 vnc = sxp.child_value(config, 'vnc')
365 sdl = sxp.child_value(config, 'sdl')
366 ret = []
367 nographic = sxp.child_value(config, 'nographic')
369 # get password from VM config (if password omitted, None)
370 vncpasswd_vmconfig = sxp.child_value(config, 'vncpasswd')
372 if nographic:
373 ret.append('-nographic')
374 return ret
376 if vnc:
377 vncdisplay = sxp.child_value(config, 'vncdisplay',
378 int(self.vm.getDomid()))
380 vncunused = sxp.child_value(config, 'vncunused')
381 if vncunused:
382 ret += ['-vncunused']
383 else:
384 ret += ['-vnc', '%d' % vncdisplay]
386 ret += ['-k', 'en-us']
388 vnclisten = sxp.child_value(config, 'vnclisten')
389 if not(vnclisten):
390 vnclisten = (xen.xend.XendRoot.instance().
391 get_vnclisten_address())
392 if vnclisten:
393 ret += ['-vnclisten', vnclisten]
395 vncpasswd = vncpasswd_vmconfig
396 if vncpasswd is None:
397 vncpasswd = (xen.xend.XendRoot.instance().
398 get_vncpasswd_default())
399 if vncpasswd is None:
400 raise VmError('vncpasswd is not set up in ' +
401 'VMconfig and xend-config.')
402 if vncpasswd != '':
403 self.vm.storeVm("vncpasswd", vncpasswd)
405 return ret
407 def createDeviceModel(self):
408 if self.pid:
409 return
410 # Execute device model.
411 #todo: Error handling
412 args = [self.device_model]
413 args = args + ([ "-d", "%d" % self.vm.getDomid(),
414 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
415 args = args + self.dmargs
416 env = dict(os.environ)
417 if self.display:
418 env['DISPLAY'] = self.display
419 if self.xauthority:
420 env['XAUTHORITY'] = self.xauthority
421 if self.vncconsole:
422 args = args + ([ "-vncviewer" ])
423 log.info("spawning device models: %s %s", self.device_model, args)
424 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
425 log.info("device model pid: %d", self.pid)
427 def destroy(self):
428 self.unregister_shutdown_watch();
429 if not self.pid:
430 return
431 os.kill(self.pid, signal.SIGKILL)
432 os.waitpid(self.pid, 0)
433 self.pid = 0
435 def register_shutdown_watch(self):
436 """ add xen store watch on control/shutdown """
437 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
438 self.hvm_shutdown)
439 log.debug("hvm shutdown watch registered")
441 def unregister_shutdown_watch(self):
442 """Remove the watch on the control/shutdown, if any. Nothrow
443 guarantee."""
445 try:
446 if self.shutdownWatch:
447 self.shutdownWatch.unwatch()
448 except:
449 log.exception("Unwatching hvm shutdown watch failed.")
450 self.shutdownWatch = None
451 log.debug("hvm shutdown watch unregistered")
453 def hvm_shutdown(self, _):
454 """ watch call back on node control/shutdown,
455 if node changed, this function will be called
456 """
457 from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
458 xd = xen.xend.XendDomain.instance()
459 vm = xd.domain_lookup( self.vm.getDomid() )
461 reason = vm._readDom('control/shutdown')
462 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
463 for x in DOMAIN_SHUTDOWN_REASONS.keys():
464 if DOMAIN_SHUTDOWN_REASONS[x] == reason:
465 vm.info['shutdown'] = 1
466 vm.info['shutdown_reason'] = x
467 vm.refreshShutdown(vm.info)
469 return 1 # Keep watching
471 class IA64_HVM_ImageHandler(HVMImageHandler):
473 ostype = "hvm"
475 def getRequiredAvailableMemory(self, mem_kb):
476 page_kb = 16
477 # ROM size for guest firmware, ioreq page and xenstore page
478 extra_pages = 1024 + 3
479 return mem_kb + extra_pages * page_kb
481 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
482 # Explicit shadow memory is not a concept
483 return 0
485 class X86_HVM_ImageHandler(HVMImageHandler):
487 ostype = "hvm"
489 def getRequiredAvailableMemory(self, mem_kb):
490 # Add 8 MiB overhead for QEMU's video RAM.
491 return mem_kb + 8192
493 def getRequiredInitialReservation(self):
494 return self.vm.getMemoryTarget()
496 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
497 # 256 pages (1MB) per vcpu,
498 # plus 1 page per MiB of RAM for the P2M map,
499 # plus 1 page per MiB of RAM to shadow the resident processes.
500 # This is higher than the minimum that Xen would allocate if no value
501 # were given (but the Xen minimum is for safety, not performance).
502 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
503 shadow_mem_kb)
506 _handlers = {
507 "powerpc": {
508 "linux": PPC_LinuxImageHandler,
509 },
510 "ia64": {
511 "linux": LinuxImageHandler,
512 "hvm": IA64_HVM_ImageHandler,
513 },
514 "x86": {
515 "linux": LinuxImageHandler,
516 "hvm": X86_HVM_ImageHandler,
517 },
518 }
520 def findImageHandlerClass(image):
521 """Find the image handler class for an image config.
523 @param image config
524 @return ImageHandler subclass or None
525 """
526 type = sxp.name(image)
527 if type is None:
528 raise VmError('missing image type')
529 try:
530 return _handlers[arch.type][type]
531 except KeyError:
532 raise VmError('unknown image type: ' + type)