ia64/xen-unstable

view tools/python/xen/xend/image.py @ 12967:b5a89f01a440

[TOOLS][POWERPC] update prose builder to define the mem_mb arg.
Signed-off-by: Jimi Xenidis <jimix@watson.ibm.com>
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author Jimi Xenidis <jimix@watson.ibm.com>
date Mon Nov 27 16:14:07 2006 -0500 (2006-11-27)
parents f2aaf35c7759
children a510c94ceaa3
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, 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
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 PPC_ProseImageHandler(LinuxImageHandler):
251 ostype = "prose"
253 def configure(self, imageConfig, deviceConfig):
254 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
255 self.imageConfig = imageConfig
257 def buildDomain(self):
258 store_evtchn = self.vm.getStorePort()
259 console_evtchn = self.vm.getConsolePort()
261 mem_mb = self.getRequiredInitialReservation() / 1024
263 log.debug("dom = %d", self.vm.getDomid())
264 log.debug("memsize = %d", mem_mb)
265 log.debug("image = %s", self.kernel)
266 log.debug("store_evtchn = %d", store_evtchn)
267 log.debug("console_evtchn = %d", console_evtchn)
268 log.debug("cmdline = %s", self.cmdline)
269 log.debug("ramdisk = %s", self.ramdisk)
270 log.debug("vcpus = %d", self.vm.getVCpuCount())
271 log.debug("features = %s", self.vm.getFeatures())
273 devtree = FlatDeviceTree.build(self)
275 return xc.prose_build(dom = self.vm.getDomid(),
276 image = self.kernel,
277 store_evtchn = store_evtchn,
278 console_evtchn = console_evtchn,
279 cmdline = self.cmdline,
280 ramdisk = self.ramdisk,
281 features = self.vm.getFeatures(),
282 arch_args = devtree.to_bin())
284 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
285 """@param shadow_mem_kb The configured shadow memory, in KiB.
286 @param maxmem_kb The configured maxmem, in KiB.
287 @return The corresponding required amount of shadow memory, also in
288 KiB.
289 PowerPC currently uses "shadow memory" to refer to the hash table."""
290 return max(maxmem_kb / 64, shadow_mem_kb)
293 class HVMImageHandler(ImageHandler):
295 def __init__(self, vm, imageConfig, deviceConfig):
296 ImageHandler.__init__(self, vm, imageConfig, deviceConfig)
297 self.shutdownWatch = None
299 def configure(self, imageConfig, deviceConfig):
300 ImageHandler.configure(self, imageConfig, deviceConfig)
302 info = xc.xeninfo()
303 if not 'hvm' in info['xen_caps']:
304 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
305 "supported by your CPU and enabled in your BIOS?")
307 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
308 self.device_model = sxp.child_value(imageConfig, 'device_model')
309 if not self.device_model:
310 raise VmError("hvm: missing device model")
311 self.display = sxp.child_value(imageConfig, 'display')
312 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
313 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
315 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
316 ("image/device-model", self.device_model),
317 ("image/display", self.display))
319 self.pid = 0
321 self.dmargs += self.configVNC(imageConfig)
323 self.pae = int(sxp.child_value(imageConfig, 'pae', 1))
324 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 1))
325 self.apic = int(sxp.child_value(imageConfig, 'apic', 1))
327 def buildDomain(self):
328 store_evtchn = self.vm.getStorePort()
330 mem_mb = self.getRequiredInitialReservation() / 1024
332 log.debug("domid = %d", self.vm.getDomid())
333 log.debug("image = %s", self.kernel)
334 log.debug("store_evtchn = %d", store_evtchn)
335 log.debug("memsize = %d", mem_mb)
336 log.debug("vcpus = %d", self.vm.getVCpuCount())
337 log.debug("pae = %d", self.pae)
338 log.debug("acpi = %d", self.acpi)
339 log.debug("apic = %d", self.apic)
341 self.register_shutdown_watch()
343 return xc.hvm_build(domid = self.vm.getDomid(),
344 image = self.kernel,
345 store_evtchn = store_evtchn,
346 memsize = mem_mb,
347 vcpus = self.vm.getVCpuCount(),
348 pae = self.pae,
349 acpi = self.acpi,
350 apic = self.apic)
352 # Return a list of cmd line args to the device models based on the
353 # xm config file
354 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
355 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
356 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
357 'acpi', 'usb', 'usbdevice', 'keymap' ]
358 ret = []
359 for a in dmargs:
360 v = sxp.child_value(imageConfig, a)
362 # python doesn't allow '-' in variable names
363 if a == 'stdvga': a = 'std-vga'
364 if a == 'keymap': a = 'k'
366 # Handle booleans gracefully
367 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
368 if v != None: v = int(v)
369 if v: ret.append("-%s" % a)
370 else:
371 if v:
372 ret.append("-%s" % a)
373 ret.append("%s" % v)
375 if a in ['fda', 'fdb' ]:
376 if v:
377 if not os.path.isabs(v):
378 raise VmError("Floppy file %s does not exist." % v)
379 log.debug("args: %s, val: %s" % (a,v))
381 # Handle disk/network related options
382 mac = None
383 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
384 nics = 0
385 for (name, info) in deviceConfig:
386 if name == 'vbd':
387 uname = sxp.child_value(info, 'uname')
388 if uname is not None and 'file:' in uname:
389 (_, vbdparam) = string.split(uname, ':', 1)
390 if not os.path.isfile(vbdparam):
391 raise VmError('Disk image does not exist: %s' %
392 vbdparam)
393 if name == 'vif':
394 type = sxp.child_value(info, 'type')
395 if type != 'ioemu':
396 continue
397 nics += 1
398 mac = sxp.child_value(info, 'mac')
399 if mac == None:
400 mac = randomMAC()
401 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
402 model = sxp.child_value(info, 'model', 'rtl8139')
403 ret.append("-net")
404 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
405 (nics, mac, model))
406 ret.append("-net")
407 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
408 return ret
410 def configVNC(self, config):
411 # Handle graphics library related options
412 vnc = sxp.child_value(config, 'vnc')
413 sdl = sxp.child_value(config, 'sdl')
414 ret = []
415 nographic = sxp.child_value(config, 'nographic')
417 # get password from VM config (if password omitted, None)
418 vncpasswd_vmconfig = sxp.child_value(config, 'vncpasswd')
420 if nographic:
421 ret.append('-nographic')
422 return ret
424 if vnc:
425 vncdisplay = int(sxp.child_value(config, 'vncdisplay',
426 self.vm.getDomid()))
428 vncunused = sxp.child_value(config, 'vncunused')
429 if vncunused:
430 ret += ['-vncunused']
431 else:
432 ret += ['-vnc', '%d' % vncdisplay]
434 vnclisten = sxp.child_value(config, 'vnclisten')
435 if not(vnclisten):
436 vnclisten = (xen.xend.XendRoot.instance().
437 get_vnclisten_address())
438 if vnclisten:
439 ret += ['-vnclisten', vnclisten]
441 vncpasswd = vncpasswd_vmconfig
442 if vncpasswd is None:
443 vncpasswd = (xen.xend.XendRoot.instance().
444 get_vncpasswd_default())
445 if vncpasswd is None:
446 raise VmError('vncpasswd is not set up in ' +
447 'VMconfig and xend-config.')
448 if vncpasswd != '':
449 self.vm.storeVm("vncpasswd", vncpasswd)
451 return ret
453 def createDeviceModel(self):
454 if self.pid:
455 return
456 # Execute device model.
457 #todo: Error handling
458 args = [self.device_model]
459 args = args + ([ "-d", "%d" % self.vm.getDomid(),
460 "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
461 args = args + self.dmargs
462 env = dict(os.environ)
463 if self.display:
464 env['DISPLAY'] = self.display
465 if self.xauthority:
466 env['XAUTHORITY'] = self.xauthority
467 if self.vncconsole:
468 args = args + ([ "-vncviewer" ])
469 log.info("spawning device models: %s %s", self.device_model, args)
470 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
471 log.info("device model pid: %d", self.pid)
473 def destroy(self):
474 self.unregister_shutdown_watch();
475 if not self.pid:
476 return
477 os.kill(self.pid, signal.SIGKILL)
478 os.waitpid(self.pid, 0)
479 self.pid = 0
481 def register_shutdown_watch(self):
482 """ add xen store watch on control/shutdown """
483 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
484 self.hvm_shutdown)
485 log.debug("hvm shutdown watch registered")
487 def unregister_shutdown_watch(self):
488 """Remove the watch on the control/shutdown, if any. Nothrow
489 guarantee."""
491 try:
492 if self.shutdownWatch:
493 self.shutdownWatch.unwatch()
494 except:
495 log.exception("Unwatching hvm shutdown watch failed.")
496 self.shutdownWatch = None
497 log.debug("hvm shutdown watch unregistered")
499 def hvm_shutdown(self, _):
500 """ watch call back on node control/shutdown,
501 if node changed, this function will be called
502 """
503 from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
504 xd = xen.xend.XendDomain.instance()
505 try:
506 vm = xd.domain_lookup( self.vm.getDomid() )
507 except XendError:
508 # domain isn't registered, no need to clean it up.
509 return
511 reason = vm.getShutdownReason()
512 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
513 for x in DOMAIN_SHUTDOWN_REASONS.keys():
514 if DOMAIN_SHUTDOWN_REASONS[x] == reason:
515 vm.info['shutdown'] = 1
516 vm.info['shutdown_reason'] = x
517 vm.refreshShutdown(vm.info)
519 return 1 # Keep watching
521 class IA64_HVM_ImageHandler(HVMImageHandler):
523 ostype = "hvm"
525 def getRequiredAvailableMemory(self, mem_kb):
526 page_kb = 16
527 # ROM size for guest firmware, ioreq page and xenstore page
528 extra_pages = 1024 + 3
529 return mem_kb + extra_pages * page_kb
531 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
532 # Explicit shadow memory is not a concept
533 return 0
535 class X86_HVM_ImageHandler(HVMImageHandler):
537 ostype = "hvm"
539 def getRequiredAvailableMemory(self, mem_kb):
540 # Add 8 MiB overhead for QEMU's video RAM.
541 return mem_kb + 8192
543 def getRequiredInitialReservation(self):
544 return self.vm.getMemoryTarget()
546 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
547 # 256 pages (1MB) per vcpu,
548 # plus 1 page per MiB of RAM for the P2M map,
549 # plus 1 page per MiB of RAM to shadow the resident processes.
550 # This is higher than the minimum that Xen would allocate if no value
551 # were given (but the Xen minimum is for safety, not performance).
552 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
553 shadow_mem_kb)
556 _handlers = {
557 "powerpc": {
558 "linux": PPC_LinuxImageHandler,
559 "prose": PPC_ProseImageHandler,
560 },
561 "ia64": {
562 "linux": LinuxImageHandler,
563 "hvm": IA64_HVM_ImageHandler,
564 },
565 "x86": {
566 "linux": LinuxImageHandler,
567 "hvm": X86_HVM_ImageHandler,
568 },
569 }
571 def findImageHandlerClass(image):
572 """Find the image handler class for an image config.
574 @param image config
575 @return ImageHandler subclass or None
576 """
577 type = sxp.name(image)
578 if type is None:
579 raise VmError('missing image type')
580 try:
581 return _handlers[arch.type][type]
582 except KeyError:
583 raise VmError('unknown image type: ' + type)