~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/xend/image.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#============================================================================
 
18
 
 
19
 
 
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
 
32
 
 
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
 
45
 
 
46
xc = xen.lowlevel.xc.xc()
 
47
 
 
48
MAX_GUEST_CMDLINE = 1024
 
49
 
 
50
sentinel_path_prefix = '/var/run/xend/dm-'
 
51
sentinel_fifos_inuse = { }
 
52
 
 
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))
 
60
 
 
61
def create(vm, vmConfig):
 
62
    """Create an image handler for a vm.
 
63
 
 
64
    @return ImageHandler instance
 
65
    """
 
66
    return findImageHandlerClass(vmConfig)(vm, vmConfig)
 
67
 
 
68
 
 
69
class ImageHandler:
 
70
    """Abstract base class for image handlers.
 
71
 
 
72
    createImage() is called to configure and build the domain from its
 
73
    kernel image and ramdisk etc.
 
74
 
 
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.
 
78
 
 
79
    The method createDeviceModel() is called to create the domain device
 
80
    model.
 
81
 
 
82
    The method destroyDeviceModel() is called to reap the device model
 
83
    """
 
84
 
 
85
    ostype = None
 
86
    superpages = 0
 
87
    memory_sharing = 0
 
88
 
 
89
    def __init__(self, vm, vmConfig):
 
90
        self.vm = vm
 
91
 
 
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
 
98
 
 
99
        self.configure(vmConfig)
 
100
 
 
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 } )
 
123
 
 
124
        self.device_model = vmConfig['platform'].get('device_model')
 
125
 
 
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)
 
139
 
 
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']
 
146
 
 
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)
 
152
 
 
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)
 
159
 
 
160
 
 
161
    def createImage(self):
 
162
        """Entry point to create domain memory image.
 
163
        Override in subclass  if needed.
 
164
        """
 
165
        return self.createDomain()
 
166
 
 
167
 
 
168
    def createDomain(self):
 
169
        """Build the domain boot image.
 
170
        """
 
171
        # Set params and call buildDomain().
 
172
 
 
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())
 
180
        
 
181
        log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype,
 
182
                 self.vm.getDomid(), self.vm.getVCpuCount())
 
183
 
 
184
        result = self.buildDomain()
 
185
 
 
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)))
 
191
 
 
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
 
199
 
 
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())
 
207
 
 
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())
 
215
 
 
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
 
223
 
 
224
    def buildDomain(self):
 
225
        """Build the domain. Define in subclass."""
 
226
        raise NotImplementedError()
 
227
 
 
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."""
 
231
        
 
232
        domid = self.vm.getDomid()
 
233
        
 
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
 
240
 
 
241
        # No device model, don't create pipes
 
242
        if self.device_model is None:
 
243
            return
 
244
 
 
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)
 
249
 
 
250
            try:
 
251
                os.makedirs('/var/run/tap', 0755)
 
252
            except:
 
253
                pass
 
254
 
 
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
 
262
 
 
263
 
 
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'])]
 
268
 
 
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))
 
276
 
 
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
 
283
 
 
284
        vram = str(vmConfig['platform'].get('videoram',4))
 
285
        ret.append('-videoram')
 
286
        ret.append(vram)
 
287
 
 
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
 
317
 
 
318
        if keymap:
 
319
            ret.append("-k")
 
320
            ret.append(keymap)
 
321
 
 
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")
 
340
 
 
341
            if XendOptions.instance().get_vnc_tls():
 
342
                vncx509certdir = XendOptions.instance().get_vnc_x509_cert_dir()
 
343
                vncx509verify = XendOptions.instance().get_vnc_x509_verify()
 
344
 
 
345
                if not os.path.exists(vncx509certdir):
 
346
                    raise VmError("VNC x509 certificate dir %s does not exist" % vncx509certdir)
 
347
 
 
348
                if vncx509verify:
 
349
                    vncopts = vncopts + ",tls,x509verify=%s" % vncx509certdir
 
350
                else:
 
351
                    vncopts = vncopts + ",tls,x509=%s" % vncx509certdir
 
352
 
 
353
 
 
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))
 
359
 
 
360
            if int(vnc_config.get('vncunused', 1)) != 0:
 
361
                ret.append('-vncunused')
 
362
 
 
363
        if has_sdl:
 
364
            ret.append('-sdl')
 
365
            if int(vmConfig['platform'].get('opengl', opengl)) != 1 :
 
366
                ret.append('-disable-opengl')
 
367
 
 
368
        if not has_sdl and not has_vnc :
 
369
            ret.append('-nographic')
 
370
 
 
371
        if vmConfig['platform'].get('parallel'):
 
372
            ret = ret + ["-parallel", vmConfig['platform'].get('parallel')]
 
373
 
 
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']
 
379
 
 
380
        return ret
 
381
 
 
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
 
387
 
 
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
 
394
 
 
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
 
420
 
 
421
        self.logfile = "/var/log/xen/qemu-dm-%s.log" %  str(self.vm.info['name_label'])
 
422
 
 
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")
 
436
 
 
437
        null = os.open("/dev/null", os.O_RDONLY)
 
438
        logfd = os.open(self.logfile, logfile_mode, 0666)
 
439
        
 
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()
 
475
 
 
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
 
483
 
 
484
                log.debug('createDeviceModel %i: orig_state is None, retrying' % self.vm.getDomid())
 
485
 
 
486
                time.sleep(0.1)
 
487
                count += 1
 
488
                if count > 100:
 
489
                    break
 
490
 
 
491
            domains.domains_lock.acquire()
 
492
 
 
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')
 
499
 
 
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
 
507
 
 
508
            log.debug('signalDeviceModel: orig_state is None, retrying')
 
509
 
 
510
            time.sleep(0.1)
 
511
            count += 1
 
512
            if count < 100:
 
513
                continue
 
514
 
 
515
            raise VmError('Device model isn\'t ready for commands')
 
516
 
 
517
        if par is not None:
 
518
            xstransact.Store("/local/domain/0/device-model/%i"
 
519
                             % self.vm.getDomid(), ('parameter', par))
 
520
 
 
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')
 
534
 
 
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)
 
539
 
 
540
    def saveDeviceModel(self):
 
541
        # Signal the device model to pause itself and save its state
 
542
        self.signalDeviceModel('save', 'paused')
 
543
 
 
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'))
 
550
 
 
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
 
557
 
 
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,())
 
588
 
 
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()
 
617
 
 
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()
 
659
            
 
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
 
672
 
 
673
    def setCpuid(self):
 
674
        xc.domain_set_policy_cpuid(self.vm.getDomid())
 
675
 
 
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
 
689
 
 
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
 
702
 
 
703
class LinuxImageHandler(ImageHandler):
 
704
 
 
705
    ostype = "linux"
 
706
    flags = 0
 
707
    vhpt = 0
 
708
 
 
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'])
 
714
 
 
715
    def buildDomain(self):
 
716
        store_evtchn = self.vm.getStorePort()
 
717
        console_evtchn = self.vm.getConsolePort()
 
718
 
 
719
        mem_mb = self.getRequiredInitialReservation() / 1024
 
720
 
 
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)
 
734
 
 
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)
 
746
 
 
747
    def getBitSize(self):
 
748
        return xc.getBitSize(image    = self.kernel,
 
749
                                 cmdline  = self.cmdline,
 
750
                                 features = self.vm.getFeatures()
 
751
                                 ).get('type') 
 
752
 
 
753
    def getRequiredAvailableMemory(self, mem_kb):
 
754
        if self.is_stubdom :
 
755
            mem_kb += self.vramsize
 
756
        return mem_kb
 
757
 
 
758
    def getRequiredInitialReservation(self):
 
759
        return self.vm.getMemoryTarget()
 
760
 
 
761
    def getRequiredMaximumReservation(self):
 
762
        return self.vm.getMemoryMaximum()
 
763
 
 
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
 
771
 
 
772
    def getDeviceModelArgs(self, restore = False):
 
773
        args = ImageHandler.getDeviceModelArgs(self, restore)
 
774
        args = args + ([ "-M", "xenpv"])
 
775
        return args
 
776
 
 
777
 
 
778
class HVMImageHandler(ImageHandler):
 
779
 
 
780
    ostype = "hvm"
 
781
 
 
782
    def __init__(self, vm, vmConfig):
 
783
        ImageHandler.__init__(self, vm, vmConfig)
 
784
        self.shutdownWatch = None
 
785
        self.rebootFeatureWatch = None
 
786
    
 
787
    def getBitSize(self):
 
788
        return None
 
789
 
 
790
    def configure(self, vmConfig):
 
791
        ImageHandler.configure(self, vmConfig)
 
792
 
 
793
        self.loader = vmConfig['platform'].get('loader')
 
794
 
 
795
        info = xc.xeninfo()
 
796
        if 'hvm' not in info['xen_caps']:
 
797
            raise HVMRequired()
 
798
 
 
799
        xen_platform_pci = int(vmConfig['platform'].get('xen_platform_pci',1))
 
800
        rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
 
801
 
 
802
        if not self.display :
 
803
            self.display = ''
 
804
 
 
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 } )
 
816
 
 
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())
 
823
 
 
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 } )
 
828
 
 
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)
 
834
 
 
835
 
 
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', hex(self.vm.getVCpuAvail())]
 
842
 
 
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]
 
852
 
 
853
 
 
854
        dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
 
855
                   'localtime', 'serial', 'stdvga', 'isa',
 
856
                   'acpi', 'usb', 'usbdevice', 'gfx_passthru' ]
 
857
 
 
858
        for a in dmargs:
 
859
            v = vmConfig['platform'].get(a)
 
860
 
 
861
            # python doesn't allow '-' in variable names
 
862
            if a == 'stdvga': a = 'std-vga'
 
863
            if a == 'keymap': a = 'k'
 
864
 
 
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)
 
884
 
 
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))
 
890
 
 
891
        # Handle disk/network related options
 
892
        mac = None
 
893
        nics = 0
 
894
        
 
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)
 
903
 
 
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))
 
926
 
 
927
        if nics == 0:
 
928
            ret.append("-net")
 
929
            ret.append("none")
 
930
 
 
931
        return ret
 
932
 
 
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
 
940
 
 
941
    def buildDomain(self):
 
942
        store_evtchn = self.vm.getStorePort()
 
943
 
 
944
        memmax_mb = self.getRequiredMaximumReservation() / 1024
 
945
        mem_mb = self.getRequiredInitialReservation() / 1024
 
946
 
 
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)
 
956
 
 
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 }
 
966
 
 
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)
 
971
 
 
972
        return rc
 
973
 
 
974
 
 
975
class IA64_HVM_ImageHandler(HVMImageHandler):
 
976
 
 
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
 
981
 
 
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)
 
988
 
 
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
 
997
 
 
998
    def getRequiredInitialReservation(self):
 
999
        return self.vm.getMemoryTarget()
 
1000
 
 
1001
    def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
 
1002
        # Explicit shadow memory is not a concept 
 
1003
        return 0
 
1004
 
 
1005
    def getDeviceModelArgs(self, restore = False):
 
1006
        args = HVMImageHandler.getDeviceModelArgs(self, restore)
 
1007
        args = args + ([ "-m", "%s" %
 
1008
                         (self.getRequiredInitialReservation() / 1024) ])
 
1009
        return args
 
1010
 
 
1011
    def setCpuid(self):
 
1012
        # Guest CPUID configuration is not implemented yet.
 
1013
        return
 
1014
 
 
1015
class IA64_Linux_ImageHandler(LinuxImageHandler):
 
1016
 
 
1017
    def configure(self, vmConfig):
 
1018
        LinuxImageHandler.configure(self, vmConfig)
 
1019
        self.vhpt = int(vmConfig['platform'].get('vhpt',  0))
 
1020
 
 
1021
    def setCpuid(self):
 
1022
        # Guest CPUID configuration is not implemented yet.
 
1023
        return
 
1024
 
 
1025
class X86_HVM_ImageHandler(HVMImageHandler):
 
1026
 
 
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
 
1031
 
 
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
 
1037
    
 
1038
    def getBitSize(self):
 
1039
        return None
 
1040
 
 
1041
    def getRequiredAvailableMemory(self, mem_kb):
 
1042
        return mem_kb + self.vramsize
 
1043
 
 
1044
    def getRequiredInitialReservation(self):
 
1045
        return self.vm.getMemoryTarget()
 
1046
 
 
1047
    def getRequiredMaximumReservation(self):
 
1048
        return self.vm.getMemoryMaximum()
 
1049
 
 
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)
 
1058
 
 
1059
 
 
1060
class X86_Linux_ImageHandler(LinuxImageHandler):
 
1061
 
 
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
 
1070
 
 
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
    },
 
1080
}
 
1081
 
 
1082
def findImageHandlerClass(image):
 
1083
    """Find the image handler class for an image config.
 
1084
 
 
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)
 
1093