ia64/xen-unstable

view tools/python/xen/xend/image.py @ 12132:0a7fb6788fa7

[XEND] Check for and fail on non-existent floppy-drive file when starting HVM guest.

The qemu device model hangs when a non-existent floppy file is passed
via 'fda' or 'fdb'. The attached patch checks whether the floppy file
exists and raises an error if it does not.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author kfraser@localhost.localdomain
date Sun Oct 15 09:19:26 2006 +0100 (2006-10-15)
parents 59f3891b94b8
children 81a80d86f77f
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
24 import xen.lowlevel.xc
25 from xen.xend import sxp
26 from xen.xend.XendError import VmError
27 from xen.xend.XendLogging import log
28 from xen.xend.server.netif import randomMAC
29 from xen.xend.xenstore.xswatch import xswatch
30 from xen.xend import arch
31 from xen.xend import FlatDeviceTree
34 xc = xen.lowlevel.xc.xc()
37 MAX_GUEST_CMDLINE = 1024
40 def create(vm, imageConfig, deviceConfig):
41 """Create an image handler for a vm.
43 @return ImageHandler instance
44 """
45 return findImageHandlerClass(imageConfig)(vm, imageConfig, 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, imageConfig, deviceConfig):
69 self.vm = vm
71 self.kernel = None
72 self.ramdisk = None
73 self.cmdline = None
75 self.configure(imageConfig, deviceConfig)
77 def configure(self, imageConfig, _):
78 """Config actions common to all unix-like domains."""
80 def get_cfg(name, default = None):
81 return sxp.child_value(imageConfig, name, default)
83 self.kernel = get_cfg("kernel")
84 self.cmdline = ""
85 ip = get_cfg("ip")
86 if ip:
87 self.cmdline += " ip=" + ip
88 root = get_cfg("root")
89 if root:
90 self.cmdline += " root=" + root
91 args = get_cfg("args")
92 if args:
93 self.cmdline += " " + args
94 self.ramdisk = get_cfg("ramdisk", '')
96 self.vm.storeVm(("image/ostype", self.ostype),
97 ("image/kernel", self.kernel),
98 ("image/cmdline", self.cmdline),
99 ("image/ramdisk", self.ramdisk))
102 def cleanupBootloading(self):
103 self.unlink(self.kernel)
104 self.unlink(self.ramdisk)
107 def unlink(self, f):
108 if not f: return
109 try:
110 os.unlink(f)
111 except OSError, ex:
112 log.warning("error removing bootloader file '%s': %s", f, ex)
115 def createImage(self):
116 """Entry point to create domain memory image.
117 Override in subclass if needed.
118 """
119 return self.createDomain()
122 def createDomain(self):
123 """Build the domain boot image.
124 """
125 # Set params and call buildDomain().
127 if not os.path.isfile(self.kernel):
128 raise VmError('Kernel image does not exist: %s' % self.kernel)
129 if self.ramdisk and not os.path.isfile(self.ramdisk):
130 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
131 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
132 log.warning('kernel cmdline too long, domain %d',
133 self.vm.getDomid())
135 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
136 self.vm.getDomid(), self.vm.getVCpuCount())
138 result = self.buildDomain()
140 if isinstance(result, dict):
141 return result
142 else:
143 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
144 % (self.ostype, self.vm.getDomid(), str(result)))
146 def getRequiredAvailableMemory(self, mem_kb):
147 """@param mem_kb The configured maxmem or memory, in KiB.
148 @return The corresponding required amount of memory for the domain,
149 also in KiB. This is normally the given mem_kb, but architecture- or
150 image-specific code may override this to add headroom where
151 necessary."""
152 return mem_kb
154 def getRequiredInitialReservation(self, mem_kb):
155 """@param mem_kb The configured memory, in KiB.
156 @return The corresponding required amount of memory to be free, also
157 in KiB. This is normally the same as getRequiredAvailableMemory, but
158 architecture- or image-specific code may override this to
159 add headroom where necessary."""
160 return self.getRequiredAvailableMemory(mem_kb)
162 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
163 """@param shadow_mem_kb The configured shadow memory, in KiB.
164 @param maxmem_kb The configured maxmem, in KiB.
165 @return The corresponding required amount of shadow memory, also in
166 KiB."""
167 # PV domains don't need any shadow memory
168 return 0
170 def buildDomain(self):
171 """Build the domain. Define in subclass."""
172 raise NotImplementedError()
174 def createDeviceModel(self):
175 """Create device model for the domain (define in subclass if needed)."""
176 pass
178 def destroy(self):
179 """Extra cleanup on domain destroy (define in subclass if needed)."""
180 pass
183 class LinuxImageHandler(ImageHandler):
185 ostype = "linux"
187 def buildDomain(self):
188 store_evtchn = self.vm.getStorePort()
189 console_evtchn = self.vm.getConsolePort()
191 log.debug("domid = %d", self.vm.getDomid())
192 log.debug("image = %s", self.kernel)
193 log.debug("store_evtchn = %d", store_evtchn)
194 log.debug("console_evtchn = %d", console_evtchn)
195 log.debug("cmdline = %s", self.cmdline)
196 log.debug("ramdisk = %s", self.ramdisk)
197 log.debug("vcpus = %d", self.vm.getVCpuCount())
198 log.debug("features = %s", self.vm.getFeatures())
200 return xc.linux_build(domid = self.vm.getDomid(),
201 image = self.kernel,
202 store_evtchn = store_evtchn,
203 console_evtchn = console_evtchn,
204 cmdline = self.cmdline,
205 ramdisk = self.ramdisk,
206 features = self.vm.getFeatures())
208 class PPC_LinuxImageHandler(LinuxImageHandler):
210 ostype = "linux"
212 def configure(self, imageConfig, deviceConfig):
213 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
214 self.imageConfig = imageConfig
216 def buildDomain(self):
217 store_evtchn = self.vm.getStorePort()
218 console_evtchn = self.vm.getConsolePort()
220 log.debug("domid = %d", self.vm.getDomid())
221 log.debug("image = %s", self.kernel)
222 log.debug("store_evtchn = %d", store_evtchn)
223 log.debug("console_evtchn = %d", console_evtchn)
224 log.debug("cmdline = %s", self.cmdline)
225 log.debug("ramdisk = %s", self.ramdisk)
226 log.debug("vcpus = %d", self.vm.getVCpuCount())
227 log.debug("features = %s", self.vm.getFeatures())
229 devtree = FlatDeviceTree.build(self)
231 return xc.linux_build(domid = self.vm.getDomid(),
232 image = self.kernel,
233 store_evtchn = store_evtchn,
234 console_evtchn = console_evtchn,
235 cmdline = self.cmdline,
236 ramdisk = self.ramdisk,
237 features = self.vm.getFeatures(),
238 arch_args = devtree.to_bin())
240 class HVMImageHandler(ImageHandler):
242 def configure(self, imageConfig, deviceConfig):
243 ImageHandler.configure(self, imageConfig, deviceConfig)
245 info = xc.xeninfo()
246 if not 'hvm' in info['xen_caps']:
247 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
248 "supported by your CPU and enabled in your BIOS?")
250 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
251 self.device_model = sxp.child_value(imageConfig, 'device_model')
252 if not self.device_model:
253 raise VmError("hvm: missing device model")
254 self.display = sxp.child_value(imageConfig, 'display')
255 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
256 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
258 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
259 ("image/device-model", self.device_model),
260 ("image/display", self.display))
262 self.pid = 0
264 self.dmargs += self.configVNC(imageConfig)
266 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
268 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
269 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
271 def buildDomain(self):
272 store_evtchn = self.vm.getStorePort()
274 log.debug("domid = %d", self.vm.getDomid())
275 log.debug("image = %s", self.kernel)
276 log.debug("store_evtchn = %d", store_evtchn)
277 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
278 log.debug("vcpus = %d", self.vm.getVCpuCount())
279 log.debug("pae = %d", self.pae)
280 log.debug("acpi = %d", self.acpi)
281 log.debug("apic = %d", self.apic)
283 self.register_shutdown_watch()
285 return xc.hvm_build(domid = self.vm.getDomid(),
286 image = self.kernel,
287 store_evtchn = store_evtchn,
288 memsize = self.vm.getMemoryTarget() / 1024,
289 vcpus = self.vm.getVCpuCount(),
290 pae = self.pae,
291 acpi = self.acpi,
292 apic = self.apic)
294 # Return a list of cmd line args to the device models based on the
295 # xm config file
296 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
297 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
298 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
299 'acpi', 'usb', 'usbdevice']
300 ret = []
301 for a in dmargs:
302 v = sxp.child_value(imageConfig, a)
304 # python doesn't allow '-' in variable names
305 if a == 'stdvga': a = 'std-vga'
307 # Handle booleans gracefully
308 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
309 if v != None: v = int(v)
310 if v: ret.append("-%s" % a)
311 else:
312 if v:
313 ret.append("-%s" % a)
314 ret.append("%s" % v)
316 if a in ['fda', 'fdb' ]:
317 if v:
318 if not os.path.isfile(v):
319 raise VmError("Floppy file %s does not exist." % v)
320 log.debug("args: %s, val: %s" % (a,v))
322 # Handle disk/network related options
323 mac = None
324 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
325 nics = 0
326 for (name, info) in deviceConfig:
327 if name == 'vbd':
328 uname = sxp.child_value(info, 'uname')
329 if uname is not None and 'file:' in uname:
330 (_, vbdparam) = string.split(uname, ':', 1)
331 if not os.path.isfile(vbdparam):
332 raise VmError('Disk image does not exist: %s' %
333 vbdparam)
334 if name == 'vif':
335 type = sxp.child_value(info, 'type')
336 if type != 'ioemu':
337 continue
338 nics += 1
339 mac = sxp.child_value(info, 'mac')
340 if mac == None:
341 mac = randomMAC()
342 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
343 model = sxp.child_value(info, 'model', 'rtl8139')
344 ret.append("-net")
345 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
346 (nics, mac, model))
347 ret.append("-net")
348 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
349 return ret
351 def configVNC(self, config):
352 # Handle graphics library related options
353 vnc = sxp.child_value(config, 'vnc')
354 sdl = sxp.child_value(config, 'sdl')
355 ret = []
356 nographic = sxp.child_value(config, 'nographic')
357 if nographic:
358 ret.append('-nographic')
359 return ret
360 if vnc:
361 vncdisplay = sxp.child_value(config, 'vncdisplay',
362 int(self.vm.getDomid()))
363 ret = ret + ['-vnc', '%d' % vncdisplay, '-k', 'en-us']
364 vncunused = sxp.child_value(config, 'vncunused')
365 if vncunused:
366 ret += ['-vncunused']
367 return ret
369 def createDeviceModel(self):
370 if self.pid:
371 return
372 # Execute device model.
373 #todo: Error handling
374 args = [self.device_model]
375 args = args + ([ "-d", "%d" % self.vm.getDomid(),
376 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
377 args = args + self.dmargs
378 env = dict(os.environ)
379 if self.display:
380 env['DISPLAY'] = self.display
381 if self.xauthority:
382 env['XAUTHORITY'] = self.xauthority
383 if self.vncconsole:
384 args = args + ([ "-vncviewer" ])
385 log.info("spawning device models: %s %s", self.device_model, args)
386 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
387 log.info("device model pid: %d", self.pid)
389 def destroy(self):
390 self.unregister_shutdown_watch();
391 import signal
392 if not self.pid:
393 return
394 os.kill(self.pid, signal.SIGKILL)
395 os.waitpid(self.pid, 0)
396 self.pid = 0
398 def register_shutdown_watch(self):
399 """ add xen store watch on control/shutdown """
400 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
401 self.hvm_shutdown)
402 log.debug("hvm shutdown watch registered")
404 def unregister_shutdown_watch(self):
405 """Remove the watch on the control/shutdown, if any. Nothrow
406 guarantee."""
408 try:
409 if self.shutdownWatch:
410 self.shutdownWatch.unwatch()
411 except:
412 log.exception("Unwatching hvm shutdown watch failed.")
413 self.shutdownWatch = None
414 log.debug("hvm shutdown watch unregistered")
416 def hvm_shutdown(self, _):
417 """ watch call back on node control/shutdown,
418 if node changed, this function will be called
419 """
420 from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
421 xd = xen.xend.XendDomain.instance()
422 vm = xd.domain_lookup( self.vm.getDomid() )
424 reason = vm.readDom('control/shutdown')
425 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
426 for x in shutdown_reasons.keys():
427 if shutdown_reasons[x] == reason:
428 vm.info['shutdown'] = 1
429 vm.info['shutdown_reason'] = x
430 vm.refreshShutdown(vm.info)
432 return 1 # Keep watching
434 class IA64_HVM_ImageHandler(HVMImageHandler):
436 ostype = "hvm"
438 def getRequiredAvailableMemory(self, mem_kb):
439 page_kb = 16
440 # ROM size for guest firmware, ioreq page and xenstore page
441 extra_pages = 1024 + 2
442 return mem_kb + extra_pages * page_kb
444 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
445 # Explicit shadow memory is not a concept
446 return 0
448 class X86_HVM_ImageHandler(HVMImageHandler):
450 ostype = "hvm"
452 def getRequiredAvailableMemory(self, mem_kb):
453 # Add 8 MiB overhead for QEMU's video RAM.
454 return self.getRequiredInitialReservation(mem_kb) + 8192
456 def getRequiredInitialReservation(self, mem_kb):
457 page_kb = 4
458 # This was derived emperically:
459 # 2.4 MB overhead per 1024 MB RAM
460 # + 4 to avoid low-memory condition
461 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
462 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
463 return mem_kb + extra_pages * page_kb
465 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
466 # The given value is the configured value -- we need to include the
467 # overhead due to getRequiredInitialReservation.
468 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
470 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
471 # the minimum that Xen would allocate if no value were given.
472 return max(1024 * self.vm.getVCpuCount() + maxmem_kb / 256,
473 shadow_mem_kb)
476 _handlers = {
477 "powerpc": {
478 "linux": PPC_LinuxImageHandler,
479 },
480 "ia64": {
481 "linux": LinuxImageHandler,
482 "hvm": IA64_HVM_ImageHandler,
483 },
484 "x86": {
485 "linux": LinuxImageHandler,
486 "hvm": X86_HVM_ImageHandler,
487 },
488 }
490 def findImageHandlerClass(image):
491 """Find the image handler class for an image config.
493 @param image config
494 @return ImageHandler subclass or None
495 """
496 type = sxp.name(image)
497 if type is None:
498 raise VmError('missing image type')
499 try:
500 return _handlers[arch.type][type]
501 except KeyError:
502 raise VmError('unknown image type: ' + type)