ia64/xen-unstable

view tools/python/xen/xend/image.py @ 19488:79f734040a83

xend: modify a warning log message

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Apr 01 11:47:59 2009 +0100 (2009-04-01)
parents 5d701be7c37b
children b920a2f799d9
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
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', {})
295 self.xauthority = dev_info.get('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 orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
458 % self.vm.getDomid())
460 if par is not None:
461 xstransact.Store("/local/domain/0/device-model/%i"
462 % self.vm.getDomid(), ('parameter', par))
464 xstransact.Store("/local/domain/0/device-model/%i"
465 % self.vm.getDomid(), ('command', cmd))
466 # Wait for confirmation. Could do this with a watch but we'd
467 # still end up spinning here waiting for the watch to fire.
468 state = ''
469 count = 0
470 while state != ret:
471 state = xstransact.Read("/local/domain/0/device-model/%i/state"
472 % self.vm.getDomid())
473 time.sleep(0.1)
474 count += 1
475 if count > 100:
476 raise VmError('Timed out waiting for device model action')
478 #resotre orig state
479 xstransact.Store("/local/domain/0/device-model/%i"
480 % self.vm.getDomid(), ('state', orig_state))
481 log.info("signalDeviceModel:restore dm state to %s", orig_state)
483 def saveDeviceModel(self):
484 # Signal the device model to pause itself and save its state
485 self.signalDeviceModel('save', 'paused')
487 def resumeDeviceModel(self):
488 if self.device_model is None:
489 return
490 # Signal the device model to resume activity after pausing to save.
491 xstransact.Store("/local/domain/0/device-model/%i"
492 % self.vm.getDomid(), ('command', 'continue'))
494 def _dmfailed(self, message):
495 log.warning("domain %s: %s", self.vm.getName(), message)
496 try:
497 xc.domain_shutdown(self.vm.getDomid(), DOMAIN_CRASH)
498 except:
499 pass
501 def recreate(self):
502 if self.device_model is None:
503 return
504 name = self.vm.getName()
505 sentinel_path_fifo = self.vm.readDom('image/device-model-fifo')
506 fifo_fd = -1
507 log.debug("rediscovering %s", sentinel_path_fifo)
508 if sentinel_path_fifo is None:
509 log.debug("%s device model no sentinel, cannot rediscover", name)
510 else:
511 try:
512 # We open it O_WRONLY because that fails ENXIO if no-one
513 # has it open for reading (see SuSv3). The dm process got
514 # a read/write descriptor from our earlier invocation.
515 fifo_fd = os.open(sentinel_path_fifo, os.O_WRONLY|os.O_NONBLOCK)
516 except OSError, e:
517 if e.errno == errno.ENXIO:
518 self._dmfailed("%s device model no longer running"%name)
519 elif e.errno == errno.ENOENT:
520 log.debug("%s device model sentinel %s absent!",
521 name, sentinel_path_fifo)
522 else:
523 raise
524 if fifo_fd >= 0:
525 self._openSentinel(sentinel_path_fifo)
526 os.close(fifo_fd)
527 self.pid = self.vm.gatherDom(('image/device-model-pid', int))
528 log.debug("%s device model rediscovered, pid %s sentinel fifo %s",
529 name, self.pid, sentinel_path_fifo)
530 self.sentinel_thread = thread.start_new_thread(self._sentinel_watch,())
532 def _sentinel_watch(self):
533 log.info("waiting for sentinel_fifo")
534 try: self.sentinel_fifo.read(1)
535 except OSError, e: pass
536 self.sentinel_lock.acquire()
537 if self.pid:
538 try:
539 (p,st) = os.waitpid(self.pid, os.WNOHANG)
540 if p == self.pid:
541 message = oshelp.waitstatus_description(st)
542 else:
543 # obviously it is malfunctioning, kill it now
544 try:
545 os.kill(self.pid, signal.SIGKILL)
546 message = "malfunctioning (closed sentinel), killed"
547 except:
548 message = "malfunctioning or died ?"
549 message = "pid %d: %s" % (self.pid, message)
550 except Exception, e:
551 message = "waitpid failed: %s" % utils.exception_string(e)
552 message = "device model failure: %s" % message
553 try: message += "; see %s " % self.logfile
554 except: pass
555 self._dmfailed(message)
556 self.pid = None
557 else:
558 log.info("%s device model terminated", self.vm.getName())
559 self.sentinel_lock.release()
561 def destroyDeviceModel(self):
562 if self.device_model is None:
563 return
564 if self.pid:
565 self.sentinel_lock.acquire()
566 try:
567 try:
568 os.kill(self.pid, signal.SIGHUP)
569 except OSError, exn:
570 log.exception(exn)
571 # Try to reap the child every 100ms for 10s. Then SIGKILL it.
572 for i in xrange(100):
573 try:
574 (p, rv) = os.waitpid(self.pid, os.WNOHANG)
575 if p == self.pid:
576 break
577 except OSError:
578 # This is expected if Xend has been restarted within
579 # the life of this domain. In this case, we can kill
580 # the process, but we can't wait for it because it's
581 # not our child. We continue this loop, and after it is
582 # terminated make really sure the process is going away
583 # (SIGKILL).
584 pass
585 time.sleep(0.1)
586 else:
587 log.warning("DeviceModel %d took more than 10s "
588 "to terminate: sending SIGKILL" % self.pid)
589 try:
590 os.kill(self.pid, signal.SIGKILL)
591 os.waitpid(self.pid, 0)
592 except OSError:
593 # This happens if the process doesn't exist.
594 pass
595 state = xstransact.Remove("/local/domain/0/device-model/%i"
596 % self.vm.getDomid())
597 finally:
598 self.pid = None
599 self.sentinel_lock.release()
601 try:
602 os.unlink('/var/run/tap/qemu-read-%d' % self.vm.getDomid())
603 os.unlink('/var/run/tap/qemu-write-%d' % self.vm.getDomid())
604 except:
605 pass
606 try:
607 del sentinel_fifos_inuse[self.sentinel_path_fifo]
608 os.unlink(self.sentinel_path_fifo)
609 except:
610 pass
612 def setCpuid(self):
613 xc.domain_set_policy_cpuid(self.vm.getDomid())
615 if self.cpuid is not None:
616 cpuid = self.cpuid
617 transformed = {}
618 for sinput, regs in cpuid.iteritems():
619 inputs = sinput.split(',')
620 input = long(inputs[0])
621 sub_input = None
622 if len(inputs) == 2:
623 sub_input = long(inputs[1])
624 t = xc.domain_set_cpuid(self.vm.getDomid(),
625 input, sub_input, regs)
626 transformed[sinput] = t
627 self.cpuid = transformed
629 if self.cpuid_check is not None:
630 cpuid_check = self.cpuid_check
631 transformed = {}
632 for sinput, regs_check in cpuid_check.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_check_cpuid(input, sub_input, regs_check)
639 transformed[sinput] = t
640 self.cpuid_check = transformed
644 class LinuxImageHandler(ImageHandler):
646 ostype = "linux"
647 flags = 0
648 vhpt = 0
650 def configure(self, vmConfig):
651 ImageHandler.configure(self, vmConfig)
652 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
653 self.is_stubdom = (self.kernel.find('stubdom') >= 0)
655 def buildDomain(self):
656 store_evtchn = self.vm.getStorePort()
657 console_evtchn = self.vm.getConsolePort()
659 mem_mb = self.getRequiredInitialReservation() / 1024
661 log.debug("domid = %d", self.vm.getDomid())
662 log.debug("memsize = %d", mem_mb)
663 log.debug("image = %s", self.kernel)
664 log.debug("store_evtchn = %d", store_evtchn)
665 log.debug("console_evtchn = %d", console_evtchn)
666 log.debug("cmdline = %s", self.cmdline)
667 log.debug("ramdisk = %s", self.ramdisk)
668 log.debug("vcpus = %d", self.vm.getVCpuCount())
669 log.debug("features = %s", self.vm.getFeatures())
670 log.debug("flags = %d", self.flags)
671 if arch.type == "ia64":
672 log.debug("vhpt = %d", self.vhpt)
674 return xc.linux_build(domid = self.vm.getDomid(),
675 memsize = mem_mb,
676 image = self.kernel,
677 store_evtchn = store_evtchn,
678 console_evtchn = console_evtchn,
679 cmdline = self.cmdline,
680 ramdisk = self.ramdisk,
681 features = self.vm.getFeatures(),
682 flags = self.flags,
683 vhpt = self.vhpt)
685 def getRequiredAvailableMemory(self, mem_kb):
686 if self.is_stubdom :
687 mem_kb += self.vramsize
688 return mem_kb
690 def getRequiredInitialReservation(self):
691 return self.vm.getMemoryTarget()
693 def getRequiredMaximumReservation(self):
694 return self.vm.getMemoryMaximum()
696 def parseDeviceModelArgs(self, vmConfig):
697 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
698 # Equivalent to old xenconsoled behaviour. Should make
699 # it configurable in future
700 ret = ret + ["-serial", "pty"]
701 return ret
703 def getDeviceModelArgs(self, restore = False):
704 args = ImageHandler.getDeviceModelArgs(self, restore)
705 args = args + ([ "-M", "xenpv"])
706 return args
709 class HVMImageHandler(ImageHandler):
711 ostype = "hvm"
713 def __init__(self, vm, vmConfig):
714 ImageHandler.__init__(self, vm, vmConfig)
715 self.shutdownWatch = None
716 self.rebootFeatureWatch = None
718 def configure(self, vmConfig):
719 ImageHandler.configure(self, vmConfig)
721 self.loader = vmConfig['platform'].get('loader')
723 info = xc.xeninfo()
724 if 'hvm' not in info['xen_caps']:
725 raise HVMRequired()
727 xen_platform_pci = int(vmConfig['platform'].get('xen_platform_pci',1))
728 rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
730 if not self.display :
731 self.display = ''
732 self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
733 ("image/device-model", self.device_model),
734 ("image/display", self.display))
735 self.vm.permissionsVm("image/dmargs", { 'dom': self.vm.getDomid(), 'read': True } )
737 if xen_platform_pci == 0:
738 disable_pf = 1
739 log.info("No need to create platform device.[domid:%d]", self.vm.getDomid())
740 else:
741 disable_pf = 0
742 log.info("Need to create platform device.[domid:%d]", self.vm.getDomid())
744 xstransact.Store("/local/domain/0/device-model/%i"%self.vm.getDomid(),
745 ('disable_pf', disable_pf))
746 self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
747 self.vm.permissionsVm("rtc/timeoffset", { 'dom': self.vm.getDomid(), 'read': True } )
749 self.apic = int(vmConfig['platform'].get('apic', 0))
750 self.acpi = int(vmConfig['platform'].get('acpi', 0))
751 self.guest_os_type = vmConfig['platform'].get('guest_os_type')
754 # Return a list of cmd line args to the device models based on the
755 # xm config file
756 def parseDeviceModelArgs(self, vmConfig):
757 ret = ImageHandler.parseDeviceModelArgs(self, vmConfig)
758 ret = ret + ['-vcpus', str(self.vm.getVCpuCount())]
760 if self.kernel:
761 log.debug("kernel = %s", self.kernel)
762 ret = ret + ['-kernel', self.kernel]
763 if self.ramdisk:
764 log.debug("ramdisk = %s", self.ramdisk)
765 ret = ret + ['-initrd', self.ramdisk]
766 if self.cmdline:
767 log.debug("cmdline = %s", self.cmdline)
768 ret = ret + ['-append', self.cmdline]
771 dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
772 'localtime', 'serial', 'stdvga', 'isa',
773 'acpi', 'usb', 'usbdevice' ]
775 for a in dmargs:
776 v = vmConfig['platform'].get(a)
778 # python doesn't allow '-' in variable names
779 if a == 'stdvga': a = 'std-vga'
780 if a == 'keymap': a = 'k'
782 # Handle booleans gracefully
783 if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
784 try:
785 if v != None: v = int(v)
786 if v: ret.append("-%s" % a)
787 except (ValueError, TypeError):
788 pass # if we can't convert it to a sane type, ignore it
789 elif a == 'serial':
790 if v:
791 if type(v) == str:
792 v = [v]
793 for s in v:
794 if s:
795 ret.append("-serial")
796 ret.append("%s" % s)
797 else:
798 if v:
799 ret.append("-%s" % a)
800 ret.append("%s" % v)
802 if a in ['fda', 'fdb']:
803 if v:
804 if not os.path.isabs(v):
805 raise VmError("Floppy file %s does not exist." % v)
806 log.debug("args: %s, val: %s" % (a,v))
808 # Handle disk/network related options
809 mac = None
810 nics = 0
812 for devuuid in vmConfig['vbd_refs']:
813 devinfo = vmConfig['devices'][devuuid][1]
814 uname = devinfo.get('uname')
815 if uname is not None and 'file:' in uname:
816 (_, vbdparam) = string.split(uname, ':', 1)
817 if not os.path.isfile(vbdparam):
818 raise VmError('Disk image does not exist: %s' %
819 vbdparam)
821 for devuuid in vmConfig['vif_refs']:
822 devinfo = vmConfig['devices'][devuuid][1]
823 dtype = devinfo.get('type', 'ioemu')
824 if dtype != 'ioemu':
825 continue
826 nics += 1
827 mac = devinfo.get('mac')
828 if mac is None:
829 raise VmError("MAC address not specified or generated.")
830 bridge = devinfo.get('bridge', 'xenbr0')
831 model = devinfo.get('model', 'rtl8139')
832 ret.append("-net")
833 ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
834 (nics, mac, model))
835 ret.append("-net")
836 ret.append("tap,vlan=%d,ifname=tap%d.%d,bridge=%s" %
837 (nics, self.vm.getDomid(), nics-1, bridge))
839 if nics == 0:
840 ret.append("-net")
841 ret.append("none")
843 return ret
845 def getDeviceModelArgs(self, restore = False):
846 args = ImageHandler.getDeviceModelArgs(self, restore)
847 args = args + ([ "-M", "xenfv"])
848 if restore:
849 args = args + ([ "-loadvm", "/var/lib/xen/qemu-save.%d" %
850 self.vm.getDomid() ])
851 return args
853 def buildDomain(self):
854 store_evtchn = self.vm.getStorePort()
856 memmax_mb = self.getRequiredMaximumReservation() / 1024
857 mem_mb = self.getRequiredInitialReservation() / 1024
859 log.debug("domid = %d", self.vm.getDomid())
860 log.debug("image = %s", self.loader)
861 log.debug("store_evtchn = %d", store_evtchn)
862 log.debug("memsize = %d", memmax_mb)
863 log.debug("target = %d", mem_mb)
864 log.debug("vcpus = %d", self.vm.getVCpuCount())
865 log.debug("acpi = %d", self.acpi)
866 log.debug("apic = %d", self.apic)
868 rc = xc.hvm_build(domid = self.vm.getDomid(),
869 image = self.loader,
870 memsize = memmax_mb,
871 target = mem_mb,
872 vcpus = self.vm.getVCpuCount(),
873 acpi = self.acpi,
874 apic = self.apic)
875 rc['notes'] = { 'SUSPEND_CANCEL': 1 }
877 rc['store_mfn'] = xc.hvm_get_param(self.vm.getDomid(),
878 HVM_PARAM_STORE_PFN)
879 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_STORE_EVTCHN,
880 store_evtchn)
882 return rc
885 class IA64_HVM_ImageHandler(HVMImageHandler):
887 def configure(self, vmConfig):
888 HVMImageHandler.configure(self, vmConfig)
889 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
890 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
892 def buildDomain(self):
893 xc.nvram_init(self.vm.getName(), self.vm.getDomid())
894 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_VHPT_SIZE, self.vhpt)
895 if self.guest_os_type is not None:
896 xc.set_os_type(self.guest_os_type.lower(), self.vm.getDomid())
897 return HVMImageHandler.buildDomain(self)
899 def getRequiredAvailableMemory(self, mem_kb):
900 page_kb = 16
901 # ROM size for guest firmware, io page, xenstore page
902 # buffer io page, buffer pio page and memmap info page
903 extra_pages = 1024 + 5
904 mem_kb += extra_pages * page_kb
905 mem_kb += self.vramsize
906 return mem_kb
908 def getRequiredInitialReservation(self):
909 return self.vm.getMemoryTarget()
911 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
912 # Explicit shadow memory is not a concept
913 return 0
915 def getDeviceModelArgs(self, restore = False):
916 args = HVMImageHandler.getDeviceModelArgs(self, restore)
917 args = args + ([ "-m", "%s" %
918 (self.getRequiredInitialReservation() / 1024) ])
919 return args
921 def setCpuid(self):
922 # Guest CPUID configuration is not implemented yet.
923 return
925 class IA64_Linux_ImageHandler(LinuxImageHandler):
927 def configure(self, vmConfig):
928 LinuxImageHandler.configure(self, vmConfig)
929 self.vhpt = int(vmConfig['platform'].get('vhpt', 0))
931 def setCpuid(self):
932 # Guest CPUID configuration is not implemented yet.
933 return
935 class X86_HVM_ImageHandler(HVMImageHandler):
937 def configure(self, vmConfig):
938 HVMImageHandler.configure(self, vmConfig)
939 self.pae = int(vmConfig['platform'].get('pae', 0))
940 self.vramsize = int(vmConfig['platform'].get('videoram',4)) * 1024
942 def buildDomain(self):
943 xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
944 rc = HVMImageHandler.buildDomain(self)
945 self.setCpuid()
946 return rc
948 def getRequiredAvailableMemory(self, mem_kb):
949 return mem_kb + self.vramsize
951 def getRequiredInitialReservation(self):
952 return self.vm.getMemoryTarget()
954 def getRequiredMaximumReservation(self):
955 return self.vm.getMemoryMaximum()
957 def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
958 # 256 pages (1MB) per vcpu,
959 # plus 1 page per MiB of RAM for the P2M map,
960 # plus 1 page per MiB of RAM to shadow the resident processes.
961 # This is higher than the minimum that Xen would allocate if no value
962 # were given (but the Xen minimum is for safety, not performance).
963 return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)),
964 shadow_mem_kb)
967 class X86_Linux_ImageHandler(LinuxImageHandler):
969 def buildDomain(self):
970 # set physical mapping limit
971 # add an 8MB slack to balance backend allocations.
972 mem_kb = self.getRequiredMaximumReservation() + (8 * 1024)
973 xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb)
974 rc = LinuxImageHandler.buildDomain(self)
975 self.setCpuid()
976 return rc
978 _handlers = {
979 "ia64": {
980 "linux": IA64_Linux_ImageHandler,
981 "hvm": IA64_HVM_ImageHandler,
982 },
983 "x86": {
984 "linux": X86_Linux_ImageHandler,
985 "hvm": X86_HVM_ImageHandler,
986 },
987 }
989 def findImageHandlerClass(image):
990 """Find the image handler class for an image config.
992 @param image config
993 @return ImageHandler subclass or None
994 """
995 image_type = image.image_type()
996 try:
997 return _handlers[arch.type][image_type]
998 except KeyError:
999 raise VmError('unknown image type: ' + image_type)