| 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, string |
|---|
| 21 | import re |
|---|
| 22 | import math |
|---|
| 23 | import signal |
|---|
| 24 | |
|---|
| 25 | import xen.lowlevel.xc |
|---|
| 26 | from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS |
|---|
| 27 | from xen.xend.XendError import VmError, XendError, HVMRequired |
|---|
| 28 | from xen.xend.XendLogging import log |
|---|
| 29 | from xen.xend.XendOptions import instance as xenopts |
|---|
| 30 | from xen.xend.server.netif import randomMAC |
|---|
| 31 | from xen.xend.xenstore.xswatch import xswatch |
|---|
| 32 | from xen.xend import arch |
|---|
| 33 | |
|---|
| 34 | xc = xen.lowlevel.xc.xc() |
|---|
| 35 | |
|---|
| 36 | MAX_GUEST_CMDLINE = 1024 |
|---|
| 37 | |
|---|
| 38 | |
|---|
| 39 | def create(vm, vmConfig): |
|---|
| 40 | """Create an image handler for a vm. |
|---|
| 41 | |
|---|
| 42 | @return ImageHandler instance |
|---|
| 43 | """ |
|---|
| 44 | return findImageHandlerClass(vmConfig)(vm, vmConfig) |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | class ImageHandler: |
|---|
| 48 | """Abstract base class for image handlers. |
|---|
| 49 | |
|---|
| 50 | createImage() is called to configure and build the domain from its |
|---|
| 51 | kernel image and ramdisk etc. |
|---|
| 52 | |
|---|
| 53 | The method buildDomain() is used to build the domain, and must be |
|---|
| 54 | defined in a subclass. Usually this is the only method that needs |
|---|
| 55 | defining in a subclass. |
|---|
| 56 | |
|---|
| 57 | The method createDeviceModel() is called to create the domain device |
|---|
| 58 | model if it needs one. The default is to do nothing. |
|---|
| 59 | |
|---|
| 60 | The method destroy() is called when the domain is destroyed. |
|---|
| 61 | The default is to do nothing. |
|---|
| 62 | """ |
|---|
| 63 | |
|---|
| 64 | ostype = None |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | def __init__(self, vm, vmConfig): |
|---|
| 68 | self.vm = vm |
|---|
| 69 | |
|---|
| 70 | self.bootloader = False |
|---|
| 71 | self.kernel = None |
|---|
| 72 | self.ramdisk = None |
|---|
| 73 | self.cmdline = None |
|---|
| 74 | |
|---|
| 75 | self.configure(vmConfig) |
|---|
| 76 | |
|---|
| 77 | def configure(self, vmConfig): |
|---|
| 78 | """Config actions common to all unix-like domains.""" |
|---|
| 79 | if '_temp_using_bootloader' in vmConfig: |
|---|
| 80 | self.bootloader = True |
|---|
| 81 | self.kernel = vmConfig['_temp_kernel'] |
|---|
| 82 | self.cmdline = vmConfig['_temp_args'] |
|---|
| 83 | self.ramdisk = vmConfig['_temp_ramdisk'] |
|---|
| 84 | else: |
|---|
| 85 | self.kernel = vmConfig['PV_kernel'] |
|---|
| 86 | self.cmdline = vmConfig['PV_args'] |
|---|
| 87 | self.ramdisk = vmConfig['PV_ramdisk'] |
|---|
| 88 | self.vm.storeVm(("image/ostype", self.ostype), |
|---|
| 89 | ("image/kernel", self.kernel), |
|---|
| 90 | ("image/cmdline", self.cmdline), |
|---|
| 91 | ("image/ramdisk", self.ramdisk)) |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | def cleanupBootloading(self): |
|---|
| 95 | if self.bootloader: |
|---|
| 96 | self.unlink(self.kernel) |
|---|
| 97 | self.unlink(self.ramdisk) |
|---|
| 98 | |
|---|
| 99 | |
|---|
| 100 | def unlink(self, f): |
|---|
| 101 | if not f: return |
|---|
| 102 | try: |
|---|
| 103 | os.unlink(f) |
|---|
| 104 | except OSError, ex: |
|---|
| 105 | log.warning("error removing bootloader file '%s': %s", f, ex) |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | def createImage(self): |
|---|
| 109 | """Entry point to create domain memory image. |
|---|
| 110 | Override in subclass if needed. |
|---|
| 111 | """ |
|---|
| 112 | return self.createDomain() |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | def createDomain(self): |
|---|
| 116 | """Build the domain boot image. |
|---|
| 117 | """ |
|---|
| 118 | # Set params and call buildDomain(). |
|---|
| 119 | |
|---|
| 120 | if not os.path.isfile(self.kernel): |
|---|
| 121 | raise VmError('Kernel image does not exist: %s' % self.kernel) |
|---|
| 122 | if self.ramdisk and not os.path.isfile(self.ramdisk): |
|---|
| 123 | raise VmError('Kernel ramdisk does not exist: %s' % self.ramdisk) |
|---|
| 124 | if len(self.cmdline) >= MAX_GUEST_CMDLINE: |
|---|
| 125 | log.warning('kernel cmdline too long, domain %d', |
|---|
| 126 | self.vm.getDomid()) |
|---|
| 127 | |
|---|
| 128 | log.info("buildDomain os=%s dom=%d vcpus=%d", self.ostype, |
|---|
| 129 | self.vm.getDomid(), self.vm.getVCpuCount()) |
|---|
| 130 | |
|---|
| 131 | result = self.buildDomain() |
|---|
| 132 | |
|---|
| 133 | if isinstance(result, dict): |
|---|
| 134 | return result |
|---|
| 135 | else: |
|---|
| 136 | raise VmError('Building domain failed: ostype=%s dom=%d err=%s' |
|---|
| 137 | % (self.ostype, self.vm.getDomid(), str(result))) |
|---|
| 138 | |
|---|
| 139 | def getRequiredAvailableMemory(self, mem_kb): |
|---|
| 140 | """@param mem_kb The configured maxmem or memory, in KiB. |
|---|
| 141 | @return The corresponding required amount of memory for the domain, |
|---|
| 142 | also in KiB. This is normally the given mem_kb, but architecture- or |
|---|
| 143 | image-specific code may override this to add headroom where |
|---|
| 144 | necessary.""" |
|---|
| 145 | return mem_kb |
|---|
| 146 | |
|---|
| 147 | def getRequiredInitialReservation(self): |
|---|
| 148 | """@param mem_kb The configured memory, in KiB. |
|---|
| 149 | @return The corresponding required amount of memory to be free, also |
|---|
| 150 | in KiB. This is normally the same as getRequiredAvailableMemory, but |
|---|
| 151 | architecture- or image-specific code may override this to |
|---|
| 152 | add headroom where necessary.""" |
|---|
| 153 | return self.getRequiredAvailableMemory(self.vm.getMemoryTarget()) |
|---|
| 154 | |
|---|
| 155 | def getRequiredMaximumReservation(self): |
|---|
| 156 | """@param mem_kb The maximum possible memory, in KiB. |
|---|
| 157 | @return The corresponding required amount of memory to be free, also |
|---|
| 158 | in KiB. This is normally the same as getRequiredAvailableMemory, but |
|---|
| 159 | architecture- or image-specific code may override this to |
|---|
| 160 | add headroom where necessary.""" |
|---|
| 161 | return self.getRequiredAvailableMemory(self.vm.getMemoryMaximum()) |
|---|
| 162 | |
|---|
| 163 | def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): |
|---|
| 164 | """@param shadow_mem_kb The configured shadow memory, in KiB. |
|---|
| 165 | @param maxmem_kb The configured maxmem, in KiB. |
|---|
| 166 | @return The corresponding required amount of shadow memory, also in |
|---|
| 167 | KiB.""" |
|---|
| 168 | # PV domains don't need any shadow memory |
|---|
| 169 | return 0 |
|---|
| 170 | |
|---|
| 171 | def buildDomain(self): |
|---|
| 172 | """Build the domain. Define in subclass.""" |
|---|
| 173 | raise NotImplementedError() |
|---|
| 174 | |
|---|
| 175 | def createDeviceModel(self, restore = False): |
|---|
| 176 | """Create device model for the domain (define in subclass if needed).""" |
|---|
| 177 | pass |
|---|
| 178 | |
|---|
| 179 | def destroy(self): |
|---|
| 180 | """Extra cleanup on domain destroy (define in subclass if needed).""" |
|---|
| 181 | pass |
|---|
| 182 | |
|---|
| 183 | |
|---|
| 184 | def recreate(self): |
|---|
| 185 | pass |
|---|
| 186 | |
|---|
| 187 | |
|---|
| 188 | class LinuxImageHandler(ImageHandler): |
|---|
| 189 | |
|---|
| 190 | ostype = "linux" |
|---|
| 191 | |
|---|
| 192 | def buildDomain(self): |
|---|
| 193 | store_evtchn = self.vm.getStorePort() |
|---|
| 194 | console_evtchn = self.vm.getConsolePort() |
|---|
| 195 | |
|---|
| 196 | mem_mb = self.getRequiredInitialReservation() / 1024 |
|---|
| 197 | |
|---|
| 198 | log.debug("domid = %d", self.vm.getDomid()) |
|---|
| 199 | log.debug("memsize = %d", mem_mb) |
|---|
| 200 | log.debug("image = %s", self.kernel) |
|---|
| 201 | log.debug("store_evtchn = %d", store_evtchn) |
|---|
| 202 | log.debug("console_evtchn = %d", console_evtchn) |
|---|
| 203 | log.debug("cmdline = %s", self.cmdline) |
|---|
| 204 | log.debug("ramdisk = %s", self.ramdisk) |
|---|
| 205 | log.debug("vcpus = %d", self.vm.getVCpuCount()) |
|---|
| 206 | log.debug("features = %s", self.vm.getFeatures()) |
|---|
| 207 | |
|---|
| 208 | return xc.linux_build(domid = self.vm.getDomid(), |
|---|
| 209 | memsize = mem_mb, |
|---|
| 210 | image = self.kernel, |
|---|
| 211 | store_evtchn = store_evtchn, |
|---|
| 212 | console_evtchn = console_evtchn, |
|---|
| 213 | cmdline = self.cmdline, |
|---|
| 214 | ramdisk = self.ramdisk, |
|---|
| 215 | features = self.vm.getFeatures()) |
|---|
| 216 | |
|---|
| 217 | class PPC_LinuxImageHandler(LinuxImageHandler): |
|---|
| 218 | |
|---|
| 219 | ostype = "linux" |
|---|
| 220 | |
|---|
| 221 | def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): |
|---|
| 222 | """@param shadow_mem_kb The configured shadow memory, in KiB. |
|---|
| 223 | @param maxmem_kb The configured maxmem, in KiB. |
|---|
| 224 | @return The corresponding required amount of shadow memory, also in |
|---|
| 225 | KiB. |
|---|
| 226 | PowerPC currently uses "shadow memory" to refer to the hash table.""" |
|---|
| 227 | return max(maxmem_kb / 64, shadow_mem_kb) |
|---|
| 228 | |
|---|
| 229 | |
|---|
| 230 | |
|---|
| 231 | class HVMImageHandler(ImageHandler): |
|---|
| 232 | |
|---|
| 233 | ostype = "hvm" |
|---|
| 234 | |
|---|
| 235 | def __init__(self, vm, vmConfig): |
|---|
| 236 | ImageHandler.__init__(self, vm, vmConfig) |
|---|
| 237 | self.shutdownWatch = None |
|---|
| 238 | self.rebootFeatureWatch = None |
|---|
| 239 | |
|---|
| 240 | def configure(self, vmConfig): |
|---|
| 241 | ImageHandler.configure(self, vmConfig) |
|---|
| 242 | |
|---|
| 243 | if not self.kernel: |
|---|
| 244 | self.kernel = '/usr/lib/xen/boot/hvmloader' |
|---|
| 245 | |
|---|
| 246 | info = xc.xeninfo() |
|---|
| 247 | if 'hvm' not in info['xen_caps']: |
|---|
| 248 | raise HVMRequired() |
|---|
| 249 | |
|---|
| 250 | self.dmargs = self.parseDeviceModelArgs(vmConfig) |
|---|
| 251 | self.device_model = vmConfig['platform'].get('device_model') |
|---|
| 252 | if not self.device_model: |
|---|
| 253 | raise VmError("hvm: missing device model") |
|---|
| 254 | |
|---|
| 255 | self.display = vmConfig['platform'].get('display') |
|---|
| 256 | self.xauthority = vmConfig['platform'].get('xauthority') |
|---|
| 257 | self.vncconsole = vmConfig['platform'].get('vncconsole') |
|---|
| 258 | |
|---|
| 259 | rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset') |
|---|
| 260 | |
|---|
| 261 | self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)), |
|---|
| 262 | ("image/device-model", self.device_model), |
|---|
| 263 | ("image/display", self.display)) |
|---|
| 264 | self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset)) |
|---|
| 265 | |
|---|
| 266 | self.pid = None |
|---|
| 267 | |
|---|
| 268 | self.pae = int(vmConfig['platform'].get('pae', 0)) |
|---|
| 269 | self.apic = int(vmConfig['platform'].get('apic', 0)) |
|---|
| 270 | self.acpi = int(vmConfig['platform'].get('acpi', 0)) |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | def buildDomain(self): |
|---|
| 274 | store_evtchn = self.vm.getStorePort() |
|---|
| 275 | |
|---|
| 276 | mem_mb = self.getRequiredInitialReservation() / 1024 |
|---|
| 277 | |
|---|
| 278 | log.debug("domid = %d", self.vm.getDomid()) |
|---|
| 279 | log.debug("image = %s", self.kernel) |
|---|
| 280 | log.debug("store_evtchn = %d", store_evtchn) |
|---|
| 281 | log.debug("memsize = %d", mem_mb) |
|---|
| 282 | log.debug("vcpus = %d", self.vm.getVCpuCount()) |
|---|
| 283 | log.debug("pae = %d", self.pae) |
|---|
| 284 | log.debug("acpi = %d", self.acpi) |
|---|
| 285 | log.debug("apic = %d", self.apic) |
|---|
| 286 | |
|---|
| 287 | rc = xc.hvm_build(domid = self.vm.getDomid(), |
|---|
| 288 | image = self.kernel, |
|---|
| 289 | store_evtchn = store_evtchn, |
|---|
| 290 | memsize = mem_mb, |
|---|
| 291 | vcpus = self.vm.getVCpuCount(), |
|---|
| 292 | pae = self.pae, |
|---|
| 293 | acpi = self.acpi, |
|---|
| 294 | apic = self.apic) |
|---|
| 295 | rc['notes'] = { 'SUSPEND_CANCEL': 1 } |
|---|
| 296 | return rc |
|---|
| 297 | |
|---|
| 298 | # Return a list of cmd line args to the device models based on the |
|---|
| 299 | # xm config file |
|---|
| 300 | def parseDeviceModelArgs(self, vmConfig): |
|---|
| 301 | dmargs = [ 'boot', 'fda', 'fdb', 'soundhw', |
|---|
| 302 | 'localtime', 'serial', 'stdvga', 'isa', |
|---|
| 303 | 'acpi', 'usb', 'usbdevice', 'keymap' ] |
|---|
| 304 | |
|---|
| 305 | ret = ['-vcpus', str(self.vm.getVCpuCount())] |
|---|
| 306 | |
|---|
| 307 | for a in dmargs: |
|---|
| 308 | v = vmConfig['platform'].get(a) |
|---|
| 309 | |
|---|
| 310 | # python doesn't allow '-' in variable names |
|---|
| 311 | if a == 'stdvga': a = 'std-vga' |
|---|
| 312 | if a == 'keymap': a = 'k' |
|---|
| 313 | |
|---|
| 314 | # Handle booleans gracefully |
|---|
| 315 | if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']: |
|---|
| 316 | try: |
|---|
| 317 | if v != None: v = int(v) |
|---|
| 318 | if v: ret.append("-%s" % a) |
|---|
| 319 | except (ValueError, TypeError): |
|---|
| 320 | pass # if we can't convert it to a sane type, ignore it |
|---|
| 321 | else: |
|---|
| 322 | if v: |
|---|
| 323 | ret.append("-%s" % a) |
|---|
| 324 | ret.append("%s" % v) |
|---|
| 325 | |
|---|
| 326 | if a in ['fda', 'fdb']: |
|---|
| 327 | if v: |
|---|
| 328 | if not os.path.isabs(v): |
|---|
| 329 | raise VmError("Floppy file %s does not exist." % v) |
|---|
| 330 | log.debug("args: %s, val: %s" % (a,v)) |
|---|
| 331 | |
|---|
| 332 | # Handle disk/network related options |
|---|
| 333 | mac = None |
|---|
| 334 | ret = ret + ["-domain-name", str(self.vm.info['name_label'])] |
|---|
| 335 | nics = 0 |
|---|
| 336 | |
|---|
| 337 | for devuuid in vmConfig['vbd_refs']: |
|---|
| 338 | devinfo = vmConfig['devices'][devuuid][1] |
|---|
| 339 | uname = devinfo.get('uname') |
|---|
| 340 | if uname is not None and 'file:' in uname: |
|---|
| 341 | (_, vbdparam) = string.split(uname, ':', 1) |
|---|
| 342 | if not os.path.isfile(vbdparam): |
|---|
| 343 | raise VmError('Disk image does not exist: %s' % |
|---|
| 344 | vbdparam) |
|---|
| 345 | |
|---|
| 346 | for devuuid in vmConfig['vif_refs']: |
|---|
| 347 | devinfo = vmConfig['devices'][devuuid][1] |
|---|
| 348 | dtype = devinfo.get('type', 'ioemu') |
|---|
| 349 | if dtype != 'ioemu': |
|---|
| 350 | continue |
|---|
| 351 | nics += 1 |
|---|
| 352 | mac = devinfo.get('mac') |
|---|
| 353 | if mac is None: |
|---|
| 354 | mac = randomMAC() |
|---|
| 355 | bridge = devinfo.get('bridge', 'xenbr0') |
|---|
| 356 | model = devinfo.get('model', 'rtl8139') |
|---|
| 357 | ret.append("-net") |
|---|
| 358 | ret.append("nic,vlan=%d,macaddr=%s,model=%s" % |
|---|
| 359 | (nics, mac, model)) |
|---|
| 360 | ret.append("-net") |
|---|
| 361 | ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge)) |
|---|
| 362 | |
|---|
| 363 | |
|---|
| 364 | # |
|---|
| 365 | # Find RFB console device, and if it exists, make QEMU enable |
|---|
| 366 | # the VNC console. |
|---|
| 367 | # |
|---|
| 368 | if int(vmConfig['platform'].get('nographic', 0)) != 0: |
|---|
| 369 | # skip vnc init if nographic is set |
|---|
| 370 | ret.append('-nographic') |
|---|
| 371 | return ret |
|---|
| 372 | |
|---|
| 373 | vnc_config = {} |
|---|
| 374 | has_vnc = int(vmConfig['platform'].get('vnc', 0)) != 0 |
|---|
| 375 | has_sdl = int(vmConfig['platform'].get('sdl', 0)) != 0 |
|---|
| 376 | for dev_uuid in vmConfig['console_refs']: |
|---|
| 377 | dev_type, dev_info = vmConfig['devices'][dev_uuid] |
|---|
| 378 | if dev_type == 'vfb': |
|---|
| 379 | vnc_config = dev_info.get('other_config', {}) |
|---|
| 380 | has_vnc = True |
|---|
| 381 | break |
|---|
| 382 | |
|---|
| 383 | if has_vnc: |
|---|
| 384 | if not vnc_config: |
|---|
| 385 | for key in ('vncunused', 'vnclisten', 'vncdisplay', |
|---|
| 386 | 'vncpasswd'): |
|---|
| 387 | if key in vmConfig['platform']: |
|---|
| 388 | vnc_config[key] = vmConfig['platform'][key] |
|---|
| 389 | |
|---|
| 390 | if not vnc_config.get('vncunused', 0) and \ |
|---|
| 391 | vnc_config.get('vncdisplay', 0): |
|---|
| 392 | vncdisplay = vnc_config.get('vncdisplay') |
|---|
| 393 | ret.append('-vnc') |
|---|
| 394 | ret.append(str(vncdisplay)) |
|---|
| 395 | else: |
|---|
| 396 | ret.append('-vncunused') |
|---|
| 397 | |
|---|
| 398 | vnclisten = vnc_config.get('vnclisten', |
|---|
| 399 | xenopts().get_vnclisten_address()) |
|---|
| 400 | ret.append('-vnclisten') |
|---|
| 401 | ret.append(str(vnclisten)) |
|---|
| 402 | |
|---|
| 403 | # Store vncpassword in xenstore |
|---|
| 404 | vncpasswd = vnc_config.get('vncpasswd') |
|---|
| 405 | if not vncpasswd: |
|---|
| 406 | vncpasswd = xenopts().get_vncpasswd_default() |
|---|
| 407 | |
|---|
| 408 | if vncpasswd is None: |
|---|
| 409 | raise VmError('vncpasswd is not setup in vmconfig or ' |
|---|
| 410 | 'xend-config.sxp') |
|---|
| 411 | |
|---|
| 412 | if vncpasswd != '': |
|---|
| 413 | self.vm.storeVm('vncpasswd', vncpasswd) |
|---|
| 414 | elif has_sdl: |
|---|
| 415 | # SDL is default in QEMU. |
|---|
| 416 | pass |
|---|
| 417 | else: |
|---|
| 418 | ret.append('-nographic') |
|---|
| 419 | |
|---|
| 420 | if int(vmConfig['platform'].get('monitor', 0)) != 0: |
|---|
| 421 | ret = ret + ['-monitor', 'vc'] |
|---|
| 422 | return ret |
|---|
| 423 | |
|---|
| 424 | def createDeviceModel(self, restore = False): |
|---|
| 425 | if self.pid: |
|---|
| 426 | return |
|---|
| 427 | # Execute device model. |
|---|
| 428 | #todo: Error handling |
|---|
| 429 | args = [self.device_model] |
|---|
| 430 | args = args + ([ "-d", "%d" % self.vm.getDomid() ]) |
|---|
| 431 | if arch.type == "ia64": |
|---|
| 432 | args = args + ([ "-m", "%s" % |
|---|
| 433 | (self.getRequiredInitialReservation() / 1024) ]) |
|---|
| 434 | args = args + self.dmargs |
|---|
| 435 | if restore: |
|---|
| 436 | args = args + ([ "-loadvm", "/tmp/xen.qemu-dm.%d" % |
|---|
| 437 | self.vm.getDomid() ]) |
|---|
| 438 | env = dict(os.environ) |
|---|
| 439 | if self.display: |
|---|
| 440 | env['DISPLAY'] = self.display |
|---|
| 441 | if self.xauthority: |
|---|
| 442 | env['XAUTHORITY'] = self.xauthority |
|---|
| 443 | if self.vncconsole: |
|---|
| 444 | args = args + ([ "-vncviewer" ]) |
|---|
| 445 | log.info("spawning device models: %s %s", self.device_model, args) |
|---|
| 446 | # keep track of pid and spawned options to kill it later |
|---|
| 447 | self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env) |
|---|
| 448 | self.vm.storeDom("image/device-model-pid", self.pid) |
|---|
| 449 | log.info("device model pid: %d", self.pid) |
|---|
| 450 | |
|---|
| 451 | def recreate(self): |
|---|
| 452 | self.pid = self.vm.gatherDom(('image/device-model-pid', int)) |
|---|
| 453 | |
|---|
| 454 | def destroy(self, suspend = False): |
|---|
| 455 | if self.pid: |
|---|
| 456 | try: |
|---|
| 457 | sig = signal.SIGKILL |
|---|
| 458 | if suspend: |
|---|
| 459 | log.info("use sigusr1 to signal qemu %d", self.pid) |
|---|
| 460 | sig = signal.SIGUSR1 |
|---|
| 461 | os.kill(self.pid, sig) |
|---|
| 462 | except OSError, exn: |
|---|
| 463 | log.exception(exn) |
|---|
| 464 | try: |
|---|
| 465 | os.waitpid(self.pid, 0) |
|---|
| 466 | except OSError, exn: |
|---|
| 467 | # This is expected if Xend has been restarted within the |
|---|
| 468 | # life of this domain. In this case, we can kill the process, |
|---|
| 469 | # but we can't wait for it because it's not our child. |
|---|
| 470 | pass |
|---|
| 471 | self.pid = None |
|---|
| 472 | |
|---|
| 473 | |
|---|
| 474 | class IA64_HVM_ImageHandler(HVMImageHandler): |
|---|
| 475 | |
|---|
| 476 | def getRequiredAvailableMemory(self, mem_kb): |
|---|
| 477 | page_kb = 16 |
|---|
| 478 | # ROM size for guest firmware, ioreq page, pio page and xenstore page |
|---|
| 479 | extra_pages = 1024 + 4 |
|---|
| 480 | return mem_kb + extra_pages * page_kb |
|---|
| 481 | |
|---|
| 482 | def getRequiredInitialReservation(self): |
|---|
| 483 | return self.vm.getMemoryTarget() |
|---|
| 484 | |
|---|
| 485 | def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): |
|---|
| 486 | # Explicit shadow memory is not a concept |
|---|
| 487 | return 0 |
|---|
| 488 | |
|---|
| 489 | class X86_HVM_ImageHandler(HVMImageHandler): |
|---|
| 490 | |
|---|
| 491 | def getRequiredAvailableMemory(self, mem_kb): |
|---|
| 492 | # Add 8 MiB overhead for QEMU's video RAM. |
|---|
| 493 | return mem_kb + 8192 |
|---|
| 494 | |
|---|
| 495 | def getRequiredInitialReservation(self): |
|---|
| 496 | return self.vm.getMemoryTarget() |
|---|
| 497 | |
|---|
| 498 | def getRequiredMaximumReservation(self): |
|---|
| 499 | return self.vm.getMemoryMaximum() |
|---|
| 500 | |
|---|
| 501 | def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb): |
|---|
| 502 | # 256 pages (1MB) per vcpu, |
|---|
| 503 | # plus 1 page per MiB of RAM for the P2M map, |
|---|
| 504 | # plus 1 page per MiB of RAM to shadow the resident processes. |
|---|
| 505 | # This is higher than the minimum that Xen would allocate if no value |
|---|
| 506 | # were given (but the Xen minimum is for safety, not performance). |
|---|
| 507 | return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)), |
|---|
| 508 | shadow_mem_kb) |
|---|
| 509 | |
|---|
| 510 | class X86_Linux_ImageHandler(LinuxImageHandler): |
|---|
| 511 | |
|---|
| 512 | def buildDomain(self): |
|---|
| 513 | # set physical mapping limit |
|---|
| 514 | # add an 8MB slack to balance backend allocations. |
|---|
| 515 | mem_kb = self.getRequiredMaximumReservation() + (8 * 1024) |
|---|
| 516 | xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb) |
|---|
| 517 | return LinuxImageHandler.buildDomain(self) |
|---|
| 518 | |
|---|
| 519 | _handlers = { |
|---|
| 520 | "powerpc": { |
|---|
| 521 | "linux": PPC_LinuxImageHandler, |
|---|
| 522 | }, |
|---|
| 523 | "ia64": { |
|---|
| 524 | "linux": LinuxImageHandler, |
|---|
| 525 | "hvm": IA64_HVM_ImageHandler, |
|---|
| 526 | }, |
|---|
| 527 | "x86": { |
|---|
| 528 | "linux": X86_Linux_ImageHandler, |
|---|
| 529 | "hvm": X86_HVM_ImageHandler, |
|---|
| 530 | }, |
|---|
| 531 | } |
|---|
| 532 | |
|---|
| 533 | def findImageHandlerClass(image): |
|---|
| 534 | """Find the image handler class for an image config. |
|---|
| 535 | |
|---|
| 536 | @param image config |
|---|
| 537 | @return ImageHandler subclass or None |
|---|
| 538 | """ |
|---|
| 539 | image_type = image.image_type() |
|---|
| 540 | try: |
|---|
| 541 | return _handlers[arch.type][image_type] |
|---|
| 542 | except KeyError: |
|---|
| 543 | raise VmError('unknown image type: ' + image_type) |
|---|