ia64/xen-unstable

view tools/python/xen/xend/image.py @ 19791:c0d2838fc10f

stubdoms: qemu monitor support

Add support for the qemu monitor in a stubdom, the same way the
emulated serial support was added few days ago. The stubdom exports
the monitor as a pty and minios opens a console frontend; qemu in dom0
provides the correspondent backend for this additional pv console that
happens to be the qemu monitor.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 18 10:20:17 2009 +0100 (2009-06-18)
parents eeb0fce9aeaf
children e2625f235940
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
86 superpages = 0
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 'monitor' in dev_info:
290 ret.append("-serial")
291 ret.append(dev_info.get('monitor',{}))
292 ret.append("-monitor")
293 ret.append("null")
294 if 'serial' in dev_info:
295 ret.append("-serial")
296 ret.append(dev_info.get('serial',{}))
297 if int(dev_info.get('vnc', 0)) != 0 :
298 has_vnc = True
299 if int(dev_info.get('sdl', 0)) != 0 :
300 has_sdl = True
301 if has_sdl:
302 self.display = dev_info.get('display', self.display)
303 self.xauthority = dev_info.get('xauthority', self.xauthority)
304 opengl = int(dev_info.get('opengl', opengl))
305 if has_vnc:
306 vnc_config = dev_info.get('other_config', {})
307 break
309 if keymap:
310 ret.append("-k")
311 ret.append(keymap)
313 if has_vnc:
314 if not vnc_config:
315 for key in ('vncunused', 'vnclisten', 'vncdisplay',
316 'vncpasswd'):
317 if key in vmConfig['platform']:
318 vnc_config[key] = vmConfig['platform'][key]
319 if vnc_config.has_key("vncpasswd"):
320 passwd = vnc_config["vncpasswd"]
321 else:
322 passwd = XendOptions.instance().get_vncpasswd_default()
323 vncopts = ""
324 if passwd:
325 self.vm.storeVm("vncpasswd", passwd)
326 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
327 vncopts = vncopts + ",password"
328 log.debug("Stored a VNC password for vfb access")
329 else:
330 log.debug("No VNC passwd configured for vfb access")
332 if XendOptions.instance().get_vnc_tls():
333 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
334 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
336 if not os.path.exists(vncx509certdir):
337 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
339 if vncx509verify:
340 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
341 else:
342 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
345 vnclisten = vnc_config.get('vnclisten',
346 XendOptions.instance().get_vnclisten_address())
347 vncdisplay = int(vnc_config.get('vncdisplay', 0))
348 ret.append('-vnc')
349 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
351 if int(vnc_config.get('vncunused', 1)) != 0:
352 ret.append('-vncunused')
354 if has_sdl:
355 ret.append('-sdl')
356 if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
357 ret.append('-disable-opengl')
359 if not has_sdl and not has_vnc :
360 ret.append('-nographic')
362 if int(vmConfig['platform'].get('monitor', 0)) != 0:
363 ret = ret + ['-monitor', 'vc']
364 return ret
366 def getDeviceModelArgs(self, restore = False):
367 args = [self.device_model]
368 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
369 args = args + self.dmargs
370 return args
372 def _openSentinel(self, sentinel_path_fifo):
373 self.sentinel_fifo = file(sentinel_path_fifo, 'r')
374 self.sentinel_lock = thread.allocate_lock()
375 oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True)
376 sentinel_fifos_inuse[sentinel_path_fifo] = 1
377 self.sentinel_path_fifo = sentinel_path_fifo
379 def createDeviceModel(self, restore = False):
380 if self.device_model is None:
381 return
382 if self.pid:
383 return
384 # Execute device model.
385 #todo: Error handling
386 args = self.getDeviceModelArgs(restore)
387 env = dict(os.environ)
388 if self.display:
389 env['DISPLAY'] = self.display
390 if self.xauthority:
391 env['XAUTHORITY'] = self.xauthority
392 unique_id = "%i-%i" % (self.vm.getDomid(), time.time())
393 sentinel_path = sentinel_path_prefix + unique_id
394 sentinel_path_fifo = sentinel_path + '.fifo'
395 os.mkfifo(sentinel_path_fifo, 0600)
396 sentinel_write = file(sentinel_path_fifo, 'r+')
397 self._openSentinel(sentinel_path_fifo)
398 self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo)
399 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
400 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
401 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
402 log.info("spawning device models: %s %s", self.device_model, args)
403 # keep track of pid and spawned options to kill it later
405 self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label'])
407 # rotate log
408 logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND
409 logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count()
410 if logrotate_count > 0:
411 logfile_mode |= os.O_TRUNC
412 if os.path.exists("%s.%d" % (self.logfile, logrotate_count)):
413 os.unlink("%s.%d" % (self.logfile, logrotate_count))
414 for n in range(logrotate_count - 1, 0, -1):
415 if os.path.exists("%s.%d" % (self.logfile, n)):
416 os.rename("%s.%d" % (self.logfile, n),
417 "%s.%d" % (self.logfile, (n + 1)))
418 if os.path.exists(self.logfile):
419 os.rename(self.logfile, self.logfile + ".1")
421 null = os.open("/dev/null", os.O_RDONLY)
422 logfd = os.open(self.logfile, logfile_mode)
424 sys.stderr.flush()
425 contract = osdep.prefork("%s:%d" %
426 (self.vm.getName(), self.vm.getDomid()))
427 pid = os.fork()
428 if pid == 0: #child
429 try:
430 osdep.postfork(contract)
431 os.dup2(null, 0)
432 os.dup2(logfd, 1)
433 os.dup2(logfd, 2)
434 os.close(null)
435 os.close(logfd)
436 self.sentinel_fifo.close()
437 try:
438 os.execve(self.device_model, args, env)
439 except Exception, e:
440 print >>sys.stderr, (
441 'failed to set up fds or execute dm %s: %s' %
442 (self.device_model, utils.exception_string(e)))
443 os._exit(126)
444 except:
445 os._exit(127)
446 else:
447 osdep.postfork(contract, abandon=True)
448 self.pid = pid
449 os.close(null)
450 os.close(logfd)
451 sentinel_write.close()
452 self.vm.storeDom("image/device-model-pid", self.pid)
453 log.info("device model pid: %d", self.pid)
454 # we would very much prefer not to have a thread here and instead
455 # have a callback but sadly we don't have Twisted in xend
456 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
458 def signalDeviceModel(self, cmd, ret, par = None):
459 if self.device_model is None:
460 return
461 # Signal the device model to for action
462 if cmd is '' or ret is '':
463 raise VmError('need valid command and result when signal device model')
465 count = 0
466 while True:
467 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
468 % self.vm.getDomid())
469 # This can occur right after start-up
470 if orig_state != None:
471 break
473 log.debug('signalDeviceModel: orig_state is None, retrying')
475 time.sleep(0.1)
476 count += 1
477 if count < 100:
478 continue
480 VmError('Device model isn\'t ready for commands')
482 if par is not None:
483 xstransact.Store("/local/domain/0/device-model/%i"
484 % self.vm.getDomid(), ('parameter', par))
486 xstransact.Store("/local/domain/0/device-model/%i"
487 % self.vm.getDomid(), ('command', cmd))
488 # Wait for confirmation. Could do this with a watch but we'd
489 # still end up spinning here waiting for the watch to fire.
490 state = ''
491 count = 0
492 while state != ret:
493 state = xstransact.Read("/local/domain/0/device-model/%i/state"
494 % self.vm.getDomid())
495 time.sleep(0.1)
496 count += 1
497 if count > 100:
498 raise VmError('Timed out waiting for device model action')
500 #resotre orig state
501 xstransact.Store("/local/domain/0/device-model/%i"
502 % self.vm.getDomid(), ('state', orig_state))
503 log.info("signalDeviceModel:restore dm state to %s", orig_state)
505 def saveDeviceModel(self):
506 # Signal the device model to pause itself and save its state
507 self.signalDeviceModel('save', 'paused')
509 def resumeDeviceModel(self):
510 if self.device_model is None:
511 return
512 # Signal the device model to resume activity after pausing to save.
513 xstransact.Store("/local/domain/0/device-model/%i"
514 % self.vm.getDomid(), ('command', 'continue'))
516 def _dmfailed(self, message):
517 log.warning("domain %s: %s", self.vm.getName(), message)
518 try:
519 xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
520 except:
521 pass
523 def recreate(self):
524 if self.device_model is None:
525 return
526 name = self.vm.getName()
527 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
528 fifo_fd = -1
529 log.debug("rediscovering %s", sentinel_path_fifo)
530 if sentinel_path_fifo is None:
531 log.debug("%s device model no sentinel, cannot rediscover", name)
532 else:
533 try:
534 # We open it O_WRONLY because that fails ENXIO if no-one
535 # has it open for reading (see SuSv3). The dm process got
536 # a read/write descriptor from our earlier invocation.
537 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
538 except OSError, e:
539 if e.errno == errno.ENXIO:
540 self._dmfailed("%s device model no longer running"%name)
541 elif e.errno == errno.ENOENT:
542 log.debug("%s device model sentinel %s absent!",
543 name, sentinel_path_fifo)
544 else:
545 raise
546 if fifo_fd >= 0:
547 self._openSentinel(sentinel_path_fifo)
548 os.close(fifo_fd)
549 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
550 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
551 name, self.pid, sentinel_path_fifo)
552 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
554 def _sentinel_watch(self):
555 log.info("waiting for sentinel_fifo")
556 try: self.sentinel_fifo.read(1)
557 except OSError, e: pass
558 self.sentinel_lock.acquire()
559 if self.pid:
560 try:
561 (p,st) = os.waitpid(self.pid, os.WNOHANG)
562 if p == self.pid:
563 message = oshelp.waitstatus_description(st)
564 else:
565 # obviously it is malfunctioning, kill it now
566 try:
567 os.kill(self.pid, signal.SIGKILL)
568 message = "malfunctioning (closed sentinel), killed"
569 except:
570 message = "malfunctioning or died ?"
571 message = "pid %d: %s" % (self.pid, message)
572 except Exception, e:
573 message = "waitpid failed: %s" % utils.exception_string(e)
574 message = "device model failure: %s" % message
575 try: message += "; see %s " % self.logfile
576 except: pass
577 self._dmfailed(message)
578 self.pid = None
579 else:
580 log.info("%s device model terminated", self.vm.getName())
581 self.sentinel_lock.release()
583 def destroyDeviceModel(self):
584 if self.device_model is None:
585 return
586 self.sentinel_lock.acquire()
587 try:
588 if self.pid:
589 try:
590 os.kill(self.pid, signal.SIGHUP)
591 except OSError, exn:
592 log.exception(exn)
593 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
594 for i in xrange(100):
595 try:
596 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
597 if p == self.pid:
598 break
599 except OSError:
600 # This is expected if Xend has been restarted within
601 # the life of this domain. In this case, we can kill
602 # the process, but we can't wait for it because it's
603 # not our child. We continue this loop, and after it is
604 # terminated make really sure the process is going away
605 # (SIGKILL).
606 pass
607 time.sleep(0.1)
608 else:
609 log.warning("DeviceModel %d took more than 10s "
610 "to terminate: sending SIGKILL" % self.pid)
611 try:
612 os.kill(self.pid, signal.SIGKILL)
613 os.waitpid(self.pid, 0)
614 except OSError:
615 # This happens if the process doesn't exist.
616 pass
617 finally:
618 self.pid = None
619 self.sentinel_lock.release()
621 state = xstransact.Remove("/local/domain/0/device-model/%i"
622 % self.vm.getDomid())
623 try:
624 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
625 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
626 except:
627 pass
628 try:
629 del sentinel_fifos_inuse[self.sentinel_path_fifo]
630 os.unlink(self.sentinel_path_fifo)
631 except:
632 pass
634 def setCpuid(self):
635 xc.domain_set_policy_cpuid(self.vm.getDomid())
637 if self.cpuid is not None:
638 cpuid = self.cpuid
639 transformed = {}
640 for sinput, regs in cpuid.iteritems():
641 inputs = sinput.split(',')
642 input = long(inputs[0])
643 sub_input = None
644 if len(inputs) == 2:
645 sub_input = long(inputs[1])
646 t = xc.domain_set_cpuid(self.vm.getDomid(),
647 input, sub_input, regs)
648 transformed[sinput] = t
649 self.cpuid = transformed
651 if self.cpuid_check is not None:
652 cpuid_check = self.cpuid_check
653 transformed = {}
654 for sinput, regs_check in cpuid_check.iteritems():
655 inputs = sinput.split(',')
656 input = long(inputs[0])
657 sub_input = None
658 if len(inputs) == 2:
659 sub_input = long(inputs[1])
660 t = xc.domain_check_cpuid(input, sub_input, regs_check)
661 transformed[sinput] = t
662 self.cpuid_check = transformed
666 class LinuxImageHandler(ImageHandler):
668 ostype = "linux"
669 flags = 0
670 vhpt = 0
672 def configure(self, vmConfig):
673 ImageHandler.configure(self, vmConfig)
674 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
675 self.is_stubdom = (self.kernel.find('stubdom') >= 0)
676 self.superpages = int(vmConfig['superpages'])
678 def buildDomain(self):
679 store_evtchn = self.vm.getStorePort()
680 console_evtchn = self.vm.getConsolePort()
682 mem_mb = self.getRequiredInitialReservation() / 1024
684 log.debug("domid = %d", self.vm.getDomid())
685 log.debug("memsize = %d", mem_mb)
686 log.debug("image = %s", self.kernel)
687 log.debug("store_evtchn = %d", store_evtchn)
688 log.debug("console_evtchn = %d", console_evtchn)
689 log.debug("cmdline = %s", self.cmdline)
690 log.debug("ramdisk = %s", self.ramdisk)
691 log.debug("vcpus = %d", self.vm.getVCpuCount())
692 log.debug("features = %s", self.vm.getFeatures())
693 log.debug("flags = %d", self.flags)
694 log.debug("superpages = %d", self.superpages)
695 if arch.type == "ia64":
696 log.debug("vhpt = %d", self.vhpt)
698 return xc.linux_build(domid = self.vm.getDomid(),
699 memsize = mem_mb,
700 image = self.kernel,
701 store_evtchn = store_evtchn,
702 console_evtchn = console_evtchn,
703 cmdline = self.cmdline,
704 ramdisk = self.ramdisk,
705 features = self.vm.getFeatures(),
706 flags = self.flags,
707 vhpt = self.vhpt,
708 superpages = self.superpages)
710 def getRequiredAvailableMemory(self, mem_kb):
711 if self.is_stubdom :
712 mem_kb += self.vramsize
713 return mem_kb
715 def getRequiredInitialReservation(self):
716 return self.vm.getMemoryTarget()
718 def getRequiredMaximumReservation(self):
719 return self.vm.getMemoryMaximum()
721 def parseDeviceModelArgs(self, vmConfig):
722 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
723 # Equivalent to old xenconsoled behaviour. Should make
724 # it configurable in future
725 ret = ["-serial", "pty"] + ret
726 return ret
728 def getDeviceModelArgs(self, restore = False):
729 args = ImageHandler.getDeviceModelArgs(self, restore)
730 args = args + ([ "-M", "xenpv"])
731 return args
734 class HVMImageHandler(ImageHandler):
736 ostype = "hvm"
738 def __init__(self, vm, vmConfig):
739 ImageHandler.__init__(self, vm, vmConfig)
740 self.shutdownWatch = None
741 self.rebootFeatureWatch = None
743 def configure(self, vmConfig):
744 ImageHandler.configure(self, vmConfig)
746 self.loader = vmConfig['platform'].get('loader')
748 info = xc.xeninfo()
749 if 'hvm' not in info['xen_caps']:
750 raise HVMRequired()
752 xen_platform_pci = int(vmConfig['platform'].get('xen_platform_pci',1))
753 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
755 if not self.display :
756 self.display = ''
758 store_dmargs = self.dmargs[:]
759 store_dmargs.remove('-sdl')
760 store_dmargs.remove('-disable-opengl')
761 try :
762 midx = store_dmargs.index('-monitor')
763 store_dmargs[midx + 1] = 'pty'
764 except ValueError :
765 pass
766 self.vm.storeVm(("image/dmargs", " ".join(store_dmargs)),
767 ("image/device-model", self.device_model),
768 ("image/display", self.display))
769 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
771 if xen_platform_pci == 0:
772 disable_pf = 1
773 log.info("No need to create platform device.[domid:%d]", self.vm.getDomid())
774 else:
775 disable_pf = 0
776 log.info("Need to create platform device.[domid:%d]", self.vm.getDomid())
778 xstransact.Store("/local/domain/0/device-model/%i"%self.vm.getDomid(),
779 ('disable_pf', disable_pf))
780 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
781 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
783 self.apic = int(vmConfig['platform'].get('apic', 0))
784 self.acpi = int(vmConfig['platform'].get('acpi', 0))
785 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
788 # Return a list of cmd line args to the device models based on the
789 # xm config file
790 def parseDeviceModelArgs(self, vmConfig):
791 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
792 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
794 if self.kernel:
795 log.debug("kernel = %s", self.kernel)
796 ret = ret + ['-kernel', self.kernel]
797 if self.ramdisk:
798 log.debug("ramdisk = %s", self.ramdisk)
799 ret = ret + ['-initrd', self.ramdisk]
800 if self.cmdline:
801 log.debug("cmdline = %s", self.cmdline)
802 ret = ret + ['-append', self.cmdline]
805 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
806 'localtime', 'serial', 'stdvga', 'isa',
807 'acpi', 'usb', 'usbdevice' ]
809 for a in dmargs:
810 v = vmConfig['platform'].get(a)
812 # python doesn't allow '-' in variable names
813 if a == 'stdvga': a = 'std-vga'
814 if a == 'keymap': a = 'k'
816 # Handle booleans gracefully
817 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
818 try:
819 if v != None: v = int(v)
820 if v: ret.append("-%s" % a)
821 except (ValueError, TypeError):
822 pass # if we can't convert it to a sane type, ignore it
823 elif a == 'serial':
824 if v:
825 if type(v) == str:
826 v = [v]
827 for s in v:
828 if s:
829 ret.append("-serial")
830 ret.append("%s" % s)
831 else:
832 if v:
833 ret.append("-%s" % a)
834 ret.append("%s" % v)
836 if a in ['fda', 'fdb']:
837 if v:
838 if not os.path.isabs(v):
839 raise VmError("Floppy file %s does not exist." % v)
840 log.debug("args: %s, val: %s" % (a,v))
842 # Handle disk/network related options
843 mac = None
844 nics = 0
846 for devuuid in vmConfig['vbd_refs']:
847 devinfo = vmConfig['devices'][devuuid][1]
848 uname = devinfo.get('uname')
849 if uname is not None and 'file:' in uname:
850 (_, vbdparam) = string.split(uname, ':', 1)
851 if not os.path.isfile(vbdparam):
852 raise VmError('Disk image does not exist: %s' %
853 vbdparam)
855 for devuuid in vmConfig['vif_refs']:
856 devinfo = vmConfig['devices'][devuuid][1]
857 dtype = devinfo.get('type', 'ioemu')
858 if dtype != 'ioemu':
859 continue
860 nics += 1
861 mac = devinfo.get('mac')
862 if mac is None:
863 raise VmError("MAC address not specified or generated.")
864 bridge = devinfo.get('bridge', 'xenbr0')
865 model = devinfo.get('model', 'rtl8139')
866 ret.append("-net")
867 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
868 (nics, mac, model))
869 ret.append("-net")
870 ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" %
871 (nics, self.vm.getDomid(), nics-1, bridge))
873 if nics == 0:
874 ret.append("-net")
875 ret.append("none")
877 return ret
879 def getDeviceModelArgs(self, restore = False):
880 args = ImageHandler.getDeviceModelArgs(self, restore)
881 args = args + ([ "-M", "xenfv"])
882 if restore:
883 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
884 self.vm.getDomid() ])
885 return args
887 def buildDomain(self):
888 store_evtchn = self.vm.getStorePort()
890 memmax_mb = self.getRequiredMaximumReservation() / 1024
891 mem_mb = self.getRequiredInitialReservation() / 1024
893 log.debug("domid = %d", self.vm.getDomid())
894 log.debug("image = %s", self.loader)
895 log.debug("store_evtchn = %d", store_evtchn)
896 log.debug("memsize = %d", memmax_mb)
897 log.debug("target = %d", mem_mb)
898 log.debug("vcpus = %d", self.vm.getVCpuCount())
899 log.debug("acpi = %d", self.acpi)
900 log.debug("apic = %d", self.apic)
902 rc = xc.hvm_build(domid = self.vm.getDomid(),
903 image = self.loader,
904 memsize = memmax_mb,
905 target = mem_mb,
906 vcpus = self.vm.getVCpuCount(),
907 acpi = self.acpi,
908 apic = self.apic)
909 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
911 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
912 HVM_PARAM_STORE_PFN)
913 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
914 store_evtchn)
916 return rc
919 class IA64_HVM_ImageHandler(HVMImageHandler):
921 def configure(self, vmConfig):
922 HVMImageHandler.configure(self, vmConfig)
923 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
924 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
926 def buildDomain(self):
927 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
928 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
929 if self.guest_os_type is not None:
930 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
931 return HVMImageHandler.buildDomain(self)
933 def getRequiredAvailableMemory(self, mem_kb):
934 page_kb = 16
935 # ROM size for guest firmware, io page, xenstore page
936 # buffer io page, buffer pio page and memmap info page
937 extra_pages = 1024 + 5
938 mem_kb += extra_pages * page_kb
939 mem_kb += self.vramsize
940 return mem_kb
942 def getRequiredInitialReservation(self):
943 return self.vm.getMemoryTarget()
945 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
946 # Explicit shadow memory is not a concept
947 return 0
949 def getDeviceModelArgs(self, restore = False):
950 args = HVMImageHandler.getDeviceModelArgs(self, restore)
951 args = args + ([ "-m", "%s" %
952 (self.getRequiredInitialReservation() / 1024) ])
953 return args
955 def setCpuid(self):
956 # Guest CPUID configuration is not implemented yet.
957 return
959 class IA64_Linux_ImageHandler(LinuxImageHandler):
961 def configure(self, vmConfig):
962 LinuxImageHandler.configure(self, vmConfig)
963 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
965 def setCpuid(self):
966 # Guest CPUID configuration is not implemented yet.
967 return
969 class X86_HVM_ImageHandler(HVMImageHandler):
971 def configure(self, vmConfig):
972 HVMImageHandler.configure(self, vmConfig)
973 self.pae = int(vmConfig['platform'].get('pae', 0))
974 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
976 def buildDomain(self):
977 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
978 rc = HVMImageHandler.buildDomain(self)
979 self.setCpuid()
980 return rc
982 def getRequiredAvailableMemory(self, mem_kb):
983 return mem_kb + self.vramsize
985 def getRequiredInitialReservation(self):
986 return self.vm.getMemoryTarget()
988 def getRequiredMaximumReservation(self):
989 return self.vm.getMemoryMaximum()
991 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
992 # 256 pages (1MB) per vcpu,
993 # plus 1 page per MiB of RAM for the P2M map,
994 # plus 1 page per MiB of RAM to shadow the resident processes.
995 # This is higher than the minimum that Xen would allocate if no value
996 # were given (but the Xen minimum is for safety, not performance).
997 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
998 shadow_mem_kb)
1001 class X86_Linux_ImageHandler(LinuxImageHandler):
1003 def buildDomain(self):
1004 # set physical mapping limit
1005 # add an 8MB slack to balance backend allocations.
1006 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
1007 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
1008 rc = LinuxImageHandler.buildDomain(self)
1009 self.setCpuid()
1010 return rc
1012 _handlers = {
1013 "ia64": {
1014 "linux": IA64_Linux_ImageHandler,
1015 "hvm": IA64_HVM_ImageHandler,
1016 },
1017 "x86": {
1018 "linux": X86_Linux_ImageHandler,
1019 "hvm": X86_HVM_ImageHandler,
1020 },
1023 def findImageHandlerClass(image):
1024 """Find the image handler class for an image config.
1026 @param image config
1027 @return ImageHandler subclass or None
1028 """
1029 image_type = image.image_type()
1030 try:
1031 return _handlers[arch.type][image_type]
1032 except KeyError:
1033 raise VmError('unknown image type: ' + image_type)