debuggers.hg

view tools/python/xen/xend/image.py @ 20865:618b3597603c

Revert 20746:042b371d8728 --- Breaks stubdoms.

Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jan 18 10:37:28 2010 +0000 (2010-01-18)
parents e406e3451835
children c5e1a0b720ba
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
87 memory_sharing = 0
89 def __init__(self, vm, vmConfig):
90 self.vm = vm
92 self.bootloader = False
93 self.use_tmp_kernel = False
94 self.use_tmp_ramdisk = False
95 self.kernel = None
96 self.ramdisk = None
97 self.cmdline = None
99 self.configure(vmConfig)
101 def configure(self, vmConfig):
102 """Config actions common to all unix-like domains."""
103 if '_temp_using_bootloader' in vmConfig:
104 self.bootloader = True
105 self.kernel = vmConfig['_temp_kernel']
106 self.cmdline = vmConfig['_temp_args']
107 self.ramdisk = vmConfig['_temp_ramdisk']
108 else:
109 self.kernel = vmConfig['PV_kernel']
110 self.cmdline = vmConfig['PV_args']
111 self.ramdisk = vmConfig['PV_ramdisk']
112 # There a code-paths where use_tmp_xxx is not set at all; but if
113 # this is set, the variable itself is a boolean.
114 if 'use_tmp_kernel' in vmConfig and vmConfig['use_tmp_kernel']:
115 self.use_tmp_kernel = True
116 if 'use_tmp_ramdisk' in vmConfig and vmConfig['use_tmp_ramdisk']:
117 self.use_tmp_ramdisk = True
118 self.vm.storeVm(("image/ostype", self.ostype),
119 ("image/kernel", self.kernel),
120 ("image/cmdline", self.cmdline),
121 ("image/ramdisk", self.ramdisk))
122 self.vm.permissionsVm("image/cmdline", { 'dom': self.vm.getDomid(), 'read': True } )
124 self.device_model = vmConfig['platform'].get('device_model')
126 self.display = vmConfig['platform'].get('display')
127 self.xauthority = vmConfig['platform'].get('xauthority')
128 self.vncconsole = int(vmConfig['platform'].get('vncconsole', 0))
129 self.dmargs = self.parseDeviceModelArgs(vmConfig)
130 self.pid = None
131 rtc_timeoffset = int(vmConfig['platform'].get('rtc_timeoffset', 0))
132 if vmConfig['platform'].get('localtime', 0):
133 if time.localtime(time.time())[8]:
134 rtc_timeoffset -= time.altzone
135 else:
136 rtc_timeoffset -= time.timezone
137 if rtc_timeoffset != 0:
138 xc.domain_set_time_offset(self.vm.getDomid(), rtc_timeoffset)
140 self.cpuid = None
141 self.cpuid_check = None
142 if 'cpuid' in vmConfig:
143 self.cpuid = vmConfig['cpuid'];
144 if 'cpuid_check' in vmConfig:
145 self.cpuid_check = vmConfig['cpuid_check']
147 def cleanupTmpImages(self):
148 if self.use_tmp_kernel:
149 self.unlink(self.kernel)
150 if self.use_tmp_ramdisk:
151 self.unlink(self.ramdisk)
153 def unlink(self, f):
154 if not f: return
155 try:
156 os.unlink(f)
157 except OSError, ex:
158 log.warning("error removing bootloader file '%s': %s", f, ex)
161 def createImage(self):
162 """Entry point to create domain memory image.
163 Override in subclass if needed.
164 """
165 return self.createDomain()
168 def createDomain(self):
169 """Build the domain boot image.
170 """
171 # Set params and call buildDomain().
173 if self.kernel and not os.path.isfile(self.kernel):
174 raise VmError('Kernel image does not exist: %s' % self.kernel)
175 if self.ramdisk and not os.path.isfile(self.ramdisk):
176 raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk)
177 if len(self.cmdline) >= MAX_GUEST_CMDLINE:
178 log.warning('kernel cmdline too long, domain %d',
179 self.vm.getDomid())
181 log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
182 self.vm.getDomid(), self.vm.getVCpuCount())
184 result = self.buildDomain()
186 if isinstance(result, dict):
187 return result
188 else:
189 raise VmError('Building domain failed: ostype=%s dom=%d err=%s'
190 % (self.ostype, self.vm.getDomid(), str(result)))
192 def getRequiredAvailableMemory(self, mem_kb):
193 """@param mem_kb The configured maxmem or memory, in KiB.
194 @return The corresponding required amount of memory for the domain,
195 also in KiB. This is normally the given mem_kb, but architecture- or
196 image-specific code may override this to add headroom where
197 necessary."""
198 return mem_kb
200 def getRequiredInitialReservation(self):
201 """@param mem_kb The configured memory, in KiB.
202 @return The corresponding required amount of memory to be free, also
203 in KiB. This is normally the same as getRequiredAvailableMemory, but
204 architecture- or image-specific code may override this to
205 add headroom where necessary."""
206 return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
208 def getRequiredMaximumReservation(self):
209 """@param mem_kb The maximum possible memory, in KiB.
210 @return The corresponding required amount of memory to be free, also
211 in KiB. This is normally the same as getRequiredAvailableMemory, but
212 architecture- or image-specific code may override this to
213 add headroom where necessary."""
214 return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum())
216 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
217 """@param shadow_mem_kb The configured shadow memory, in KiB.
218 @param maxmem_kb The configured maxmem, in KiB.
219 @return The corresponding required amount of shadow memory, also in
220 KiB."""
221 # PV domains don't need any shadow memory
222 return 0
224 def buildDomain(self):
225 """Build the domain. Define in subclass."""
226 raise NotImplementedError()
228 def prepareEnvironment(self):
229 """Prepare the environment for the execution of the domain. This
230 method is called before any devices are set up."""
232 domid = self.vm.getDomid()
234 # Delete left-over pipes
235 try:
236 os.unlink('/var/run/tap/qemu-read-%d' % domid)
237 os.unlink('/var/run/tap/qemu-write-%d' % domid)
238 except:
239 pass
241 # No device model, don't create pipes
242 if self.device_model is None:
243 return
245 if platform.system() != 'SunOS':
246 # If we use a device model, the pipes for communication between
247 # blktapctrl and ioemu must be present before the devices are
248 # created (blktapctrl must access them for new block devices)
250 try:
251 os.makedirs('/var/run/tap', 0755)
252 except:
253 pass
255 try:
256 os.mkfifo('/var/run/tap/qemu-read-%d' % domid, 0600)
257 os.mkfifo('/var/run/tap/qemu-write-%d' % domid, 0600)
258 except OSError, e:
259 log.warn('Could not create blktap pipes for domain %d' % domid)
260 log.exception(e)
261 pass
264 # Return a list of cmd line args to the device models based on the
265 # xm config file
266 def parseDeviceModelArgs(self, vmConfig):
267 ret = ["-domain-name", str(self.vm.info['name_label'])]
269 xen_extended_power_mgmt = int(vmConfig['platform'].get(
270 'xen_extended_power_mgmt', 0))
271 if xen_extended_power_mgmt != 0:
272 xstransact.Store("/local/domain/0/device-model/%i"
273 % self.vm.getDomid(),
274 ('xen_extended_power_mgmt',
275 xen_extended_power_mgmt))
277 # Find RFB console device, and if it exists, make QEMU enable
278 # the VNC console.
279 if int(vmConfig['platform'].get('nographic', 0)) != 0:
280 # skip vnc init if nographic is set
281 ret.append('-nographic')
282 return ret
284 vram = str(vmConfig['platform'].get('videoram',4))
285 ret.append('-videoram')
286 ret.append(vram)
288 vnc_config = {}
289 has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0
290 has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0
291 opengl = 1
292 keymap = vmConfig['platform'].get("keymap")
293 for dev_uuid in vmConfig['console_refs']:
294 dev_type, dev_info = vmConfig['devices'][dev_uuid]
295 if dev_type == 'vfb':
296 if 'keymap' in dev_info:
297 keymap = dev_info.get('keymap',{})
298 if 'monitor' in dev_info:
299 ret.append("-serial")
300 ret.append(dev_info.get('monitor',{}))
301 ret.append("-monitor")
302 ret.append("null")
303 if 'serial' in dev_info:
304 ret.append("-serial")
305 ret.append(dev_info.get('serial',{}))
306 if int(dev_info.get('vnc', 0)) != 0 :
307 has_vnc = True
308 if int(dev_info.get('sdl', 0)) != 0 :
309 has_sdl = True
310 if has_sdl:
311 self.display = dev_info.get('display', self.display)
312 self.xauthority = dev_info.get('xauthority', self.xauthority)
313 opengl = int(dev_info.get('opengl', opengl))
314 if has_vnc:
315 vnc_config = dev_info.get('other_config', {})
316 break
318 if keymap:
319 ret.append("-k")
320 ret.append(keymap)
322 if has_vnc:
323 if not vnc_config:
324 for key in ('vncunused', 'vnclisten', 'vncdisplay',
325 'vncpasswd'):
326 if key in vmConfig['platform']:
327 vnc_config[key] = vmConfig['platform'][key]
328 if vnc_config.has_key("vncpasswd"):
329 passwd = vnc_config["vncpasswd"]
330 else:
331 passwd = XendOptions.instance().get_vncpasswd_default()
332 vncopts = ""
333 if passwd:
334 self.vm.storeVm("vncpasswd", passwd)
335 self.vm.permissionsVm("vncpasswd", { 'dom': self.vm.getDomid(), 'read': True } )
336 vncopts = vncopts + ",password"
337 log.debug("Stored a VNC password for vfb access")
338 else:
339 log.debug("No VNC passwd configured for vfb access")
341 if XendOptions.instance().get_vnc_tls():
342 vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
343 vncx509verify = XendOptions.instance().get_vnc_x509_verify()
345 if not os.path.exists(vncx509certdir):
346 raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
348 if vncx509verify:
349 vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
350 else:
351 vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
354 vnclisten = vnc_config.get('vnclisten',
355 XendOptions.instance().get_vnclisten_address())
356 vncdisplay = int(vnc_config.get('vncdisplay', 0))
357 ret.append('-vnc')
358 ret.append("%s:%s%s" % (vnclisten, vncdisplay, vncopts))
360 if int(vnc_config.get('vncunused', 1)) != 0:
361 ret.append('-vncunused')
363 if has_sdl:
364 ret.append('-sdl')
365 if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
366 ret.append('-disable-opengl')
368 if not has_sdl and not has_vnc :
369 ret.append('-nographic')
371 if vmConfig['platform'].get('parallel'):
372 ret = ret + ["-parallel", vmConfig['platform'].get('parallel')]
374 if int(vmConfig['platform'].get('monitor', 0)) != 0:
375 if vmConfig['platform'].get('monitor_path'):
376 ret = ret + ['-monitor', vmConfig['platform'].get('monitor_path')]
377 else:
378 ret = ret + ['-monitor', 'vc']
380 return ret
382 def getDeviceModelArgs(self, restore = False):
383 args = [self.device_model]
384 args = args + ([ "-d", "%d" % self.vm.getDomid() ])
385 args = args + self.dmargs
386 return args
388 def _openSentinel(self, sentinel_path_fifo):
389 self.sentinel_fifo = file(sentinel_path_fifo, 'r')
390 self.sentinel_lock = thread.allocate_lock()
391 oshelp.fcntl_setfd_cloexec(self.sentinel_fifo, True)
392 sentinel_fifos_inuse[sentinel_path_fifo] = 1
393 self.sentinel_path_fifo = sentinel_path_fifo
395 def createDeviceModel(self, restore = False):
396 if self.device_model is None:
397 return
398 if self.pid:
399 return
400 # Execute device model.
401 #todo: Error handling
402 args = self.getDeviceModelArgs(restore)
403 env = dict(os.environ)
404 if self.display:
405 env['DISPLAY'] = self.display
406 if self.xauthority:
407 env['XAUTHORITY'] = self.xauthority
408 unique_id = "%i-%i" % (self.vm.getDomid(), time.time())
409 sentinel_path = sentinel_path_prefix + unique_id
410 sentinel_path_fifo = sentinel_path + '.fifo'
411 os.mkfifo(sentinel_path_fifo, 0600)
412 sentinel_write = file(sentinel_path_fifo, 'r+')
413 self._openSentinel(sentinel_path_fifo)
414 self.vm.storeDom("image/device-model-fifo", sentinel_path_fifo)
415 xstransact.Mkdir("/local/domain/0/device-model/%i" % self.vm.getDomid())
416 xstransact.SetPermissions("/local/domain/0/device-model/%i" % self.vm.getDomid(),
417 { 'dom': self.vm.getDomid(), 'read': True, 'write': True })
418 log.info("spawning device models: %s %s", self.device_model, args)
419 # keep track of pid and spawned options to kill it later
421 self.logfile = "/var/log/xen/qemu-dm-%s.log" % str(self.vm.info['name_label'])
423 # rotate log
424 logfile_mode = os.O_WRONLY|os.O_CREAT|os.O_APPEND
425 logrotate_count = XendOptions.instance().get_qemu_dm_logrotate_count()
426 if logrotate_count > 0:
427 logfile_mode |= os.O_TRUNC
428 if os.path.exists("%s.%d" % (self.logfile, logrotate_count)):
429 os.unlink("%s.%d" % (self.logfile, logrotate_count))
430 for n in range(logrotate_count - 1, 0, -1):
431 if os.path.exists("%s.%d" % (self.logfile, n)):
432 os.rename("%s.%d" % (self.logfile, n),
433 "%s.%d" % (self.logfile, (n + 1)))
434 if os.path.exists(self.logfile):
435 os.rename(self.logfile, self.logfile + ".1")
437 null = os.open("/dev/null", os.O_RDONLY)
438 logfd = os.open(self.logfile, logfile_mode)
440 sys.stderr.flush()
441 contract = osdep.prefork("%s:%d" %
442 (self.vm.getName(), self.vm.getDomid()))
443 pid = os.fork()
444 if pid == 0: #child
445 try:
446 osdep.postfork(contract)
447 os.dup2(null, 0)
448 os.dup2(logfd, 1)
449 os.dup2(logfd, 2)
450 oshelp.close_fds((sentinel_write.fileno(),))
451 try:
452 os.execve(self.device_model, args, env)
453 except Exception, e:
454 print >>sys.stderr, (
455 'failed to set up fds or execute dm %s: %s' %
456 (self.device_model, utils.exception_string(e)))
457 os._exit(126)
458 except:
459 os._exit(127)
460 else:
461 osdep.postfork(contract, abandon=True)
462 self.pid = pid
463 os.close(null)
464 os.close(logfd)
465 sentinel_write.close()
466 self.vm.storeDom("image/device-model-pid", self.pid)
467 log.info("device model pid: %d", self.pid)
468 # we would very much prefer not to have a thread here and instead
469 # have a callback but sadly we don't have Twisted in xend
470 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
471 if self.device_model.find('stubdom-dm') > -1 :
472 from xen.xend import XendDomain
473 domains = XendDomain.instance()
474 domains.domains_lock.release()
476 count = 0
477 while True:
478 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
479 % self.vm.getDomid())
480 # This can occur right after start-up
481 if orig_state != None:
482 break
484 log.debug('createDeviceModel %i: orig_state is None, retrying' % self.vm.getDomid())
486 time.sleep(0.1)
487 count += 1
488 if count > 100:
489 break
491 domains.domains_lock.acquire()
493 def signalDeviceModel(self, cmd, ret, par = None):
494 if self.device_model is None:
495 return
496 # Signal the device model to for action
497 if cmd is '' or ret is '':
498 raise VmError('need valid command and result when signal device model')
500 count = 0
501 while True:
502 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
503 % self.vm.getDomid())
504 # This can occur right after start-up
505 if orig_state != None:
506 break
508 log.debug('signalDeviceModel: orig_state is None, retrying')
510 time.sleep(0.1)
511 count += 1
512 if count < 100:
513 continue
515 raise VmError('Device model isn\'t ready for commands')
517 if par is not None:
518 xstransact.Store("/local/domain/0/device-model/%i"
519 % self.vm.getDomid(), ('parameter', par))
521 xstransact.Store("/local/domain/0/device-model/%i"
522 % self.vm.getDomid(), ('command', cmd))
523 # Wait for confirmation. Could do this with a watch but we'd
524 # still end up spinning here waiting for the watch to fire.
525 state = ''
526 count = 0
527 while state != ret:
528 state = xstransact.Read("/local/domain/0/device-model/%i/state"
529 % self.vm.getDomid())
530 time.sleep(0.1)
531 count += 1
532 if count > 100:
533 raise VmError('Timed out waiting for device model action')
535 #resotre orig state
536 xstransact.Store("/local/domain/0/device-model/%i"
537 % self.vm.getDomid(), ('state', orig_state))
538 log.info("signalDeviceModel:restore dm state to %s", orig_state)
540 def saveDeviceModel(self):
541 # Signal the device model to pause itself and save its state
542 self.signalDeviceModel('save', 'paused')
544 def resumeDeviceModel(self):
545 if self.device_model is None:
546 return
547 # Signal the device model to resume activity after pausing to save.
548 xstransact.Store("/local/domain/0/device-model/%i"
549 % self.vm.getDomid(), ('command', 'continue'))
551 def _dmfailed(self, message):
552 log.warning("domain %s: %s", self.vm.getName(), message)
553 try:
554 xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
555 except:
556 pass
558 def recreate(self):
559 if self.device_model is None:
560 return
561 name = self.vm.getName()
562 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
563 fifo_fd = -1
564 log.debug("rediscovering %s", sentinel_path_fifo)
565 if sentinel_path_fifo is None:
566 log.debug("%s device model no sentinel, cannot rediscover", name)
567 else:
568 try:
569 # We open it O_WRONLY because that fails ENXIO if no-one
570 # has it open for reading (see SuSv3). The dm process got
571 # a read/write descriptor from our earlier invocation.
572 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
573 except OSError, e:
574 if e.errno == errno.ENXIO:
575 self._dmfailed("%s device model no longer running"%name)
576 elif e.errno == errno.ENOENT:
577 log.debug("%s device model sentinel %s absent!",
578 name, sentinel_path_fifo)
579 else:
580 raise
581 if fifo_fd >= 0:
582 self._openSentinel(sentinel_path_fifo)
583 os.close(fifo_fd)
584 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
585 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
586 name, self.pid, sentinel_path_fifo)
587 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
589 def _sentinel_watch(self):
590 log.info("waiting for sentinel_fifo")
591 try: self.sentinel_fifo.read(1)
592 except OSError, e: pass
593 self.sentinel_lock.acquire()
594 if self.pid:
595 try:
596 (p,st) = os.waitpid(self.pid, os.WNOHANG)
597 if p == self.pid:
598 message = oshelp.waitstatus_description(st)
599 else:
600 # obviously it is malfunctioning, kill it now
601 try:
602 os.kill(self.pid, signal.SIGKILL)
603 message = "malfunctioning (closed sentinel), killed"
604 except:
605 message = "malfunctioning or died ?"
606 message = "pid %d: %s" % (self.pid, message)
607 except Exception, e:
608 message = "waitpid failed: %s" % utils.exception_string(e)
609 message = "device model failure: %s" % message
610 try: message += "; see %s " % self.logfile
611 except: pass
612 self._dmfailed(message)
613 self.pid = None
614 else:
615 log.info("%s device model terminated", self.vm.getName())
616 self.sentinel_lock.release()
618 def destroyDeviceModel(self):
619 if self.device_model is None:
620 return
621 self.sentinel_lock.acquire()
622 try:
623 stubdomid = self.vm.getStubdomDomid()
624 if stubdomid is not None :
625 from xen.xend import XendDomain
626 XendDomain.instance().domain_destroy(stubdomid)
627 elif self.pid:
628 try:
629 os.kill(self.pid, signal.SIGHUP)
630 except OSError, exn:
631 log.exception(exn)
632 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
633 for i in xrange(100):
634 try:
635 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
636 if p == self.pid:
637 break
638 except OSError:
639 # This is expected if Xend has been restarted within
640 # the life of this domain. In this case, we can kill
641 # the process, but we can't wait for it because it's
642 # not our child. We continue this loop, and after it is
643 # terminated make really sure the process is going away
644 # (SIGKILL).
645 pass
646 time.sleep(0.1)
647 else:
648 log.warning("DeviceModel %d took more than 10s "
649 "to terminate: sending SIGKILL" % self.pid)
650 try:
651 os.kill(self.pid, signal.SIGKILL)
652 os.waitpid(self.pid, 0)
653 except OSError:
654 # This happens if the process doesn't exist.
655 pass
656 finally:
657 self.pid = None
658 self.sentinel_lock.release()
660 state = xstransact.Remove("/local/domain/0/device-model/%i"
661 % self.vm.getDomid())
662 try:
663 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
664 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
665 except:
666 pass
667 try:
668 del sentinel_fifos_inuse[self.sentinel_path_fifo]
669 os.unlink(self.sentinel_path_fifo)
670 except:
671 pass
673 def setCpuid(self):
674 xc.domain_set_policy_cpuid(self.vm.getDomid())
676 if self.cpuid is not None:
677 cpuid = self.cpuid
678 transformed = {}
679 for sinput, regs in cpuid.iteritems():
680 inputs = sinput.split(',')
681 input = long(inputs[0])
682 sub_input = None
683 if len(inputs) == 2:
684 sub_input = long(inputs[1])
685 t = xc.domain_set_cpuid(self.vm.getDomid(),
686 input, sub_input, regs)
687 transformed[sinput] = t
688 self.cpuid = transformed
690 if self.cpuid_check is not None:
691 cpuid_check = self.cpuid_check
692 transformed = {}
693 for sinput, regs_check in cpuid_check.iteritems():
694 inputs = sinput.split(',')
695 input = long(inputs[0])
696 sub_input = None
697 if len(inputs) == 2:
698 sub_input = long(inputs[1])
699 t = xc.domain_check_cpuid(input, sub_input, regs_check)
700 transformed[sinput] = t
701 self.cpuid_check = transformed
703 class LinuxImageHandler(ImageHandler):
705 ostype = "linux"
706 flags = 0
707 vhpt = 0
709 def configure(self, vmConfig):
710 ImageHandler.configure(self, vmConfig)
711 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
712 self.is_stubdom = (self.kernel.find('stubdom') >= 0)
713 self.superpages = int(vmConfig['superpages'])
715 def buildDomain(self):
716 store_evtchn = self.vm.getStorePort()
717 console_evtchn = self.vm.getConsolePort()
719 mem_mb = self.getRequiredInitialReservation() / 1024
721 log.debug("domid = %d", self.vm.getDomid())
722 log.debug("memsize = %d", mem_mb)
723 log.debug("image = %s", self.kernel)
724 log.debug("store_evtchn = %d", store_evtchn)
725 log.debug("console_evtchn = %d", console_evtchn)
726 log.debug("cmdline = %s", self.cmdline)
727 log.debug("ramdisk = %s", self.ramdisk)
728 log.debug("vcpus = %d", self.vm.getVCpuCount())
729 log.debug("features = %s", self.vm.getFeatures())
730 log.debug("flags = %d", self.flags)
731 log.debug("superpages = %d", self.superpages)
732 if arch.type == "ia64":
733 log.debug("vhpt = %d", self.vhpt)
735 return xc.linux_build(domid = self.vm.getDomid(),
736 memsize = mem_mb,
737 image = self.kernel,
738 store_evtchn = store_evtchn,
739 console_evtchn = console_evtchn,
740 cmdline = self.cmdline,
741 ramdisk = self.ramdisk,
742 features = self.vm.getFeatures(),
743 flags = self.flags,
744 vhpt = self.vhpt,
745 superpages = self.superpages)
747 def getBitSize(self):
748 return xc.getBitSize(image = self.kernel,
749 cmdline = self.cmdline,
750 features = self.vm.getFeatures()
751 ).get('type')
753 def getRequiredAvailableMemory(self, mem_kb):
754 if self.is_stubdom :
755 mem_kb += self.vramsize
756 return mem_kb
758 def getRequiredInitialReservation(self):
759 return self.vm.getMemoryTarget()
761 def getRequiredMaximumReservation(self):
762 return self.vm.getMemoryMaximum()
764 def parseDeviceModelArgs(self, vmConfig):
765 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
766 if vmConfig['platform'].get('serial'):
767 ret = ["-serial", vmConfig['platform'].get('serial')] + ret
768 else:
769 ret = ["-serial", "pty"] + ret
770 return ret
772 def getDeviceModelArgs(self, restore = False):
773 args = ImageHandler.getDeviceModelArgs(self, restore)
774 args = args + ([ "-M", "xenpv"])
775 return args
778 class HVMImageHandler(ImageHandler):
780 ostype = "hvm"
782 def __init__(self, vm, vmConfig):
783 ImageHandler.__init__(self, vm, vmConfig)
784 self.shutdownWatch = None
785 self.rebootFeatureWatch = None
787 def getBitSize(self):
788 return None
790 def configure(self, vmConfig):
791 ImageHandler.configure(self, vmConfig)
793 self.loader = vmConfig['platform'].get('loader')
795 info = xc.xeninfo()
796 if 'hvm' not in info['xen_caps']:
797 raise HVMRequired()
799 xen_platform_pci = int(vmConfig['platform'].get('xen_platform_pci',1))
800 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
802 if not self.display :
803 self.display = ''
805 store_dmargs = [ x for x in self.dmargs
806 if x not in ['-sdl', '-disable-opengl'] ]
807 try :
808 midx = store_dmargs.index('-monitor')
809 store_dmargs[midx + 1] = 'pty'
810 except ValueError :
811 pass
812 self.vm.storeVm(("image/dmargs", " ".join(store_dmargs)),
813 ("image/device-model", self.device_model),
814 ("image/display", self.display))
815 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
817 if xen_platform_pci == 0:
818 disable_pf = 1
819 log.info("No need to create platform device.[domid:%d]", self.vm.getDomid())
820 else:
821 disable_pf = 0
822 log.info("Need to create platform device.[domid:%d]", self.vm.getDomid())
824 xstransact.Store("/local/domain/0/device-model/%i"%self.vm.getDomid(),
825 ('disable_pf', disable_pf))
826 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
827 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
829 self.apic = int(vmConfig['platform'].get('apic', 0))
830 self.acpi = int(vmConfig['platform'].get('acpi', 0))
831 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
832 self.memory_sharing = int(vmConfig['memory_sharing'])
833 xc.dom_set_memshr(self.vm.getDomid(), self.memory_sharing)
836 # Return a list of cmd line args to the device models based on the
837 # xm config file
838 def parseDeviceModelArgs(self, vmConfig):
839 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
840 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
841 ret = ret + ['-vcpu_avail', str(self.vm.getVCpuAvail())]
843 if self.kernel:
844 log.debug("kernel = %s", self.kernel)
845 ret = ret + ['-kernel', self.kernel]
846 if self.ramdisk:
847 log.debug("ramdisk = %s", self.ramdisk)
848 ret = ret + ['-initrd', self.ramdisk]
849 if self.cmdline:
850 log.debug("cmdline = %s", self.cmdline)
851 ret = ret + ['-append', self.cmdline]
854 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
855 'localtime', 'serial', 'stdvga', 'isa',
856 'acpi', 'usb', 'usbdevice', 'gfx_passthru' ]
858 for a in dmargs:
859 v = vmConfig['platform'].get(a)
861 # python doesn't allow '-' in variable names
862 if a == 'stdvga': a = 'std-vga'
863 if a == 'keymap': a = 'k'
865 # Handle booleans gracefully
866 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
867 try:
868 if v != None: v = int(v)
869 if v: ret.append("-%s" % a)
870 except (ValueError, TypeError):
871 pass # if we can't convert it to a sane type, ignore it
872 elif a == 'serial':
873 if v:
874 if type(v) == str:
875 v = [v]
876 for s in v:
877 if s:
878 ret.append("-serial")
879 ret.append("%s" % s)
880 else:
881 if v:
882 ret.append("-%s" % a)
883 ret.append("%s" % v)
885 if a in ['fda', 'fdb']:
886 if v:
887 if not os.path.isabs(v):
888 raise VmError("Floppy file %s does not exist." % v)
889 log.debug("args: %s, val: %s" % (a,v))
891 # Handle disk/network related options
892 mac = None
893 nics = 0
895 for devuuid in vmConfig['vbd_refs']:
896 devinfo = vmConfig['devices'][devuuid][1]
897 uname = devinfo.get('uname')
898 if uname is not None and 'file:' in uname:
899 (_, vbdparam) = string.split(uname, ':', 1)
900 if not os.path.isfile(vbdparam):
901 raise VmError('Disk image does not exist: %s' %
902 vbdparam)
904 for devuuid in vmConfig['vif_refs']:
905 devinfo = vmConfig['devices'][devuuid][1]
906 dtype = devinfo.get('type', 'ioemu')
907 if dtype != 'ioemu':
908 continue
909 nics += 1
910 mac = devinfo.get('mac')
911 if mac is None:
912 raise VmError("MAC address not specified or generated.")
913 bridge = devinfo.get('bridge', 'xenbr0')
914 model = devinfo.get('model', 'rtl8139')
915 ret.append("-net")
916 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
917 (nics, mac, model))
918 vifname = devinfo.get('vifname')
919 if vifname:
920 vifname = "tap-" + vifname
921 else:
922 vifname = "tap%d.%d" % (self.vm.getDomid(), nics-1)
923 ret.append("-net")
924 ret.append("tap,vlan=%d,ifname=%s,bridge=%s" %
925 (nics, vifname, bridge))
927 if nics == 0:
928 ret.append("-net")
929 ret.append("none")
931 return ret
933 def getDeviceModelArgs(self, restore = False):
934 args = ImageHandler.getDeviceModelArgs(self, restore)
935 args = args + ([ "-M", "xenfv"])
936 if restore:
937 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
938 self.vm.getDomid() ])
939 return args
941 def buildDomain(self):
942 store_evtchn = self.vm.getStorePort()
944 memmax_mb = self.getRequiredMaximumReservation() / 1024
945 mem_mb = self.getRequiredInitialReservation() / 1024
947 log.debug("domid = %d", self.vm.getDomid())
948 log.debug("image = %s", self.loader)
949 log.debug("store_evtchn = %d", store_evtchn)
950 log.debug("memsize = %d", memmax_mb)
951 log.debug("target = %d", mem_mb)
952 log.debug("vcpus = %d", self.vm.getVCpuCount())
953 log.debug("vcpu_avail = %li", self.vm.getVCpuAvail())
954 log.debug("acpi = %d", self.acpi)
955 log.debug("apic = %d", self.apic)
957 rc = xc.hvm_build(domid = self.vm.getDomid(),
958 image = self.loader,
959 memsize = memmax_mb,
960 target = mem_mb,
961 vcpus = self.vm.getVCpuCount(),
962 vcpu_avail = self.vm.getVCpuAvail(),
963 acpi = self.acpi,
964 apic = self.apic)
965 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
967 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
968 HVM_PARAM_STORE_PFN)
969 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
970 store_evtchn)
972 return rc
975 class IA64_HVM_ImageHandler(HVMImageHandler):
977 def configure(self, vmConfig):
978 HVMImageHandler.configure(self, vmConfig)
979 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
980 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
982 def buildDomain(self):
983 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
984 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
985 if self.guest_os_type is not None:
986 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
987 return HVMImageHandler.buildDomain(self)
989 def getRequiredAvailableMemory(self, mem_kb):
990 page_kb = 16
991 # ROM size for guest firmware, io page, xenstore page
992 # buffer io page, buffer pio page and memmap info page
993 extra_pages = 1024 + 5
994 mem_kb += extra_pages * page_kb
995 mem_kb += self.vramsize
996 return mem_kb
998 def getRequiredInitialReservation(self):
999 return self.vm.getMemoryTarget()
1001 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
1002 # Explicit shadow memory is not a concept
1003 return 0
1005 def getDeviceModelArgs(self, restore = False):
1006 args = HVMImageHandler.getDeviceModelArgs(self, restore)
1007 args = args + ([ "-m", "%s" %
1008 (self.getRequiredInitialReservation() / 1024) ])
1009 return args
1011 def setCpuid(self):
1012 # Guest CPUID configuration is not implemented yet.
1013 return
1015 class IA64_Linux_ImageHandler(LinuxImageHandler):
1017 def configure(self, vmConfig):
1018 LinuxImageHandler.configure(self, vmConfig)
1019 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
1021 def setCpuid(self):
1022 # Guest CPUID configuration is not implemented yet.
1023 return
1025 class X86_HVM_ImageHandler(HVMImageHandler):
1027 def configure(self, vmConfig):
1028 HVMImageHandler.configure(self, vmConfig)
1029 self.pae = int(vmConfig['platform'].get('pae', 0))
1030 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
1032 def buildDomain(self):
1033 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
1034 rc = HVMImageHandler.buildDomain(self)
1035 self.setCpuid()
1036 return rc
1038 def getBitSize(self):
1039 return None
1041 def getRequiredAvailableMemory(self, mem_kb):
1042 return mem_kb + self.vramsize
1044 def getRequiredInitialReservation(self):
1045 return self.vm.getMemoryTarget()
1047 def getRequiredMaximumReservation(self):
1048 return self.vm.getMemoryMaximum()
1050 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
1051 # 256 pages (1MB) per vcpu,
1052 # plus 1 page per MiB of RAM for the P2M map,
1053 # plus 1 page per MiB of RAM to shadow the resident processes.
1054 # This is higher than the minimum that Xen would allocate if no value
1055 # were given (but the Xen minimum is for safety, not performance).
1056 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
1057 shadow_mem_kb)
1060 class X86_Linux_ImageHandler(LinuxImageHandler):
1062 def buildDomain(self):
1063 # set physical mapping limit
1064 # add an 8MB slack to balance backend allocations.
1065 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
1066 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
1067 rc = LinuxImageHandler.buildDomain(self)
1068 self.setCpuid()
1069 return rc
1071 _handlers = {
1072 "ia64": {
1073 "linux": IA64_Linux_ImageHandler,
1074 "hvm": IA64_HVM_ImageHandler,
1075 },
1076 "x86": {
1077 "linux": X86_Linux_ImageHandler,
1078 "hvm": X86_HVM_ImageHandler,
1079 },
1082 def findImageHandlerClass(image):
1083 """Find the image handler class for an image config.
1085 @param image config
1086 @return ImageHandler subclass or None
1087 """
1088 image_type = image.image_type()
1089 try:
1090 return _handlers[arch.type][image_type]
1091 except KeyError:
1092 raise VmError('unknown image type: ' + image_type)