ia64/xen-unstable

view tools/python/xen/xend/image.py @ 11700:02311d8aba86

[HVM][TOOLS] Improve warnign message when trying to create
an HVM guest on a non-capable platform.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Sat Sep 30 11:35:02 2006 +0100 (2006-09-30)
parents 97f3368d1dd3
children ddc56007bf3f 0ae1d493f37c
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("dom = %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(dom = 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("dom = %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(dom = 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("dom = %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(dom = 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)
315 log.debug("args: %s, val: %s" % (a,v))
317 # Handle disk/network related options
318 mac = None
319 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
320 nics = 0
321 for (name, info) in deviceConfig:
322 if name == 'vbd':
323 uname = sxp.child_value(info, 'uname')
324 if uname is not None and 'file:' in uname:
325 (_, vbdparam) = string.split(uname, ':', 1)
326 if not os.path.isfile(vbdparam):
327 raise VmError('Disk image does not exist: %s' %
328 vbdparam)
329 if name == 'vif':
330 type = sxp.child_value(info, 'type')
331 if type != 'ioemu':
332 continue
333 nics += 1
334 mac = sxp.child_value(info, 'mac')
335 if mac == None:
336 mac = randomMAC()
337 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
338 model = sxp.child_value(info, 'model', 'rtl8139')
339 ret.append("-net")
340 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
341 (nics, mac, model))
342 ret.append("-net")
343 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
344 return ret
346 def configVNC(self, config):
347 # Handle graphics library related options
348 vnc = sxp.child_value(config, 'vnc')
349 sdl = sxp.child_value(config, 'sdl')
350 ret = []
351 nographic = sxp.child_value(config, 'nographic')
352 if nographic:
353 ret.append('-nographic')
354 return ret
355 if vnc:
356 vncdisplay = sxp.child_value(config, 'vncdisplay',
357 int(self.vm.getDomid()))
358 ret = ret + ['-vnc', '%d' % vncdisplay, '-k', 'en-us']
359 vncunused = sxp.child_value(config, 'vncunused')
360 if vncunused:
361 ret += ['-vncunused']
362 return ret
364 def createDeviceModel(self):
365 if self.pid:
366 return
367 # Execute device model.
368 #todo: Error handling
369 args = [self.device_model]
370 args = args + ([ "-d", "%d" % self.vm.getDomid(),
371 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
372 args = args + self.dmargs
373 env = dict(os.environ)
374 if self.display:
375 env['DISPLAY'] = self.display
376 if self.xauthority:
377 env['XAUTHORITY'] = self.xauthority
378 if self.vncconsole:
379 args = args + ([ "-vncviewer" ])
380 log.info("spawning device models: %s %s", self.device_model, args)
381 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
382 log.info("device model pid: %d", self.pid)
384 def destroy(self):
385 self.unregister_shutdown_watch();
386 import signal
387 if not self.pid:
388 return
389 os.kill(self.pid, signal.SIGKILL)
390 os.waitpid(self.pid, 0)
391 self.pid = 0
393 def register_shutdown_watch(self):
394 """ add xen store watch on control/shutdown """
395 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
396 self.hvm_shutdown)
397 log.debug("hvm shutdown watch registered")
399 def unregister_shutdown_watch(self):
400 """Remove the watch on the control/shutdown, if any. Nothrow
401 guarantee."""
403 try:
404 if self.shutdownWatch:
405 self.shutdownWatch.unwatch()
406 except:
407 log.exception("Unwatching hvm shutdown watch failed.")
408 self.shutdownWatch = None
409 log.debug("hvm shutdown watch unregistered")
411 def hvm_shutdown(self, _):
412 """ watch call back on node control/shutdown,
413 if node changed, this function will be called
414 """
415 from xen.xend.XendDomainInfo import shutdown_reasons
416 xd = xen.xend.XendDomain.instance()
417 vm = xd.domain_lookup( self.vm.getDomid() )
419 reason = vm.readDom('control/shutdown')
420 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
421 for x in shutdown_reasons.keys():
422 if shutdown_reasons[x] == reason:
423 vm.info['shutdown'] = 1
424 vm.info['shutdown_reason'] = x
425 vm.refreshShutdown(vm.info)
427 return 1 # Keep watching
429 class IA64_HVM_ImageHandler(HVMImageHandler):
431 ostype = "hvm"
433 def getRequiredAvailableMemory(self, mem_kb):
434 page_kb = 16
435 # ROM size for guest firmware, ioreq page and xenstore page
436 extra_pages = 1024 + 2
437 return mem_kb + extra_pages * page_kb
439 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
440 # Explicit shadow memory is not a concept
441 return 0
443 class X86_HVM_ImageHandler(HVMImageHandler):
445 ostype = "hvm"
447 def getRequiredAvailableMemory(self, mem_kb):
448 # Add 8 MiB overhead for QEMU's video RAM.
449 return self.getRequiredInitialReservation(mem_kb) + 8192
451 def getRequiredInitialReservation(self, mem_kb):
452 page_kb = 4
453 # This was derived emperically:
454 # 2.4 MB overhead per 1024 MB RAM
455 # + 4 to avoid low-memory condition
456 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
457 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
458 return mem_kb + extra_pages * page_kb
460 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
461 # The given value is the configured value -- we need to include the
462 # overhead due to getRequiredInitialReservation.
463 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
465 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
466 # the minimum that Xen would allocate if no value were given.
467 return max(1024 * self.vm.getVCpuCount() + maxmem_kb / 256,
468 shadow_mem_kb)
471 _handlers = {
472 "powerpc": {
473 "linux": PPC_LinuxImageHandler,
474 },
475 "ia64": {
476 "linux": LinuxImageHandler,
477 "hvm": IA64_HVM_ImageHandler,
478 },
479 "x86": {
480 "linux": LinuxImageHandler,
481 "hvm": X86_HVM_ImageHandler,
482 },
483 }
485 def findImageHandlerClass(image):
486 """Find the image handler class for an image config.
488 @param image config
489 @return ImageHandler subclass or None
490 """
491 type = sxp.name(image)
492 if type is None:
493 raise VmError('missing image type')
494 try:
495 return _handlers[arch.type][type]
496 except KeyError:
497 raise VmError('unknown image type: ' + type)