ia64/xen-unstable

view tools/python/xen/xend/image.py @ 11447:a1988768828d

[IA64][XEND] Fix getRequiredShadowMemory parameters

Parameter count changed in xen-unstable.hg 11417:65a41e3206ac

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
author awilliam@xenbuild.aw
date Sun Sep 10 15:34:14 2006 -0600 (2006-09-10)
parents 0419253c81de
children 97f3368d1dd3
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("Not an HVM capable platform, we stop creating!")
249 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
250 self.device_model = sxp.child_value(imageConfig, 'device_model')
251 if not self.device_model:
252 raise VmError("hvm: missing device model")
253 self.display = sxp.child_value(imageConfig, 'display')
254 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
255 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
257 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
258 ("image/device-model", self.device_model),
259 ("image/display", self.display))
261 self.pid = 0
263 self.dmargs += self.configVNC(imageConfig)
265 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
267 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
268 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
270 def buildDomain(self):
271 store_evtchn = self.vm.getStorePort()
273 log.debug("dom = %d", self.vm.getDomid())
274 log.debug("image = %s", self.kernel)
275 log.debug("store_evtchn = %d", store_evtchn)
276 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
277 log.debug("vcpus = %d", self.vm.getVCpuCount())
278 log.debug("pae = %d", self.pae)
279 log.debug("acpi = %d", self.acpi)
280 log.debug("apic = %d", self.apic)
282 self.register_shutdown_watch()
284 return xc.hvm_build(dom = self.vm.getDomid(),
285 image = self.kernel,
286 store_evtchn = store_evtchn,
287 memsize = self.vm.getMemoryTarget() / 1024,
288 vcpus = self.vm.getVCpuCount(),
289 pae = self.pae,
290 acpi = self.acpi,
291 apic = self.apic)
293 # Return a list of cmd line args to the device models based on the
294 # xm config file
295 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
296 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
297 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
298 'acpi', 'usb', 'usbdevice']
299 ret = []
300 for a in dmargs:
301 v = sxp.child_value(imageConfig, a)
303 # python doesn't allow '-' in variable names
304 if a == 'stdvga': a = 'std-vga'
306 # Handle booleans gracefully
307 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
308 if v != None: v = int(v)
309 if v: ret.append("-%s" % a)
310 else:
311 if v:
312 ret.append("-%s" % a)
313 ret.append("%s" % v)
314 log.debug("args: %s, val: %s" % (a,v))
316 # Handle disk/network related options
317 mac = None
318 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
319 nics = 0
320 for (name, info) in deviceConfig:
321 if name == 'vbd':
322 uname = sxp.child_value(info, 'uname')
323 if uname is not None and 'file:' in uname:
324 (_, vbdparam) = string.split(uname, ':', 1)
325 if not os.path.isfile(vbdparam):
326 raise VmError('Disk image does not exist: %s' %
327 vbdparam)
328 if name == 'vif':
329 type = sxp.child_value(info, 'type')
330 if type != 'ioemu':
331 continue
332 nics += 1
333 mac = sxp.child_value(info, 'mac')
334 if mac == None:
335 mac = randomMAC()
336 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
337 model = sxp.child_value(info, 'model', 'rtl8139')
338 ret.append("-net")
339 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
340 (nics, mac, model))
341 ret.append("-net")
342 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
343 if name == 'vtpm':
344 instance = sxp.child_value(info, 'pref_instance')
345 ret.append("-instance")
346 ret.append("%s" % instance)
347 return ret
349 def configVNC(self, config):
350 # Handle graphics library related options
351 vnc = sxp.child_value(config, 'vnc')
352 sdl = sxp.child_value(config, 'sdl')
353 ret = []
354 nographic = sxp.child_value(config, 'nographic')
355 if nographic:
356 ret.append('-nographic')
357 return ret
358 if vnc:
359 vncdisplay = sxp.child_value(config, 'vncdisplay',
360 int(self.vm.getDomid()))
361 ret = ret + ['-vnc', '%d' % vncdisplay, '-k', 'en-us']
362 vncunused = sxp.child_value(config, 'vncunused')
363 if vncunused:
364 ret += ['-vncunused']
365 return ret
367 def createDeviceModel(self):
368 if self.pid:
369 return
370 # Execute device model.
371 #todo: Error handling
372 args = [self.device_model]
373 args = args + ([ "-d", "%d" % self.vm.getDomid(),
374 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
375 args = args + self.dmargs
376 env = dict(os.environ)
377 if self.display:
378 env['DISPLAY'] = self.display
379 if self.xauthority:
380 env['XAUTHORITY'] = self.xauthority
381 if self.vncconsole:
382 args = args + ([ "-vncviewer" ])
383 log.info("spawning device models: %s %s", self.device_model, args)
384 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
385 log.info("device model pid: %d", self.pid)
387 def destroy(self):
388 self.unregister_shutdown_watch();
389 import signal
390 if not self.pid:
391 return
392 os.kill(self.pid, signal.SIGKILL)
393 os.waitpid(self.pid, 0)
394 self.pid = 0
396 def register_shutdown_watch(self):
397 """ add xen store watch on control/shutdown """
398 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
399 self.hvm_shutdown)
400 log.debug("hvm shutdown watch registered")
402 def unregister_shutdown_watch(self):
403 """Remove the watch on the control/shutdown, if any. Nothrow
404 guarantee."""
406 try:
407 if self.shutdownWatch:
408 self.shutdownWatch.unwatch()
409 except:
410 log.exception("Unwatching hvm shutdown watch failed.")
411 self.shutdownWatch = None
412 log.debug("hvm shutdown watch unregistered")
414 def hvm_shutdown(self, _):
415 """ watch call back on node control/shutdown,
416 if node changed, this function will be called
417 """
418 from xen.xend.XendDomainInfo import shutdown_reasons
419 xd = xen.xend.XendDomain.instance()
420 vm = xd.domain_lookup( self.vm.getDomid() )
422 reason = vm.readDom('control/shutdown')
423 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
424 for x in shutdown_reasons.keys():
425 if shutdown_reasons[x] == reason:
426 vm.info['shutdown'] = 1
427 vm.info['shutdown_reason'] = x
428 vm.refreshShutdown(vm.info)
430 return 1 # Keep watching
432 class IA64_HVM_ImageHandler(HVMImageHandler):
434 ostype = "hvm"
436 def getRequiredAvailableMemory(self, mem_kb):
437 page_kb = 16
438 # ROM size for guest firmware, ioreq page and xenstore page
439 extra_pages = 1024 + 2
440 return mem_kb + extra_pages * page_kb
442 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
443 # Explicit shadow memory is not a concept
444 return 0
446 class X86_HVM_ImageHandler(HVMImageHandler):
448 ostype = "hvm"
450 def getRequiredAvailableMemory(self, mem_kb):
451 # Add 8 MiB overhead for QEMU's video RAM.
452 return self.getRequiredInitialReservation(mem_kb) + 8192
454 def getRequiredInitialReservation(self, mem_kb):
455 page_kb = 4
456 # This was derived emperically:
457 # 2.4 MB overhead per 1024 MB RAM
458 # + 4 to avoid low-memory condition
459 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
460 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
461 return mem_kb + extra_pages * page_kb
463 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
464 # The given value is the configured value -- we need to include the
465 # overhead due to getRequiredInitialReservation.
466 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
468 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
469 # the minimum that Xen would allocate if no value were given.
470 return max(1024 * self.vm.getVCpuCount() + maxmem_kb / 256,
471 shadow_mem_kb)
474 _handlers = {
475 "powerpc": {
476 "linux": PPC_LinuxImageHandler,
477 },
478 "ia64": {
479 "linux": LinuxImageHandler,
480 "hvm": IA64_HVM_ImageHandler,
481 },
482 "x86": {
483 "linux": LinuxImageHandler,
484 "hvm": X86_HVM_ImageHandler,
485 },
486 }
488 def findImageHandlerClass(image):
489 """Find the image handler class for an image config.
491 @param image config
492 @return ImageHandler subclass or None
493 """
494 type = sxp.name(image)
495 if type is None:
496 raise VmError('missing image type')
497 try:
498 return _handlers[arch.type][type]
499 except KeyError:
500 raise VmError('unknown image type: ' + type)