ia64/xen-unstable

view tools/python/xen/xend/image.py @ 19639:205b1badbcfd

Add support for superpages (hugepages) in PV domain

This patch adds the option "superpages" to the domain configuration
file. If it is set, the domain is populated using 2M pages.

This code does not support fallback to small pages. If the domain can
not be created with 2M pages, the create will fail.

The patch also includes support for saving and restoring domains with
the superpage flag set. However, if a domain has freed small pages
within its physical page array and then extended the array, the
restore will fill in those freed pages. It will then attempt to
allocate more than its memory limit and will fail. This is
significant because apparently Linux does this during boot, thus a
freshly booted Linux image can not be saved and restored successfully.

Signed-off-by: Dave McCracken <dcm@mccr.org>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 26 09:58:38 2009 +0100 (2009-05-26)
parents cd92432a7af4
children ec2bc4b9fa32
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, os.path, string
21 import re
22 import math
23 import time
24 import signal
25 import thread
26 import fcntl
27 import sys
28 import errno
29 import glob
30 import traceback
31 import platform
33 import xen.lowlevel.xc
34 from xen.xend.XendConstants import *
35 from xen.xend.XendError import VmError, XendError, HVMRequired
36 from xen.xend.XendLogging import log
37 from xen.xend.XendOptions import instance as xenopts
38 from xen.xend.xenstore.xstransact import xstransact
39 from xen.xend.xenstore.xswatch import xswatch
40 from xen.xend import arch
41 from xen.xend import XendOptions
42 from xen.util import oshelp
43 from xen.util import utils
44 from xen.xend import osdep
46 xc = xen.lowlevel.xc.xc()
48 MAX_GUEST_CMDLINE = 1024
50 sentinel_path_prefix = '/var/run/xend/dm-'
51 sentinel_fifos_inuse = { }
53 def cleanup_stale_sentinel_fifos():
54 for path in glob.glob(sentinel_path_prefix + '*.fifo'):
55 if path in sentinel_fifos_inuse: continue
56 try: os.unlink(path)
57 except OSError, e:
58 log.warning('could not delete stale fifo %s: %s',
59 path, utils.exception_string(e))
61 def create(vm, vmConfig):
62 """Create an image handler for a vm.
64 @return ImageHandler instance
65 """
66 return findImageHandlerClass(vmConfig)(vm, vmConfig)
69 class ImageHandler:
70 """Abstract base class for image handlers.
72 createImage() is called to configure and build the domain from its
73 kernel image and ramdisk etc.
75 The method buildDomain() is used to build the domain, and must be
76 defined in a subclass. Usually this is the only method that needs
77 defining in a subclass.
79 The method createDeviceModel() is called to create the domain device
80 model.
82 The method destroyDeviceModel() is called to reap the device model
83 """
85 ostype = None
88 def __init__(self, vm, vmConfig):
89 self.vm = vm
91 self.bootloader = False
92 self.kernel = None
93 self.ramdisk = None
94 self.cmdline = None
96 self.configure(vmConfig)
98 def configure(self, vmConfig):
99 """Config actions common to all unix-like domains."""
100 if '_temp_using_bootloader' in vmConfig:
101 self.bootloader = True
102 self.kernel = vmConfig['_temp_kernel']
103 self.cmdline = vmConfig['_temp_args']
104 self.ramdisk = vmConfig['_temp_ramdisk']
105 else:
106 self.kernel = vmConfig['PV_kernel']
107 self.cmdline = vmConfig['PV_args']
108 self.ramdisk = vmConfig['PV_ramdisk']
109 self.vm.storeVm(("image/ostype", self.ostype),
110 ("image/kernel", self.kernel),
111 ("image/cmdline", self.cmdline),
112 ("image/ramdisk", self.ramdisk))
113 self.vm.permissionsVm("image/cmdline", { 'dom': self.vm.getDomid(), 'read': True } )
115 self.device_model = vmConfig['platform'].get('device_model')
117 self.display = vmConfig['platform'].get('display')
118 self.xauthority = vmConfig['platform'].get('xauthority')
119 self.vncconsole = int(vmConfig['platform'].get('vncconsole', 0))
120 self.dmargs = self.parseDeviceModelArgs(vmConfig)
121 self.pid = None
122 rtc_timeoffset = int(vmConfig['platform'].get('rtc_timeoffset', 0))
123 if vmConfig['platform'].get('localtime', 0):
124 if time.localtime(time.time())[8]:
125 rtc_timeoffset -= time.altzone
126 else:
127 rtc_timeoffset -= time.timezone
128 if rtc_timeoffset != 0:
129 xc.domain_set_time_offset(self.vm.getDomid(), rtc_timeoffset)
131 self.cpuid = None
132 self.cpuid_check = None
133 if 'cpuid' in vmConfig:
134 self.cpuid = vmConfig['cpuid'];
135 if 'cpuid_check' in vmConfig:
136 self.cpuid_check = vmConfig['cpuid_check']
138 def cleanupBootloading(self):
139 if self.bootloader:
140 self.unlink(self.kernel)
141 self.unlink(self.ramdisk)
144 def unlink(self, f):
145 if not f: return
146 try:
147 os.unlink(f)
148 except OSError, ex:
149 log.warning("error removing bootloader file '%s': %s", f, ex)
152 def createImage(self):
153 """Entry point to create domain memory image.
154 Override in subclass if needed.
155 """
156 return self.createDomain()
159 def createDomain(self):
160 """Build the domain boot image.
161 """
162 # Set params and call buildDomain().
164 if self.kernel and not os.path.isfile(self.kernel):
165 raise VmError('Kernel image does not exist: %s' % self.kernel)
166 if self.ramdisk and not os.path.isfile(self.ramdisk):
167 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
168 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
169 log.warning('kernel cmdline too long, domain %d',
170 self.vm.getDomid())
172 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
173 self.vm.getDomid(), self.vm.getVCpuCount())
175 result = self.buildDomain()
177 if isinstance(result, dict):
178 return result
179 else:
180 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
181 % (self.ostype, self.vm.getDomid(), str(result)))
183 def getRequiredAvailableMemory(self, mem_kb):
184 """@param mem_kb The configured maxmem or memory, in KiB.
185 @return The corresponding required amount of memory for the domain,
186 also in KiB. This is normally the given mem_kb, but architecture- or
187 image-specific code may override this to add headroom where
188 necessary."""
189 return mem_kb
191 def getRequiredInitialReservation(self):
192 """@param mem_kb The configured memory, in KiB.
193 @return The corresponding required amount of memory to be free, also
194 in KiB. This is normally the same as getRequiredAvailableMemory, but
195 architecture- or image-specific code may override this to
196 add headroom where necessary."""
197 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
199 def getRequiredMaximumReservation(self):
200 """@param mem_kb The maximum possible memory, in KiB.
201 @return The corresponding required amount of memory to be free, also
202 in KiB. This is normally the same as getRequiredAvailableMemory, but
203 architecture- or image-specific code may override this to
204 add headroom where necessary."""
205 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
207 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
208 """@param shadow_mem_kb The configured shadow memory, in KiB.
209 @param maxmem_kb The configured maxmem, in KiB.
210 @return The corresponding required amount of shadow memory, also in
211 KiB."""
212 # PV domains don't need any shadow memory
213 return 0
215 def buildDomain(self):
216 """Build the domain. Define in subclass."""
217 raise NotImplementedError()
219 def prepareEnvironment(self):
220 """Prepare the environment for the execution of the domain. This
221 method is called before any devices are set up."""
223 domid = self.vm.getDomid()
225 # Delete left-over pipes
226 try:
227 os.unlink('/var/run/tap/qemu-read-%d' % domid)
228 os.unlink('/var/run/tap/qemu-write-%d' % domid)
229 except:
230 pass
232 # No device model, don't create pipes
233 if self.device_model is None:
234 return
236 if platform.system() != 'SunOS':
237 # If we use a device model, the pipes for communication between
238 # blktapctrl and ioemu must be present before the devices are
239 # created (blktapctrl must access them for new block devices)
241 try:
242 os.makedirs('/var/run/tap', 0755)
243 except:
244 pass
246 try:
247 os.mkfifo('/var/run/tap/qemu-read-%d' % domid, 0600)
248 os.mkfifo('/var/run/tap/qemu-write-%d' % domid, 0600)
249 except OSError, e:
250 log.warn('Could not create blktap pipes for domain %d' % domid)
251 log.exception(e)
252 pass
255 # Return a list of cmd line args to the device models based on the
256 # xm config file
257 def parseDeviceModelArgs(self, vmConfig):
258 ret = ["-domain-name", str(self.vm.info['name_label'])]
260 xen_extended_power_mgmt = int(vmConfig['platform'].get(
261 'xen_extended_power_mgmt', 0))
262 if xen_extended_power_mgmt != 0:
263 xstransact.Store("/local/domain/0/device-model/%i"
264 % self.vm.getDomid(),
265 ('xen_extended_power_mgmt',
266 xen_extended_power_mgmt))
268 # Find RFB console device, and if it exists, make QEMU enable
269 # the VNC console.
270 if int(vmConfig['platform'].get('nographic', 0)) != 0:
271 # skip vnc init if nographic is set
272 ret.append('-nographic')
273 return ret
275 vram = str(vmConfig['platform'].get('videoram',4))
276 ret.append('-videoram')
277 ret.append(vram)
279 vnc_config = {}
280 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
281 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
282 opengl = 1
283 keymap = vmConfig['platform'].get("keymap")
284 for dev_uuid in vmConfig['console_refs']:
285 dev_type, dev_info = vmConfig['devices'][dev_uuid]
286 if dev_type == 'vfb':
287 if 'keymap' in dev_info:
288 keymap = dev_info.get('keymap',{})
289 if int(dev_info.get('vnc', 0)) != 0 :
290 has_vnc = True
291 if int(dev_info.get('sdl', 0)) != 0 :
292 has_sdl = True
293 if has_sdl:
294 self.display = dev_info.get('display', self.display)
295 self.xauthority = dev_info.get('xauthority', self.xauthority)
296 opengl = int(dev_info.get('opengl', opengl))
297 if has_vnc:
298 vnc_config = dev_info.get('other_config', {})
299 break
301 if keymap:
302 ret.append("-k")
303 ret.append(keymap)
305 if has_vnc:
306 if not vnc_config:
307 for key in ('vncunused', 'vnclisten', 'vncdisplay',
308 'vncpasswd'):
309 if key in vmConfig['platform']:
310 vnc_config[key] = vmConfig['platform'][key]
311 if vnc_config.has_key("vncpasswd"):
312 passwd = vnc_config["vncpasswd"]
313 else:
314 passwd = XendOptions.instance().get_vncpasswd_default()
315 vncopts = ""
316 if passwd:
317 self.vm.storeVm("vncpasswd", passwd)
318 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
319 vncopts = vncopts + ",password"
320 log.debug("Stored a VNC password for vfb access")
321 else:
322 log.debug("No VNC passwd configured for vfb access")
324 if XendOptions.instance().get_vnc_tls():
325 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
326 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
328 if not os.path.exists(vncx509certdir):
329 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
331 if vncx509verify:
332 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
333 else:
334 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
337 vnclisten = vnc_config.get('vnclisten',
338 XendOptions.instance().get_vnclisten_address())
339 vncdisplay = int(vnc_config.get('vncdisplay', 0))
340 ret.append('-vnc')
341 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
343 if int(vnc_config.get('vncunused', 1)) != 0:
344 ret.append('-vncunused')
346 if has_sdl:
347 ret.append('-sdl')
348 if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
349 ret.append('-disable-opengl')
351 if not has_sdl and not has_vnc :
352 ret.append('-nographic')
354 if int(vmConfig['platform'].get('monitor', 0)) != 0:
355 ret = ret + ['-monitor', 'vc']
356 return ret
358 def getDeviceModelArgs(self, restore = False):
359 args = [self.device_model]
360 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
361 args = args + self.dmargs
362 return args
364 def _openSentinel(self, sentinel_path_fifo):
365 self.sentinel_fifo = file(sentinel_path_fifo, 'r')
366 self.sentinel_lock = thread.allocate_lock()
367 oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True)
368 sentinel_fifos_inuse[sentinel_path_fifo] = 1
369 self.sentinel_path_fifo = sentinel_path_fifo
371 def createDeviceModel(self, restore = False):
372 if self.device_model is None:
373 return
374 if self.pid:
375 return
376 # Execute device model.
377 #todo: Error handling
378 args = self.getDeviceModelArgs(restore)
379 env = dict(os.environ)
380 if self.display:
381 env['DISPLAY'] = self.display
382 if self.xauthority:
383 env['XAUTHORITY'] = self.xauthority
384 unique_id = "%i-%i" % (self.vm.getDomid(), time.time())
385 sentinel_path = sentinel_path_prefix + unique_id
386 sentinel_path_fifo = sentinel_path + '.fifo'
387 os.mkfifo(sentinel_path_fifo, 0600)
388 sentinel_write = file(sentinel_path_fifo, 'r+')
389 self._openSentinel(sentinel_path_fifo)
390 self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo)
391 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
392 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
393 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
394 log.info("spawning device models: %s %s", self.device_model, args)
395 # keep track of pid and spawned options to kill it later
397 self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label'])
399 # rotate log
400 logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND
401 logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count()
402 if logrotate_count > 0:
403 logfile_mode |= os.O_TRUNC
404 if os.path.exists("%s.%d" % (self.logfile, logrotate_count)):
405 os.unlink("%s.%d" % (self.logfile, logrotate_count))
406 for n in range(logrotate_count - 1, 0, -1):
407 if os.path.exists("%s.%d" % (self.logfile, n)):
408 os.rename("%s.%d" % (self.logfile, n),
409 "%s.%d" % (self.logfile, (n + 1)))
410 if os.path.exists(self.logfile):
411 os.rename(self.logfile, self.logfile + ".1")
413 null = os.open("/dev/null", os.O_RDONLY)
414 logfd = os.open(self.logfile, logfile_mode)
416 sys.stderr.flush()
417 contract = osdep.prefork("%s:%d" %
418 (self.vm.getName(), self.vm.getDomid()))
419 pid = os.fork()
420 if pid == 0: #child
421 try:
422 osdep.postfork(contract)
423 os.dup2(null, 0)
424 os.dup2(logfd, 1)
425 os.dup2(logfd, 2)
426 os.close(null)
427 os.close(logfd)
428 self.sentinel_fifo.close()
429 try:
430 os.execve(self.device_model, args, env)
431 except Exception, e:
432 print >>sys.stderr, (
433 'failed to set up fds or execute dm %s: %s' %
434 (self.device_model, utils.exception_string(e)))
435 os._exit(126)
436 except:
437 os._exit(127)
438 else:
439 osdep.postfork(contract, abandon=True)
440 self.pid = pid
441 os.close(null)
442 os.close(logfd)
443 sentinel_write.close()
444 self.vm.storeDom("image/device-model-pid", self.pid)
445 log.info("device model pid: %d", self.pid)
446 # we would very much prefer not to have a thread here and instead
447 # have a callback but sadly we don't have Twisted in xend
448 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
450 def signalDeviceModel(self, cmd, ret, par = None):
451 if self.device_model is None:
452 return
453 # Signal the device model to for action
454 if cmd is '' or ret is '':
455 raise VmError('need valid command and result when signal device model')
457 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
458 % self.vm.getDomid())
460 if par is not None:
461 xstransact.Store("/local/domain/0/device-model/%i"
462 % self.vm.getDomid(), ('parameter', par))
464 xstransact.Store("/local/domain/0/device-model/%i"
465 % self.vm.getDomid(), ('command', cmd))
466 # Wait for confirmation. Could do this with a watch but we'd
467 # still end up spinning here waiting for the watch to fire.
468 state = ''
469 count = 0
470 while state != ret:
471 state = xstransact.Read("/local/domain/0/device-model/%i/state"
472 % self.vm.getDomid())
473 time.sleep(0.1)
474 count += 1
475 if count > 100:
476 raise VmError('Timed out waiting for device model action')
478 #resotre orig state
479 xstransact.Store("/local/domain/0/device-model/%i"
480 % self.vm.getDomid(), ('state', orig_state))
481 log.info("signalDeviceModel:restore dm state to %s", orig_state)
483 def saveDeviceModel(self):
484 # Signal the device model to pause itself and save its state
485 self.signalDeviceModel('save', 'paused')
487 def resumeDeviceModel(self):
488 if self.device_model is None:
489 return
490 # Signal the device model to resume activity after pausing to save.
491 xstransact.Store("/local/domain/0/device-model/%i"
492 % self.vm.getDomid(), ('command', 'continue'))
494 def _dmfailed(self, message):
495 log.warning("domain %s: %s", self.vm.getName(), message)
496 try:
497 xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
498 except:
499 pass
501 def recreate(self):
502 if self.device_model is None:
503 return
504 name = self.vm.getName()
505 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
506 fifo_fd = -1
507 log.debug("rediscovering %s", sentinel_path_fifo)
508 if sentinel_path_fifo is None:
509 log.debug("%s device model no sentinel, cannot rediscover", name)
510 else:
511 try:
512 # We open it O_WRONLY because that fails ENXIO if no-one
513 # has it open for reading (see SuSv3). The dm process got
514 # a read/write descriptor from our earlier invocation.
515 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
516 except OSError, e:
517 if e.errno == errno.ENXIO:
518 self._dmfailed("%s device model no longer running"%name)
519 elif e.errno == errno.ENOENT:
520 log.debug("%s device model sentinel %s absent!",
521 name, sentinel_path_fifo)
522 else:
523 raise
524 if fifo_fd >= 0:
525 self._openSentinel(sentinel_path_fifo)
526 os.close(fifo_fd)
527 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
528 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
529 name, self.pid, sentinel_path_fifo)
530 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
532 def _sentinel_watch(self):
533 log.info("waiting for sentinel_fifo")
534 try: self.sentinel_fifo.read(1)
535 except OSError, e: pass
536 self.sentinel_lock.acquire()
537 if self.pid:
538 try:
539 (p,st) = os.waitpid(self.pid, os.WNOHANG)
540 if p == self.pid:
541 message = oshelp.waitstatus_description(st)
542 else:
543 # obviously it is malfunctioning, kill it now
544 try:
545 os.kill(self.pid, signal.SIGKILL)
546 message = "malfunctioning (closed sentinel), killed"
547 except:
548 message = "malfunctioning or died ?"
549 message = "pid %d: %s" % (self.pid, message)
550 except Exception, e:
551 message = "waitpid failed: %s" % utils.exception_string(e)
552 message = "device model failure: %s" % message
553 try: message += "; see %s " % self.logfile
554 except: pass
555 self._dmfailed(message)
556 self.pid = None
557 else:
558 log.info("%s device model terminated", self.vm.getName())
559 self.sentinel_lock.release()
561 def destroyDeviceModel(self):
562 if self.device_model is None:
563 return
564 self.sentinel_lock.acquire()
565 try:
566 if self.pid:
567 try:
568 os.kill(self.pid, signal.SIGHUP)
569 except OSError, exn:
570 log.exception(exn)
571 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
572 for i in xrange(100):
573 try:
574 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
575 if p == self.pid:
576 break
577 except OSError:
578 # This is expected if Xend has been restarted within
579 # the life of this domain. In this case, we can kill
580 # the process, but we can't wait for it because it's
581 # not our child. We continue this loop, and after it is
582 # terminated make really sure the process is going away
583 # (SIGKILL).
584 pass
585 time.sleep(0.1)
586 else:
587 log.warning("DeviceModel %d took more than 10s "
588 "to terminate: sending SIGKILL" % self.pid)
589 try:
590 os.kill(self.pid, signal.SIGKILL)
591 os.waitpid(self.pid, 0)
592 except OSError:
593 # This happens if the process doesn't exist.
594 pass
595 finally:
596 self.pid = None
597 self.sentinel_lock.release()
599 state = xstransact.Remove("/local/domain/0/device-model/%i"
600 % self.vm.getDomid())
601 try:
602 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
603 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
604 except:
605 pass
606 try:
607 del sentinel_fifos_inuse[self.sentinel_path_fifo]
608 os.unlink(self.sentinel_path_fifo)
609 except:
610 pass
612 def setCpuid(self):
613 xc.domain_set_policy_cpuid(self.vm.getDomid())
615 if self.cpuid is not None:
616 cpuid = self.cpuid
617 transformed = {}
618 for sinput, regs in cpuid.iteritems():
619 inputs = sinput.split(',')
620 input = long(inputs[0])
621 sub_input = None
622 if len(inputs) == 2:
623 sub_input = long(inputs[1])
624 t = xc.domain_set_cpuid(self.vm.getDomid(),
625 input, sub_input, regs)
626 transformed[sinput] = t
627 self.cpuid = transformed
629 if self.cpuid_check is not None:
630 cpuid_check = self.cpuid_check
631 transformed = {}
632 for sinput, regs_check in cpuid_check.iteritems():
633 inputs = sinput.split(',')
634 input = long(inputs[0])
635 sub_input = None
636 if len(inputs) == 2:
637 sub_input = long(inputs[1])
638 t = xc.domain_check_cpuid(input, sub_input, regs_check)
639 transformed[sinput] = t
640 self.cpuid_check = transformed
644 class LinuxImageHandler(ImageHandler):
646 ostype = "linux"
647 flags = 0
648 vhpt = 0
650 def configure(self, vmConfig):
651 ImageHandler.configure(self, vmConfig)
652 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
653 self.is_stubdom = (self.kernel.find('stubdom') >= 0)
654 self.superpages = vmConfig['superpages']
656 def buildDomain(self):
657 store_evtchn = self.vm.getStorePort()
658 console_evtchn = self.vm.getConsolePort()
660 mem_mb = self.getRequiredInitialReservation() / 1024
662 log.debug("domid = %d", self.vm.getDomid())
663 log.debug("memsize = %d", mem_mb)
664 log.debug("image = %s", self.kernel)
665 log.debug("store_evtchn = %d", store_evtchn)
666 log.debug("console_evtchn = %d", console_evtchn)
667 log.debug("cmdline = %s", self.cmdline)
668 log.debug("ramdisk = %s", self.ramdisk)
669 log.debug("vcpus = %d", self.vm.getVCpuCount())
670 log.debug("features = %s", self.vm.getFeatures())
671 log.debug("flags = %d", self.flags)
672 log.debug("superpages = %d", self.superpages)
673 if arch.type == "ia64":
674 log.debug("vhpt = %d", self.vhpt)
676 return xc.linux_build(domid = self.vm.getDomid(),
677 memsize = mem_mb,
678 image = self.kernel,
679 store_evtchn = store_evtchn,
680 console_evtchn = console_evtchn,
681 cmdline = self.cmdline,
682 ramdisk = self.ramdisk,
683 features = self.vm.getFeatures(),
684 flags = self.flags,
685 vhpt = self.vhpt,
686 superpages = self.superpages)
688 def getRequiredAvailableMemory(self, mem_kb):
689 if self.is_stubdom :
690 mem_kb += self.vramsize
691 return mem_kb
693 def getRequiredInitialReservation(self):
694 return self.vm.getMemoryTarget()
696 def getRequiredMaximumReservation(self):
697 return self.vm.getMemoryMaximum()
699 def parseDeviceModelArgs(self, vmConfig):
700 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
701 # Equivalent to old xenconsoled behaviour. Should make
702 # it configurable in future
703 ret = ret + ["-serial", "pty"]
704 return ret
706 def getDeviceModelArgs(self, restore = False):
707 args = ImageHandler.getDeviceModelArgs(self, restore)
708 args = args + ([ "-M", "xenpv"])
709 return args
712 class HVMImageHandler(ImageHandler):
714 ostype = "hvm"
716 def __init__(self, vm, vmConfig):
717 ImageHandler.__init__(self, vm, vmConfig)
718 self.shutdownWatch = None
719 self.rebootFeatureWatch = None
721 def configure(self, vmConfig):
722 ImageHandler.configure(self, vmConfig)
724 self.loader = vmConfig['platform'].get('loader')
726 info = xc.xeninfo()
727 if 'hvm' not in info['xen_caps']:
728 raise HVMRequired()
730 xen_platform_pci = int(vmConfig['platform'].get('xen_platform_pci',1))
731 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
733 if not self.display :
734 self.display = ''
735 # Do not store sdl, opengl and serial related qemu cli options
736 self.vm.storeVm(("image/dmargs", " ".join([ x for x in self.dmargs
737 if x != "-sdl"
738 and x != "-disable-opengl"
739 and x != "-serial"
740 and x != "pty" ])),
741 ("image/device-model", self.device_model),
742 ("image/display", self.display))
743 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
745 if xen_platform_pci == 0:
746 disable_pf = 1
747 log.info("No need to create platform device.[domid:%d]", self.vm.getDomid())
748 else:
749 disable_pf = 0
750 log.info("Need to create platform device.[domid:%d]", self.vm.getDomid())
752 xstransact.Store("/local/domain/0/device-model/%i"%self.vm.getDomid(),
753 ('disable_pf', disable_pf))
754 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
755 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
757 self.apic = int(vmConfig['platform'].get('apic', 0))
758 self.acpi = int(vmConfig['platform'].get('acpi', 0))
759 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
762 # Return a list of cmd line args to the device models based on the
763 # xm config file
764 def parseDeviceModelArgs(self, vmConfig):
765 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
766 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
768 if self.kernel:
769 log.debug("kernel = %s", self.kernel)
770 ret = ret + ['-kernel', self.kernel]
771 if self.ramdisk:
772 log.debug("ramdisk = %s", self.ramdisk)
773 ret = ret + ['-initrd', self.ramdisk]
774 if self.cmdline:
775 log.debug("cmdline = %s", self.cmdline)
776 ret = ret + ['-append', self.cmdline]
779 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
780 'localtime', 'serial', 'stdvga', 'isa',
781 'acpi', 'usb', 'usbdevice' ]
783 for a in dmargs:
784 v = vmConfig['platform'].get(a)
786 # python doesn't allow '-' in variable names
787 if a == 'stdvga': a = 'std-vga'
788 if a == 'keymap': a = 'k'
790 # Handle booleans gracefully
791 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
792 try:
793 if v != None: v = int(v)
794 if v: ret.append("-%s" % a)
795 except (ValueError, TypeError):
796 pass # if we can't convert it to a sane type, ignore it
797 elif a == 'serial':
798 if v:
799 if type(v) == str:
800 v = [v]
801 for s in v:
802 if s:
803 ret.append("-serial")
804 ret.append("%s" % s)
805 else:
806 if v:
807 ret.append("-%s" % a)
808 ret.append("%s" % v)
810 if a in ['fda', 'fdb']:
811 if v:
812 if not os.path.isabs(v):
813 raise VmError("Floppy file %s does not exist." % v)
814 log.debug("args: %s, val: %s" % (a,v))
816 # Handle disk/network related options
817 mac = None
818 nics = 0
820 for devuuid in vmConfig['vbd_refs']:
821 devinfo = vmConfig['devices'][devuuid][1]
822 uname = devinfo.get('uname')
823 if uname is not None and 'file:' in uname:
824 (_, vbdparam) = string.split(uname, ':', 1)
825 if not os.path.isfile(vbdparam):
826 raise VmError('Disk image does not exist: %s' %
827 vbdparam)
829 for devuuid in vmConfig['vif_refs']:
830 devinfo = vmConfig['devices'][devuuid][1]
831 dtype = devinfo.get('type', 'ioemu')
832 if dtype != 'ioemu':
833 continue
834 nics += 1
835 mac = devinfo.get('mac')
836 if mac is None:
837 raise VmError("MAC address not specified or generated.")
838 bridge = devinfo.get('bridge', 'xenbr0')
839 model = devinfo.get('model', 'rtl8139')
840 ret.append("-net")
841 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
842 (nics, mac, model))
843 ret.append("-net")
844 ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" %
845 (nics, self.vm.getDomid(), nics-1, bridge))
847 if nics == 0:
848 ret.append("-net")
849 ret.append("none")
851 return ret
853 def getDeviceModelArgs(self, restore = False):
854 args = ImageHandler.getDeviceModelArgs(self, restore)
855 args = args + ([ "-M", "xenfv"])
856 if restore:
857 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
858 self.vm.getDomid() ])
859 return args
861 def buildDomain(self):
862 store_evtchn = self.vm.getStorePort()
864 memmax_mb = self.getRequiredMaximumReservation() / 1024
865 mem_mb = self.getRequiredInitialReservation() / 1024
867 log.debug("domid = %d", self.vm.getDomid())
868 log.debug("image = %s", self.loader)
869 log.debug("store_evtchn = %d", store_evtchn)
870 log.debug("memsize = %d", memmax_mb)
871 log.debug("target = %d", mem_mb)
872 log.debug("vcpus = %d", self.vm.getVCpuCount())
873 log.debug("acpi = %d", self.acpi)
874 log.debug("apic = %d", self.apic)
876 rc = xc.hvm_build(domid = self.vm.getDomid(),
877 image = self.loader,
878 memsize = memmax_mb,
879 target = mem_mb,
880 vcpus = self.vm.getVCpuCount(),
881 acpi = self.acpi,
882 apic = self.apic)
883 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
885 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
886 HVM_PARAM_STORE_PFN)
887 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
888 store_evtchn)
890 return rc
893 class IA64_HVM_ImageHandler(HVMImageHandler):
895 def configure(self, vmConfig):
896 HVMImageHandler.configure(self, vmConfig)
897 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
898 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
900 def buildDomain(self):
901 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
902 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
903 if self.guest_os_type is not None:
904 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
905 return HVMImageHandler.buildDomain(self)
907 def getRequiredAvailableMemory(self, mem_kb):
908 page_kb = 16
909 # ROM size for guest firmware, io page, xenstore page
910 # buffer io page, buffer pio page and memmap info page
911 extra_pages = 1024 + 5
912 mem_kb += extra_pages * page_kb
913 mem_kb += self.vramsize
914 return mem_kb
916 def getRequiredInitialReservation(self):
917 return self.vm.getMemoryTarget()
919 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
920 # Explicit shadow memory is not a concept
921 return 0
923 def getDeviceModelArgs(self, restore = False):
924 args = HVMImageHandler.getDeviceModelArgs(self, restore)
925 args = args + ([ "-m", "%s" %
926 (self.getRequiredInitialReservation() / 1024) ])
927 return args
929 def setCpuid(self):
930 # Guest CPUID configuration is not implemented yet.
931 return
933 class IA64_Linux_ImageHandler(LinuxImageHandler):
935 def configure(self, vmConfig):
936 LinuxImageHandler.configure(self, vmConfig)
937 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
939 def setCpuid(self):
940 # Guest CPUID configuration is not implemented yet.
941 return
943 class X86_HVM_ImageHandler(HVMImageHandler):
945 def configure(self, vmConfig):
946 HVMImageHandler.configure(self, vmConfig)
947 self.pae = int(vmConfig['platform'].get('pae', 0))
948 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
950 def buildDomain(self):
951 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
952 rc = HVMImageHandler.buildDomain(self)
953 self.setCpuid()
954 return rc
956 def getRequiredAvailableMemory(self, mem_kb):
957 return mem_kb + self.vramsize
959 def getRequiredInitialReservation(self):
960 return self.vm.getMemoryTarget()
962 def getRequiredMaximumReservation(self):
963 return self.vm.getMemoryMaximum()
965 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
966 # 256 pages (1MB) per vcpu,
967 # plus 1 page per MiB of RAM for the P2M map,
968 # plus 1 page per MiB of RAM to shadow the resident processes.
969 # This is higher than the minimum that Xen would allocate if no value
970 # were given (but the Xen minimum is for safety, not performance).
971 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
972 shadow_mem_kb)
975 class X86_Linux_ImageHandler(LinuxImageHandler):
977 def buildDomain(self):
978 # set physical mapping limit
979 # add an 8MB slack to balance backend allocations.
980 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
981 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
982 rc = LinuxImageHandler.buildDomain(self)
983 self.setCpuid()
984 return rc
986 _handlers = {
987 "ia64": {
988 "linux": IA64_Linux_ImageHandler,
989 "hvm": IA64_HVM_ImageHandler,
990 },
991 "x86": {
992 "linux": X86_Linux_ImageHandler,
993 "hvm": X86_HVM_ImageHandler,
994 },
995 }
997 def findImageHandlerClass(image):
998 """Find the image handler class for an image config.
1000 @param image config
1001 @return ImageHandler subclass or None
1002 """
1003 image_type = image.image_type()
1004 try:
1005 return _handlers[arch.type][image_type]
1006 except KeyError:
1007 raise VmError('unknown image type: ' + image_type)