debuggers.hg

view tools/python/xen/xend/image.py @ 17011:4e9b5427f2d8

ioemu: use SIGHUP instead of SIGKILL

The stub domain device model needs to trap the termination signal so
as to actually destroy the stub domain. SIGKILL can't be trapped,
SIGTERM is caught by SDL and so may be unnoticed. SIGHUP can be
trapped and is not caught by SDL (and by default causes a process
termination without core).

Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Feb 04 13:44:29 2008 +0000 (2008-02-04)
parents 39ddd51318e9
children f8db1c6baad9
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-2007 XenSource Ltd
17 #============================================================================
20 import os, os.path, string
21 import re
22 import math
23 import time
24 import signal
26 import xen.lowlevel.xc
27 from xen.xend.XendConstants import *
28 from xen.xend.XendError import VmError, XendError, HVMRequired
29 from xen.xend.XendLogging import log
30 from xen.xend.XendOptions import instance as xenopts
31 from xen.xend.xenstore.xstransact import xstransact
32 from xen.xend.xenstore.xswatch import xswatch
33 from xen.xend import arch
34 from xen.xend import XendOptions
36 xc = xen.lowlevel.xc.xc()
38 MAX_GUEST_CMDLINE = 1024
41 def create(vm, vmConfig):
42 """Create an image handler for a vm.
44 @return ImageHandler instance
45 """
46 return findImageHandlerClass(vmConfig)(vm, vmConfig)
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.
62 The method destroyDeviceModel() is called to reap the device model
63 """
65 ostype = None
68 def __init__(self, vm, vmConfig):
69 self.vm = vm
71 self.bootloader = False
72 self.kernel = None
73 self.ramdisk = None
74 self.cmdline = None
76 self.configure(vmConfig)
78 def configure(self, vmConfig):
79 """Config actions common to all unix-like domains."""
80 if '_temp_using_bootloader' in vmConfig:
81 self.bootloader = True
82 self.kernel = vmConfig['_temp_kernel']
83 self.cmdline = vmConfig['_temp_args']
84 self.ramdisk = vmConfig['_temp_ramdisk']
85 else:
86 self.kernel = vmConfig['PV_kernel']
87 self.cmdline = vmConfig['PV_args']
88 self.ramdisk = vmConfig['PV_ramdisk']
89 self.vm.storeVm(("image/ostype", self.ostype),
90 ("image/kernel", self.kernel),
91 ("image/cmdline", self.cmdline),
92 ("image/ramdisk", self.ramdisk))
94 self.dmargs = self.parseDeviceModelArgs(vmConfig)
95 self.device_model = vmConfig['platform'].get('device_model')
97 self.display = vmConfig['platform'].get('display')
98 self.xauthority = vmConfig['platform'].get('xauthority')
99 self.vncconsole = vmConfig['platform'].get('vncconsole')
100 self.pid = None
104 def cleanupBootloading(self):
105 if self.bootloader:
106 self.unlink(self.kernel)
107 self.unlink(self.ramdisk)
110 def unlink(self, f):
111 if not f: return
112 try:
113 os.unlink(f)
114 except OSError, ex:
115 log.warning("error removing bootloader file '%s': %s", f, ex)
118 def createImage(self):
119 """Entry point to create domain memory image.
120 Override in subclass if needed.
121 """
122 return self.createDomain()
125 def createDomain(self):
126 """Build the domain boot image.
127 """
128 # Set params and call buildDomain().
130 if self.kernel and not os.path.isfile(self.kernel):
131 raise VmError('Kernel image does not exist: %s' % self.kernel)
132 if self.ramdisk and not os.path.isfile(self.ramdisk):
133 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
134 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
135 log.warning('kernel cmdline too long, domain %d',
136 self.vm.getDomid())
138 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
139 self.vm.getDomid(), self.vm.getVCpuCount())
141 result = self.buildDomain()
143 if isinstance(result, dict):
144 return result
145 else:
146 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
147 % (self.ostype, self.vm.getDomid(), str(result)))
149 def getRequiredAvailableMemory(self, mem_kb):
150 """@param mem_kb The configured maxmem or memory, in KiB.
151 @return The corresponding required amount of memory for the domain,
152 also in KiB. This is normally the given mem_kb, but architecture- or
153 image-specific code may override this to add headroom where
154 necessary."""
155 return mem_kb
157 def getRequiredInitialReservation(self):
158 """@param mem_kb The configured memory, in KiB.
159 @return The corresponding required amount of memory to be free, also
160 in KiB. This is normally the same as getRequiredAvailableMemory, but
161 architecture- or image-specific code may override this to
162 add headroom where necessary."""
163 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
165 def getRequiredMaximumReservation(self):
166 """@param mem_kb The maximum possible memory, in KiB.
167 @return The corresponding required amount of memory to be free, also
168 in KiB. This is normally the same as getRequiredAvailableMemory, but
169 architecture- or image-specific code may override this to
170 add headroom where necessary."""
171 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
173 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
174 """@param shadow_mem_kb The configured shadow memory, in KiB.
175 @param maxmem_kb The configured maxmem, in KiB.
176 @return The corresponding required amount of shadow memory, also in
177 KiB."""
178 # PV domains don't need any shadow memory
179 return 0
181 def buildDomain(self):
182 """Build the domain. Define in subclass."""
183 raise NotImplementedError()
185 # Return a list of cmd line args to the device models based on the
186 # xm config file
187 def parseDeviceModelArgs(self, vmConfig):
188 ret = ["-domain-name", str(self.vm.info['name_label'])]
190 # Tell QEMU how large the guest's memory allocation is
191 # to help it when loading the initrd (if neccessary)
192 ret += ["-m", str(self.getRequiredInitialReservation() / 1024)]
194 # Find RFB console device, and if it exists, make QEMU enable
195 # the VNC console.
196 if int(vmConfig['platform'].get('nographic', 0)) != 0:
197 # skip vnc init if nographic is set
198 ret.append('-nographic')
199 return ret
201 vnc_config = {}
202 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
203 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
204 for dev_uuid in vmConfig['console_refs']:
205 dev_type, dev_info = vmConfig['devices'][dev_uuid]
206 if dev_type == 'vfb':
207 vnc_config = dev_info.get('other_config', {})
208 has_vnc = True
209 break
211 keymap = vmConfig['platform'].get("keymap")
212 if keymap:
213 ret.append("-k")
214 ret.append(keymap)
216 if has_vnc:
217 if not vnc_config:
218 for key in ('vncunused', 'vnclisten', 'vncdisplay',
219 'vncpasswd'):
220 if key in vmConfig['platform']:
221 vnc_config[key] = vmConfig['platform'][key]
222 if vnc_config.has_key("vncpasswd"):
223 passwd = vnc_config["vncpasswd"]
224 else:
225 passwd = XendOptions.instance().get_vncpasswd_default()
226 vncopts = ""
227 if passwd:
228 self.vm.storeVm("vncpasswd", passwd)
229 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
230 vncopts = vncopts + ",password"
231 log.debug("Stored a VNC password for vfb access")
232 else:
233 log.debug("No VNC passwd configured for vfb access")
235 if XendOptions.instance().get_vnc_tls():
236 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
237 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
239 if not os.path.exists(vncx509certdir):
240 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
242 if vncx509verify:
243 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
244 else:
245 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
248 vnclisten = vnc_config.get('vnclisten',
249 XendOptions.instance().get_vnclisten_address())
250 vncdisplay = vnc_config.get('vncdisplay', 0)
251 ret.append('-vnc')
252 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
254 if vnc_config.get('vncunused', 0):
255 ret.append('-vncunused')
257 elif has_sdl:
258 # SDL is default in QEMU.
259 pass
260 else:
261 ret.append('-nographic')
263 if int(vmConfig['platform'].get('monitor', 0)) != 0:
264 ret = ret + ['-monitor', 'vc']
265 return ret
267 def getDeviceModelArgs(self, restore = False):
268 args = [self.device_model]
269 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
270 args = args + self.dmargs
271 return args
273 def createDeviceModel(self, restore = False):
274 if self.device_model is None:
275 return
276 if self.pid:
277 return
278 # Execute device model.
279 #todo: Error handling
280 args = self.getDeviceModelArgs(restore)
281 env = dict(os.environ)
282 if self.display:
283 env['DISPLAY'] = self.display
284 if self.xauthority:
285 env['XAUTHORITY'] = self.xauthority
286 if self.vncconsole:
287 args = args + ([ "-vncviewer" ])
288 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
289 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
290 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
291 log.info("spawning device models: %s %s", self.device_model, args)
292 # keep track of pid and spawned options to kill it later
293 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
294 self.vm.storeDom("image/device-model-pid", self.pid)
295 log.info("device model pid: %d", self.pid)
297 def saveDeviceModel(self):
298 if self.device_model is None:
299 return
300 # Signal the device model to pause itself and save its state
301 xstransact.Store("/local/domain/0/device-model/%i"
302 % self.vm.getDomid(), ('command', 'save'))
303 # Wait for confirmation. Could do this with a watch but we'd
304 # still end up spinning here waiting for the watch to fire.
305 state = ''
306 count = 0
307 while state != 'paused':
308 state = xstransact.Read("/local/domain/0/device-model/%i/state"
309 % self.vm.getDomid())
310 time.sleep(0.1)
311 count += 1
312 if count > 100:
313 raise VmError('Timed out waiting for device model to save')
315 def resumeDeviceModel(self):
316 if self.device_model is None:
317 return
318 # Signal the device model to resume activity after pausing to save.
319 xstransact.Store("/local/domain/0/device-model/%i"
320 % self.vm.getDomid(), ('command', 'continue'))
322 def recreate(self):
323 if self.device_model is None:
324 return
325 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
327 def destroyDeviceModel(self):
328 if self.device_model is None:
329 return
330 if self.pid:
331 try:
332 os.kill(self.pid, signal.SIGHUP)
333 except OSError, exn:
334 log.exception(exn)
335 try:
336 os.waitpid(self.pid, 0)
337 except OSError, exn:
338 # This is expected if Xend has been restarted within the
339 # life of this domain. In this case, we can kill the process,
340 # but we can't wait for it because it's not our child.
341 pass
342 self.pid = None
343 state = xstransact.Remove("/local/domain/0/device-model/%i"
344 % self.vm.getDomid())
347 class LinuxImageHandler(ImageHandler):
349 ostype = "linux"
350 flags = 0
351 vhpt = 0
353 def configure(self, vmConfig):
354 ImageHandler.configure(self, vmConfig)
355 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
356 if rtc_timeoffset is not None:
357 xc.domain_set_time_offset(self.vm.getDomid(), rtc_timeoffset)
359 def buildDomain(self):
360 store_evtchn = self.vm.getStorePort()
361 console_evtchn = self.vm.getConsolePort()
363 mem_mb = self.getRequiredInitialReservation() / 1024
365 log.debug("domid = %d", self.vm.getDomid())
366 log.debug("memsize = %d", mem_mb)
367 log.debug("image = %s", self.kernel)
368 log.debug("store_evtchn = %d", store_evtchn)
369 log.debug("console_evtchn = %d", console_evtchn)
370 log.debug("cmdline = %s", self.cmdline)
371 log.debug("ramdisk = %s", self.ramdisk)
372 log.debug("vcpus = %d", self.vm.getVCpuCount())
373 log.debug("features = %s", self.vm.getFeatures())
374 if arch.type == "ia64":
375 log.debug("vhpt = %d", self.flags)
377 return xc.linux_build(domid = self.vm.getDomid(),
378 memsize = mem_mb,
379 image = self.kernel,
380 store_evtchn = store_evtchn,
381 console_evtchn = console_evtchn,
382 cmdline = self.cmdline,
383 ramdisk = self.ramdisk,
384 features = self.vm.getFeatures(),
385 flags = self.flags,
386 vhpt = self.vhpt)
388 def parseDeviceModelArgs(self, vmConfig):
389 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
390 # Equivalent to old xenconsoled behaviour. Should make
391 # it configurable in future
392 ret = ret + ["-serial", "pty"]
393 return ret
395 def getDeviceModelArgs(self, restore = False):
396 args = ImageHandler.getDeviceModelArgs(self, restore)
397 args = args + ([ "-M", "xenpv"])
398 return args
401 class PPC_LinuxImageHandler(LinuxImageHandler):
403 ostype = "linux"
405 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
406 """@param shadow_mem_kb The configured shadow memory, in KiB.
407 @param maxmem_kb The configured maxmem, in KiB.
408 @return The corresponding required amount of shadow memory, also in
409 KiB.
410 PowerPC currently uses "shadow memory" to refer to the hash table."""
411 return max(maxmem_kb / 64, shadow_mem_kb)
415 class HVMImageHandler(ImageHandler):
417 ostype = "hvm"
419 def __init__(self, vm, vmConfig):
420 ImageHandler.__init__(self, vm, vmConfig)
421 self.shutdownWatch = None
422 self.rebootFeatureWatch = None
424 def configure(self, vmConfig):
425 ImageHandler.configure(self, vmConfig)
427 self.loader = vmConfig['platform'].get('loader')
429 info = xc.xeninfo()
430 if 'hvm' not in info['xen_caps']:
431 raise HVMRequired()
433 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
435 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
436 ("image/device-model", self.device_model),
437 ("image/display", self.display))
438 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
439 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
440 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
442 self.apic = int(vmConfig['platform'].get('apic', 0))
443 self.acpi = int(vmConfig['platform'].get('acpi', 0))
444 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
446 # Return a list of cmd line args to the device models based on the
447 # xm config file
448 def parseDeviceModelArgs(self, vmConfig):
449 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
450 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
452 if self.kernel and self.kernel != "/usr/lib/xen/boot/hvmloader":
453 log.debug("kernel = %s", self.kernel)
454 ret = ret + ['-kernel', self.kernel]
455 if self.ramdisk:
456 log.debug("ramdisk = %s", self.ramdisk)
457 ret = ret + ['-initrd', self.ramdisk]
458 if self.cmdline:
459 log.debug("cmdline = %s", self.cmdline)
460 ret = ret + ['-append', self.cmdline]
463 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
464 'localtime', 'serial', 'stdvga', 'isa',
465 'acpi', 'usb', 'usbdevice', 'pci' ]
467 for a in dmargs:
468 v = vmConfig['platform'].get(a)
470 # python doesn't allow '-' in variable names
471 if a == 'stdvga': a = 'std-vga'
472 if a == 'keymap': a = 'k'
474 # Handle booleans gracefully
475 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
476 try:
477 if v != None: v = int(v)
478 if v: ret.append("-%s" % a)
479 except (ValueError, TypeError):
480 pass # if we can't convert it to a sane type, ignore it
481 else:
482 if v:
483 ret.append("-%s" % a)
484 ret.append("%s" % v)
486 if a in ['fda', 'fdb']:
487 if v:
488 if not os.path.isabs(v):
489 raise VmError("Floppy file %s does not exist." % v)
490 log.debug("args: %s, val: %s" % (a,v))
492 # Handle disk/network related options
493 mac = None
494 nics = 0
496 for devuuid in vmConfig['vbd_refs']:
497 devinfo = vmConfig['devices'][devuuid][1]
498 uname = devinfo.get('uname')
499 if uname is not None and 'file:' in uname:
500 (_, vbdparam) = string.split(uname, ':', 1)
501 if not os.path.isfile(vbdparam):
502 raise VmError('Disk image does not exist: %s' %
503 vbdparam)
505 for devuuid in vmConfig['vif_refs']:
506 devinfo = vmConfig['devices'][devuuid][1]
507 dtype = devinfo.get('type', 'ioemu')
508 if dtype != 'ioemu':
509 continue
510 nics += 1
511 mac = devinfo.get('mac')
512 if mac is None:
513 raise VmError("MAC address not specified or generated.")
514 bridge = devinfo.get('bridge', 'xenbr0')
515 model = devinfo.get('model', 'rtl8139')
516 ret.append("-net")
517 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
518 (nics, mac, model))
519 ret.append("-net")
520 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
522 return ret
524 def getDeviceModelArgs(self, restore = False):
525 args = ImageHandler.getDeviceModelArgs(self, restore)
526 args = args + ([ "-M", "xenfv"])
527 if restore:
528 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
529 self.vm.getDomid() ])
530 return args
532 def buildDomain(self):
533 store_evtchn = self.vm.getStorePort()
535 mem_mb = self.getRequiredInitialReservation() / 1024
537 log.debug("domid = %d", self.vm.getDomid())
538 log.debug("image = %s", self.loader)
539 log.debug("store_evtchn = %d", store_evtchn)
540 log.debug("memsize = %d", mem_mb)
541 log.debug("vcpus = %d", self.vm.getVCpuCount())
542 log.debug("acpi = %d", self.acpi)
543 log.debug("apic = %d", self.apic)
545 rc = xc.hvm_build(domid = self.vm.getDomid(),
546 image = self.loader,
547 memsize = mem_mb,
548 vcpus = self.vm.getVCpuCount(),
549 acpi = self.acpi,
550 apic = self.apic)
551 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
553 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
554 HVM_PARAM_STORE_PFN)
555 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
556 store_evtchn)
558 return rc
561 class IA64_HVM_ImageHandler(HVMImageHandler):
563 def configure(self, vmConfig):
564 HVMImageHandler.configure(self, vmConfig)
565 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
567 def buildDomain(self):
568 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
569 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
570 if self.guest_os_type is not None:
571 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
572 return HVMImageHandler.buildDomain(self)
574 def getRequiredAvailableMemory(self, mem_kb):
575 page_kb = 16
576 # ROM size for guest firmware, io page, xenstore page
577 # buffer io page, buffer pio page and memmap info page
578 extra_pages = 1024 + 5
579 return mem_kb + extra_pages * page_kb
581 def getRequiredInitialReservation(self):
582 return self.vm.getMemoryTarget()
584 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
585 # Explicit shadow memory is not a concept
586 return 0
588 def getDeviceModelArgs(self, restore = False):
589 args = HVMImageHandler.getDeviceModelArgs(self, restore)
590 args = args + ([ "-m", "%s" %
591 (self.getRequiredInitialReservation() / 1024) ])
592 return args
595 class IA64_Linux_ImageHandler(LinuxImageHandler):
597 def configure(self, vmConfig):
598 LinuxImageHandler.configure(self, vmConfig)
599 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
602 class X86_HVM_ImageHandler(HVMImageHandler):
604 def configure(self, vmConfig):
605 HVMImageHandler.configure(self, vmConfig)
606 self.pae = int(vmConfig['platform'].get('pae', 0))
608 def buildDomain(self):
609 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
610 return HVMImageHandler.buildDomain(self)
612 def getRequiredAvailableMemory(self, mem_kb):
613 # Add 8 MiB overhead for QEMU's video RAM.
614 return mem_kb + 8192
616 def getRequiredInitialReservation(self):
617 return self.vm.getMemoryTarget()
619 def getRequiredMaximumReservation(self):
620 return self.vm.getMemoryMaximum()
622 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
623 # 256 pages (1MB) per vcpu,
624 # plus 1 page per MiB of RAM for the P2M map,
625 # plus 1 page per MiB of RAM to shadow the resident processes.
626 # This is higher than the minimum that Xen would allocate if no value
627 # were given (but the Xen minimum is for safety, not performance).
628 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
629 shadow_mem_kb)
632 class X86_Linux_ImageHandler(LinuxImageHandler):
634 def buildDomain(self):
635 # set physical mapping limit
636 # add an 8MB slack to balance backend allocations.
637 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
638 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
639 return LinuxImageHandler.buildDomain(self)
641 _handlers = {
642 "powerpc": {
643 "linux": PPC_LinuxImageHandler,
644 },
645 "ia64": {
646 "linux": IA64_Linux_ImageHandler,
647 "hvm": IA64_HVM_ImageHandler,
648 },
649 "x86": {
650 "linux": X86_Linux_ImageHandler,
651 "hvm": X86_HVM_ImageHandler,
652 },
653 }
655 def findImageHandlerClass(image):
656 """Find the image handler class for an image config.
658 @param image config
659 @return ImageHandler subclass or None
660 """
661 image_type = image.image_type()
662 try:
663 return _handlers[arch.type][image_type]
664 except KeyError:
665 raise VmError('unknown image type: ' + image_type)