ia64/xen-unstable

view tools/python/xen/xend/image.py @ 19684:46468a3b2d5f

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