ia64/xen-unstable

view tools/python/xen/xend/image.py @ 19333:e2de0e760a0d

Fix qemu spawn for Solaris

On Solaris, xend runs in a 'process contract' such that all children
are killed when the service is restarted. Spawn qemu processes in a
new contract to avoid this.

Signed-off-by: John Levon <john.levon@sun.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Mar 12 11:07:00 2009 +0000 (2009-03-12)
parents 0942baa2a088
children 1ad8216abda5
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
43 from xen.xend import osdep
45 xc = xen.lowlevel.xc.xc()
47 MAX_GUEST_CMDLINE = 1024
49 sentinel_path_prefix = '/var/run/xend/dm-'
50 sentinel_fifos_inuse = { }
52 def cleanup_stale_sentinel_fifos():
53 for path in glob.glob(sentinel_path_prefix + '*.fifo'):
54 if path in sentinel_fifos_inuse: continue
55 try: os.unlink(path)
56 except OSError, e:
57 log.warning('could not delete stale fifo %s: %s',
58 path, utils.exception_string(e))
60 def create(vm, vmConfig):
61 """Create an image handler for a vm.
63 @return ImageHandler instance
64 """
65 return findImageHandlerClass(vmConfig)(vm, vmConfig)
68 class ImageHandler:
69 """Abstract base class for image handlers.
71 createImage() is called to configure and build the domain from its
72 kernel image and ramdisk etc.
74 The method buildDomain() is used to build the domain, and must be
75 defined in a subclass. Usually this is the only method that needs
76 defining in a subclass.
78 The method createDeviceModel() is called to create the domain device
79 model.
81 The method destroyDeviceModel() is called to reap the device model
82 """
84 ostype = None
87 def __init__(self, vm, vmConfig):
88 self.vm = vm
90 self.bootloader = False
91 self.kernel = None
92 self.ramdisk = None
93 self.cmdline = None
95 self.configure(vmConfig)
97 def configure(self, vmConfig):
98 """Config actions common to all unix-like domains."""
99 if '_temp_using_bootloader' in vmConfig:
100 self.bootloader = True
101 self.kernel = vmConfig['_temp_kernel']
102 self.cmdline = vmConfig['_temp_args']
103 self.ramdisk = vmConfig['_temp_ramdisk']
104 else:
105 self.kernel = vmConfig['PV_kernel']
106 self.cmdline = vmConfig['PV_args']
107 self.ramdisk = vmConfig['PV_ramdisk']
108 self.vm.storeVm(("image/ostype", self.ostype),
109 ("image/kernel", self.kernel),
110 ("image/cmdline", self.cmdline),
111 ("image/ramdisk", self.ramdisk))
112 self.vm.permissionsVm("image/cmdline", { 'dom': self.vm.getDomid(), 'read': True } )
114 self.device_model = vmConfig['platform'].get('device_model')
116 self.display = vmConfig['platform'].get('display')
117 self.xauthority = vmConfig['platform'].get('xauthority')
118 self.vncconsole = int(vmConfig['platform'].get('vncconsole', 0))
119 self.dmargs = self.parseDeviceModelArgs(vmConfig)
120 self.pid = None
121 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
122 if rtc_timeoffset is not None:
123 xc.domain_set_time_offset(self.vm.getDomid(), int(rtc_timeoffset))
125 self.cpuid = None
126 self.cpuid_check = None
127 if 'cpuid' in vmConfig:
128 self.cpuid = vmConfig['cpuid'];
129 if 'cpuid_check' in vmConfig:
130 self.cpuid_check = vmConfig['cpuid_check']
132 def cleanupBootloading(self):
133 if self.bootloader:
134 self.unlink(self.kernel)
135 self.unlink(self.ramdisk)
138 def unlink(self, f):
139 if not f: return
140 try:
141 os.unlink(f)
142 except OSError, ex:
143 log.warning("error removing bootloader file '%s': %s", f, ex)
146 def createImage(self):
147 """Entry point to create domain memory image.
148 Override in subclass if needed.
149 """
150 return self.createDomain()
153 def createDomain(self):
154 """Build the domain boot image.
155 """
156 # Set params and call buildDomain().
158 if self.kernel and not os.path.isfile(self.kernel):
159 raise VmError('Kernel image does not exist: %s' % self.kernel)
160 if self.ramdisk and not os.path.isfile(self.ramdisk):
161 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
162 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
163 log.warning('kernel cmdline too long, domain %d',
164 self.vm.getDomid())
166 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
167 self.vm.getDomid(), self.vm.getVCpuCount())
169 result = self.buildDomain()
171 if isinstance(result, dict):
172 return result
173 else:
174 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
175 % (self.ostype, self.vm.getDomid(), str(result)))
177 def getRequiredAvailableMemory(self, mem_kb):
178 """@param mem_kb The configured maxmem or memory, in KiB.
179 @return The corresponding required amount of memory for the domain,
180 also in KiB. This is normally the given mem_kb, but architecture- or
181 image-specific code may override this to add headroom where
182 necessary."""
183 return mem_kb
185 def getRequiredInitialReservation(self):
186 """@param mem_kb The configured memory, in KiB.
187 @return The corresponding required amount of memory to be free, also
188 in KiB. This is normally the same as getRequiredAvailableMemory, but
189 architecture- or image-specific code may override this to
190 add headroom where necessary."""
191 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
193 def getRequiredMaximumReservation(self):
194 """@param mem_kb The maximum possible memory, in KiB.
195 @return The corresponding required amount of memory to be free, also
196 in KiB. This is normally the same as getRequiredAvailableMemory, but
197 architecture- or image-specific code may override this to
198 add headroom where necessary."""
199 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
201 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
202 """@param shadow_mem_kb The configured shadow memory, in KiB.
203 @param maxmem_kb The configured maxmem, in KiB.
204 @return The corresponding required amount of shadow memory, also in
205 KiB."""
206 # PV domains don't need any shadow memory
207 return 0
209 def buildDomain(self):
210 """Build the domain. Define in subclass."""
211 raise NotImplementedError()
213 def prepareEnvironment(self):
214 """Prepare the environment for the execution of the domain. This
215 method is called before any devices are set up."""
217 domid = self.vm.getDomid()
219 # Delete left-over pipes
220 try:
221 os.unlink('/var/run/tap/qemu-read-%d' % domid)
222 os.unlink('/var/run/tap/qemu-write-%d' % domid)
223 except:
224 pass
226 # No device model, don't create pipes
227 if self.device_model is None:
228 return
230 # If we use a device model, the pipes for communication between
231 # blktapctrl and ioemu must be present before the devices are
232 # created (blktapctrl must access them for new block devices)
234 # mkdir throws an exception if the path already exists
235 try:
236 os.mkdir('/var/run/tap', 0755)
237 except:
238 pass
240 try:
241 os.mkfifo('/var/run/tap/qemu-read-%d' % domid, 0600)
242 os.mkfifo('/var/run/tap/qemu-write-%d' % domid, 0600)
243 except OSError, e:
244 log.warn('Could not create blktap pipes for domain %d' % domid)
245 log.exception(e)
246 pass
249 # Return a list of cmd line args to the device models based on the
250 # xm config file
251 def parseDeviceModelArgs(self, vmConfig):
252 ret = ["-domain-name", str(self.vm.info['name_label'])]
254 xen_extended_power_mgmt = int(vmConfig['platform'].get(
255 'xen_extended_power_mgmt', 0))
256 if xen_extended_power_mgmt != 0:
257 xstransact.Store("/local/domain/0/device-model/%i"
258 % self.vm.getDomid(),
259 ('xen_extended_power_mgmt',
260 xen_extended_power_mgmt))
262 # Find RFB console device, and if it exists, make QEMU enable
263 # the VNC console.
264 if int(vmConfig['platform'].get('nographic', 0)) != 0:
265 # skip vnc init if nographic is set
266 ret.append('-nographic')
267 return ret
269 vram = str(vmConfig['platform'].get('videoram',4))
270 ret.append('-videoram')
271 ret.append(vram)
273 vnc_config = {}
274 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
275 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
276 opengl = 1
277 keymap = vmConfig['platform'].get("keymap")
278 for dev_uuid in vmConfig['console_refs']:
279 dev_type, dev_info = vmConfig['devices'][dev_uuid]
280 if dev_type == 'vfb':
281 if 'keymap' in dev_info:
282 keymap = dev_info.get('keymap',{})
283 if int(dev_info.get('vnc', 0)) != 0 :
284 has_vnc = True
285 if int(dev_info.get('sdl', 0)) != 0 :
286 has_sdl = True
287 if has_sdl:
288 self.display = dev_info.get('display', {})
289 self.xauthority = dev_info.get('xauthority', {})
290 opengl = int(dev_info.get('opengl', opengl))
291 if has_vnc:
292 vnc_config = dev_info.get('other_config', {})
293 break
295 if keymap:
296 ret.append("-k")
297 ret.append(keymap)
299 if has_vnc:
300 if not vnc_config:
301 for key in ('vncunused', 'vnclisten', 'vncdisplay',
302 'vncpasswd'):
303 if key in vmConfig['platform']:
304 vnc_config[key] = vmConfig['platform'][key]
305 if vnc_config.has_key("vncpasswd"):
306 passwd = vnc_config["vncpasswd"]
307 else:
308 passwd = XendOptions.instance().get_vncpasswd_default()
309 vncopts = ""
310 if passwd:
311 self.vm.storeVm("vncpasswd", passwd)
312 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
313 vncopts = vncopts + ",password"
314 log.debug("Stored a VNC password for vfb access")
315 else:
316 log.debug("No VNC passwd configured for vfb access")
318 if XendOptions.instance().get_vnc_tls():
319 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
320 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
322 if not os.path.exists(vncx509certdir):
323 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
325 if vncx509verify:
326 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
327 else:
328 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
331 vnclisten = vnc_config.get('vnclisten',
332 XendOptions.instance().get_vnclisten_address())
333 vncdisplay = int(vnc_config.get('vncdisplay', 0))
334 ret.append('-vnc')
335 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
337 if int(vnc_config.get('vncunused', 1)) != 0:
338 ret.append('-vncunused')
340 if has_sdl:
341 ret.append('-sdl')
342 if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
343 ret.append('-disable-opengl')
345 if not has_sdl and not has_vnc :
346 ret.append('-nographic')
348 if int(vmConfig['platform'].get('monitor', 0)) != 0:
349 ret = ret + ['-monitor', 'vc']
350 return ret
352 def getDeviceModelArgs(self, restore = False):
353 args = [self.device_model]
354 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
355 args = args + self.dmargs
356 return args
358 def _openSentinel(self, sentinel_path_fifo):
359 self.sentinel_fifo = file(sentinel_path_fifo, 'r')
360 self.sentinel_lock = thread.allocate_lock()
361 oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True)
362 sentinel_fifos_inuse[sentinel_path_fifo] = 1
363 self.sentinel_path_fifo = sentinel_path_fifo
365 def createDeviceModel(self, restore = False):
366 if self.device_model is None:
367 return
368 if self.pid:
369 return
370 # Execute device model.
371 #todo: Error handling
372 args = self.getDeviceModelArgs(restore)
373 env = dict(os.environ)
374 if self.display:
375 env['DISPLAY'] = self.display
376 if self.xauthority:
377 env['XAUTHORITY'] = self.xauthority
378 unique_id = "%i-%i" % (self.vm.getDomid(), time.time())
379 sentinel_path = sentinel_path_prefix + unique_id
380 sentinel_path_fifo = sentinel_path + '.fifo'
381 os.mkfifo(sentinel_path_fifo, 0600)
382 sentinel_write = file(sentinel_path_fifo, 'r+')
383 self._openSentinel(sentinel_path_fifo)
384 self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo)
385 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
386 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
387 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
388 log.info("spawning device models: %s %s", self.device_model, args)
389 # keep track of pid and spawned options to kill it later
391 self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label'])
393 # rotate log
394 logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND
395 logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count()
396 if logrotate_count > 0:
397 logfile_mode |= os.O_TRUNC
398 if os.path.exists("%s.%d" % (self.logfile, logrotate_count)):
399 os.unlink("%s.%d" % (self.logfile, logrotate_count))
400 for n in range(logrotate_count - 1, 0, -1):
401 if os.path.exists("%s.%d" % (self.logfile, n)):
402 os.rename("%s.%d" % (self.logfile, n),
403 "%s.%d" % (self.logfile, (n + 1)))
404 if os.path.exists(self.logfile):
405 os.rename(self.logfile, self.logfile + ".1")
407 null = os.open("/dev/null", os.O_RDONLY)
408 logfd = os.open(self.logfile, logfile_mode)
410 sys.stderr.flush()
411 contract = osdep.prefork("%s:%d" %
412 (self.vm.getName(), self.vm.getDomid()))
413 pid = os.fork()
414 if pid == 0: #child
415 try:
416 osdep.postfork(contract)
417 os.dup2(null, 0)
418 os.dup2(logfd, 1)
419 os.dup2(logfd, 2)
420 os.close(null)
421 os.close(logfd)
422 self.sentinel_fifo.close()
423 try:
424 os.execve(self.device_model, args, env)
425 except Exception, e:
426 print >>sys.stderr, (
427 'failed to set up fds or execute dm %s: %s' %
428 (self.device_model, utils.exception_string(e)))
429 os._exit(126)
430 except:
431 os._exit(127)
432 else:
433 osdep.postfork(contract, abandon=True)
434 self.pid = pid
435 os.close(null)
436 os.close(logfd)
437 sentinel_write.close()
438 self.vm.storeDom("image/device-model-pid", self.pid)
439 log.info("device model pid: %d", self.pid)
440 # we would very much prefer not to have a thread here and instead
441 # have a callback but sadly we don't have Twisted in xend
442 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
444 def signalDeviceModel(self, cmd, ret, par = None):
445 if self.device_model is None:
446 return
447 # Signal the device model to for action
448 if cmd is '' or ret is '':
449 raise VmError('need valid command and result when signal device model')
451 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
452 % self.vm.getDomid())
454 if par is not None:
455 xstransact.Store("/local/domain/0/device-model/%i"
456 % self.vm.getDomid(), ('parameter', par))
458 xstransact.Store("/local/domain/0/device-model/%i"
459 % self.vm.getDomid(), ('command', cmd))
460 # Wait for confirmation. Could do this with a watch but we'd
461 # still end up spinning here waiting for the watch to fire.
462 state = ''
463 count = 0
464 while state != ret:
465 state = xstransact.Read("/local/domain/0/device-model/%i/state"
466 % self.vm.getDomid())
467 time.sleep(0.1)
468 count += 1
469 if count > 100:
470 raise VmError('Timed out waiting for device model action')
472 #resotre orig state
473 xstransact.Store("/local/domain/0/device-model/%i"
474 % self.vm.getDomid(), ('state', orig_state))
475 log.info("signalDeviceModel:restore dm state to %s", orig_state)
477 def saveDeviceModel(self):
478 # Signal the device model to pause itself and save its state
479 self.signalDeviceModel('save', 'paused')
481 def resumeDeviceModel(self):
482 if self.device_model is None:
483 return
484 # Signal the device model to resume activity after pausing to save.
485 xstransact.Store("/local/domain/0/device-model/%i"
486 % self.vm.getDomid(), ('command', 'continue'))
488 def _dmfailed(self, message):
489 log.warning("domain %s: %s", self.vm.getName(), message)
490 # ideally we would like to forcibly crash the domain with
491 # something like
492 # xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
493 # but this can easily lead to very rapid restart loops against
494 # which we currently have no protection
496 def recreate(self):
497 if self.device_model is None:
498 return
499 name = self.vm.getName()
500 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
501 fifo_fd = -1
502 log.debug("rediscovering %s", sentinel_path_fifo)
503 if sentinel_path_fifo is None:
504 log.debug("%s device model no sentinel, cannot rediscover", name)
505 else:
506 try:
507 # We open it O_WRONLY because that fails ENXIO if no-one
508 # has it open for reading (see SuSv3). The dm process got
509 # a read/write descriptor from our earlier invocation.
510 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
511 except OSError, e:
512 if e.errno == errno.ENXIO:
513 self._dmfailed("%s device model no longer running"%name)
514 elif e.errno == errno.ENOENT:
515 log.debug("%s device model sentinel %s absent!",
516 name, sentinel_path_fifo)
517 else:
518 raise
519 if fifo_fd >= 0:
520 self._openSentinel(sentinel_path_fifo)
521 os.close(fifo_fd)
522 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
523 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
524 name, self.pid, sentinel_path_fifo)
525 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
527 def _sentinel_watch(self):
528 log.info("waiting for sentinel_fifo")
529 try: self.sentinel_fifo.read(1)
530 except OSError, e: pass
531 self.sentinel_lock.acquire()
532 try:
533 if self.pid:
534 (p,st) = os.waitpid(self.pid, os.WNOHANG)
535 if p == self.pid:
536 message = oshelp.waitstatus_description(st)
537 else:
538 # obviously it is malfunctioning, kill it now
539 try:
540 os.kill(self.pid, signal.SIGKILL)
541 message = "malfunctioning (closed sentinel), killed"
542 except:
543 message = "malfunctioning or died ?"
544 message = "pid %d: %s" % (self.pid, message)
545 else:
546 message = "no longer running"
547 except Exception, e:
548 message = "waitpid failed: %s" % utils.exception_string(e)
549 message = "device model failure: %s" % message
550 try: message += "; see %s " % self.logfile
551 except: pass
552 self._dmfailed(message)
553 self.pid = None
554 self.sentinel_lock.release()
556 def destroyDeviceModel(self):
557 if self.device_model is None:
558 return
559 if self.pid:
560 self.sentinel_lock.acquire()
561 try:
562 try:
563 os.kill(self.pid, signal.SIGHUP)
564 except OSError, exn:
565 log.exception(exn)
566 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
567 for i in xrange(100):
568 try:
569 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
570 if p == self.pid:
571 break
572 except OSError:
573 # This is expected if Xend has been restarted within
574 # the life of this domain. In this case, we can kill
575 # the process, but we can't wait for it because it's
576 # not our child. We continue this loop, and after it is
577 # terminated make really sure the process is going away
578 # (SIGKILL).
579 pass
580 time.sleep(0.1)
581 else:
582 log.warning("DeviceModel %d took more than 10s "
583 "to terminate: sending SIGKILL" % self.pid)
584 try:
585 os.kill(self.pid, signal.SIGKILL)
586 os.waitpid(self.pid, 0)
587 except OSError:
588 # This happens if the process doesn't exist.
589 pass
590 state = xstransact.Remove("/local/domain/0/device-model/%i"
591 % self.vm.getDomid())
592 finally:
593 self.pid = None
594 self.sentinel_lock.release()
596 try:
597 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
598 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
599 except:
600 pass
601 try:
602 del sentinel_fifos_inuse[self.sentinel_path_fifo]
603 os.unlink(self.sentinel_path_fifo)
604 except:
605 pass
607 def setCpuid(self):
608 xc.domain_set_policy_cpuid(self.vm.getDomid())
610 if self.cpuid is not None:
611 cpuid = self.cpuid
612 transformed = {}
613 for sinput, regs in cpuid.iteritems():
614 inputs = sinput.split(',')
615 input = long(inputs[0])
616 sub_input = None
617 if len(inputs) == 2:
618 sub_input = long(inputs[1])
619 t = xc.domain_set_cpuid(self.vm.getDomid(),
620 input, sub_input, regs)
621 transformed[sinput] = t
622 self.cpuid = transformed
624 if self.cpuid_check is not None:
625 cpuid_check = self.cpuid_check
626 transformed = {}
627 for sinput, regs_check in cpuid_check.iteritems():
628 inputs = sinput.split(',')
629 input = long(inputs[0])
630 sub_input = None
631 if len(inputs) == 2:
632 sub_input = long(inputs[1])
633 t = xc.domain_check_cpuid(input, sub_input, regs_check)
634 transformed[sinput] = t
635 self.cpuid_check = transformed
639 class LinuxImageHandler(ImageHandler):
641 ostype = "linux"
642 flags = 0
643 vhpt = 0
645 def configure(self, vmConfig):
646 ImageHandler.configure(self, vmConfig)
647 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
648 self.is_stubdom = (self.kernel.find('stubdom') >= 0)
650 def buildDomain(self):
651 store_evtchn = self.vm.getStorePort()
652 console_evtchn = self.vm.getConsolePort()
654 mem_mb = self.getRequiredInitialReservation() / 1024
656 log.debug("domid = %d", self.vm.getDomid())
657 log.debug("memsize = %d", mem_mb)
658 log.debug("image = %s", self.kernel)
659 log.debug("store_evtchn = %d", store_evtchn)
660 log.debug("console_evtchn = %d", console_evtchn)
661 log.debug("cmdline = %s", self.cmdline)
662 log.debug("ramdisk = %s", self.ramdisk)
663 log.debug("vcpus = %d", self.vm.getVCpuCount())
664 log.debug("features = %s", self.vm.getFeatures())
665 log.debug("flags = %d", self.flags)
666 if arch.type == "ia64":
667 log.debug("vhpt = %d", self.vhpt)
669 return xc.linux_build(domid = self.vm.getDomid(),
670 memsize = mem_mb,
671 image = self.kernel,
672 store_evtchn = store_evtchn,
673 console_evtchn = console_evtchn,
674 cmdline = self.cmdline,
675 ramdisk = self.ramdisk,
676 features = self.vm.getFeatures(),
677 flags = self.flags,
678 vhpt = self.vhpt)
680 def getRequiredAvailableMemory(self, mem_kb):
681 if self.is_stubdom :
682 mem_kb += self.vramsize
683 return mem_kb
685 def getRequiredInitialReservation(self):
686 return self.vm.getMemoryTarget()
688 def getRequiredMaximumReservation(self):
689 return self.vm.getMemoryMaximum()
691 def parseDeviceModelArgs(self, vmConfig):
692 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
693 # Equivalent to old xenconsoled behaviour. Should make
694 # it configurable in future
695 ret = ret + ["-serial", "pty"]
696 return ret
698 def getDeviceModelArgs(self, restore = False):
699 args = ImageHandler.getDeviceModelArgs(self, restore)
700 args = args + ([ "-M", "xenpv"])
701 return args
704 class HVMImageHandler(ImageHandler):
706 ostype = "hvm"
708 def __init__(self, vm, vmConfig):
709 ImageHandler.__init__(self, vm, vmConfig)
710 self.shutdownWatch = None
711 self.rebootFeatureWatch = None
713 def configure(self, vmConfig):
714 ImageHandler.configure(self, vmConfig)
716 self.loader = vmConfig['platform'].get('loader')
718 info = xc.xeninfo()
719 if 'hvm' not in info['xen_caps']:
720 raise HVMRequired()
722 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
724 if not self.display :
725 self.display = ''
726 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
727 ("image/device-model", self.device_model),
728 ("image/display", self.display))
729 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
730 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
731 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
733 self.apic = int(vmConfig['platform'].get('apic', 0))
734 self.acpi = int(vmConfig['platform'].get('acpi', 0))
735 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
738 # Return a list of cmd line args to the device models based on the
739 # xm config file
740 def parseDeviceModelArgs(self, vmConfig):
741 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
742 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
744 if self.kernel:
745 log.debug("kernel = %s", self.kernel)
746 ret = ret + ['-kernel', self.kernel]
747 if self.ramdisk:
748 log.debug("ramdisk = %s", self.ramdisk)
749 ret = ret + ['-initrd', self.ramdisk]
750 if self.cmdline:
751 log.debug("cmdline = %s", self.cmdline)
752 ret = ret + ['-append', self.cmdline]
755 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
756 'localtime', 'serial', 'stdvga', 'isa',
757 'acpi', 'usb', 'usbdevice' ]
759 for a in dmargs:
760 v = vmConfig['platform'].get(a)
762 # python doesn't allow '-' in variable names
763 if a == 'stdvga': a = 'std-vga'
764 if a == 'keymap': a = 'k'
766 # Handle booleans gracefully
767 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
768 try:
769 if v != None: v = int(v)
770 if v: ret.append("-%s" % a)
771 except (ValueError, TypeError):
772 pass # if we can't convert it to a sane type, ignore it
773 else:
774 if v:
775 ret.append("-%s" % a)
776 ret.append("%s" % v)
778 if a in ['fda', 'fdb']:
779 if v:
780 if not os.path.isabs(v):
781 raise VmError("Floppy file %s does not exist." % v)
782 log.debug("args: %s, val: %s" % (a,v))
784 # Handle disk/network related options
785 mac = None
786 nics = 0
788 for devuuid in vmConfig['vbd_refs']:
789 devinfo = vmConfig['devices'][devuuid][1]
790 uname = devinfo.get('uname')
791 if uname is not None and 'file:' in uname:
792 (_, vbdparam) = string.split(uname, ':', 1)
793 if not os.path.isfile(vbdparam):
794 raise VmError('Disk image does not exist: %s' %
795 vbdparam)
797 for devuuid in vmConfig['vif_refs']:
798 devinfo = vmConfig['devices'][devuuid][1]
799 dtype = devinfo.get('type', 'ioemu')
800 if dtype != 'ioemu':
801 continue
802 nics += 1
803 mac = devinfo.get('mac')
804 if mac is None:
805 raise VmError("MAC address not specified or generated.")
806 bridge = devinfo.get('bridge', 'xenbr0')
807 model = devinfo.get('model', 'rtl8139')
808 ret.append("-net")
809 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
810 (nics, mac, model))
811 ret.append("-net")
812 ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" %
813 (nics, self.vm.getDomid(), nics-1, bridge))
815 if nics == 0:
816 ret.append("-net")
817 ret.append("none")
819 return ret
821 def getDeviceModelArgs(self, restore = False):
822 args = ImageHandler.getDeviceModelArgs(self, restore)
823 args = args + ([ "-M", "xenfv"])
824 if restore:
825 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
826 self.vm.getDomid() ])
827 return args
829 def buildDomain(self):
830 store_evtchn = self.vm.getStorePort()
832 memmax_mb = self.getRequiredMaximumReservation() / 1024
833 mem_mb = self.getRequiredInitialReservation() / 1024
835 log.debug("domid = %d", self.vm.getDomid())
836 log.debug("image = %s", self.loader)
837 log.debug("store_evtchn = %d", store_evtchn)
838 log.debug("memsize = %d", memmax_mb)
839 log.debug("target = %d", mem_mb)
840 log.debug("vcpus = %d", self.vm.getVCpuCount())
841 log.debug("acpi = %d", self.acpi)
842 log.debug("apic = %d", self.apic)
844 rc = xc.hvm_build(domid = self.vm.getDomid(),
845 image = self.loader,
846 memsize = memmax_mb,
847 target = mem_mb,
848 vcpus = self.vm.getVCpuCount(),
849 acpi = self.acpi,
850 apic = self.apic)
851 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
853 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
854 HVM_PARAM_STORE_PFN)
855 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
856 store_evtchn)
858 return rc
861 class IA64_HVM_ImageHandler(HVMImageHandler):
863 def configure(self, vmConfig):
864 HVMImageHandler.configure(self, vmConfig)
865 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
866 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
868 def buildDomain(self):
869 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
870 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
871 if self.guest_os_type is not None:
872 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
873 return HVMImageHandler.buildDomain(self)
875 def getRequiredAvailableMemory(self, mem_kb):
876 page_kb = 16
877 # ROM size for guest firmware, io page, xenstore page
878 # buffer io page, buffer pio page and memmap info page
879 extra_pages = 1024 + 5
880 mem_kb += extra_pages * page_kb
881 mem_kb += self.vramsize
882 return mem_kb
884 def getRequiredInitialReservation(self):
885 return self.vm.getMemoryTarget()
887 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
888 # Explicit shadow memory is not a concept
889 return 0
891 def getDeviceModelArgs(self, restore = False):
892 args = HVMImageHandler.getDeviceModelArgs(self, restore)
893 args = args + ([ "-m", "%s" %
894 (self.getRequiredInitialReservation() / 1024) ])
895 return args
897 def setCpuid(self):
898 # Guest CPUID configuration is not implemented yet.
899 return
901 class IA64_Linux_ImageHandler(LinuxImageHandler):
903 def configure(self, vmConfig):
904 LinuxImageHandler.configure(self, vmConfig)
905 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
907 def setCpuid(self):
908 # Guest CPUID configuration is not implemented yet.
909 return
911 class X86_HVM_ImageHandler(HVMImageHandler):
913 def configure(self, vmConfig):
914 HVMImageHandler.configure(self, vmConfig)
915 self.pae = int(vmConfig['platform'].get('pae', 0))
916 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
918 def buildDomain(self):
919 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
920 rc = HVMImageHandler.buildDomain(self)
921 self.setCpuid()
922 return rc
924 def getRequiredAvailableMemory(self, mem_kb):
925 return mem_kb + self.vramsize
927 def getRequiredInitialReservation(self):
928 return self.vm.getMemoryTarget()
930 def getRequiredMaximumReservation(self):
931 return self.vm.getMemoryMaximum()
933 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
934 # 256 pages (1MB) per vcpu,
935 # plus 1 page per MiB of RAM for the P2M map,
936 # plus 1 page per MiB of RAM to shadow the resident processes.
937 # This is higher than the minimum that Xen would allocate if no value
938 # were given (but the Xen minimum is for safety, not performance).
939 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
940 shadow_mem_kb)
943 class X86_Linux_ImageHandler(LinuxImageHandler):
945 def buildDomain(self):
946 # set physical mapping limit
947 # add an 8MB slack to balance backend allocations.
948 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
949 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
950 rc = LinuxImageHandler.buildDomain(self)
951 self.setCpuid()
952 return rc
954 _handlers = {
955 "ia64": {
956 "linux": IA64_Linux_ImageHandler,
957 "hvm": IA64_HVM_ImageHandler,
958 },
959 "x86": {
960 "linux": X86_Linux_ImageHandler,
961 "hvm": X86_HVM_ImageHandler,
962 },
963 }
965 def findImageHandlerClass(image):
966 """Find the image handler class for an image config.
968 @param image config
969 @return ImageHandler subclass or None
970 """
971 image_type = image.image_type()
972 try:
973 return _handlers[arch.type][image_type]
974 except KeyError:
975 raise VmError('unknown image type: ' + image_type)