ia64/xen-unstable

view tools/python/xen/xend/image.py @ 17933:763c32fdbd13

xend: open qemu-dm logfile in append mode if log rotation is disabled by config.

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jul 01 10:59:09 2008 +0100 (2008-07-01)
parents 20966aa89739
children 40e7329105fa
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
32 import xen.lowlevel.xc
33 from xen.xend.XendConstants import *
34 from xen.xend.XendError import VmError, XendError, HVMRequired
35 from xen.xend.XendLogging import log
36 from xen.xend.XendOptions import instance as xenopts
37 from xen.xend.xenstore.xstransact import xstransact
38 from xen.xend.xenstore.xswatch import xswatch
39 from xen.xend import arch
40 from xen.xend import XendOptions
41 from xen.util import oshelp
42 from xen.util import utils
44 xc = xen.lowlevel.xc.xc()
46 MAX_GUEST_CMDLINE = 1024
48 sentinel_path_prefix = '/var/run/xend/dm-'
49 sentinel_fifos_inuse = { }
51 def cleanup_stale_sentinel_fifos():
52 for path in glob.glob(sentinel_path_prefix + '*.fifo'):
53 if path in sentinel_fifos_inuse: continue
54 try: os.unlink(path)
55 except OSError, e:
56 log.warning('could not delete stale fifo %s: %s',
57 path, utils.exception_string(e))
59 def create(vm, vmConfig):
60 """Create an image handler for a vm.
62 @return ImageHandler instance
63 """
64 return findImageHandlerClass(vmConfig)(vm, vmConfig)
67 class ImageHandler:
68 """Abstract base class for image handlers.
70 createImage() is called to configure and build the domain from its
71 kernel image and ramdisk etc.
73 The method buildDomain() is used to build the domain, and must be
74 defined in a subclass. Usually this is the only method that needs
75 defining in a subclass.
77 The method createDeviceModel() is called to create the domain device
78 model.
80 The method destroyDeviceModel() is called to reap the device model
81 """
83 ostype = None
86 def __init__(self, vm, vmConfig):
87 self.vm = vm
89 self.bootloader = False
90 self.kernel = None
91 self.ramdisk = None
92 self.cmdline = None
94 self.configure(vmConfig)
96 def configure(self, vmConfig):
97 """Config actions common to all unix-like domains."""
98 if '_temp_using_bootloader' in vmConfig:
99 self.bootloader = True
100 self.kernel = vmConfig['_temp_kernel']
101 self.cmdline = vmConfig['_temp_args']
102 self.ramdisk = vmConfig['_temp_ramdisk']
103 else:
104 self.kernel = vmConfig['PV_kernel']
105 self.cmdline = vmConfig['PV_args']
106 self.ramdisk = vmConfig['PV_ramdisk']
107 self.vm.storeVm(("image/ostype", self.ostype),
108 ("image/kernel", self.kernel),
109 ("image/cmdline", self.cmdline),
110 ("image/ramdisk", self.ramdisk))
111 self.vm.permissionsVm("image/cmdline", { 'dom': self.vm.getDomid(), 'read': True } )
113 self.device_model = vmConfig['platform'].get('device_model')
115 self.display = vmConfig['platform'].get('display')
116 self.xauthority = vmConfig['platform'].get('xauthority')
117 self.vncconsole = vmConfig['platform'].get('vncconsole')
118 self.dmargs = self.parseDeviceModelArgs(vmConfig)
119 self.pid = None
120 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
121 if rtc_timeoffset is not None:
122 xc.domain_set_time_offset(self.vm.getDomid(), int(rtc_timeoffset))
124 self.cpuid = None
125 self.cpuid_check = None
126 if 'cpuid' in vmConfig:
127 self.cpuid = vmConfig['cpuid'];
128 if 'cpuid_check' in vmConfig:
129 self.cpuid_check = vmConfig['cpuid_check']
131 def cleanupBootloading(self):
132 if self.bootloader:
133 self.unlink(self.kernel)
134 self.unlink(self.ramdisk)
137 def unlink(self, f):
138 if not f: return
139 try:
140 os.unlink(f)
141 except OSError, ex:
142 log.warning("error removing bootloader file '%s': %s", f, ex)
145 def createImage(self):
146 """Entry point to create domain memory image.
147 Override in subclass if needed.
148 """
149 return self.createDomain()
152 def createDomain(self):
153 """Build the domain boot image.
154 """
155 # Set params and call buildDomain().
157 if self.kernel and not os.path.isfile(self.kernel):
158 raise VmError('Kernel image does not exist: %s' % self.kernel)
159 if self.ramdisk and not os.path.isfile(self.ramdisk):
160 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
161 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
162 log.warning('kernel cmdline too long, domain %d',
163 self.vm.getDomid())
165 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
166 self.vm.getDomid(), self.vm.getVCpuCount())
168 result = self.buildDomain()
170 if isinstance(result, dict):
171 return result
172 else:
173 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
174 % (self.ostype, self.vm.getDomid(), str(result)))
176 def getRequiredAvailableMemory(self, mem_kb):
177 """@param mem_kb The configured maxmem or memory, in KiB.
178 @return The corresponding required amount of memory for the domain,
179 also in KiB. This is normally the given mem_kb, but architecture- or
180 image-specific code may override this to add headroom where
181 necessary."""
182 return mem_kb
184 def getRequiredInitialReservation(self):
185 """@param mem_kb The configured memory, in KiB.
186 @return The corresponding required amount of memory to be free, also
187 in KiB. This is normally the same as getRequiredAvailableMemory, but
188 architecture- or image-specific code may override this to
189 add headroom where necessary."""
190 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
192 def getRequiredMaximumReservation(self):
193 """@param mem_kb The maximum possible memory, in KiB.
194 @return The corresponding required amount of memory to be free, also
195 in KiB. This is normally the same as getRequiredAvailableMemory, but
196 architecture- or image-specific code may override this to
197 add headroom where necessary."""
198 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
200 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
201 """@param shadow_mem_kb The configured shadow memory, in KiB.
202 @param maxmem_kb The configured maxmem, in KiB.
203 @return The corresponding required amount of shadow memory, also in
204 KiB."""
205 # PV domains don't need any shadow memory
206 return 0
208 def buildDomain(self):
209 """Build the domain. Define in subclass."""
210 raise NotImplementedError()
212 def prepareEnvironment(self):
213 """Prepare the environment for the execution of the domain. This
214 method is called before any devices are set up."""
216 domid = self.vm.getDomid()
218 # Delete left-over pipes
219 try:
220 os.unlink('/var/run/tap/qemu-read-%d' % domid)
221 os.unlink('/var/run/tap/qemu-write-%d' % domid)
222 except:
223 pass
225 # No device model, don't create pipes
226 if self.device_model is None:
227 return
229 # If we use a device model, the pipes for communication between
230 # blktapctrl and ioemu must be present before the devices are
231 # created (blktapctrl must access them for new block devices)
233 # mkdir throws an exception if the path already exists
234 try:
235 os.mkdir('/var/run/tap', 0755)
236 except:
237 pass
239 try:
240 os.mkfifo('/var/run/tap/qemu-read-%d' % domid, 0600)
241 os.mkfifo('/var/run/tap/qemu-write-%d' % domid, 0600)
242 except OSError, e:
243 log.warn('Could not create blktap pipes for domain %d' % domid)
244 log.exception(e)
245 pass
248 # Return a list of cmd line args to the device models based on the
249 # xm config file
250 def parseDeviceModelArgs(self, vmConfig):
251 ret = ["-domain-name", str(self.vm.info['name_label'])]
253 # Tell QEMU how large the guest's memory allocation is
254 # to help it when loading the initrd (if neccessary)
255 ret += ["-m", str(self.getRequiredInitialReservation() / 1024)]
257 # Find RFB console device, and if it exists, make QEMU enable
258 # the VNC console.
259 if int(vmConfig['platform'].get('nographic', 0)) != 0:
260 # skip vnc init if nographic is set
261 ret.append('-nographic')
262 return ret
264 vnc_config = {}
265 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
266 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
267 opengl = 1
268 for dev_uuid in vmConfig['console_refs']:
269 dev_type, dev_info = vmConfig['devices'][dev_uuid]
270 if dev_type == 'vfb':
271 vfb_type = dev_info.get('type', {})
272 if vfb_type == 'sdl':
273 self.display = dev_info.get('display', {})
274 self.xauthority = dev_info.get('xauthority', {})
275 opengl = int(dev_info.get('opengl', opengl))
276 has_sdl = True
277 else:
278 vnc_config = dev_info.get('other_config', {})
279 has_vnc = True
280 break
282 keymap = vmConfig['platform'].get("keymap")
283 if keymap:
284 ret.append("-k")
285 ret.append(keymap)
287 if has_vnc:
288 if not vnc_config:
289 for key in ('vncunused', 'vnclisten', 'vncdisplay',
290 'vncpasswd'):
291 if key in vmConfig['platform']:
292 vnc_config[key] = vmConfig['platform'][key]
293 if vnc_config.has_key("vncpasswd"):
294 passwd = vnc_config["vncpasswd"]
295 else:
296 passwd = XendOptions.instance().get_vncpasswd_default()
297 vncopts = ""
298 if passwd:
299 self.vm.storeVm("vncpasswd", passwd)
300 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
301 vncopts = vncopts + ",password"
302 log.debug("Stored a VNC password for vfb access")
303 else:
304 log.debug("No VNC passwd configured for vfb access")
306 if XendOptions.instance().get_vnc_tls():
307 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
308 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
310 if not os.path.exists(vncx509certdir):
311 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
313 if vncx509verify:
314 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
315 else:
316 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
319 vnclisten = vnc_config.get('vnclisten',
320 XendOptions.instance().get_vnclisten_address())
321 vncdisplay = int(vnc_config.get('vncdisplay', 0))
322 ret.append('-vnc')
323 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
325 if int(vnc_config.get('vncunused', 1)) != 0:
326 ret.append('-vncunused')
328 elif has_sdl:
329 # SDL is default in QEMU.
330 if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
331 ret.append('-disable-opengl')
332 else:
333 ret.append('-nographic')
335 if int(vmConfig['platform'].get('monitor', 0)) != 0:
336 ret = ret + ['-monitor', 'vc']
337 return ret
339 def getDeviceModelArgs(self, restore = False):
340 args = [self.device_model]
341 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
342 args = args + self.dmargs
343 return args
345 def _openSentinel(self, sentinel_path_fifo):
346 self.sentinel_fifo = file(sentinel_path_fifo, 'r')
347 self.sentinel_lock = thread.allocate_lock()
348 oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True)
349 sentinel_fifos_inuse[sentinel_path_fifo] = 1
350 self.sentinel_path_fifo = sentinel_path_fifo
352 def createDeviceModel(self, restore = False):
353 if self.device_model is None:
354 return
355 if self.pid:
356 return
357 # Execute device model.
358 #todo: Error handling
359 args = self.getDeviceModelArgs(restore)
360 env = dict(os.environ)
361 if self.display:
362 env['DISPLAY'] = self.display
363 if self.xauthority:
364 env['XAUTHORITY'] = self.xauthority
365 if self.vncconsole:
366 args = args + ([ "-vncviewer" ])
367 unique_id = "%i-%i" % (self.vm.getDomid(), time.time())
368 sentinel_path = sentinel_path_prefix + unique_id
369 sentinel_path_fifo = sentinel_path + '.fifo'
370 os.mkfifo(sentinel_path_fifo, 0600)
371 sentinel_write = file(sentinel_path_fifo, 'r+')
372 self._openSentinel(sentinel_path_fifo)
373 self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo)
374 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
375 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
376 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
377 log.info("spawning device models: %s %s", self.device_model, args)
378 # keep track of pid and spawned options to kill it later
380 self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label'])
382 # rotate log
383 logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND
384 logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count()
385 if logrotate_count > 0:
386 logfile_mode |= os.O_TRUNC
387 if os.path.exists("%s.%d" % (self.logfile, logrotate_count)):
388 os.unlink("%s.%d" % (self.logfile, logrotate_count))
389 for n in range(logrotate_count - 1, 0, -1):
390 if os.path.exists("%s.%d" % (self.logfile, n)):
391 os.rename("%s.%d" % (self.logfile, n),
392 "%s.%d" % (self.logfile, (n + 1)))
393 if os.path.exists(self.logfile):
394 os.rename(self.logfile, self.logfile + ".1")
396 null = os.open("/dev/null", os.O_RDONLY)
397 logfd = os.open(self.logfile, logfile_mode)
399 sys.stderr.flush()
400 pid = os.fork()
401 if pid == 0: #child
402 try:
403 os.dup2(null, 0)
404 os.dup2(logfd, 1)
405 os.dup2(logfd, 2)
406 os.close(null)
407 os.close(logfd)
408 self.sentinel_fifo.close()
409 try:
410 os.execve(self.device_model, args, env)
411 except Exception, e:
412 print >>sys.stderr, (
413 'failed to set up fds or execute dm %s: %s' %
414 (self.device_model, utils.exception_string(e)))
415 os._exit(126)
416 except:
417 os._exit(127)
418 else:
419 self.pid = pid
420 os.close(null)
421 os.close(logfd)
422 sentinel_write.close()
423 self.vm.storeDom("image/device-model-pid", self.pid)
424 log.info("device model pid: %d", self.pid)
425 # we would very much prefer not to have a thread here and instead
426 # have a callback but sadly we don't have Twisted in xend
427 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
429 def signalDeviceModel(self, cmd, ret, par = None):
430 if self.device_model is None:
431 return
432 # Signal the device model to for action
433 if cmd is '' or ret is '':
434 raise VmError('need valid command and result when signal device model')
436 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
437 % self.vm.getDomid())
439 if par is not None:
440 xstransact.Store("/local/domain/0/device-model/%i"
441 % self.vm.getDomid(), ('parameter', par))
443 xstransact.Store("/local/domain/0/device-model/%i"
444 % self.vm.getDomid(), ('command', cmd))
445 # Wait for confirmation. Could do this with a watch but we'd
446 # still end up spinning here waiting for the watch to fire.
447 state = ''
448 count = 0
449 while state != ret:
450 state = xstransact.Read("/local/domain/0/device-model/%i/state"
451 % self.vm.getDomid())
452 time.sleep(0.1)
453 count += 1
454 if count > 100:
455 raise VmError('Timed out waiting for device model action')
457 #resotre orig state
458 xstransact.Store("/local/domain/0/device-model/%i"
459 % self.vm.getDomid(), ('state', orig_state))
460 log.info("signalDeviceModel:restore dm state to %s", orig_state)
462 def saveDeviceModel(self):
463 # Signal the device model to pause itself and save its state
464 self.signalDeviceModel('save', 'paused')
466 def resumeDeviceModel(self):
467 if self.device_model is None:
468 return
469 # Signal the device model to resume activity after pausing to save.
470 xstransact.Store("/local/domain/0/device-model/%i"
471 % self.vm.getDomid(), ('command', 'continue'))
473 def _dmfailed(self, message):
474 log.warning("domain %s: %s", self.vm.getName(), message)
475 # ideally we would like to forcibly crash the domain with
476 # something like
477 # xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
478 # but this can easily lead to very rapid restart loops against
479 # which we currently have no protection
481 def recreate(self):
482 if self.device_model is None:
483 return
484 name = self.vm.getName()
485 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
486 fifo_fd = -1
487 log.debug("rediscovering %s", sentinel_path_fifo)
488 if sentinel_path_fifo is None:
489 log.debug("%s device model no sentinel, cannot rediscover", name)
490 else:
491 try:
492 # We open it O_WRONLY because that fails ENXIO if no-one
493 # has it open for reading (see SuSv3). The dm process got
494 # a read/write descriptor from our earlier invocation.
495 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
496 except OSError, e:
497 if e.errno == errno.ENXIO:
498 self._dmfailed("%s device model no longer running"%name)
499 elif e.errno == errno.ENOENT:
500 log.debug("%s device model sentinel %s absent!",
501 name, sentinel_path_fifo)
502 else:
503 raise
504 if fifo_fd >= 0:
505 self._openSentinel(sentinel_path_fifo)
506 os.close(fifo_fd)
507 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
508 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
509 name, self.pid, sentinel_path_fifo)
510 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
512 def _sentinel_watch(self):
513 log.info("waiting for sentinel_fifo")
514 try: self.sentinel_fifo.read(1)
515 except OSError, e: pass
516 self.sentinel_lock.acquire()
517 try:
518 if self.pid:
519 (p,st) = os.waitpid(self.pid, os.WNOHANG)
520 if p == self.pid:
521 message = oshelp.waitstatus_description(st)
522 else:
523 # obviously it is malfunctioning, kill it now
524 try:
525 os.kill(self.pid, signal.SIGKILL)
526 message = "malfunctioning (closed sentinel), killed"
527 except:
528 message = "malfunctioning or died ?"
529 message = "pid %d: %s" % (self.pid, message)
530 else:
531 message = "no longer running"
532 except Exception, e:
533 message = "waitpid failed: %s" % utils.exception_string(e)
534 message = "device model failure: %s" % message
535 try: message += "; see %s " % self.logfile
536 except: pass
537 self._dmfailed(message)
538 self.pid = None
539 self.sentinel_lock.release()
541 def destroyDeviceModel(self):
542 if self.device_model is None:
543 return
544 if self.pid:
545 self.sentinel_lock.acquire()
546 try:
547 try:
548 os.kill(self.pid, signal.SIGHUP)
549 except OSError, exn:
550 log.exception(exn)
551 try:
552 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
553 for i in xrange(100):
554 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
555 if p == self.pid:
556 break
557 time.sleep(0.1)
558 else:
559 log.warning("DeviceModel %d took more than 10s "
560 "to terminate: sending SIGKILL" % self.pid)
561 os.kill(self.pid, signal.SIGKILL)
562 os.waitpid(self.pid, 0)
563 except OSError, exn:
564 # This is expected if Xend has been restarted within the
565 # life of this domain. In this case, we can kill the process,
566 # but we can't wait for it because it's not our child.
567 # We just make really sure it's going away (SIGKILL) first.
568 os.kill(self.pid, signal.SIGKILL)
569 state = xstransact.Remove("/local/domain/0/device-model/%i"
570 % self.vm.getDomid())
571 finally:
572 self.pid = None
573 self.sentinel_lock.release()
575 try:
576 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
577 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
578 except:
579 pass
580 try:
581 del sentinel_fifos_inuse[self.sentinel_path_fifo]
582 os.unlink(self.sentinel_path_fifo)
583 except:
584 pass
586 def setCpuid(self):
587 xc.domain_set_policy_cpuid(self.vm.getDomid())
589 if self.cpuid is not None:
590 cpuid = self.cpuid
591 transformed = {}
592 for sinput, regs in cpuid.iteritems():
593 inputs = sinput.split(',')
594 input = long(inputs[0])
595 sub_input = None
596 if len(inputs) == 2:
597 sub_input = long(inputs[1])
598 t = xc.domain_set_cpuid(self.vm.getDomid(),
599 input, sub_input, regs)
600 transformed[sinput] = t
601 self.cpuid = transformed
603 if self.cpuid_check is not None:
604 cpuid_check = self.cpuid_check
605 transformed = {}
606 for sinput, regs_check in cpuid_check.iteritems():
607 inputs = sinput.split(',')
608 input = long(inputs[0])
609 sub_input = None
610 if len(inputs) == 2:
611 sub_input = long(inputs[1])
612 t = xc.domain_check_cpuid(input, sub_input, regs_check)
613 transformed[sinput] = t
614 self.cpuid_check = transformed
618 class LinuxImageHandler(ImageHandler):
620 ostype = "linux"
621 flags = 0
622 vhpt = 0
624 def configure(self, vmConfig):
625 ImageHandler.configure(self, vmConfig)
627 def buildDomain(self):
628 store_evtchn = self.vm.getStorePort()
629 console_evtchn = self.vm.getConsolePort()
631 mem_mb = self.getRequiredInitialReservation() / 1024
633 log.debug("domid = %d", self.vm.getDomid())
634 log.debug("memsize = %d", mem_mb)
635 log.debug("image = %s", self.kernel)
636 log.debug("store_evtchn = %d", store_evtchn)
637 log.debug("console_evtchn = %d", console_evtchn)
638 log.debug("cmdline = %s", self.cmdline)
639 log.debug("ramdisk = %s", self.ramdisk)
640 log.debug("vcpus = %d", self.vm.getVCpuCount())
641 log.debug("features = %s", self.vm.getFeatures())
642 if arch.type == "ia64":
643 log.debug("vhpt = %d", self.flags)
645 return xc.linux_build(domid = self.vm.getDomid(),
646 memsize = mem_mb,
647 image = self.kernel,
648 store_evtchn = store_evtchn,
649 console_evtchn = console_evtchn,
650 cmdline = self.cmdline,
651 ramdisk = self.ramdisk,
652 features = self.vm.getFeatures(),
653 flags = self.flags,
654 vhpt = self.vhpt)
656 def parseDeviceModelArgs(self, vmConfig):
657 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
658 # Equivalent to old xenconsoled behaviour. Should make
659 # it configurable in future
660 ret = ret + ["-serial", "pty"]
661 return ret
663 def getDeviceModelArgs(self, restore = False):
664 args = ImageHandler.getDeviceModelArgs(self, restore)
665 args = args + ([ "-M", "xenpv"])
666 return args
669 class HVMImageHandler(ImageHandler):
671 ostype = "hvm"
673 def __init__(self, vm, vmConfig):
674 ImageHandler.__init__(self, vm, vmConfig)
675 self.shutdownWatch = None
676 self.rebootFeatureWatch = None
678 def configure(self, vmConfig):
679 ImageHandler.configure(self, vmConfig)
681 self.loader = vmConfig['platform'].get('loader')
683 info = xc.xeninfo()
684 if 'hvm' not in info['xen_caps']:
685 raise HVMRequired()
687 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
689 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
690 ("image/device-model", self.device_model),
691 ("image/display", self.display))
692 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
693 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
694 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
696 self.apic = int(vmConfig['platform'].get('apic', 0))
697 self.acpi = int(vmConfig['platform'].get('acpi', 0))
698 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
701 # Return a list of cmd line args to the device models based on the
702 # xm config file
703 def parseDeviceModelArgs(self, vmConfig):
704 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
705 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
707 if self.kernel:
708 log.debug("kernel = %s", self.kernel)
709 ret = ret + ['-kernel', self.kernel]
710 if self.ramdisk:
711 log.debug("ramdisk = %s", self.ramdisk)
712 ret = ret + ['-initrd', self.ramdisk]
713 if self.cmdline:
714 log.debug("cmdline = %s", self.cmdline)
715 ret = ret + ['-append', self.cmdline]
718 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
719 'localtime', 'serial', 'stdvga', 'isa',
720 'acpi', 'usb', 'usbdevice' ]
722 for a in dmargs:
723 v = vmConfig['platform'].get(a)
725 # python doesn't allow '-' in variable names
726 if a == 'stdvga': a = 'std-vga'
727 if a == 'keymap': a = 'k'
729 # Handle booleans gracefully
730 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
731 try:
732 if v != None: v = int(v)
733 if v: ret.append("-%s" % a)
734 except (ValueError, TypeError):
735 pass # if we can't convert it to a sane type, ignore it
736 else:
737 if v:
738 ret.append("-%s" % a)
739 ret.append("%s" % v)
741 if a in ['fda', 'fdb']:
742 if v:
743 if not os.path.isabs(v):
744 raise VmError("Floppy file %s does not exist." % v)
745 log.debug("args: %s, val: %s" % (a,v))
747 # Handle disk/network related options
748 mac = None
749 nics = 0
751 for devuuid in vmConfig['vbd_refs']:
752 devinfo = vmConfig['devices'][devuuid][1]
753 uname = devinfo.get('uname')
754 if uname is not None and 'file:' in uname:
755 (_, vbdparam) = string.split(uname, ':', 1)
756 if not os.path.isfile(vbdparam):
757 raise VmError('Disk image does not exist: %s' %
758 vbdparam)
760 for devuuid in vmConfig['vif_refs']:
761 devinfo = vmConfig['devices'][devuuid][1]
762 dtype = devinfo.get('type', 'ioemu')
763 if dtype != 'ioemu':
764 continue
765 nics += 1
766 mac = devinfo.get('mac')
767 if mac is None:
768 raise VmError("MAC address not specified or generated.")
769 bridge = devinfo.get('bridge', 'xenbr0')
770 model = devinfo.get('model', 'rtl8139')
771 ret.append("-net")
772 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
773 (nics, mac, model))
774 ret.append("-net")
775 ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" %
776 (nics, self.vm.getDomid(), nics-1, bridge))
778 return ret
780 def getDeviceModelArgs(self, restore = False):
781 args = ImageHandler.getDeviceModelArgs(self, restore)
782 args = args + ([ "-M", "xenfv"])
783 if restore:
784 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
785 self.vm.getDomid() ])
786 return args
788 def buildDomain(self):
789 store_evtchn = self.vm.getStorePort()
791 mem_mb = self.getRequiredInitialReservation() / 1024
793 log.debug("domid = %d", self.vm.getDomid())
794 log.debug("image = %s", self.loader)
795 log.debug("store_evtchn = %d", store_evtchn)
796 log.debug("memsize = %d", mem_mb)
797 log.debug("vcpus = %d", self.vm.getVCpuCount())
798 log.debug("acpi = %d", self.acpi)
799 log.debug("apic = %d", self.apic)
801 rc = xc.hvm_build(domid = self.vm.getDomid(),
802 image = self.loader,
803 memsize = mem_mb,
804 vcpus = self.vm.getVCpuCount(),
805 acpi = self.acpi,
806 apic = self.apic)
807 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
809 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
810 HVM_PARAM_STORE_PFN)
811 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
812 store_evtchn)
814 return rc
817 class IA64_HVM_ImageHandler(HVMImageHandler):
819 def configure(self, vmConfig):
820 HVMImageHandler.configure(self, vmConfig)
821 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
823 def buildDomain(self):
824 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
825 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
826 if self.guest_os_type is not None:
827 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
828 return HVMImageHandler.buildDomain(self)
830 def getRequiredAvailableMemory(self, mem_kb):
831 page_kb = 16
832 # ROM size for guest firmware, io page, xenstore page
833 # buffer io page, buffer pio page and memmap info page
834 extra_pages = 1024 + 5
835 mem_kb += extra_pages * page_kb
836 # Add 8 MiB overhead for QEMU's video RAM.
837 return mem_kb + 8192
839 def getRequiredInitialReservation(self):
840 return self.vm.getMemoryTarget()
842 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
843 # Explicit shadow memory is not a concept
844 return 0
846 def getDeviceModelArgs(self, restore = False):
847 args = HVMImageHandler.getDeviceModelArgs(self, restore)
848 args = args + ([ "-m", "%s" %
849 (self.getRequiredInitialReservation() / 1024) ])
850 return args
852 def setCpuid(self):
853 # Guest CPUID configuration is not implemented yet.
854 return
856 class IA64_Linux_ImageHandler(LinuxImageHandler):
858 def configure(self, vmConfig):
859 LinuxImageHandler.configure(self, vmConfig)
860 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
862 def setCpuid(self):
863 # Guest CPUID configuration is not implemented yet.
864 return
866 class X86_HVM_ImageHandler(HVMImageHandler):
868 def configure(self, vmConfig):
869 HVMImageHandler.configure(self, vmConfig)
870 self.pae = int(vmConfig['platform'].get('pae', 0))
872 def buildDomain(self):
873 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
874 rc = HVMImageHandler.buildDomain(self)
875 self.setCpuid()
876 return rc
878 def getRequiredAvailableMemory(self, mem_kb):
879 # Add 8 MiB overhead for QEMU's video RAM.
880 return mem_kb + 8192
882 def getRequiredInitialReservation(self):
883 return self.vm.getMemoryTarget()
885 def getRequiredMaximumReservation(self):
886 return self.vm.getMemoryMaximum()
888 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
889 # 256 pages (1MB) per vcpu,
890 # plus 1 page per MiB of RAM for the P2M map,
891 # plus 1 page per MiB of RAM to shadow the resident processes.
892 # This is higher than the minimum that Xen would allocate if no value
893 # were given (but the Xen minimum is for safety, not performance).
894 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
895 shadow_mem_kb)
898 class X86_Linux_ImageHandler(LinuxImageHandler):
900 def buildDomain(self):
901 # set physical mapping limit
902 # add an 8MB slack to balance backend allocations.
903 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
904 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
905 rc = LinuxImageHandler.buildDomain(self)
906 self.setCpuid()
907 return rc
909 _handlers = {
910 "ia64": {
911 "linux": IA64_Linux_ImageHandler,
912 "hvm": IA64_HVM_ImageHandler,
913 },
914 "x86": {
915 "linux": X86_Linux_ImageHandler,
916 "hvm": X86_HVM_ImageHandler,
917 },
918 }
920 def findImageHandlerClass(image):
921 """Find the image handler class for an image config.
923 @param image config
924 @return ImageHandler subclass or None
925 """
926 image_type = image.image_type()
927 try:
928 return _handlers[arch.type][image_type]
929 except KeyError:
930 raise VmError('unknown image type: ' + image_type)