direct-io.hg

view tools/python/xen/xend/image.py @ 12178:e3c70530cb28

[XEND] Remove lazy import of signal module.
Signed-off-by: Jeremy Katz <katzj@redhat.com>
author kfraser@localhost.localdomain
date Fri Oct 20 11:00:08 2006 +0100 (2006-10-20)
parents 4441715c9a67
children 09aa7ed2431f
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, mem_kb):
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(mem_kb)
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 log.debug("domid = %d", self.vm.getDomid())
193 log.debug("image = %s", self.kernel)
194 log.debug("store_evtchn = %d", store_evtchn)
195 log.debug("console_evtchn = %d", console_evtchn)
196 log.debug("cmdline = %s", self.cmdline)
197 log.debug("ramdisk = %s", self.ramdisk)
198 log.debug("vcpus = %d", self.vm.getVCpuCount())
199 log.debug("features = %s", self.vm.getFeatures())
201 return xc.linux_build(domid = self.vm.getDomid(),
202 image = self.kernel,
203 store_evtchn = store_evtchn,
204 console_evtchn = console_evtchn,
205 cmdline = self.cmdline,
206 ramdisk = self.ramdisk,
207 features = self.vm.getFeatures())
209 class PPC_LinuxImageHandler(LinuxImageHandler):
211 ostype = "linux"
213 def configure(self, imageConfig, deviceConfig):
214 LinuxImageHandler.configure(self, imageConfig, deviceConfig)
215 self.imageConfig = imageConfig
217 def buildDomain(self):
218 store_evtchn = self.vm.getStorePort()
219 console_evtchn = self.vm.getConsolePort()
221 log.debug("domid = %d", self.vm.getDomid())
222 log.debug("image = %s", self.kernel)
223 log.debug("store_evtchn = %d", store_evtchn)
224 log.debug("console_evtchn = %d", console_evtchn)
225 log.debug("cmdline = %s", self.cmdline)
226 log.debug("ramdisk = %s", self.ramdisk)
227 log.debug("vcpus = %d", self.vm.getVCpuCount())
228 log.debug("features = %s", self.vm.getFeatures())
230 devtree = FlatDeviceTree.build(self)
232 return xc.linux_build(domid = self.vm.getDomid(),
233 image = self.kernel,
234 store_evtchn = store_evtchn,
235 console_evtchn = console_evtchn,
236 cmdline = self.cmdline,
237 ramdisk = self.ramdisk,
238 features = self.vm.getFeatures(),
239 arch_args = devtree.to_bin())
241 class HVMImageHandler(ImageHandler):
243 def configure(self, imageConfig, deviceConfig):
244 ImageHandler.configure(self, imageConfig, deviceConfig)
246 info = xc.xeninfo()
247 if not 'hvm' in info['xen_caps']:
248 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
249 "supported by your CPU and enabled in your BIOS?")
251 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
252 self.device_model = sxp.child_value(imageConfig, 'device_model')
253 if not self.device_model:
254 raise VmError("hvm: missing device model")
255 self.display = sxp.child_value(imageConfig, 'display')
256 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
257 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
259 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
260 ("image/device-model", self.device_model),
261 ("image/display", self.display))
263 self.pid = 0
265 self.dmargs += self.configVNC(imageConfig)
267 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
269 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
270 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
272 def buildDomain(self):
273 store_evtchn = self.vm.getStorePort()
275 log.debug("domid = %d", self.vm.getDomid())
276 log.debug("image = %s", self.kernel)
277 log.debug("store_evtchn = %d", store_evtchn)
278 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
279 log.debug("vcpus = %d", self.vm.getVCpuCount())
280 log.debug("pae = %d", self.pae)
281 log.debug("acpi = %d", self.acpi)
282 log.debug("apic = %d", self.apic)
284 self.register_shutdown_watch()
286 return xc.hvm_build(domid = self.vm.getDomid(),
287 image = self.kernel,
288 store_evtchn = store_evtchn,
289 memsize = self.vm.getMemoryTarget() / 1024,
290 vcpus = self.vm.getVCpuCount(),
291 pae = self.pae,
292 acpi = self.acpi,
293 apic = self.apic)
295 # Return a list of cmd line args to the device models based on the
296 # xm config file
297 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
298 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
299 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
300 'acpi', 'usb', 'usbdevice']
301 ret = []
302 for a in dmargs:
303 v = sxp.child_value(imageConfig, a)
305 # python doesn't allow '-' in variable names
306 if a == 'stdvga': a = 'std-vga'
308 # Handle booleans gracefully
309 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
310 if v != None: v = int(v)
311 if v: ret.append("-%s" % a)
312 else:
313 if v:
314 ret.append("-%s" % a)
315 ret.append("%s" % v)
317 if a in ['fda', 'fdb' ]:
318 if v:
319 if not os.path.isfile(v):
320 raise VmError("Floppy file %s does not exist." % v)
321 log.debug("args: %s, val: %s" % (a,v))
323 # Handle disk/network related options
324 mac = None
325 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
326 nics = 0
327 for (name, info) in deviceConfig:
328 if name == 'vbd':
329 uname = sxp.child_value(info, 'uname')
330 if uname is not None and 'file:' in uname:
331 (_, vbdparam) = string.split(uname, ':', 1)
332 if not os.path.isfile(vbdparam):
333 raise VmError('Disk image does not exist: %s' %
334 vbdparam)
335 if name == 'vif':
336 type = sxp.child_value(info, 'type')
337 if type != 'ioemu':
338 continue
339 nics += 1
340 mac = sxp.child_value(info, 'mac')
341 if mac == None:
342 mac = randomMAC()
343 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
344 model = sxp.child_value(info, 'model', 'rtl8139')
345 ret.append("-net")
346 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
347 (nics, mac, model))
348 ret.append("-net")
349 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
350 return ret
352 def configVNC(self, config):
353 # Handle graphics library related options
354 vnc = sxp.child_value(config, 'vnc')
355 sdl = sxp.child_value(config, 'sdl')
356 ret = []
357 nographic = sxp.child_value(config, 'nographic')
359 # get password from VM config (if password omitted, None)
360 vncpasswd_vmconfig = sxp.child_value(config, 'vncpasswd')
362 if nographic:
363 ret.append('-nographic')
364 # remove password
365 if vncpasswd_vmconfig:
366 config.remove(['vncpasswd', vncpasswd_vmconfig])
367 return ret
369 if vnc:
370 vncdisplay = sxp.child_value(config, 'vncdisplay',
371 int(self.vm.getDomid()))
373 vncunused = sxp.child_value(config, 'vncunused')
374 if vncunused:
375 ret += ['-vncunused']
376 else:
377 ret += ['-vnc', '%d' % vncdisplay]
379 ret += ['-k', 'en-us']
381 vnclisten = sxp.child_value(config, 'vnclisten')
382 if not(vnclisten):
383 vnclisten = (xen.xend.XendRoot.instance().
384 get_vnclisten_address())
385 if vnclisten:
386 ret += ['-vnclisten', vnclisten]
388 vncpasswd = vncpasswd_vmconfig
389 if vncpasswd is None:
390 vncpasswd = (xen.xend.XendRoot.instance().
391 get_vncpasswd_default())
392 if vncpasswd is None:
393 raise VmError('vncpasswd is not set up in ' +
394 'VMconfig and xend-config.')
395 if vncpasswd != '':
396 self.vm.storeVm("vncpasswd", vncpasswd)
398 # remove password
399 config.remove(['vncpasswd', vncpasswd_vmconfig])
401 return ret
403 def createDeviceModel(self):
404 if self.pid:
405 return
406 # Execute device model.
407 #todo: Error handling
408 args = [self.device_model]
409 args = args + ([ "-d", "%d" % self.vm.getDomid(),
410 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
411 args = args + self.dmargs
412 env = dict(os.environ)
413 if self.display:
414 env['DISPLAY'] = self.display
415 if self.xauthority:
416 env['XAUTHORITY'] = self.xauthority
417 if self.vncconsole:
418 args = args + ([ "-vncviewer" ])
419 log.info("spawning device models: %s %s", self.device_model, args)
420 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
421 log.info("device model pid: %d", self.pid)
423 def destroy(self):
424 self.unregister_shutdown_watch();
425 if not self.pid:
426 return
427 os.kill(self.pid, signal.SIGKILL)
428 os.waitpid(self.pid, 0)
429 self.pid = 0
431 def register_shutdown_watch(self):
432 """ add xen store watch on control/shutdown """
433 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
434 self.hvm_shutdown)
435 log.debug("hvm shutdown watch registered")
437 def unregister_shutdown_watch(self):
438 """Remove the watch on the control/shutdown, if any. Nothrow
439 guarantee."""
441 try:
442 if self.shutdownWatch:
443 self.shutdownWatch.unwatch()
444 except:
445 log.exception("Unwatching hvm shutdown watch failed.")
446 self.shutdownWatch = None
447 log.debug("hvm shutdown watch unregistered")
449 def hvm_shutdown(self, _):
450 """ watch call back on node control/shutdown,
451 if node changed, this function will be called
452 """
453 from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
454 xd = xen.xend.XendDomain.instance()
455 vm = xd.domain_lookup( self.vm.getDomid() )
457 reason = vm.readDom('control/shutdown')
458 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
459 for x in shutdown_reasons.keys():
460 if shutdown_reasons[x] == reason:
461 vm.info['shutdown'] = 1
462 vm.info['shutdown_reason'] = x
463 vm.refreshShutdown(vm.info)
465 return 1 # Keep watching
467 class IA64_HVM_ImageHandler(HVMImageHandler):
469 ostype = "hvm"
471 def getRequiredAvailableMemory(self, mem_kb):
472 page_kb = 16
473 # ROM size for guest firmware, ioreq page and xenstore page
474 extra_pages = 1024 + 2
475 return mem_kb + extra_pages * page_kb
477 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
478 # Explicit shadow memory is not a concept
479 return 0
481 class X86_HVM_ImageHandler(HVMImageHandler):
483 ostype = "hvm"
485 def getRequiredAvailableMemory(self, mem_kb):
486 # Add 8 MiB overhead for QEMU's video RAM.
487 return self.getRequiredInitialReservation(mem_kb) + 8192
489 def getRequiredInitialReservation(self, mem_kb):
490 page_kb = 4
491 # This was derived emperically:
492 # 2.4 MB overhead per 1024 MB RAM
493 # + 4 to avoid low-memory condition
494 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
495 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
496 return mem_kb + extra_pages * page_kb
498 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
499 # The given value is the configured value -- we need to include the
500 # overhead due to getRequiredInitialReservation.
501 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
503 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
504 # the minimum that Xen would allocate if no value were given.
505 return max(1024 * self.vm.getVCpuCount() + maxmem_kb / 256,
506 shadow_mem_kb)
509 _handlers = {
510 "powerpc": {
511 "linux": PPC_LinuxImageHandler,
512 },
513 "ia64": {
514 "linux": LinuxImageHandler,
515 "hvm": IA64_HVM_ImageHandler,
516 },
517 "x86": {
518 "linux": LinuxImageHandler,
519 "hvm": X86_HVM_ImageHandler,
520 },
521 }
523 def findImageHandlerClass(image):
524 """Find the image handler class for an image config.
526 @param image config
527 @return ImageHandler subclass or None
528 """
529 type = sxp.name(image)
530 if type is None:
531 raise VmError('missing image type')
532 try:
533 return _handlers[arch.type][type]
534 except KeyError:
535 raise VmError('unknown image type: ' + type)