ia64/xen-unstable

view tools/python/xen/xend/image.py @ 12029:50fa06501e54

[XEND] Do not delete VNC password from configuration.
Fixes reboot of HVM guest when VNC authentication is in use.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@localhost.localdomain
date Fri Oct 27 18:15:14 2006 +0100 (2006-10-27)
parents 1e6b0a8a0822
children eb3fe0620e3d
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("dom = %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(dom = 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("dom = %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(dom = 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("dom = %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(dom = 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 return ret
366 if vnc:
367 vncdisplay = sxp.child_value(config, 'vncdisplay',
368 int(self.vm.getDomid()))
370 vncunused = sxp.child_value(config, 'vncunused')
371 if vncunused:
372 ret += ['-vncunused']
373 else:
374 ret += ['-vnc', '%d' % vncdisplay]
376 ret += ['-k', 'en-us']
378 vnclisten = sxp.child_value(config, 'vnclisten')
379 if not(vnclisten):
380 vnclisten = (xen.xend.XendRoot.instance().
381 get_vnclisten_address())
382 if vnclisten:
383 ret += ['-vnclisten', vnclisten]
385 vncpasswd = vncpasswd_vmconfig
386 if vncpasswd is None:
387 vncpasswd = (xen.xend.XendRoot.instance().
388 get_vncpasswd_default())
389 if vncpasswd is None:
390 raise VmError('vncpasswd is not set up in ' +
391 'VMconfig and xend-config.')
392 if vncpasswd != '':
393 self.vm.storeVm("vncpasswd", vncpasswd)
395 return ret
397 def createDeviceModel(self):
398 if self.pid:
399 return
400 # Execute device model.
401 #todo: Error handling
402 args = [self.device_model]
403 args = args + ([ "-d", "%d" % self.vm.getDomid(),
404 "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
405 args = args + self.dmargs
406 env = dict(os.environ)
407 if self.display:
408 env['DISPLAY'] = self.display
409 if self.xauthority:
410 env['XAUTHORITY'] = self.xauthority
411 if self.vncconsole:
412 args = args + ([ "-vncviewer" ])
413 log.info("spawning device models: %s %s", self.device_model, args)
414 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
415 log.info("device model pid: %d", self.pid)
417 def destroy(self):
418 self.unregister_shutdown_watch();
419 if not self.pid:
420 return
421 os.kill(self.pid, signal.SIGKILL)
422 os.waitpid(self.pid, 0)
423 self.pid = 0
425 def register_shutdown_watch(self):
426 """ add xen store watch on control/shutdown """
427 self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \
428 self.hvm_shutdown)
429 log.debug("hvm shutdown watch registered")
431 def unregister_shutdown_watch(self):
432 """Remove the watch on the control/shutdown, if any. Nothrow
433 guarantee."""
435 try:
436 if self.shutdownWatch:
437 self.shutdownWatch.unwatch()
438 except:
439 log.exception("Unwatching hvm shutdown watch failed.")
440 self.shutdownWatch = None
441 log.debug("hvm shutdown watch unregistered")
443 def hvm_shutdown(self, _):
444 """ watch call back on node control/shutdown,
445 if node changed, this function will be called
446 """
447 from xen.xend.XendDomainInfo import shutdown_reasons
448 xd = xen.xend.XendDomain.instance()
449 vm = xd.domain_lookup( self.vm.getDomid() )
451 reason = vm.readDom('control/shutdown')
452 log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
453 for x in shutdown_reasons.keys():
454 if shutdown_reasons[x] == reason:
455 vm.info['shutdown'] = 1
456 vm.info['shutdown_reason'] = x
457 vm.refreshShutdown(vm.info)
459 return 1 # Keep watching
461 class IA64_HVM_ImageHandler(HVMImageHandler):
463 ostype = "hvm"
465 def getRequiredAvailableMemory(self, mem_kb):
466 page_kb = 16
467 # ROM size for guest firmware, ioreq page and xenstore page
468 extra_pages = 1024 + 3
469 return mem_kb + extra_pages * page_kb
471 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
472 # Explicit shadow memory is not a concept
473 return 0
475 class X86_HVM_ImageHandler(HVMImageHandler):
477 ostype = "hvm"
479 def getRequiredAvailableMemory(self, mem_kb):
480 # Add 8 MiB overhead for QEMU's video RAM.
481 return self.getRequiredInitialReservation(mem_kb) + 8192
483 def getRequiredInitialReservation(self, mem_kb):
484 page_kb = 4
485 # This was derived emperically:
486 # 2.4 MB overhead per 1024 MB RAM
487 # + 4 to avoid low-memory condition
488 extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
489 extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
490 return mem_kb + extra_pages * page_kb
492 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
493 # The given value is the configured value -- we need to include the
494 # overhead due to getRequiredInitialReservation.
495 maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
497 # 256 pages (1MB) per vcpu,
498 # plus 1 page per MiB of RAM for the P2M map,
499 # plus 1 page per MiB of RAM to shadow the resident processes.
500 # This is higher than the minimum that Xen would allocate if no value
501 # were given (but the Xen minimum is for safety, not performance).
502 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
503 shadow_mem_kb)
506 _handlers = {
507 "powerpc": {
508 "linux": PPC_LinuxImageHandler,
509 },
510 "ia64": {
511 "linux": LinuxImageHandler,
512 "hvm": IA64_HVM_ImageHandler,
513 },
514 "x86": {
515 "linux": LinuxImageHandler,
516 "hvm": X86_HVM_ImageHandler,
517 },
518 }
520 def findImageHandlerClass(image):
521 """Find the image handler class for an image config.
523 @param image config
524 @return ImageHandler subclass or None
525 """
526 type = sxp.name(image)
527 if type is None:
528 raise VmError('missing image type')
529 try:
530 return _handlers[arch.type][type]
531 except KeyError:
532 raise VmError('unknown image type: ' + type)