ia64/xen-unstable

view tools/python/xen/xend/image.py @ 16187:dc2ff26bbdf6

ia64: Fix ia64 PV domain creation with new vhpt-size parameter.
Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir@xensource.com>
date Mon Oct 22 14:21:13 2007 +0100 (2007-10-22)
parents c5530fbbe9d0
children f63aba1b5387
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 time
24 import signal
26 import xen.lowlevel.xc
27 from xen.xend.XendConstants import *
28 from xen.xend.XendError import VmError, XendError, HVMRequired
29 from xen.xend.XendLogging import log
30 from xen.xend.XendOptions import instance as xenopts
31 from xen.xend.xenstore.xstransact import xstransact
32 from xen.xend.xenstore.xswatch import xswatch
33 from xen.xend import arch
35 xc = xen.lowlevel.xc.xc()
37 MAX_GUEST_CMDLINE = 1024
40 def create(vm, vmConfig):
41 """Create an image handler for a vm.
43 @return ImageHandler instance
44 """
45 return findImageHandlerClass(vmConfig)(vm, vmConfig)
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, vmConfig):
69 self.vm = vm
71 self.bootloader = False
72 self.kernel = None
73 self.ramdisk = None
74 self.cmdline = None
76 self.configure(vmConfig)
78 def configure(self, vmConfig):
79 """Config actions common to all unix-like domains."""
80 if '_temp_using_bootloader' in vmConfig:
81 self.bootloader = True
82 self.kernel = vmConfig['_temp_kernel']
83 self.cmdline = vmConfig['_temp_args']
84 self.ramdisk = vmConfig['_temp_ramdisk']
85 else:
86 self.kernel = vmConfig['PV_kernel']
87 self.cmdline = vmConfig['PV_args']
88 self.ramdisk = vmConfig['PV_ramdisk']
89 self.vm.storeVm(("image/ostype", self.ostype),
90 ("image/kernel", self.kernel),
91 ("image/cmdline", self.cmdline),
92 ("image/ramdisk", self.ramdisk))
95 def cleanupBootloading(self):
96 if self.bootloader:
97 self.unlink(self.kernel)
98 self.unlink(self.ramdisk)
101 def unlink(self, f):
102 if not f: return
103 try:
104 os.unlink(f)
105 except OSError, ex:
106 log.warning("error removing bootloader file '%s': %s", f, ex)
109 def createImage(self):
110 """Entry point to create domain memory image.
111 Override in subclass if needed.
112 """
113 return self.createDomain()
116 def createDomain(self):
117 """Build the domain boot image.
118 """
119 # Set params and call buildDomain().
121 if not os.path.isfile(self.kernel):
122 raise VmError('Kernel image does not exist: %s' % self.kernel)
123 if self.ramdisk and not os.path.isfile(self.ramdisk):
124 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
125 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
126 log.warning('kernel cmdline too long, domain %d',
127 self.vm.getDomid())
129 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
130 self.vm.getDomid(), self.vm.getVCpuCount())
132 result = self.buildDomain()
134 if isinstance(result, dict):
135 return result
136 else:
137 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
138 % (self.ostype, self.vm.getDomid(), str(result)))
140 def getRequiredAvailableMemory(self, mem_kb):
141 """@param mem_kb The configured maxmem or memory, in KiB.
142 @return The corresponding required amount of memory for the domain,
143 also in KiB. This is normally the given mem_kb, but architecture- or
144 image-specific code may override this to add headroom where
145 necessary."""
146 return mem_kb
148 def getRequiredInitialReservation(self):
149 """@param mem_kb The configured memory, in KiB.
150 @return The corresponding required amount of memory to be free, also
151 in KiB. This is normally the same as getRequiredAvailableMemory, but
152 architecture- or image-specific code may override this to
153 add headroom where necessary."""
154 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
156 def getRequiredMaximumReservation(self):
157 """@param mem_kb The maximum possible memory, in KiB.
158 @return The corresponding required amount of memory to be free, also
159 in KiB. This is normally the same as getRequiredAvailableMemory, but
160 architecture- or image-specific code may override this to
161 add headroom where necessary."""
162 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
164 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
165 """@param shadow_mem_kb The configured shadow memory, in KiB.
166 @param maxmem_kb The configured maxmem, in KiB.
167 @return The corresponding required amount of shadow memory, also in
168 KiB."""
169 # PV domains don't need any shadow memory
170 return 0
172 def buildDomain(self):
173 """Build the domain. Define in subclass."""
174 raise NotImplementedError()
176 def createDeviceModel(self, restore = False):
177 """Create device model for the domain (define in subclass if needed)."""
178 pass
180 def saveDeviceModel(self):
181 """Save device model for the domain (define in subclass if needed)."""
182 pass
184 def resumeDeviceModel(self):
185 """Unpause device model for the domain (define in subclass if needed)."""
186 pass
188 def destroy(self):
189 """Extra cleanup on domain destroy (define in subclass if needed)."""
190 pass
193 def recreate(self):
194 pass
197 class LinuxImageHandler(ImageHandler):
199 ostype = "linux"
200 flags = 0
201 vhpt = 0
203 def buildDomain(self):
204 store_evtchn = self.vm.getStorePort()
205 console_evtchn = self.vm.getConsolePort()
207 mem_mb = self.getRequiredInitialReservation() / 1024
209 log.debug("domid = %d", self.vm.getDomid())
210 log.debug("memsize = %d", mem_mb)
211 log.debug("image = %s", self.kernel)
212 log.debug("store_evtchn = %d", store_evtchn)
213 log.debug("console_evtchn = %d", console_evtchn)
214 log.debug("cmdline = %s", self.cmdline)
215 log.debug("ramdisk = %s", self.ramdisk)
216 log.debug("vcpus = %d", self.vm.getVCpuCount())
217 log.debug("features = %s", self.vm.getFeatures())
218 if arch.type == "ia64":
219 log.debug("vhpt = %d", self.flags)
221 return xc.linux_build(domid = self.vm.getDomid(),
222 memsize = mem_mb,
223 image = self.kernel,
224 store_evtchn = store_evtchn,
225 console_evtchn = console_evtchn,
226 cmdline = self.cmdline,
227 ramdisk = self.ramdisk,
228 features = self.vm.getFeatures(),
229 flags = self.flags,
230 vhpt = self.vhpt)
232 class PPC_LinuxImageHandler(LinuxImageHandler):
234 ostype = "linux"
236 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
237 """@param shadow_mem_kb The configured shadow memory, in KiB.
238 @param maxmem_kb The configured maxmem, in KiB.
239 @return The corresponding required amount of shadow memory, also in
240 KiB.
241 PowerPC currently uses "shadow memory" to refer to the hash table."""
242 return max(maxmem_kb / 64, shadow_mem_kb)
246 class HVMImageHandler(ImageHandler):
248 ostype = "hvm"
250 def __init__(self, vm, vmConfig):
251 ImageHandler.__init__(self, vm, vmConfig)
252 self.shutdownWatch = None
253 self.rebootFeatureWatch = None
255 def configure(self, vmConfig):
256 ImageHandler.configure(self, vmConfig)
258 if not self.kernel:
259 self.kernel = '/usr/lib/xen/boot/hvmloader'
261 info = xc.xeninfo()
262 if 'hvm' not in info['xen_caps']:
263 raise HVMRequired()
265 self.dmargs = self.parseDeviceModelArgs(vmConfig)
266 self.device_model = vmConfig['platform'].get('device_model')
267 if not self.device_model:
268 raise VmError("hvm: missing device model")
270 self.display = vmConfig['platform'].get('display')
271 self.xauthority = vmConfig['platform'].get('xauthority')
272 self.vncconsole = vmConfig['platform'].get('vncconsole')
274 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
276 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
277 ("image/device-model", self.device_model),
278 ("image/display", self.display))
279 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
281 self.pid = None
283 self.apic = int(vmConfig['platform'].get('apic', 0))
284 self.acpi = int(vmConfig['platform'].get('acpi', 0))
287 def buildDomain(self):
288 store_evtchn = self.vm.getStorePort()
290 mem_mb = self.getRequiredInitialReservation() / 1024
292 log.debug("domid = %d", self.vm.getDomid())
293 log.debug("image = %s", self.kernel)
294 log.debug("store_evtchn = %d", store_evtchn)
295 log.debug("memsize = %d", mem_mb)
296 log.debug("vcpus = %d", self.vm.getVCpuCount())
297 log.debug("acpi = %d", self.acpi)
298 log.debug("apic = %d", self.apic)
300 rc = xc.hvm_build(domid = self.vm.getDomid(),
301 image = self.kernel,
302 memsize = mem_mb,
303 vcpus = self.vm.getVCpuCount(),
304 acpi = self.acpi,
305 apic = self.apic)
307 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
309 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
310 HVM_PARAM_STORE_PFN)
311 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
312 store_evtchn)
314 return rc
316 # Return a list of cmd line args to the device models based on the
317 # xm config file
318 def parseDeviceModelArgs(self, vmConfig):
319 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
320 'localtime', 'serial', 'stdvga', 'isa',
321 'acpi', 'usb', 'usbdevice', 'keymap', 'pci' ]
323 ret = ['-vcpus', str(self.vm.getVCpuCount())]
325 for a in dmargs:
326 v = vmConfig['platform'].get(a)
328 # python doesn't allow '-' in variable names
329 if a == 'stdvga': a = 'std-vga'
330 if a == 'keymap': a = 'k'
332 # Handle booleans gracefully
333 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
334 try:
335 if v != None: v = int(v)
336 if v: ret.append("-%s" % a)
337 except (ValueError, TypeError):
338 pass # if we can't convert it to a sane type, ignore it
339 else:
340 if v:
341 ret.append("-%s" % a)
342 ret.append("%s" % v)
344 if a in ['fda', 'fdb']:
345 if v:
346 if not os.path.isabs(v):
347 raise VmError("Floppy file %s does not exist." % v)
348 log.debug("args: %s, val: %s" % (a,v))
350 # Handle disk/network related options
351 mac = None
352 ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
353 nics = 0
355 for devuuid in vmConfig['vbd_refs']:
356 devinfo = vmConfig['devices'][devuuid][1]
357 uname = devinfo.get('uname')
358 if uname is not None and 'file:' in uname:
359 (_, vbdparam) = string.split(uname, ':', 1)
360 if not os.path.isfile(vbdparam):
361 raise VmError('Disk image does not exist: %s' %
362 vbdparam)
364 for devuuid in vmConfig['vif_refs']:
365 devinfo = vmConfig['devices'][devuuid][1]
366 dtype = devinfo.get('type', 'ioemu')
367 if dtype != 'ioemu':
368 continue
369 nics += 1
370 mac = devinfo.get('mac')
371 if mac is None:
372 raise VmError("MAC address not specified or generated.")
373 bridge = devinfo.get('bridge', 'xenbr0')
374 model = devinfo.get('model', 'rtl8139')
375 ret.append("-net")
376 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
377 (nics, mac, model))
378 ret.append("-net")
379 ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
382 #
383 # Find RFB console device, and if it exists, make QEMU enable
384 # the VNC console.
385 #
386 if int(vmConfig['platform'].get('nographic', 0)) != 0:
387 # skip vnc init if nographic is set
388 ret.append('-nographic')
389 return ret
391 vnc_config = {}
392 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
393 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
394 for dev_uuid in vmConfig['console_refs']:
395 dev_type, dev_info = vmConfig['devices'][dev_uuid]
396 if dev_type == 'vfb':
397 vnc_config = dev_info.get('other_config', {})
398 has_vnc = True
399 break
401 if has_vnc:
402 if not vnc_config:
403 for key in ('vncunused', 'vnclisten', 'vncdisplay',
404 'vncpasswd'):
405 if key in vmConfig['platform']:
406 vnc_config[key] = vmConfig['platform'][key]
408 vnclisten = vnc_config.get('vnclisten',
409 xenopts().get_vnclisten_address())
410 vncdisplay = vnc_config.get('vncdisplay', 0)
411 ret.append('-vnc')
412 ret.append("%s:%d" % (vnclisten, vncdisplay))
414 if vnc_config.get('vncunused', 0):
415 ret.append('-vncunused')
417 # Store vncpassword in xenstore
418 vncpasswd = vnc_config.get('vncpasswd')
419 if not vncpasswd:
420 vncpasswd = xenopts().get_vncpasswd_default()
422 if vncpasswd is None:
423 raise VmError('vncpasswd is not setup in vmconfig or '
424 'xend-config.sxp')
426 if vncpasswd != '':
427 self.vm.storeVm('vncpasswd', vncpasswd)
428 elif has_sdl:
429 # SDL is default in QEMU.
430 pass
431 else:
432 ret.append('-nographic')
434 if int(vmConfig['platform'].get('monitor', 0)) != 0:
435 ret = ret + ['-monitor', 'vc']
436 return ret
438 def createDeviceModel(self, restore = False):
439 if self.pid:
440 return
441 # Execute device model.
442 #todo: Error handling
443 args = [self.device_model]
444 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
445 if arch.type == "ia64":
446 args = args + ([ "-m", "%s" %
447 (self.getRequiredInitialReservation() / 1024) ])
448 args = args + self.dmargs
449 if restore:
450 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
451 self.vm.getDomid() ])
452 env = dict(os.environ)
453 if self.display:
454 env['DISPLAY'] = self.display
455 if self.xauthority:
456 env['XAUTHORITY'] = self.xauthority
457 if self.vncconsole:
458 args = args + ([ "-vncviewer" ])
459 log.info("spawning device models: %s %s", self.device_model, args)
460 # keep track of pid and spawned options to kill it later
461 self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env)
462 self.vm.storeDom("image/device-model-pid", self.pid)
463 log.info("device model pid: %d", self.pid)
465 def saveDeviceModel(self):
466 # Signal the device model to pause itself and save its state
467 xstransact.Store("/local/domain/0/device-model/%i"
468 % self.vm.getDomid(), ('command', 'save'))
469 # Wait for confirmation. Could do this with a watch but we'd
470 # still end up spinning here waiting for the watch to fire.
471 state = ''
472 count = 0
473 while state != 'paused':
474 state = xstransact.Read("/local/domain/0/device-model/%i/state"
475 % self.vm.getDomid())
476 time.sleep(0.1)
477 count += 1
478 if count > 100:
479 raise VmError('Timed out waiting for device model to save')
481 def resumeDeviceModel(self):
482 # Signal the device model to resume activity after pausing to save.
483 xstransact.Store("/local/domain/0/device-model/%i"
484 % self.vm.getDomid(), ('command', 'continue'))
486 def recreate(self):
487 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
489 def destroy(self, suspend = False):
490 if self.pid and not suspend:
491 try:
492 os.kill(self.pid, signal.SIGKILL)
493 except OSError, exn:
494 log.exception(exn)
495 try:
496 os.waitpid(self.pid, 0)
497 except OSError, exn:
498 # This is expected if Xend has been restarted within the
499 # life of this domain. In this case, we can kill the process,
500 # but we can't wait for it because it's not our child.
501 pass
502 self.pid = None
503 state = xstransact.Remove("/local/domain/0/device-model/%i"
504 % self.vm.getDomid())
507 class IA64_HVM_ImageHandler(HVMImageHandler):
509 def configure(self, vmConfig):
510 HVMImageHandler.configure(self, vmConfig)
511 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
513 def buildDomain(self):
514 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
515 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
516 return HVMImageHandler.buildDomain(self)
518 def getRequiredAvailableMemory(self, mem_kb):
519 page_kb = 16
520 # ROM size for guest firmware, io page, xenstore page
521 # buffer io page, buffer pio page and memmap info page
522 extra_pages = 1024 + 5
523 return mem_kb + extra_pages * page_kb
525 def getRequiredInitialReservation(self):
526 return self.vm.getMemoryTarget()
528 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
529 # Explicit shadow memory is not a concept
530 return 0
532 class IA64_Linux_ImageHandler(LinuxImageHandler):
534 def configure(self, vmConfig):
535 LinuxImageHandler.configure(self, vmConfig)
536 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
539 class X86_HVM_ImageHandler(HVMImageHandler):
541 def configure(self, vmConfig):
542 HVMImageHandler.configure(self, vmConfig)
543 self.pae = int(vmConfig['platform'].get('pae', 0))
545 def buildDomain(self):
546 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
547 return HVMImageHandler.buildDomain(self)
549 def getRequiredAvailableMemory(self, mem_kb):
550 # Add 8 MiB overhead for QEMU's video RAM.
551 return mem_kb + 8192
553 def getRequiredInitialReservation(self):
554 return self.vm.getMemoryTarget()
556 def getRequiredMaximumReservation(self):
557 return self.vm.getMemoryMaximum()
559 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
560 # 256 pages (1MB) per vcpu,
561 # plus 1 page per MiB of RAM for the P2M map,
562 # plus 1 page per MiB of RAM to shadow the resident processes.
563 # This is higher than the minimum that Xen would allocate if no value
564 # were given (but the Xen minimum is for safety, not performance).
565 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
566 shadow_mem_kb)
569 class X86_Linux_ImageHandler(LinuxImageHandler):
571 def buildDomain(self):
572 # set physical mapping limit
573 # add an 8MB slack to balance backend allocations.
574 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
575 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
576 return LinuxImageHandler.buildDomain(self)
578 _handlers = {
579 "powerpc": {
580 "linux": PPC_LinuxImageHandler,
581 },
582 "ia64": {
583 "linux": IA64_Linux_ImageHandler,
584 "hvm": IA64_HVM_ImageHandler,
585 },
586 "x86": {
587 "linux": X86_Linux_ImageHandler,
588 "hvm": X86_HVM_ImageHandler,
589 },
590 }
592 def findImageHandlerClass(image):
593 """Find the image handler class for an image config.
595 @param image config
596 @return ImageHandler subclass or None
597 """
598 image_type = image.image_type()
599 try:
600 return _handlers[arch.type][image_type]
601 except KeyError:
602 raise VmError('unknown image type: ' + image_type)