ia64/xen-unstable

view tools/python/xen/xend/image.py @ 15067:52a8d6387725

[qemu] Use qemu's -vnc option to specify the interface to listen on.
Domain config files still use the original syntax with vnc and vnclisten.

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author Christian Limpach <Christian.Limpach@xensource.com>
date Thu May 10 15:02:05 2007 +0100 (2007-05-10)
parents 3ef0510e44d0
children fc49dbce4868 17f6163ae930
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-2007 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.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS
27 from xen.xend.XendError import VmError, XendError, HVMRequired
28 from xen.xend.XendLogging import log
29 from xen.xend.XendOptions import instance as xenopts
30 from xen.xend.server.netif import randomMAC
31 from xen.xend.xenstore.xswatch import xswatch
32 from xen.xend import arch
34 xc = xen.lowlevel.xc.xc()
36 MAX_GUEST_CMDLINE = 1024
39 def create(vm, vmConfig):
40 """Create an image handler for a vm.
42 @return ImageHandler instance
43 """
44 return findImageHandlerClass(vmConfig)(vm, vmConfig)
47 class ImageHandler:
48 """Abstract base class for image handlers.
50 createImage() is called to configure and build the domain from its
51 kernel image and ramdisk etc.
53 The method buildDomain() is used to build the domain, and must be
54 defined in a subclass. Usually this is the only method that needs
55 defining in a subclass.
57 The method createDeviceModel() is called to create the domain device
58 model if it needs one. The default is to do nothing.
60 The method destroy() is called when the domain is destroyed.
61 The default is to do nothing.
62 """
64 ostype = None
67 def __init__(self, vm, vmConfig):
68 self.vm = vm
70 self.bootloader = False
71 self.kernel = None
72 self.ramdisk = None
73 self.cmdline = None
75 self.configure(vmConfig)
77 def configure(self, vmConfig):
78 """Config actions common to all unix-like domains."""
79 if '_temp_using_bootloader' in vmConfig:
80 self.bootloader = True
81 self.kernel = vmConfig['_temp_kernel']
82 self.cmdline = vmConfig['_temp_args']
83 self.ramdisk = vmConfig['_temp_ramdisk']
84 else:
85 self.kernel = vmConfig['PV_kernel']
86 self.cmdline = vmConfig['PV_args']
87 self.ramdisk = vmConfig['PV_ramdisk']
88 self.vm.storeVm(("image/ostype", self.ostype),
89 ("image/kernel", self.kernel),
90 ("image/cmdline", self.cmdline),
91 ("image/ramdisk", self.ramdisk))
94 def cleanupBootloading(self):
95 if self.bootloader:
96 self.unlink(self.kernel)
97 self.unlink(self.ramdisk)
100 def unlink(self, f):
101 if not f: return
102 try:
103 os.unlink(f)
104 except OSError, ex:
105 log.warning("error removing bootloader file '%s': %s", f, ex)
108 def createImage(self):
109 """Entry point to create domain memory image.
110 Override in subclass if needed.
111 """
112 return self.createDomain()
115 def createDomain(self):
116 """Build the domain boot image.
117 """
118 # Set params and call buildDomain().
120 if not os.path.isfile(self.kernel):
121 raise VmError('Kernel image does not exist: %s' % self.kernel)
122 if self.ramdisk and not os.path.isfile(self.ramdisk):
123 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
124 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
125 log.warning('kernel cmdline too long, domain %d',
126 self.vm.getDomid())
128 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
129 self.vm.getDomid(), self.vm.getVCpuCount())
131 result = self.buildDomain()
133 if isinstance(result, dict):
134 return result
135 else:
136 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
137 % (self.ostype, self.vm.getDomid(), str(result)))
139 def getRequiredAvailableMemory(self, mem_kb):
140 """@param mem_kb The configured maxmem or memory, in KiB.
141 @return The corresponding required amount of memory for the domain,
142 also in KiB. This is normally the given mem_kb, but architecture- or
143 image-specific code may override this to add headroom where
144 necessary."""
145 return mem_kb
147 def getRequiredInitialReservation(self):
148 """@param mem_kb The configured memory, in KiB.
149 @return The corresponding required amount of memory to be free, also
150 in KiB. This is normally the same as getRequiredAvailableMemory, but
151 architecture- or image-specific code may override this to
152 add headroom where necessary."""
153 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
155 def getRequiredMaximumReservation(self):
156 """@param mem_kb The maximum possible 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.getMemoryMaximum())
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, restore = False):
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 def recreate(self):
185 pass
188 class LinuxImageHandler(ImageHandler):
190 ostype = "linux"
192 def buildDomain(self):
193 store_evtchn = self.vm.getStorePort()
194 console_evtchn = self.vm.getConsolePort()
196 mem_mb = self.getRequiredInitialReservation() / 1024
198 log.debug("domid = %d", self.vm.getDomid())
199 log.debug("memsize = %d", mem_mb)
200 log.debug("image = %s", self.kernel)
201 log.debug("store_evtchn = %d", store_evtchn)
202 log.debug("console_evtchn = %d", console_evtchn)
203 log.debug("cmdline = %s", self.cmdline)
204 log.debug("ramdisk = %s", self.ramdisk)
205 log.debug("vcpus = %d", self.vm.getVCpuCount())
206 log.debug("features = %s", self.vm.getFeatures())
208 return xc.linux_build(domid = self.vm.getDomid(),
209 memsize = mem_mb,
210 image = self.kernel,
211 store_evtchn = store_evtchn,
212 console_evtchn = console_evtchn,
213 cmdline = self.cmdline,
214 ramdisk = self.ramdisk,
215 features = self.vm.getFeatures())
217 class PPC_LinuxImageHandler(LinuxImageHandler):
219 ostype = "linux"
221 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
222 """@param shadow_mem_kb The configured shadow memory, in KiB.
223 @param maxmem_kb The configured maxmem, in KiB.
224 @return The corresponding required amount of shadow memory, also in
225 KiB.
226 PowerPC currently uses "shadow memory" to refer to the hash table."""
227 return max(maxmem_kb / 64, shadow_mem_kb)
231 class HVMImageHandler(ImageHandler):
233 ostype = "hvm"
235 def __init__(self, vm, vmConfig):
236 ImageHandler.__init__(self, vm, vmConfig)
237 self.shutdownWatch = None
238 self.rebootFeatureWatch = None
240 def configure(self, vmConfig):
241 ImageHandler.configure(self, vmConfig)
243 if not self.kernel:
244 self.kernel = '/usr/lib/xen/boot/hvmloader'
246 info = xc.xeninfo()
247 if 'hvm' not in info['xen_caps']:
248 raise HVMRequired()
250 self.dmargs = self.parseDeviceModelArgs(vmConfig)
251 self.device_model = vmConfig['platform'].get('device_model')
252 if not self.device_model:
253 raise VmError("hvm: missing device model")
255 self.display = vmConfig['platform'].get('display')
256 self.xauthority = vmConfig['platform'].get('xauthority')
257 self.vncconsole = vmConfig['platform'].get('vncconsole')
259 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
261 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
262 ("image/device-model", self.device_model),
263 ("image/display", self.display))
264 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
266 self.pid = None
268 self.pae = int(vmConfig['platform'].get('pae', 0))
269 self.apic = int(vmConfig['platform'].get('apic', 0))
270 self.acpi = int(vmConfig['platform'].get('acpi', 0))
273 def buildDomain(self):
274 store_evtchn = self.vm.getStorePort()
276 mem_mb = self.getRequiredInitialReservation() / 1024
278 log.debug("domid = %d", self.vm.getDomid())
279 log.debug("image = %s", self.kernel)
280 log.debug("store_evtchn = %d", store_evtchn)
281 log.debug("memsize = %d", mem_mb)
282 log.debug("vcpus = %d", self.vm.getVCpuCount())
283 log.debug("pae = %d", self.pae)
284 log.debug("acpi = %d", self.acpi)
285 log.debug("apic = %d", self.apic)
287 rc = xc.hvm_build(domid = self.vm.getDomid(),
288 image = self.kernel,
289 store_evtchn = store_evtchn,
290 memsize = mem_mb,
291 vcpus = self.vm.getVCpuCount(),
292 pae = self.pae,
293 acpi = self.acpi,
294 apic = self.apic)
295 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
296 return rc
298 # Return a list of cmd line args to the device models based on the
299 # xm config file
300 def parseDeviceModelArgs(self, vmConfig):
301 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
302 'localtime', 'serial', 'stdvga', 'isa',
303 'acpi', 'usb', 'usbdevice', 'keymap' ]
305 ret = ['-vcpus', str(self.vm.getVCpuCount())]
307 for a in dmargs:
308 v = vmConfig['platform'].get(a)
310 # python doesn't allow '-' in variable names
311 if a == 'stdvga': a = 'std-vga'
312 if a == 'keymap': a = 'k'
314 # Handle booleans gracefully
315 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
316 try:
317 if v != None: v = int(v)
318 if v: ret.append("-%s" % a)
319 except (ValueError, TypeError):
320 pass # if we can't convert it to a sane type, ignore it
321 else:
322 if v:
323 ret.append("-%s" % a)
324 ret.append("%s" % v)
326 if a in ['fda', 'fdb']:
327 if v:
328 if not os.path.isabs(v):
329 raise VmError("Floppy file %s does not exist." % v)
330 log.debug("args: %s, val: %s" % (a,v))
332 # Handle disk/network related options
333 mac = None
334 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
335 nics = 0
337 for devuuid in vmConfig['vbd_refs']:
338 devinfo = vmConfig['devices'][devuuid][1]
339 uname = devinfo.get('uname')
340 if uname is not None and 'file:' in uname:
341 (_, vbdparam) = string.split(uname, ':', 1)
342 if not os.path.isfile(vbdparam):
343 raise VmError('Disk image does not exist: %s' %
344 vbdparam)
346 for devuuid in vmConfig['vif_refs']:
347 devinfo = vmConfig['devices'][devuuid][1]
348 dtype = devinfo.get('type', 'ioemu')
349 if dtype != 'ioemu':
350 continue
351 nics += 1
352 mac = devinfo.get('mac')
353 if mac is None:
354 mac = randomMAC()
355 bridge = devinfo.get('bridge', 'xenbr0')
356 model = devinfo.get('model', 'rtl8139')
357 ret.append("-net")
358 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
359 (nics, mac, model))
360 ret.append("-net")
361 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
364 #
365 # Find RFB console device, and if it exists, make QEMU enable
366 # the VNC console.
367 #
368 if int(vmConfig['platform'].get('nographic', 0)) != 0:
369 # skip vnc init if nographic is set
370 ret.append('-nographic')
371 return ret
373 vnc_config = {}
374 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
375 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
376 for dev_uuid in vmConfig['console_refs']:
377 dev_type, dev_info = vmConfig['devices'][dev_uuid]
378 if dev_type == 'vfb':
379 vnc_config = dev_info.get('other_config', {})
380 has_vnc = True
381 break
383 if has_vnc:
384 if not vnc_config:
385 for key in ('vncunused', 'vnclisten', 'vncdisplay',
386 'vncpasswd'):
387 if key in vmConfig['platform']:
388 vnc_config[key] = vmConfig['platform'][key]
390 vnclisten = vnc_config.get('vnclisten',
391 xenopts().get_vnclisten_address())
392 vncdisplay = vnc_config.get('vncdisplay', 0)
393 ret.append('-vnc')
394 ret.append("%s:%d" % (vnclisten, vncdisplay))
396 if vnc_config.get('vncunused', 0):
397 ret.append('-vncunused')
399 # Store vncpassword in xenstore
400 vncpasswd = vnc_config.get('vncpasswd')
401 if not vncpasswd:
402 vncpasswd = xenopts().get_vncpasswd_default()
404 if vncpasswd is None:
405 raise VmError('vncpasswd is not setup in vmconfig or '
406 'xend-config.sxp')
408 if vncpasswd != '':
409 self.vm.storeVm('vncpasswd', vncpasswd)
410 elif has_sdl:
411 # SDL is default in QEMU.
412 pass
413 else:
414 ret.append('-nographic')
416 if int(vmConfig['platform'].get('monitor', 0)) != 0:
417 ret = ret + ['-monitor', 'vc']
418 return ret
420 def createDeviceModel(self, restore = False):
421 if self.pid:
422 return
423 # Execute device model.
424 #todo: Error handling
425 args = [self.device_model]
426 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
427 if arch.type == "ia64":
428 args = args + ([ "-m", "%s" %
429 (self.getRequiredInitialReservation() / 1024) ])
430 args = args + self.dmargs
431 if restore:
432 args = args + ([ "-loadvm", "/tmp/xen.qemu-dm.%d" %
433 self.vm.getDomid() ])
434 env = dict(os.environ)
435 if self.display:
436 env['DISPLAY'] = self.display
437 if self.xauthority:
438 env['XAUTHORITY'] = self.xauthority
439 if self.vncconsole:
440 args = args + ([ "-vncviewer" ])
441 log.info("spawning device models: %s %s", self.device_model, args)
442 # keep track of pid and spawned options to kill it later
443 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
444 self.vm.storeDom("image/device-model-pid", self.pid)
445 log.info("device model pid: %d", self.pid)
447 def recreate(self):
448 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
450 def destroy(self, suspend = False):
451 if self.pid:
452 try:
453 sig = signal.SIGKILL
454 if suspend:
455 log.info("use sigusr1 to signal qemu %d", self.pid)
456 sig = signal.SIGUSR1
457 os.kill(self.pid, sig)
458 except OSError, exn:
459 log.exception(exn)
460 try:
461 os.waitpid(self.pid, 0)
462 except OSError, exn:
463 # This is expected if Xend has been restarted within the
464 # life of this domain. In this case, we can kill the process,
465 # but we can't wait for it because it's not our child.
466 pass
467 self.pid = None
470 class IA64_HVM_ImageHandler(HVMImageHandler):
472 def getRequiredAvailableMemory(self, mem_kb):
473 page_kb = 16
474 # ROM size for guest firmware, ioreq page, pio page and xenstore page
475 extra_pages = 1024 + 4
476 return mem_kb + extra_pages * page_kb
478 def getRequiredInitialReservation(self):
479 return self.vm.getMemoryTarget()
481 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
482 # Explicit shadow memory is not a concept
483 return 0
485 class X86_HVM_ImageHandler(HVMImageHandler):
487 def getRequiredAvailableMemory(self, mem_kb):
488 # Add 8 MiB overhead for QEMU's video RAM.
489 return mem_kb + 8192
491 def getRequiredInitialReservation(self):
492 return self.vm.getMemoryTarget()
494 def getRequiredMaximumReservation(self):
495 return self.vm.getMemoryMaximum()
497 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
498 # 256 pages (1MB) per vcpu,
499 # plus 1 page per MiB of RAM for the P2M map,
500 # plus 1 page per MiB of RAM to shadow the resident processes.
501 # This is higher than the minimum that Xen would allocate if no value
502 # were given (but the Xen minimum is for safety, not performance).
503 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
504 shadow_mem_kb)
506 class X86_Linux_ImageHandler(LinuxImageHandler):
508 def buildDomain(self):
509 # set physical mapping limit
510 # add an 8MB slack to balance backend allocations.
511 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
512 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
513 return LinuxImageHandler.buildDomain(self)
515 _handlers = {
516 "powerpc": {
517 "linux": PPC_LinuxImageHandler,
518 },
519 "ia64": {
520 "linux": LinuxImageHandler,
521 "hvm": IA64_HVM_ImageHandler,
522 },
523 "x86": {
524 "linux": X86_Linux_ImageHandler,
525 "hvm": X86_HVM_ImageHandler,
526 },
527 }
529 def findImageHandlerClass(image):
530 """Find the image handler class for an image config.
532 @param image config
533 @return ImageHandler subclass or None
534 """
535 image_type = image.image_type()
536 try:
537 return _handlers[arch.type][image_type]
538 except KeyError:
539 raise VmError('unknown image type: ' + image_type)