ia64/xen-unstable

view tools/python/xen/xend/image.py @ 10990:d25efa45355b

[HVM] Fix hvm boot when acpi=1 is specified.
Signed-off-by: Winston Wang <winston.l.wang@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Aug 08 09:25:46 2006 +0100 (2006-08-08)
parents 386990d004b8
children bd04004865ba
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
32 xc = xen.lowlevel.xc.xc()
35 MAX_GUEST_CMDLINE = 1024
38 def create(vm, imageConfig, deviceConfig):
39 """Create an image handler for a vm.
41 @return ImageHandler instance
42 """
43 return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig)
46 class ImageHandler:
47 """Abstract base class for image handlers.
49 createImage() is called to configure and build the domain from its
50 kernel image and ramdisk etc.
52 The method buildDomain() is used to build the domain, and must be
53 defined in a subclass. Usually this is the only method that needs
54 defining in a subclass.
56 The method createDeviceModel() is called to create the domain device
57 model if it needs one. The default is to do nothing.
59 The method destroy() is called when the domain is destroyed.
60 The default is to do nothing.
61 """
63 ostype = None
66 def __init__(self, vm, imageConfig, deviceConfig):
67 self.vm = vm
69 self.kernel = None
70 self.ramdisk = None
71 self.cmdline = None
73 self.configure(imageConfig, deviceConfig)
75 def configure(self, imageConfig, _):
76 """Config actions common to all unix-like domains."""
78 def get_cfg(name, default = None):
79 return sxp.child_value(imageConfig, name, default)
81 self.kernel = get_cfg("kernel")
82 self.cmdline = ""
83 ip = get_cfg("ip")
84 if ip:
85 self.cmdline += " ip=" + ip
86 root = get_cfg("root")
87 if root:
88 self.cmdline += " root=" + root
89 args = get_cfg("args")
90 if args:
91 self.cmdline += " " + args
92 self.ramdisk = get_cfg("ramdisk", '')
94 self.vm.storeVm(("image/ostype", self.ostype),
95 ("image/kernel", self.kernel),
96 ("image/cmdline", self.cmdline),
97 ("image/ramdisk", self.ramdisk))
100 def cleanupBootloading(self):
101 self.unlink(self.kernel)
102 self.unlink(self.ramdisk)
105 def unlink(self, f):
106 if not f: return
107 try:
108 os.unlink(f)
109 except OSError, ex:
110 log.warning("error removing bootloader file '%s': %s", f, ex)
113 def createImage(self):
114 """Entry point to create domain memory image.
115 Override in subclass if needed.
116 """
117 return self.createDomain()
120 def createDomain(self):
121 """Build the domain boot image.
122 """
123 # Set params and call buildDomain().
125 if not os.path.isfile(self.kernel):
126 raise VmError('Kernel image does not exist: %s' % self.kernel)
127 if self.ramdisk and not os.path.isfile(self.ramdisk):
128 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
129 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
130 log.warning('kernel cmdline too long, domain %d',
131 self.vm.getDomid())
133 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
134 self.vm.getDomid(), self.vm.getVCpuCount())
136 result = self.buildDomain()
138 if isinstance(result, dict):
139 return result
140 else:
141 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
142 % (self.ostype, self.vm.getDomid(), str(result)))
145 def getDomainMemory(self, mem_kb):
146 """@return The memory required, in KiB, by the domain to store the
147 given amount, also in KiB."""
148 if os.uname()[4] != 'ia64':
149 # A little extra because auto-ballooning is broken w.r.t. HVM
150 # guests. Also, slack is necessary for live migration since that
151 # uses shadow page tables.
152 if 'hvm' in xc.xeninfo()['xen_caps']:
153 mem_kb += 4*1024;
154 return mem_kb
156 def buildDomain(self):
157 """Build the domain. Define in subclass."""
158 raise NotImplementedError()
160 def createDeviceModel(self):
161 """Create device model for the domain (define in subclass if needed)."""
162 pass
164 def destroy(self):
165 """Extra cleanup on domain destroy (define in subclass if needed)."""
166 pass
169 class LinuxImageHandler(ImageHandler):
171 ostype = "linux"
173 def buildDomain(self):
174 store_evtchn = self.vm.getStorePort()
175 console_evtchn = self.vm.getConsolePort()
177 log.debug("dom = %d", self.vm.getDomid())
178 log.debug("image = %s", self.kernel)
179 log.debug("store_evtchn = %d", store_evtchn)
180 log.debug("console_evtchn = %d", console_evtchn)
181 log.debug("cmdline = %s", self.cmdline)
182 log.debug("ramdisk = %s", self.ramdisk)
183 log.debug("vcpus = %d", self.vm.getVCpuCount())
184 log.debug("features = %s", self.vm.getFeatures())
186 return xc.linux_build(dom = self.vm.getDomid(),
187 image = self.kernel,
188 store_evtchn = store_evtchn,
189 console_evtchn = console_evtchn,
190 cmdline = self.cmdline,
191 ramdisk = self.ramdisk,
192 features = self.vm.getFeatures())
194 class HVMImageHandler(ImageHandler):
196 ostype = "hvm"
198 def configure(self, imageConfig, deviceConfig):
199 ImageHandler.configure(self, imageConfig, deviceConfig)
201 info = xc.xeninfo()
202 if not 'hvm' in info['xen_caps']:
203 raise VmError("Not an HVM capable platform, we stop creating!")
205 self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
206 self.device_model = sxp.child_value(imageConfig, 'device_model')
207 if not self.device_model:
208 raise VmError("hvm: missing device model")
209 self.display = sxp.child_value(imageConfig, 'display')
210 self.xauthority = sxp.child_value(imageConfig, 'xauthority')
211 self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
213 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
214 ("image/device-model", self.device_model),
215 ("image/display", self.display))
217 self.pid = 0
219 self.dmargs += self.configVNC(imageConfig)
221 self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
223 self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
224 self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
226 def buildDomain(self):
227 store_evtchn = self.vm.getStorePort()
229 log.debug("dom = %d", self.vm.getDomid())
230 log.debug("image = %s", self.kernel)
231 log.debug("store_evtchn = %d", store_evtchn)
232 log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
233 log.debug("vcpus = %d", self.vm.getVCpuCount())
234 log.debug("pae = %d", self.pae)
235 log.debug("acpi = %d", self.acpi)
236 log.debug("apic = %d", self.apic)
238 self.register_shutdown_watch()
240 return xc.hvm_build(dom = self.vm.getDomid(),
241 image = self.kernel,
242 store_evtchn = store_evtchn,
243 memsize = self.vm.getMemoryTarget() / 1024,
244 vcpus = self.vm.getVCpuCount(),
245 pae = self.pae,
246 acpi = self.acpi,
247 apic = self.apic)
249 # Return a list of cmd line args to the device models based on the
250 # xm config file
251 def parseDeviceModelArgs(self, imageConfig, deviceConfig):
252 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
253 'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
254 'acpi', 'usb', 'usbdevice']
255 ret = []
256 for a in dmargs:
257 v = sxp.child_value(imageConfig, a)
259 # python doesn't allow '-' in variable names
260 if a == 'stdvga': a = 'std-vga'
262 # Handle booleans gracefully
263 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
264 if v != None: v = int(v)
265 if v: ret.append("-%s" % a)
266 else:
267 if v:
268 ret.append("-%s" % a)
269 ret.append("%s" % v)
270 log.debug("args: %s, val: %s" % (a,v))
272 # Handle disk/network related options
273 mac = None
274 ret = ret + ["-domain-name", "%s" % self.vm.info['name']]
275 nics = 0
276 for (name, info) in deviceConfig:
277 if name == 'vbd':
278 uname = sxp.child_value(info, 'uname')
279 if 'file:' in uname:
280 (_, vbdparam) = string.split(uname, ':', 1)
281 if not os.path.isfile(vbdparam):
282 raise VmError('Disk image does not exist: %s' %
283 vbdparam)
284 if name == 'vif':
285 type = sxp.child_value(info, 'type')
286 if type != 'ioemu':
287 continue
288 nics += 1
289 mac = sxp.child_value(info, 'mac')
290 if mac == None:
291 mac = randomMAC()
292 bridge = sxp.child_value(info, 'bridge', 'xenbr0')
293 model = sxp.child_value(info, 'model', 'rtl8139')
294 ret.append("-net")
295 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
296 (nics, mac, model))
297 ret.append("-net")
298 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
299 if name == 'vtpm':
300 instance = sxp.child_value(info, 'pref_instance')
301 ret.append("-instance")
302 ret.append("%s" % instance)
303 return ret
305 def configVNC(self, config):
306 # Handle graphics library related options
307 vnc = sxp.child_value(config, 'vnc')
308 vncdisplay = sxp.child_value(config, 'vncdisplay',
309 int(self.vm.getDomid()))
310 sdl = sxp.child_value(config, 'sdl')
311 ret = []
312 nographic = sxp.child_value(config, 'nographic')
313 if nographic:
314 ret.append('-nographic')
315 return ret
316 if vnc:
317 ret = ret + ['-vnc', '%d' % vncdisplay, '-k', 'en-us']
318 return ret
320 def createDeviceModel(self):
321 if self.pid:
322 return
323 # Execute device model.
324 #todo: Error handling
325 args = [self.device_model]
326 args = args + ([ "-d", "%d" % self.vm.getDomid(),
327 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
328 args = args + self.dmargs
329 env = dict(os.environ)
330 if self.display:
331 env['DISPLAY'] = self.display
332 if self.xauthority:
333 env['XAUTHORITY'] = self.xauthority
334 if self.vncconsole:
335 args = args + ([ "-vncviewer" ])
336 log.info("spawning device models: %s %s", self.device_model, args)
337 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
338 log.info("device model pid: %d", self.pid)
340 def destroy(self):
341 self.unregister_shutdown_watch();
342 import signal
343 if not self.pid:
344 return
345 os.kill(self.pid, signal.SIGKILL)
346 os.waitpid(self.pid, 0)
347 self.pid = 0
349 def getDomainMemory(self, mem_kb):
350 """@see ImageHandler.getDomainMemory"""
351 if os.uname()[4] == 'ia64':
352 page_kb = 16
353 # ROM size for guest firmware, ioreq page and xenstore page
354 extra_pages = 1024 + 2
355 else:
356 page_kb = 4
357 # This was derived emperically:
358 # 2.4 MB overhead per 1024 MB RAM + 8 MB constant
359 # + 4 to avoid low-memory condition
360 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 12;
361 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
362 return mem_kb + extra_pages * page_kb
364 def register_shutdown_watch(self):
365 """ add xen store watch on control/shutdown """
366 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
367 self.hvm_shutdown)
368 log.debug("hvm shutdown watch registered")
370 def unregister_shutdown_watch(self):
371 """Remove the watch on the control/shutdown, if any. Nothrow
372 guarantee."""
374 try:
375 if self.shutdownWatch:
376 self.shutdownWatch.unwatch()
377 except:
378 log.exception("Unwatching hvm shutdown watch failed.")
379 self.shutdownWatch = None
380 log.debug("hvm shutdown watch unregistered")
382 def hvm_shutdown(self, _):
383 """ watch call back on node control/shutdown,
384 if node changed, this function will be called
385 """
386 from xen.xend.XendDomainInfo import shutdown_reasons
387 xd = xen.xend.XendDomain.instance()
388 vm = xd.domain_lookup( self.vm.getDomid() )
390 reason = vm.readDom('control/shutdown')
391 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
392 for x in shutdown_reasons.keys():
393 if shutdown_reasons[x] == reason:
394 vm.info['shutdown'] = 1
395 vm.info['shutdown_reason'] = x
396 vm.refreshShutdown(vm.info)
398 return 1 # Keep watching
400 """Table of image handler classes for virtual machine images. Indexed by
401 image type.
402 """
403 imageHandlerClasses = {}
406 for h in LinuxImageHandler, HVMImageHandler:
407 imageHandlerClasses[h.ostype] = h
410 def findImageHandlerClass(image):
411 """Find the image handler class for an image config.
413 @param image config
414 @return ImageHandler subclass or None
415 """
416 ty = sxp.name(image)
417 if ty is None:
418 raise VmError('missing image type')
419 imageClass = imageHandlerClasses.get(ty)
420 if imageClass is None:
421 raise VmError('unknown image type: ' + ty)
422 return imageClass