debuggers.hg
annotate tools/python/xen/xend/XendDomainInfo.py @ 16702:9fe92a88912b
Fix xend xenstore handling.
xend can get into a situation where two processes are attempting to
interact with the xenstore socket, with disastrous results. Fix the
two bad users of xstransact, add a big warning, and fix the destructor
so future mistakes will be detected earlier.
Signed-off-by: John Levon <john.levon@sun.com>
xend can get into a situation where two processes are attempting to
interact with the xenstore socket, with disastrous results. Fix the
two bad users of xstransact, add a big warning, and fix the destructor
so future mistakes will be detected earlier.
Signed-off-by: John Levon <john.levon@sun.com>
author | Keir Fraser <keir.fraser@citrix.com> |
---|---|
date | Thu Dec 27 12:27:34 2007 +0000 (2007-12-27) |
parents | 9b37cabe0485 |
children | 3f26758bcc02 |
rev | line source |
---|---|
emellor@6963 | 1 #=========================================================================== |
kaf24@6102 | 2 # This library is free software; you can redistribute it and/or |
kaf24@6102 | 3 # modify it under the terms of version 2.1 of the GNU Lesser General Public |
kaf24@6102 | 4 # License as published by the Free Software Foundation. |
kaf24@6102 | 5 # |
kaf24@6102 | 6 # This library is distributed in the hope that it will be useful, |
kaf24@6102 | 7 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
kaf24@6102 | 8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
kaf24@6102 | 9 # Lesser General Public License for more details. |
kaf24@6102 | 10 # |
kaf24@6102 | 11 # You should have received a copy of the GNU Lesser General Public |
kaf24@6102 | 12 # License along with this library; if not, write to the Free Software |
kaf24@6102 | 13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
kaf24@6102 | 14 #============================================================================ |
kaf24@6102 | 15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com> |
ewan@14379 | 16 # Copyright (C) 2005-2007 XenSource Ltd |
kaf24@6102 | 17 #============================================================================ |
mjw@1661 | 18 |
mjw@1661 | 19 """Representation of a single domain. |
mjw@1661 | 20 Includes support for domain construction, using |
mjw@1661 | 21 open-ended configurations. |
mjw@1661 | 22 |
mjw@4655 | 23 Author: Mike Wray <mike.wray@hp.com> |
mjw@1661 | 24 |
mjw@1661 | 25 """ |
mjw@1661 | 26 |
emellor@7436 | 27 import logging |
tw275@1847 | 28 import time |
cl349@5159 | 29 import threading |
atse@12109 | 30 import re |
atse@12151 | 31 import copy |
atse@12277 | 32 import os |
tom@14924 | 33 import traceback |
atse@12165 | 34 from types import StringTypes |
mjw@1661 | 35 |
emellor@6965 | 36 import xen.lowlevel.xc |
emellor@7311 | 37 from xen.util import asserts |
markmc@14481 | 38 from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype |
kfraser@15848 | 39 import xen.util.xsm.xsm as security |
atse@12109 | 40 |
Tim@13337 | 41 from xen.xend import balloon, sxp, uuid, image, arch, osdep |
ewan@13444 | 42 from xen.xend import XendOptions, XendNode, XendConfig |
emellor@8166 | 43 |
ewan@12816 | 44 from xen.xend.XendConfig import scrub_password |
Tim@13578 | 45 from xen.xend.XendBootloader import bootloader, bootloader_tidy |
cl349@5606 | 46 from xen.xend.XendError import XendError, VmError |
atse@12109 | 47 from xen.xend.XendDevices import XendDevices |
atse@13619 | 48 from xen.xend.XendTask import XendTask |
emellor@8209 | 49 from xen.xend.xenstore.xstransact import xstransact, complete |
kfraser@13555 | 50 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, ResumeDomain |
emellor@7884 | 51 from xen.xend.xenstore.xswatch import xswatch |
atse@12109 | 52 from xen.xend.XendConstants import * |
atse@12109 | 53 from xen.xend.XendAPIConstants import * |
mjw@1767 | 54 |
ewan@14417 | 55 from xen.xend.XendVMMetrics import XendVMMetrics |
ewan@14417 | 56 |
jchesterfield@11520 | 57 MIGRATE_TIMEOUT = 30.0 |
ewan@13080 | 58 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp' |
emellor@7099 | 59 |
emellor@7995 | 60 xc = xen.lowlevel.xc.xc() |
ewan@13444 | 61 xoptions = XendOptions.instance() |
emellor@7099 | 62 |
emellor@7436 | 63 log = logging.getLogger("xend.XendDomainInfo") |
emellor@7436 | 64 #log.setLevel(logging.TRACE) |
emellor@7436 | 65 |
ewan@12715 | 66 |
emellor@7205 | 67 def create(config): |
atse@12109 | 68 """Creates and start a VM using the supplied configuration. |
emellor@7205 | 69 |
atse@12109 | 70 @param config: A configuration object involving lists of tuples. |
atse@12109 | 71 @type config: list of lists, eg ['vm', ['image', 'xen.gz']] |
atse@12109 | 72 |
atse@12109 | 73 @rtype: XendDomainInfo |
ewan@13166 | 74 @return: An up and running XendDomainInfo instance |
atse@12109 | 75 @raise VmError: Invalid configuration or failure to start. |
emellor@7205 | 76 """ |
keir@16036 | 77 from xen.xend import XendDomain |
keir@16036 | 78 domconfig = XendConfig.XendConfig(sxp_obj = config) |
keir@16036 | 79 othervm = XendDomain.instance().domain_lookup_nr(domconfig["name_label"]) |
keir@16036 | 80 if othervm is None or othervm.domid is None: |
keir@16036 | 81 othervm = XendDomain.instance().domain_lookup_nr(domconfig["uuid"]) |
keir@16036 | 82 if othervm is not None and othervm.domid is not None: |
keir@16036 | 83 raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid)) |
ewan@12816 | 84 log.debug("XendDomainInfo.create(%s)", scrub_password(config)) |
keir@16036 | 85 vm = XendDomainInfo(domconfig) |
emellor@7240 | 86 try: |
atse@12109 | 87 vm.start() |
emellor@7240 | 88 except: |
emellor@7260 | 89 log.exception('Domain construction failed') |
emellor@7240 | 90 vm.destroy() |
emellor@7240 | 91 raise |
emellor@7205 | 92 |
atse@12109 | 93 return vm |
emellor@7205 | 94 |
ewan@13166 | 95 def create_from_dict(config_dict): |
ewan@13166 | 96 """Creates and start a VM using the supplied configuration. |
ewan@13166 | 97 |
ewan@13166 | 98 @param config_dict: An configuration dictionary. |
ewan@13166 | 99 |
ewan@13166 | 100 @rtype: XendDomainInfo |
ewan@13166 | 101 @return: An up and running XendDomainInfo instance |
ewan@13166 | 102 @raise VmError: Invalid configuration or failure to start. |
ewan@13166 | 103 """ |
ewan@13166 | 104 |
ewan@13166 | 105 log.debug("XendDomainInfo.create_from_dict(%s)", |
ewan@13166 | 106 scrub_password(config_dict)) |
ewan@13166 | 107 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict)) |
ewan@13166 | 108 try: |
ewan@13166 | 109 vm.start() |
ewan@13166 | 110 except: |
ewan@13166 | 111 log.exception('Domain construction failed') |
ewan@13166 | 112 vm.destroy() |
ewan@13166 | 113 raise |
ewan@13166 | 114 return vm |
ewan@13166 | 115 |
atse@12109 | 116 def recreate(info, priv): |
emellor@7205 | 117 """Create the VM object for an existing domain. The domain must not |
emellor@7205 | 118 be dying, as the paths in the store should already have been removed, |
atse@12109 | 119 and asking us to recreate them causes problems. |
emellor@7205 | 120 |
atse@12109 | 121 @param xeninfo: Parsed configuration |
atse@12109 | 122 @type xeninfo: Dictionary |
ewan@13033 | 123 @param priv: Is a privileged domain (Dom 0) |
atse@12109 | 124 @type priv: bool |
emellor@7205 | 125 |
atse@12109 | 126 @rtype: XendDomainInfo |
atse@12109 | 127 @return: A up and running XendDomainInfo instance |
atse@12109 | 128 @raise VmError: Invalid configuration. |
atse@12109 | 129 @raise XendError: Errors with configuration. |
atse@12109 | 130 """ |
emellor@7205 | 131 |
ewan@12816 | 132 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info)) |
atse@12109 | 133 |
atse@12109 | 134 assert not info['dying'] |
atse@12109 | 135 |
atse@12671 | 136 xeninfo = XendConfig.XendConfig(dominfo = info) |
ewan@14582 | 137 xeninfo['is_control_domain'] = priv |
ewan@14582 | 138 xeninfo['is_a_template'] = False |
atse@12109 | 139 domid = xeninfo['domid'] |
atse@12671 | 140 uuid1 = uuid.fromString(xeninfo['uuid']) |
atse@12109 | 141 needs_reinitialising = False |
atse@12109 | 142 |
emellor@7442 | 143 dompath = GetDomainPath(domid) |
emellor@7442 | 144 if not dompath: |
atse@12109 | 145 raise XendError('No domain path in store for existing ' |
atse@12109 | 146 'domain %d' % domid) |
emellor@7482 | 147 |
atse@12165 | 148 log.info("Recreating domain %d, UUID %s. at %s" % |
atse@12165 | 149 (domid, xeninfo['uuid'], dompath)) |
atse@12109 | 150 |
atse@12109 | 151 # need to verify the path and uuid if not Domain-0 |
atse@12109 | 152 # if the required uuid and vm aren't set, then that means |
atse@12109 | 153 # we need to recreate the dom with our own values |
atse@12109 | 154 # |
atse@12109 | 155 # NOTE: this is probably not desirable, really we should just |
atse@12109 | 156 # abort or ignore, but there may be cases where xenstore's |
atse@12109 | 157 # entry disappears (eg. xenstore-rm /) |
atse@12109 | 158 # |
atse@12165 | 159 try: |
atse@12165 | 160 vmpath = xstransact.Read(dompath, "vm") |
atse@12165 | 161 if not vmpath: |
ewan@14618 | 162 if not priv: |
ewan@14618 | 163 log.warn('/local/domain/%d/vm is missing. recreate is ' |
ewan@14618 | 164 'confused, trying our best to recover' % domid) |
atse@12165 | 165 needs_reinitialising = True |
atse@12165 | 166 raise XendError('reinit') |
atse@12165 | 167 |
atse@12165 | 168 uuid2_str = xstransact.Read(vmpath, "uuid") |
atse@12165 | 169 if not uuid2_str: |
atse@12165 | 170 log.warn('%s/uuid/ is missing. recreate is confused, ' |
atse@12165 | 171 'trying our best to recover' % vmpath) |
atse@12165 | 172 needs_reinitialising = True |
atse@12165 | 173 raise XendError('reinit') |
atse@12165 | 174 |
atse@12165 | 175 uuid2 = uuid.fromString(uuid2_str) |
atse@12165 | 176 if uuid1 != uuid2: |
atse@12165 | 177 log.warn('UUID in /vm does not match the UUID in /dom/%d.' |
atse@12165 | 178 'Trying out best to recover' % domid) |
atse@12165 | 179 needs_reinitialising = True |
atse@12165 | 180 except XendError: |
atse@12165 | 181 pass # our best shot at 'goto' in python :) |
emellor@7205 | 182 |
keir@15995 | 183 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv, |
keir@15995 | 184 vmpath = vmpath) |
atse@12109 | 185 |
atse@12109 | 186 if needs_reinitialising: |
atse@12109 | 187 vm._recreateDom() |
atse@12109 | 188 vm._removeVm() |
atse@12109 | 189 vm._storeVmDetails() |
atse@12109 | 190 vm._storeDomDetails() |
atse@12109 | 191 |
ewan@14379 | 192 vm.image = image.create(vm, vm.info) |
ewan@14379 | 193 vm.image.recreate() |
ewan@12695 | 194 |
atse@12109 | 195 vm._registerWatches() |
atse@12275 | 196 vm.refreshShutdown(xeninfo) |
Tim@13578 | 197 |
Tim@13578 | 198 # register the domain in the list |
Tim@13578 | 199 from xen.xend import XendDomain |
Tim@13578 | 200 XendDomain.instance().add_domain(vm) |
Tim@13578 | 201 |
emellor@7205 | 202 return vm |
emellor@7205 | 203 |
emellor@7205 | 204 |
emellor@7180 | 205 def restore(config): |
emellor@7180 | 206 """Create a domain and a VM object to do a restore. |
emellor@7180 | 207 |
atse@12671 | 208 @param config: Domain SXP configuration |
atse@12109 | 209 @type config: list of lists. (see C{create}) |
atse@12109 | 210 |
atse@12109 | 211 @rtype: XendDomainInfo |
atse@12109 | 212 @return: A up and running XendDomainInfo instance |
atse@12109 | 213 @raise VmError: Invalid configuration or failure to start. |
atse@12109 | 214 @raise XendError: Errors with configuration. |
emellor@7180 | 215 """ |
emellor@7180 | 216 |
ewan@12816 | 217 log.debug("XendDomainInfo.restore(%s)", scrub_password(config)) |
atse@12671 | 218 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config), |
atse@12671 | 219 resume = True) |
emellor@7240 | 220 try: |
atse@12109 | 221 vm.resume() |
ewan@12238 | 222 return vm |
emellor@7240 | 223 except: |
emellor@7240 | 224 vm.destroy() |
emellor@7240 | 225 raise |
emellor@7180 | 226 |
atse@12671 | 227 def createDormant(domconfig): |
atse@12109 | 228 """Create a dormant/inactive XenDomainInfo without creating VM. |
atse@12109 | 229 This is for creating instances of persistent domains that are not |
atse@12109 | 230 yet start. |
emellor@8217 | 231 |
atse@12671 | 232 @param domconfig: Parsed configuration |
atse@12671 | 233 @type domconfig: XendConfig object |
atse@12109 | 234 |
atse@12109 | 235 @rtype: XendDomainInfo |
atse@12109 | 236 @return: A up and running XendDomainInfo instance |
atse@12109 | 237 @raise XendError: Errors with configuration. |
atse@12109 | 238 """ |
atse@12109 | 239 |
ewan@12816 | 240 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig)) |
atse@12109 | 241 |
atse@12138 | 242 # domid does not make sense for non-running domains. |
atse@12671 | 243 domconfig.pop('domid', None) |
atse@12671 | 244 vm = XendDomainInfo(domconfig) |
atse@12109 | 245 return vm |
emellor@7205 | 246 |
emellor@7205 | 247 def domain_by_name(name): |
atse@12109 | 248 """Get domain by name |
atse@12109 | 249 |
atse@12109 | 250 @params name: Name of the domain |
atse@12109 | 251 @type name: string |
atse@12109 | 252 @return: XendDomainInfo or None |
atse@12109 | 253 """ |
atse@12109 | 254 from xen.xend import XendDomain |
emellor@8166 | 255 return XendDomain.instance().domain_lookup_by_name_nr(name) |
emellor@8166 | 256 |
mjw@2267 | 257 |
mjw@1767 | 258 def shutdown_reason(code): |
mjw@1767 | 259 """Get a shutdown reason from a code. |
mjw@1767 | 260 |
mjw@1767 | 261 @param code: shutdown code |
mjw@1767 | 262 @type code: int |
mjw@1767 | 263 @return: shutdown reason |
mjw@1767 | 264 @rtype: string |
mjw@1767 | 265 """ |
atse@12109 | 266 return DOMAIN_SHUTDOWN_REASONS.get(code, "?") |
mjw@1767 | 267 |
mjw@1661 | 268 def dom_get(dom): |
mjw@2013 | 269 """Get info from xen for an existing domain. |
mjw@2013 | 270 |
mjw@2013 | 271 @param dom: domain id |
atse@12109 | 272 @type dom: int |
mjw@2013 | 273 @return: info or None |
atse@12109 | 274 @rtype: dictionary |
mjw@2013 | 275 """ |
shand@6866 | 276 try: |
shand@6866 | 277 domlist = xc.domain_getinfo(dom, 1) |
atse@12109 | 278 if domlist and dom == domlist[0]['domid']: |
shand@6866 | 279 return domlist[0] |
shand@6866 | 280 except Exception, err: |
shand@6866 | 281 # ignore missing domain |
emellor@7669 | 282 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err)) |
mjw@1661 | 283 return None |
kaf24@6169 | 284 |
emellor@7518 | 285 |
mjw@1661 | 286 class XendDomainInfo: |
atse@12109 | 287 """An object represents a domain. |
mjw@1667 | 288 |
atse@12109 | 289 @TODO: try to unify dom and domid, they mean the same thing, but |
atse@12109 | 290 xc refers to it as dom, and everywhere else, including |
atse@12109 | 291 xenstore it is domid. The best way is to change xc's |
atse@12109 | 292 python interface. |
atse@12109 | 293 |
atse@12109 | 294 @ivar info: Parsed configuration |
atse@12109 | 295 @type info: dictionary |
atse@12109 | 296 @ivar domid: Domain ID (if VM has started) |
atse@12109 | 297 @type domid: int or None |
atse@12109 | 298 @ivar vmpath: XenStore path to this VM. |
atse@12109 | 299 @type vmpath: string |
atse@12109 | 300 @ivar dompath: XenStore path to this Domain. |
atse@12109 | 301 @type dompath: string |
atse@12109 | 302 @ivar image: Reference to the VM Image. |
atse@12109 | 303 @type image: xen.xend.image.ImageHandler |
atse@12109 | 304 @ivar store_port: event channel to xenstored |
atse@12109 | 305 @type store_port: int |
atse@12109 | 306 @ivar console_port: event channel to xenconsoled |
atse@12109 | 307 @type console_port: int |
atse@12109 | 308 @ivar store_mfn: xenstored mfn |
atse@12109 | 309 @type store_mfn: int |
atse@12109 | 310 @ivar console_mfn: xenconsoled mfn |
atse@12109 | 311 @type console_mfn: int |
kfraser@14158 | 312 @ivar notes: OS image notes |
kfraser@14158 | 313 @type notes: dictionary |
atse@12109 | 314 @ivar vmWatch: reference to a watch on the xenstored vmpath |
atse@12109 | 315 @type vmWatch: xen.xend.xenstore.xswatch |
atse@12109 | 316 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown |
atse@12109 | 317 @type shutdownWatch: xen.xend.xenstore.xswatch |
atse@12109 | 318 @ivar shutdownStartTime: UNIX Time when domain started shutting down. |
atse@12109 | 319 @type shutdownStartTime: float or None |
tom@14924 | 320 # @ivar state: Domain state |
tom@14924 | 321 # @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...) |
atse@12109 | 322 @ivar state_updated: lock for self.state |
atse@12109 | 323 @type state_updated: threading.Condition |
atse@12109 | 324 @ivar refresh_shutdown_lock: lock for polling shutdown state |
atse@12109 | 325 @type refresh_shutdown_lock: threading.Condition |
atse@12109 | 326 @ivar _deviceControllers: device controller cache for this domain |
atse@12109 | 327 @type _deviceControllers: dict 'string' to DevControllers |
atse@12109 | 328 """ |
atse@12109 | 329 |
emellor@8183 | 330 def __init__(self, info, domid = None, dompath = None, augment = False, |
keir@15995 | 331 priv = False, resume = False, vmpath = None): |
atse@12109 | 332 """Constructor for a domain |
atse@12109 | 333 |
atse@12109 | 334 @param info: parsed configuration |
atse@12109 | 335 @type info: dictionary |
atse@12109 | 336 @keyword domid: Set initial domain id (if any) |
atse@12109 | 337 @type domid: int |
atse@12109 | 338 @keyword dompath: Set initial dompath (if any) |
atse@12109 | 339 @type dompath: string |
atse@12109 | 340 @keyword augment: Augment given info with xenstored VM info |
atse@12109 | 341 @type augment: bool |
ewan@13033 | 342 @keyword priv: Is a privileged domain (Dom 0) |
atse@12109 | 343 @type priv: bool |
atse@12109 | 344 @keyword resume: Is this domain being resumed? |
atse@12109 | 345 @type resume: bool |
atse@12109 | 346 """ |
emellor@7022 | 347 |
emellor@7022 | 348 self.info = info |
atse@12109 | 349 if domid == None: |
atse@12109 | 350 self.domid = self.info.get('domid') |
atse@12109 | 351 else: |
emellor@7022 | 352 self.domid = domid |
atse@12109 | 353 |
atse@12109 | 354 #REMOVE: uuid is now generated in XendConfig |
atse@12109 | 355 #if not self._infoIsSet('uuid'): |
atse@12109 | 356 # self.info['uuid'] = uuid.toString(uuid.create()) |
emellor@7022 | 357 |
keir@15995 | 358 # Find a unique /vm/<uuid>/<integer> path if not specified. |
keir@15995 | 359 # This avoids conflict between pre-/post-migrate domains when doing |
keir@15995 | 360 # localhost relocation. |
keir@15995 | 361 self.vmpath = vmpath |
keir@15995 | 362 i = 0 |
keir@15995 | 363 while self.vmpath == None: |
keir@16005 | 364 self.vmpath = XS_VMROOT + self.info['uuid'] |
keir@16005 | 365 if i != 0: |
keir@16005 | 366 self.vmpath = self.vmpath + '-' + str(i) |
keir@15995 | 367 try: |
keir@15995 | 368 if self._readVm("uuid"): |
keir@15995 | 369 self.vmpath = None |
keir@15995 | 370 i = i + 1 |
keir@15995 | 371 except: |
keir@15995 | 372 pass |
keir@15995 | 373 |
emellor@7442 | 374 self.dompath = dompath |
emellor@7180 | 375 |
mjw@1661 | 376 self.image = None |
emellor@7461 | 377 self.store_port = None |
cl349@5389 | 378 self.store_mfn = None |
emellor@7461 | 379 self.console_port = None |
cl349@6512 | 380 self.console_mfn = None |
emellor@7099 | 381 |
ian@15113 | 382 self.native_protocol = None |
ian@15113 | 383 |
emellor@7884 | 384 self.vmWatch = None |
emellor@9163 | 385 self.shutdownWatch = None |
emellor@9163 | 386 self.shutdownStartTime = None |
atse@12671 | 387 self._resume = resume |
atse@12524 | 388 |
cl349@5159 | 389 self.state_updated = threading.Condition() |
emellor@7180 | 390 self.refresh_shutdown_lock = threading.Condition() |
tom@14924 | 391 self._stateSet(DOM_STATE_HALTED) |
cl349@6849 | 392 |
atse@12109 | 393 self._deviceControllers = {} |
emellor@7022 | 394 |
atse@12109 | 395 for state in DOM_STATES_OLD: |
atse@12109 | 396 self.info[state] = 0 |
atse@12109 | 397 |
atse@12109 | 398 if augment: |
atse@12109 | 399 self._augmentInfo(priv) |
emellor@7438 | 400 |
atse@12671 | 401 self._checkName(self.info['name_label']) |
ewan@14417 | 402 |
ewan@14417 | 403 self.metrics = XendVMMetrics(uuid.createString(), self) |
atse@12109 | 404 |
atse@12109 | 405 |
atse@12109 | 406 # |
atse@12109 | 407 # Public functions available through XMLRPC |
atse@12109 | 408 # |
emellor@7884 | 409 |
emellor@7884 | 410 |
ewan@12640 | 411 def start(self, is_managed = False): |
atse@12109 | 412 """Attempts to start the VM by do the appropriate |
atse@12109 | 413 initialisation if it not started. |
atse@12109 | 414 """ |
atse@12109 | 415 from xen.xend import XendDomain |
atse@12524 | 416 |
tom@14924 | 417 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED): |
atse@12109 | 418 try: |
atse@13619 | 419 XendTask.log_progress(0, 30, self._constructDomain) |
atse@13619 | 420 XendTask.log_progress(31, 60, self._initDomain) |
atse@13619 | 421 |
atse@13619 | 422 XendTask.log_progress(61, 70, self._storeVmDetails) |
atse@13619 | 423 XendTask.log_progress(71, 80, self._storeDomDetails) |
atse@13619 | 424 XendTask.log_progress(81, 90, self._registerWatches) |
atse@13619 | 425 XendTask.log_progress(91, 100, self.refreshShutdown) |
emellor@7884 | 426 |
ewan@14169 | 427 xendomains = XendDomain.instance() |
ewan@14169 | 428 xennode = XendNode.instance() |
ewan@14169 | 429 |
atse@12109 | 430 # save running configuration if XendDomains believe domain is |
atse@12109 | 431 # persistent |
atse@12109 | 432 if is_managed: |
atse@12109 | 433 xendomains.managed_config_save(self) |
ewan@14169 | 434 |
ewan@14169 | 435 if xennode.xenschedinfo() == 'credit': |
ewan@14169 | 436 xendomains.domain_sched_credit_set(self.getDomid(), |
ewan@14169 | 437 self.getWeight(), |
ewan@14169 | 438 self.getCap()) |
atse@12109 | 439 except: |
atse@12109 | 440 log.exception('VM start failed') |
atse@12109 | 441 self.destroy() |
atse@12109 | 442 raise |
atse@12109 | 443 else: |
atse@12109 | 444 raise XendError('VM already running') |
atse@12109 | 445 |
atse@12109 | 446 def resume(self): |
atse@12109 | 447 """Resumes a domain that has come back from suspension.""" |
tom@14924 | 448 state = self._stateGet() |
tom@14924 | 449 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED): |
atse@12109 | 450 try: |
atse@12109 | 451 self._constructDomain() |
atse@12109 | 452 self._storeVmDetails() |
ssmith@12717 | 453 self._createDevices() |
atse@12109 | 454 self._createChannels() |
atse@12109 | 455 self._storeDomDetails() |
atse@12109 | 456 self._endRestore() |
atse@12109 | 457 except: |
atse@12109 | 458 log.exception('VM resume failed') |
kfraser@14812 | 459 self.destroy() |
atse@12109 | 460 raise |
atse@12109 | 461 else: |
tom@14924 | 462 raise XendError('VM is not susupened; it is %s' |
tom@14924 | 463 % XEN_API_VM_POWER_STATE[state]) |
emellor@7884 | 464 |
atse@12109 | 465 def shutdown(self, reason): |
atse@12109 | 466 """Shutdown a domain by signalling this via xenstored.""" |
ewan@13247 | 467 log.debug('XendDomainInfo.shutdown(%s)', reason) |
tom@14924 | 468 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,): |
atse@12109 | 469 raise XendError('Domain cannot be shutdown') |
atse@12524 | 470 |
atse@12524 | 471 if self.domid == 0: |
atse@12524 | 472 raise XendError('Domain 0 cannot be shutdown') |
atse@12109 | 473 |
atse@12671 | 474 if reason not in DOMAIN_SHUTDOWN_REASONS.values(): |
atse@12109 | 475 raise XendError('Invalid reason: %s' % reason) |
ewan@12700 | 476 self._removeVm('xend/previous_restart_time') |
ewan@12695 | 477 self.storeDom("control/shutdown", reason) |
ewan@12700 | 478 |
kfraser@14702 | 479 # HVM domain shuts itself down only if it has PV drivers |
kfraser@14702 | 480 if self.info.is_hvm(): |
kfraser@14702 | 481 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ) |
kfraser@14702 | 482 if not hvm_pvdrv: |
kfraser@14702 | 483 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason] |
alex@15595 | 484 xc.domain_destroy_hook(self.domid) |
kfraser@14702 | 485 log.info("HVM save:remote shutdown dom %d!", self.domid) |
kfraser@14702 | 486 xc.domain_shutdown(self.domid, code) |
Tim@13451 | 487 |
atse@12109 | 488 def pause(self): |
atse@12109 | 489 """Pause domain |
atse@12109 | 490 |
atse@12109 | 491 @raise XendError: Failed pausing a domain |
atse@12109 | 492 """ |
atse@12109 | 493 try: |
atse@12109 | 494 xc.domain_pause(self.domid) |
atse@12109 | 495 self._stateSet(DOM_STATE_PAUSED) |
atse@12109 | 496 except Exception, ex: |
ewan@13080 | 497 log.exception(ex) |
atse@12109 | 498 raise XendError("Domain unable to be paused: %s" % str(ex)) |
atse@12109 | 499 |
atse@12109 | 500 def unpause(self): |
atse@12109 | 501 """Unpause domain |
atse@12109 | 502 |
atse@12109 | 503 @raise XendError: Failed unpausing a domain |
atse@12109 | 504 """ |
atse@12109 | 505 try: |
atse@12109 | 506 xc.domain_unpause(self.domid) |
atse@12109 | 507 self._stateSet(DOM_STATE_RUNNING) |
atse@12109 | 508 except Exception, ex: |
ewan@13080 | 509 log.exception(ex) |
atse@12109 | 510 raise XendError("Domain unable to be unpaused: %s" % str(ex)) |
atse@12109 | 511 |
atse@12109 | 512 def send_sysrq(self, key): |
atse@12109 | 513 """ Send a Sysrq equivalent key via xenstored.""" |
kfraser@15538 | 514 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
kfraser@15538 | 515 raise XendError("Domain '%s' is not started" % self.info['name_label']) |
kfraser@15538 | 516 |
atse@12109 | 517 asserts.isCharConvertible(key) |
ewan@12695 | 518 self.storeDom("control/sysrq", '%c' % key) |
atse@12109 | 519 |
atse@12109 | 520 def device_create(self, dev_config): |
atse@12109 | 521 """Create a new device. |
emellor@8314 | 522 |
atse@12109 | 523 @param dev_config: device configuration |
atse@12671 | 524 @type dev_config: SXP object (parsed config) |
atse@12109 | 525 """ |
ewan@12816 | 526 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config)) |
atse@12109 | 527 dev_type = sxp.name(dev_config) |
atse@12671 | 528 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config) |
atse@12671 | 529 dev_config_dict = self.info['devices'][dev_uuid][1] |
ewan@12816 | 530 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict)) |
kfraser@15515 | 531 |
kfraser@15515 | 532 if self.domid is not None: |
kfraser@15515 | 533 try: |
kfraser@15515 | 534 dev_config_dict['devid'] = devid = \ |
kfraser@15515 | 535 self._createDevice(dev_type, dev_config_dict) |
kfraser@15515 | 536 self._waitForDevice(dev_type, devid) |
kfraser@15515 | 537 except VmError, ex: |
kfraser@15515 | 538 raise ex |
kfraser@15515 | 539 else: |
kfraser@15515 | 540 devid = None |
kfraser@15515 | 541 |
kfraser@15515 | 542 xen.xend.XendDomain.instance().managed_config_save(self) |
atse@12109 | 543 return self.getDeviceController(dev_type).sxpr(devid) |
atse@12109 | 544 |
atse@13371 | 545 def device_configure(self, dev_sxp, devid = None): |
atse@12109 | 546 """Configure an existing device. |
atse@12109 | 547 |
atse@12109 | 548 @param dev_config: device configuration |
atse@12671 | 549 @type dev_config: SXP object (parsed config) |
atse@12109 | 550 @param devid: device id |
atse@12109 | 551 @type devid: int |
atse@12671 | 552 @return: Returns True if successfully updated device |
atse@12671 | 553 @rtype: boolean |
atse@12109 | 554 """ |
atse@12671 | 555 |
atse@13371 | 556 # convert device sxp to a dict |
atse@13371 | 557 dev_class = sxp.name(dev_sxp) |
atse@13371 | 558 dev_config = {} |
atse@13371 | 559 for opt_val in dev_sxp[1:]: |
atse@13371 | 560 try: |
atse@13371 | 561 dev_config[opt_val[0]] = opt_val[1] |
atse@13371 | 562 except IndexError: |
atse@13371 | 563 pass |
atse@13371 | 564 |
atse@13371 | 565 # use DevController.reconfigureDevice to change device config |
atse@13371 | 566 dev_control = self.getDeviceController(dev_class) |
atse@13371 | 567 dev_uuid = dev_control.reconfigureDevice(devid, dev_config) |
atse@13371 | 568 |
atse@13371 | 569 # update XendConfig with new device info |
atse@13371 | 570 if dev_uuid: |
atse@13371 | 571 self.info.device_update(dev_uuid, dev_sxp) |
atse@13371 | 572 |
atse@12671 | 573 return True |
emellor@7884 | 574 |
atse@12109 | 575 def waitForDevices(self): |
atse@12109 | 576 """Wait for this domain's configured devices to connect. |
atse@12109 | 577 |
atse@12109 | 578 @raise VmError: if any device fails to initialise. |
atse@12109 | 579 """ |
atse@12109 | 580 for devclass in XendDevices.valid_devices(): |
atse@12109 | 581 self.getDeviceController(devclass).waitForDevices() |
atse@12109 | 582 |
kfraser@15754 | 583 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False): |
kfraser@15754 | 584 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s", |
kfraser@15754 | 585 deviceClass, devid) |
kfraser@15754 | 586 |
kfraser@15754 | 587 if rm_cfg: |
kfraser@15754 | 588 # Convert devid to device number. A device number is |
kfraser@15754 | 589 # needed to remove its configuration. |
kfraser@15754 | 590 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid) |
kfraser@15754 | 591 |
kfraser@15754 | 592 # Save current sxprs. A device number and a backend |
kfraser@15754 | 593 # path are needed to remove its configuration but sxprs |
kfraser@15754 | 594 # do not have those after calling destroyDevice. |
kfraser@15754 | 595 sxprs = self.getDeviceSxprs(deviceClass) |
kfraser@15754 | 596 |
kfraser@15754 | 597 rc = None |
kfraser@15754 | 598 if self.domid is not None: |
kfraser@15754 | 599 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force) |
kfraser@15754 | 600 if not force and rm_cfg: |
kfraser@15754 | 601 # The backend path, other than the device itself, |
kfraser@15754 | 602 # has to be passed because its accompanied frontend |
kfraser@15754 | 603 # path may be void until its removal is actually |
kfraser@15754 | 604 # issued. It is probable because destroyDevice is |
kfraser@15754 | 605 # issued first. |
kfraser@15754 | 606 for dev_num, dev_info in sxprs: |
kfraser@15754 | 607 dev_num = int(dev_num) |
kfraser@15754 | 608 if dev_num == dev: |
kfraser@15754 | 609 for x in dev_info: |
kfraser@15754 | 610 if x[0] == 'backend': |
kfraser@15754 | 611 backend = x[1] |
kfraser@15754 | 612 break |
kfraser@15754 | 613 break |
kfraser@15754 | 614 self._waitForDevice_destroy(deviceClass, devid, backend) |
kfraser@15754 | 615 |
kfraser@15754 | 616 if rm_cfg: |
kfraser@15754 | 617 if deviceClass == 'vif': |
kfraser@15754 | 618 if self.domid is not None: |
kfraser@15754 | 619 for dev_num, dev_info in sxprs: |
kfraser@15754 | 620 dev_num = int(dev_num) |
kfraser@15754 | 621 if dev_num == dev: |
kfraser@15754 | 622 for x in dev_info: |
kfraser@15754 | 623 if x[0] == 'mac': |
kfraser@15754 | 624 mac = x[1] |
kfraser@15754 | 625 break |
kfraser@15754 | 626 break |
kfraser@15874 | 627 dev_info = self._getDeviceInfo_vif(mac) |
kfraser@15754 | 628 else: |
kfraser@15754 | 629 _, dev_info = sxprs[dev] |
kfraser@15754 | 630 else: # 'vbd' or 'tap' |
kfraser@15874 | 631 dev_info = self._getDeviceInfo_vbd(dev) |
kfraser@15774 | 632 # To remove the UUID of the device from refs, |
kfraser@15774 | 633 # deviceClass must be always 'vbd'. |
kfraser@15774 | 634 deviceClass = 'vbd' |
kfraser@15754 | 635 if dev_info is None: |
kfraser@15874 | 636 raise XendError("Device %s is not defined" % devid) |
kfraser@15754 | 637 |
kfraser@15754 | 638 dev_uuid = sxp.child_value(dev_info, 'uuid') |
kfraser@15754 | 639 del self.info['devices'][dev_uuid] |
kfraser@15754 | 640 self.info['%s_refs' % deviceClass].remove(dev_uuid) |
kfraser@15754 | 641 xen.xend.XendDomain.instance().managed_config_save(self) |
kfraser@15754 | 642 |
kfraser@15754 | 643 return rc |
emellor@7884 | 644 |
atse@12109 | 645 def getDeviceSxprs(self, deviceClass): |
tom@14924 | 646 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
atse@12738 | 647 return self.getDeviceController(deviceClass).sxprs() |
atse@12738 | 648 else: |
atse@12738 | 649 sxprs = [] |
atse@12738 | 650 dev_num = 0 |
atse@12738 | 651 for dev_type, dev_info in self.info.all_devices_sxpr(): |
atse@12738 | 652 if dev_type == deviceClass: |
atse@12738 | 653 sxprs.append([dev_num, dev_info]) |
atse@12738 | 654 dev_num += 1 |
atse@12738 | 655 return sxprs |
atse@12109 | 656 |
kfraser@15874 | 657 def getBlockDeviceClass(self, devid): |
kfraser@15874 | 658 # To get a device number from the devid, |
kfraser@15874 | 659 # we temporarily use the device controller of VBD. |
kfraser@15874 | 660 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid) |
kfraser@15874 | 661 dev_info = self._getDeviceInfo_vbd(dev) |
kfraser@15874 | 662 if dev_info: |
kfraser@15874 | 663 return dev_info[0] |
kfraser@15874 | 664 |
kfraser@15874 | 665 def _getDeviceInfo_vif(self, mac): |
kfraser@15754 | 666 for dev_type, dev_info in self.info.all_devices_sxpr(): |
kfraser@15754 | 667 if dev_type != 'vif': |
kfraser@15754 | 668 continue |
kfraser@15754 | 669 if mac == sxp.child_value(dev_info, 'mac'): |
kfraser@15754 | 670 return dev_info |
kfraser@15754 | 671 |
kfraser@15874 | 672 def _getDeviceInfo_vbd(self, devid): |
kfraser@15754 | 673 for dev_type, dev_info in self.info.all_devices_sxpr(): |
kfraser@15754 | 674 if dev_type != 'vbd' and dev_type != 'tap': |
kfraser@15754 | 675 continue |
kfraser@15754 | 676 dev = sxp.child_value(dev_info, 'dev') |
kfraser@15754 | 677 dev = dev.split(':')[0] |
kfraser@15754 | 678 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev) |
kfraser@15754 | 679 if devid == dev: |
kfraser@15754 | 680 return dev_info |
kfraser@15754 | 681 |
keir@15409 | 682 |
atse@12109 | 683 def setMemoryTarget(self, target): |
atse@12109 | 684 """Set the memory target of this domain. |
atse@12109 | 685 @param target: In MiB. |
atse@12109 | 686 """ |
ewan@14596 | 687 log.debug("Setting memory target of domain %s (%s) to %d MiB.", |
ewan@14596 | 688 self.info['name_label'], str(self.domid), target) |
atse@12109 | 689 |
ewan@14471 | 690 MiB = 1024 * 1024 |
steven@15010 | 691 self._safe_set_memory('memory_dynamic_min', target * MiB) |
steven@15010 | 692 self._safe_set_memory('memory_dynamic_max', target * MiB) |
ewan@14471 | 693 |
ewan@13099 | 694 if self.domid >= 0: |
ewan@13099 | 695 self.storeVm("memory", target) |
ewan@13099 | 696 self.storeDom("memory/target", target << 10) |
ewan@14597 | 697 xen.xend.XendDomain.instance().managed_config_save(self) |
ewan@13099 | 698 |
ewan@13099 | 699 def setMemoryMaximum(self, limit): |
ewan@13099 | 700 """Set the maximum memory limit of this domain |
ewan@13099 | 701 @param limit: In MiB. |
ewan@13099 | 702 """ |
ewan@14594 | 703 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.", |
ewan@14594 | 704 self.info['name_label'], str(self.domid), limit) |
ewan@13099 | 705 |
ewan@13099 | 706 if limit <= 0: |
ewan@13099 | 707 raise XendError('Invalid memory size') |
ewan@13099 | 708 |
ewan@14594 | 709 MiB = 1024 * 1024 |
kfraser@15278 | 710 self._safe_set_memory('memory_static_max', limit * MiB) |
ewan@14594 | 711 |
ewan@13099 | 712 if self.domid >= 0: |
ewan@13099 | 713 maxmem = int(limit) * 1024 |
ewan@13099 | 714 try: |
ewan@13099 | 715 return xc.domain_setmaxmem(self.domid, maxmem) |
ewan@13099 | 716 except Exception, ex: |
ewan@13099 | 717 raise XendError(str(ex)) |
ewan@14597 | 718 xen.xend.XendDomain.instance().managed_config_save(self) |
ewan@13099 | 719 |
atse@12109 | 720 |
atse@12109 | 721 def getVCPUInfo(self): |
atse@12109 | 722 try: |
atse@12109 | 723 # We include the domain name and ID, to help xm. |
atse@12109 | 724 sxpr = ['domain', |
atse@12109 | 725 ['domid', self.domid], |
atse@12671 | 726 ['name', self.info['name_label']], |
ewan@14519 | 727 ['vcpu_count', self.info['VCPUs_max']]] |
ewan@14519 | 728 |
ewan@14519 | 729 for i in range(0, self.info['VCPUs_max']): |
kfraser@15660 | 730 if self.domid is not None: |
kfraser@15660 | 731 info = xc.vcpu_getinfo(self.domid, i) |
kfraser@15660 | 732 |
kfraser@15660 | 733 sxpr.append(['vcpu', |
kfraser@15660 | 734 ['number', i], |
kfraser@15660 | 735 ['online', info['online']], |
kfraser@15660 | 736 ['blocked', info['blocked']], |
kfraser@15660 | 737 ['running', info['running']], |
kfraser@15660 | 738 ['cpu_time', info['cpu_time'] / 1e9], |
kfraser@15660 | 739 ['cpu', info['cpu']], |
kfraser@15660 | 740 ['cpumap', info['cpumap']]]) |
kfraser@15660 | 741 else: |
kfraser@15660 | 742 sxpr.append(['vcpu', |
kfraser@15660 | 743 ['number', i], |
kfraser@15660 | 744 ['online', 0], |
kfraser@15660 | 745 ['blocked', 0], |
kfraser@15660 | 746 ['running', 0], |
kfraser@15660 | 747 ['cpu_time', 0.0], |
kfraser@15660 | 748 ['cpu', -1], |
kfraser@15660 | 749 ['cpumap', self.info['cpus'] and \ |
kfraser@15660 | 750 self.info['cpus'] or range(64)]]) |
atse@12109 | 751 |
atse@12109 | 752 return sxpr |
atse@12109 | 753 |
atse@12109 | 754 except RuntimeError, exn: |
atse@12109 | 755 raise XendError(str(exn)) |
atse@12109 | 756 |
ewan@14417 | 757 |
ewan@14417 | 758 def getDomInfo(self): |
ewan@14417 | 759 return dom_get(self.domid) |
ewan@14417 | 760 |
atse@12109 | 761 # |
atse@12109 | 762 # internal functions ... TODO: re-categorised |
atse@12109 | 763 # |
atse@12109 | 764 |
atse@12109 | 765 def _augmentInfo(self, priv): |
atse@12109 | 766 """Augment self.info, as given to us through L{recreate}, with |
atse@12109 | 767 values taken from the store. This recovers those values known |
atse@12109 | 768 to xend but not to the hypervisor. |
emellor@7099 | 769 """ |
atse@12671 | 770 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:] |
emellor@8183 | 771 if priv: |
atse@12671 | 772 augment_entries.remove('memory') |
atse@12671 | 773 augment_entries.remove('maxmem') |
ewan@13033 | 774 augment_entries.remove('vcpus') |
ewan@13033 | 775 augment_entries.remove('vcpu_avail') |
emellor@8183 | 776 |
atse@12671 | 777 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k]) |
atse@12671 | 778 for k in augment_entries]) |
atse@12671 | 779 |
atse@12671 | 780 # make returned lists into a dictionary |
atse@12671 | 781 vm_config = dict(zip(augment_entries, vm_config)) |
atse@12671 | 782 |
atse@12671 | 783 for arg in augment_entries: |
atse@12671 | 784 val = vm_config[arg] |
atse@12671 | 785 if val != None: |
atse@12671 | 786 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: |
atse@12671 | 787 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] |
atse@12671 | 788 self.info[xapiarg] = val |
ewan@14463 | 789 elif arg == "memory": |
ewan@14463 | 790 self.info["static_memory_min"] = val |
ewan@14463 | 791 elif arg == "maxmem": |
ewan@14463 | 792 self.info["static_memory_max"] = val |
atse@12671 | 793 else: |
atse@12671 | 794 self.info[arg] = val |
emellor@7180 | 795 |
ewan@13033 | 796 # For dom0, we ignore any stored value for the vcpus fields, and |
ewan@13033 | 797 # read the current value from Xen instead. This allows boot-time |
ewan@13033 | 798 # settings to take precedence over any entries in the store. |
ewan@13033 | 799 if priv: |
ewan@13033 | 800 xeninfo = dom_get(self.domid) |
ewan@14519 | 801 self.info['VCPUs_max'] = xeninfo['online_vcpus'] |
ewan@13033 | 802 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1 |
ewan@13033 | 803 |
atse@12671 | 804 # read image value |
atse@12671 | 805 image_sxp = self._readVm('image') |
atse@12671 | 806 if image_sxp: |
atse@12671 | 807 self.info.update_with_image_sxp(sxp.from_string(image_sxp)) |
atse@12671 | 808 |
atse@12671 | 809 # read devices |
atse@12109 | 810 devices = [] |
atse@12109 | 811 for devclass in XendDevices.valid_devices(): |
atse@12109 | 812 devconfig = self.getDeviceController(devclass).configurations() |
atse@12109 | 813 if devconfig: |
ewan@12695 | 814 devices.extend(devconfig) |
smh22@7745 | 815 |
atse@12671 | 816 if not self.info['devices'] and devices is not None: |
atse@12109 | 817 for device in devices: |
atse@12123 | 818 self.info.device_add(device[0], cfg_sxp = device) |
smh22@9769 | 819 |
atse@13654 | 820 self._update_consoles() |
atse@13654 | 821 |
keir@16298 | 822 def _update_consoles(self, transaction = None): |
atse@13654 | 823 if self.domid == None or self.domid == 0: |
atse@13654 | 824 return |
atse@13654 | 825 |
atse@13654 | 826 # Update VT100 port if it exists |
keir@16298 | 827 if transaction is None: |
keir@16298 | 828 self.console_port = self.readDom('console/port') |
keir@16298 | 829 else: |
keir@16298 | 830 self.console_port = self.readDomTxn(transaction, 'console/port') |
atse@13654 | 831 if self.console_port is not None: |
atse@13654 | 832 serial_consoles = self.info.console_get_all('vt100') |
atse@13654 | 833 if not serial_consoles: |
atse@13654 | 834 cfg = self.info.console_add('vt100', self.console_port) |
atse@13654 | 835 self._createDevice('console', cfg) |
atse@13783 | 836 else: |
atse@13783 | 837 console_uuid = serial_consoles[0].get('uuid') |
atse@13783 | 838 self.info.console_update(console_uuid, 'location', |
atse@13783 | 839 self.console_port) |
atse@13783 | 840 |
atse@13654 | 841 |
atse@13783 | 842 # Update VNC port if it exists and write to xenstore |
keir@16298 | 843 if transaction is None: |
keir@16298 | 844 vnc_port = self.readDom('console/vnc-port') |
keir@16298 | 845 else: |
keir@16298 | 846 vnc_port = self.readDomTxn(transaction, 'console/vnc-port') |
atse@13654 | 847 if vnc_port is not None: |
atse@13783 | 848 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items(): |
atse@13783 | 849 if dev_type == 'vfb': |
atse@13783 | 850 old_location = dev_info.get('location') |
atse@13783 | 851 listen_host = dev_info.get('vnclisten', 'localhost') |
atse@13783 | 852 new_location = '%s:%s' % (listen_host, str(vnc_port)) |
atse@13783 | 853 if old_location == new_location: |
atse@13783 | 854 break |
atse@13654 | 855 |
atse@13783 | 856 dev_info['location'] = new_location |
atse@13783 | 857 self.info.device_update(dev_uuid, cfg_xenapi = dev_info) |
atse@13783 | 858 vfb_ctrl = self.getDeviceController('vfb') |
atse@13783 | 859 vfb_ctrl.reconfigureDevice(0, dev_info) |
atse@13783 | 860 break |
atse@13783 | 861 |
atse@12109 | 862 # |
atse@12109 | 863 # Function to update xenstore /vm/* |
atse@12109 | 864 # |
emellor@7037 | 865 |
atse@12109 | 866 def _readVm(self, *args): |
emellor@7180 | 867 return xstransact.Read(self.vmpath, *args) |
cl349@6849 | 868 |
atse@12109 | 869 def _writeVm(self, *args): |
emellor@7180 | 870 return xstransact.Write(self.vmpath, *args) |
cl349@6845 | 871 |
atse@12109 | 872 def _removeVm(self, *args): |
emellor@7180 | 873 return xstransact.Remove(self.vmpath, *args) |
cl349@6849 | 874 |
atse@12109 | 875 def _gatherVm(self, *args): |
emellor@7180 | 876 return xstransact.Gather(self.vmpath, *args) |
cl349@6860 | 877 |
cl349@6860 | 878 def storeVm(self, *args): |
emellor@7180 | 879 return xstransact.Store(self.vmpath, *args) |
cl349@6860 | 880 |
keir@16298 | 881 |
keir@16298 | 882 def _readVmTxn(self, transaction, *args): |
keir@16298 | 883 paths = map(lambda x: self.vmpath + "/" + x, args) |
keir@16298 | 884 return transaction.read(*paths) |
keir@16298 | 885 |
keir@16298 | 886 def _writeVmTxn(self, transaction, *args): |
keir@16298 | 887 paths = map(lambda x: self.vmpath + "/" + x, args) |
keir@16298 | 888 return transaction.write(*paths) |
keir@16298 | 889 |
keir@16298 | 890 def _removeVmTxn(self, transaction, *args): |
keir@16298 | 891 paths = map(lambda x: self.vmpath + "/" + x, args) |
keir@16298 | 892 return transaction.remove(*paths) |
keir@16298 | 893 |
keir@16298 | 894 def _gatherVmTxn(self, transaction, *args): |
keir@16298 | 895 paths = map(lambda x: self.vmpath + "/" + x, args) |
keir@16298 | 896 return transaction.gather(paths) |
keir@16298 | 897 |
keir@16298 | 898 def storeVmTxn(self, transaction, *args): |
keir@16298 | 899 paths = map(lambda x: self.vmpath + "/" + x, args) |
keir@16298 | 900 return transaction.store(*paths) |
keir@16298 | 901 |
atse@12109 | 902 # |
atse@12109 | 903 # Function to update xenstore /dom/* |
atse@12109 | 904 # |
emellor@7438 | 905 |
ewan@12695 | 906 def readDom(self, *args): |
emellor@7180 | 907 return xstransact.Read(self.dompath, *args) |
cl349@6849 | 908 |
ewan@12695 | 909 def gatherDom(self, *args): |
ewan@12695 | 910 return xstransact.Gather(self.dompath, *args) |
ewan@12695 | 911 |
atse@12109 | 912 def _writeDom(self, *args): |
emellor@7180 | 913 return xstransact.Write(self.dompath, *args) |
cl349@6849 | 914 |
atse@12109 | 915 def _removeDom(self, *args): |
emellor@7180 | 916 return xstransact.Remove(self.dompath, *args) |
cl349@6848 | 917 |
ewan@12695 | 918 def storeDom(self, *args): |
atse@12109 | 919 return xstransact.Store(self.dompath, *args) |
emellor@8209 | 920 |
keir@16298 | 921 |
keir@16298 | 922 def readDomTxn(self, transaction, *args): |
keir@16418 | 923 paths = map(lambda x: self.dompath + "/" + x, args) |
keir@16298 | 924 return transaction.read(*paths) |
keir@16298 | 925 |
keir@16298 | 926 def gatherDomTxn(self, transaction, *args): |
keir@16418 | 927 paths = map(lambda x: self.dompath + "/" + x, args) |
keir@16298 | 928 return transaction.gather(*paths) |
keir@16298 | 929 |
keir@16298 | 930 def _writeDomTxn(self, transaction, *args): |
keir@16418 | 931 paths = map(lambda x: self.dompath + "/" + x, args) |
keir@16298 | 932 return transaction.write(*paths) |
keir@16298 | 933 |
keir@16298 | 934 def _removeDomTxn(self, transaction, *args): |
keir@16418 | 935 paths = map(lambda x: self.dompath + "/" + x, args) |
keir@16298 | 936 return transaction.remove(*paths) |
keir@16298 | 937 |
keir@16298 | 938 def storeDomTxn(self, transaction, *args): |
keir@16418 | 939 paths = map(lambda x: self.dompath + "/" + x, args) |
keir@16298 | 940 return transaction.store(*paths) |
keir@16298 | 941 |
keir@16298 | 942 |
atse@12109 | 943 def _recreateDom(self): |
atse@12109 | 944 complete(self.dompath, lambda t: self._recreateDomFunc(t)) |
atse@12109 | 945 |
atse@12109 | 946 def _recreateDomFunc(self, t): |
emellor@8209 | 947 t.remove() |
emellor@8209 | 948 t.mkdir() |
atse@12671 | 949 t.set_permissions({'dom' : self.domid}) |
ewan@12350 | 950 t.write('vm', self.vmpath) |
emellor@8209 | 951 |
atse@12109 | 952 def _storeDomDetails(self): |
emellor@7022 | 953 to_store = { |
emellor@7022 | 954 'domid': str(self.domid), |
emellor@7180 | 955 'vm': self.vmpath, |
atse@12671 | 956 'name': self.info['name_label'], |
ewan@13444 | 957 'console/limit': str(xoptions.get_console_limit() * 1024), |
ewan@14463 | 958 'memory/target': str(self.info['memory_dynamic_max'] / 1024), |
emellor@7022 | 959 } |
cl349@5426 | 960 |
emellor@7461 | 961 def f(n, v): |
emellor@7461 | 962 if v is not None: |
kfraser@14158 | 963 if type(v) == bool: |
kfraser@14158 | 964 to_store[n] = v and "1" or "0" |
kfraser@14158 | 965 else: |
kfraser@14158 | 966 to_store[n] = str(v) |
emellor@7461 | 967 |
keir@16267 | 968 # Figure out if we need to tell xenconsoled to ignore this guest's |
keir@16267 | 969 # console - device model will handle console if it is running |
keir@16267 | 970 constype = "ioemu" |
keir@16267 | 971 if 'device_model' not in self.info['platform']: |
keir@16267 | 972 constype = "xenconsoled" |
keir@16267 | 973 |
emellor@7461 | 974 f('console/port', self.console_port) |
emellor@7461 | 975 f('console/ring-ref', self.console_mfn) |
keir@16267 | 976 f('console/type', constype) |
emellor@7461 | 977 f('store/port', self.store_port) |
emellor@7461 | 978 f('store/ring-ref', self.store_mfn) |
emellor@7461 | 979 |
kfraser@14324 | 980 if arch.type == "x86": |
kfraser@14324 | 981 f('control/platform-feature-multiprocessor-suspend', True) |
kfraser@14324 | 982 |
kfraser@14158 | 983 # elfnotes |
kfraser@14159 | 984 for n, v in self.info.get_notes().iteritems(): |
kfraser@14158 | 985 n = n.lower().replace('_', '-') |
kfraser@14158 | 986 if n == 'features': |
kfraser@14158 | 987 for v in v.split('|'): |
kfraser@14158 | 988 v = v.replace('_', '-') |
kfraser@14158 | 989 if v.startswith('!'): |
kfraser@14158 | 990 f('image/%s/%s' % (n, v[1:]), False) |
kfraser@14158 | 991 else: |
kfraser@14158 | 992 f('image/%s/%s' % (n, v), True) |
kfraser@14158 | 993 else: |
kfraser@14158 | 994 f('image/%s' % n, v) |
kfraser@14158 | 995 |
kfraser@15550 | 996 if self.info.has_key('security_label'): |
kfraser@15550 | 997 f('security_label', self.info['security_label']) |
kfraser@15550 | 998 |
atse@12109 | 999 to_store.update(self._vcpuDomDetails()) |
emellor@7438 | 1000 |
ewan@12816 | 1001 log.debug("Storing domain details: %s", scrub_password(to_store)) |
emellor@7438 | 1002 |
atse@12109 | 1003 self._writeDom(to_store) |
emellor@7438 | 1004 |
atse@12109 | 1005 def _vcpuDomDetails(self): |
emellor@7260 | 1006 def availability(n): |
emellor@7260 | 1007 if self.info['vcpu_avail'] & (1 << n): |
emellor@7260 | 1008 return 'online' |
emellor@7260 | 1009 else: |
emellor@7260 | 1010 return 'offline' |
emellor@7260 | 1011 |
emellor@7438 | 1012 result = {} |
ewan@14519 | 1013 for v in range(0, self.info['VCPUs_max']): |
emellor@7438 | 1014 result["cpu/%d/availability" % v] = availability(v) |
emellor@7438 | 1015 return result |
emellor@7022 | 1016 |
atse@12109 | 1017 # |
atse@12109 | 1018 # xenstore watches |
atse@12109 | 1019 # |
cl349@5426 | 1020 |
atse@12109 | 1021 def _registerWatches(self): |
emellor@9163 | 1022 """Register a watch on this VM's entries in the store, and the |
emellor@9163 | 1023 domain's control/shutdown node, so that when they are changed |
emellor@9163 | 1024 externally, we keep up to date. This should only be called by {@link |
emellor@9163 | 1025 #create}, {@link #recreate}, or {@link #restore}, once the domain's |
emellor@9163 | 1026 details have been written, but before the new instance is returned.""" |
atse@12109 | 1027 self.vmWatch = xswatch(self.vmpath, self._storeChanged) |
emellor@9163 | 1028 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown', |
atse@12109 | 1029 self._handleShutdownWatch) |
atse@12109 | 1030 |
atse@12109 | 1031 def _storeChanged(self, _): |
atse@12109 | 1032 log.trace("XendDomainInfo.storeChanged"); |
atse@12109 | 1033 |
atse@12109 | 1034 changed = False |
atse@12671 | 1035 |
atse@12671 | 1036 # Check whether values in the configuration have |
atse@12671 | 1037 # changed in Xenstore. |
atse@12109 | 1038 |
Christian@14741 | 1039 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash', |
Christian@14741 | 1040 'rtc/timeoffset'] |
atse@12671 | 1041 |
atse@12671 | 1042 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k]) |
atse@12671 | 1043 for k in cfg_vm]) |
atse@12109 | 1044 |
atse@12671 | 1045 # convert two lists into a python dictionary |
atse@12671 | 1046 vm_details = dict(zip(cfg_vm, vm_details)) |
kfraser@15054 | 1047 |
kfraser@15054 | 1048 if vm_details['rtc/timeoffset'] == None: |
kfraser@15054 | 1049 vm_details['rtc/timeoffset'] = "0" |
kfraser@15054 | 1050 |
atse@12671 | 1051 for arg, val in vm_details.items(): |
atse@12671 | 1052 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: |
atse@12671 | 1053 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] |
atse@12671 | 1054 if val != None and val != self.info[xapiarg]: |
atse@12671 | 1055 self.info[xapiarg] = val |
ewan@14463 | 1056 changed = True |
ewan@14463 | 1057 elif arg == "memory": |
ewan@14463 | 1058 if val != None and val != self.info["static_memory_min"]: |
ewan@14463 | 1059 self.info["static_memory_min"] = val |
ewan@14463 | 1060 changed = True |
ewan@14463 | 1061 elif arg == "maxmem": |
ewan@14463 | 1062 if val != None and val != self.info["static_memory_max"]: |
ewan@14463 | 1063 self.info["static_memory_max"] = val |
ewan@14463 | 1064 changed = True |
atse@12109 | 1065 |
atse@12671 | 1066 # Check whether image definition has been updated |
atse@12671 | 1067 image_sxp = self._readVm('image') |
kfraser@15028 | 1068 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()): |
atse@12671 | 1069 self.info.update_with_image_sxp(sxp.from_string(image_sxp)) |
atse@12109 | 1070 changed = True |
atse@12109 | 1071 |
Christian@14741 | 1072 # Check if the rtc offset has changes |
kfraser@15772 | 1073 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"): |
Christian@14741 | 1074 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0) |
Christian@14741 | 1075 changed = True |
Christian@14741 | 1076 |
atse@12109 | 1077 if changed: |
atse@12109 | 1078 # Update the domain section of the store, as this contains some |
atse@12109 | 1079 # parameters derived from the VM configuration. |
atse@12109 | 1080 self._storeDomDetails() |
atse@12109 | 1081 |
atse@12109 | 1082 return 1 |
atse@12109 | 1083 |
atse@12109 | 1084 def _handleShutdownWatch(self, _): |
atse@12109 | 1085 log.debug('XendDomainInfo.handleShutdownWatch') |
atse@12109 | 1086 |
ewan@12695 | 1087 reason = self.readDom('control/shutdown') |
atse@12109 | 1088 |
atse@12109 | 1089 if reason and reason != 'suspend': |
ewan@12695 | 1090 sst = self.readDom('xend/shutdown_start_time') |
atse@12109 | 1091 now = time.time() |
atse@12109 | 1092 if sst: |
atse@12109 | 1093 self.shutdownStartTime = float(sst) |
atse@12109 | 1094 timeout = float(sst) + SHUTDOWN_TIMEOUT - now |
atse@12109 | 1095 else: |
atse@12109 | 1096 self.shutdownStartTime = now |
ewan@12695 | 1097 self.storeDom('xend/shutdown_start_time', now) |
atse@12109 | 1098 timeout = SHUTDOWN_TIMEOUT |
atse@12109 | 1099 |
atse@12109 | 1100 log.trace( |
atse@12109 | 1101 "Scheduling refreshShutdown on domain %d in %ds.", |
atse@12109 | 1102 self.domid, timeout) |
atse@12275 | 1103 threading.Timer(timeout, self.refreshShutdown).start() |
atse@12254 | 1104 |
atse@12109 | 1105 return True |
atse@12109 | 1106 |
atse@12109 | 1107 |
atse@12109 | 1108 # |
atse@12109 | 1109 # Public Attributes for the VM |
atse@12109 | 1110 # |
emellor@7884 | 1111 |
mjw@2013 | 1112 |
emellor@7041 | 1113 def getDomid(self): |
cl349@6847 | 1114 return self.domid |
mjw@4618 | 1115 |
cl349@5377 | 1116 def setName(self, name): |
atse@12109 | 1117 self._checkName(name) |
atse@12671 | 1118 self.info['name_label'] = name |
cl349@6863 | 1119 self.storeVm("name", name) |
cl349@5377 | 1120 |
mjw@4618 | 1121 def getName(self): |
atse@12671 | 1122 return self.info['name_label'] |
mjw@4618 | 1123 |
emellor@7180 | 1124 def getDomainPath(self): |
emellor@7180 | 1125 return self.dompath |
emellor@6963 | 1126 |
atse@12276 | 1127 def getShutdownReason(self): |
ewan@12695 | 1128 return self.readDom('control/shutdown') |
emellor@7438 | 1129 |
emellor@7461 | 1130 def getStorePort(self): |
emellor@7461 | 1131 """For use only by image.py and XendCheckpoint.py.""" |
emellor@7461 | 1132 return self.store_port |
emellor@7461 | 1133 |
emellor@7461 | 1134 def getConsolePort(self): |
emellor@7461 | 1135 """For use only by image.py and XendCheckpoint.py""" |
emellor@7461 | 1136 return self.console_port |
emellor@7461 | 1137 |
kaf24@9915 | 1138 def getFeatures(self): |
kaf24@9915 | 1139 """For use only by image.py.""" |
kaf24@9915 | 1140 return self.info['features'] |
emellor@7461 | 1141 |
emellor@6956 | 1142 def getVCpuCount(self): |
ewan@14519 | 1143 return self.info['VCPUs_max'] |
emellor@6956 | 1144 |
emellor@7438 | 1145 def setVCpuCount(self, vcpus): |
ewan@14170 | 1146 if vcpus <= 0: |
ewan@14170 | 1147 raise XendError('Invalid VCPUs') |
ewan@14170 | 1148 |
emellor@7438 | 1149 self.info['vcpu_avail'] = (1 << vcpus) - 1 |
ewan@13099 | 1150 if self.domid >= 0: |
ewan@13099 | 1151 self.storeVm('vcpu_avail', self.info['vcpu_avail']) |
ewan@13099 | 1152 # update dom differently depending on whether we are adjusting |
ewan@13099 | 1153 # vcpu number up or down, otherwise _vcpuDomDetails does not |
ewan@13099 | 1154 # disable the vcpus |
ewan@14519 | 1155 if self.info['VCPUs_max'] > vcpus: |
ewan@13099 | 1156 # decreasing |
ewan@13099 | 1157 self._writeDom(self._vcpuDomDetails()) |
ewan@14519 | 1158 self.info['VCPUs_live'] = vcpus |
ewan@13099 | 1159 else: |
ewan@13099 | 1160 # same or increasing |
ewan@14519 | 1161 self.info['VCPUs_live'] = vcpus |
ewan@13099 | 1162 self._writeDom(self._vcpuDomDetails()) |
ewan@13099 | 1163 else: |
kfraser@15432 | 1164 self.info['VCPUs_max'] = vcpus |
ewan@13099 | 1165 xen.xend.XendDomain.instance().managed_config_save(self) |
ewan@13463 | 1166 log.info("Set VCPU count on domain %s to %d", self.info['name_label'], |
ewan@13463 | 1167 vcpus) |
emellor@7438 | 1168 |
emellor@6964 | 1169 def getMemoryTarget(self): |
smh22@7745 | 1170 """Get this domain's target memory size, in KB.""" |
ewan@14471 | 1171 return self.info['memory_dynamic_max'] / 1024 |
emellor@6954 | 1172 |
kfraser@13029 | 1173 def getMemoryMaximum(self): |
kfraser@13029 | 1174 """Get this domain's maximum memory size, in KB.""" |
ewan@14463 | 1175 # remember, info now stores memory in bytes |
ewan@14463 | 1176 return self.info['memory_static_max'] / 1024 |
kfraser@13029 | 1177 |
kaf24@8789 | 1178 def getResume(self): |
atse@12671 | 1179 return str(self._resume) |
kaf24@8789 | 1180 |
keir@16675 | 1181 def setResume(self, isresume): |
keir@16675 | 1182 self._resume = isresume |
keir@16675 | 1183 |
ewan@12219 | 1184 def getCap(self): |
keir@16003 | 1185 return self.info['vcpus_params']['cap'] |
ewan@12219 | 1186 |
kfraser@15187 | 1187 def setCap(self, cpu_cap): |
keir@16003 | 1188 self.info['vcpus_params']['cap'] = cpu_cap |
kfraser@15187 | 1189 |
ewan@12219 | 1190 def getWeight(self): |
keir@16003 | 1191 return self.info['vcpus_params']['weight'] |
ewan@12219 | 1192 |
kfraser@15187 | 1193 def setWeight(self, cpu_weight): |
keir@16003 | 1194 self.info['vcpus_params']['weight'] = cpu_weight |
kfraser@15187 | 1195 |
kaf24@8789 | 1196 def setResume(self, state): |
atse@12671 | 1197 self._resume = state |
emellor@6957 | 1198 |
ewan@11319 | 1199 def getRestartCount(self): |
atse@12109 | 1200 return self._readVm('xend/restart_count') |
ewan@11319 | 1201 |
atse@12275 | 1202 def refreshShutdown(self, xeninfo = None): |
atse@12275 | 1203 """ Checks the domain for whether a shutdown is required. |
atse@12275 | 1204 |
atse@12275 | 1205 Called from XendDomainInfo and also image.py for HVM images. |
atse@12275 | 1206 """ |
atse@12253 | 1207 |
emellor@7180 | 1208 # If set at the end of this method, a restart is required, with the |
emellor@7180 | 1209 # given reason. This restart has to be done out of the scope of |
emellor@7180 | 1210 # refresh_shutdown_lock. |
emellor@7180 | 1211 restart_reason = None |
atse@12254 | 1212 |
emellor@7180 | 1213 self.refresh_shutdown_lock.acquire() |
emellor@7180 | 1214 try: |
emellor@7099 | 1215 if xeninfo is None: |
emellor@7180 | 1216 xeninfo = dom_get(self.domid) |
emellor@7180 | 1217 if xeninfo is None: |
emellor@7180 | 1218 # The domain no longer exists. This will occur if we have |
emellor@7180 | 1219 # scheduled a timer to check for shutdown timeouts and the |
emellor@7180 | 1220 # shutdown succeeded. It will also occur if someone |
emellor@7210 | 1221 # destroys a domain beneath us. We clean up the domain, |
emellor@7210 | 1222 # just in case, but we can't clean up the VM, because that |
emellor@7210 | 1223 # VM may have migrated to a different domain on this |
emellor@7210 | 1224 # machine. |
emellor@7180 | 1225 self.cleanupDomain() |
atse@12109 | 1226 self._stateSet(DOM_STATE_HALTED) |
emellor@7180 | 1227 return |
emellor@7180 | 1228 |
emellor@7180 | 1229 if xeninfo['dying']: |
emellor@7180 | 1230 # Dying means that a domain has been destroyed, but has not |
emellor@7210 | 1231 # yet been cleaned up by Xen. This state could persist |
emellor@7210 | 1232 # indefinitely if, for example, another domain has some of its |
emellor@7210 | 1233 # pages mapped. We might like to diagnose this problem in the |
emellor@7210 | 1234 # future, but for now all we do is make sure that it's not us |
emellor@7210 | 1235 # holding the pages, by calling cleanupDomain. We can't |
emellor@7210 | 1236 # clean up the VM, as above. |
emellor@7180 | 1237 self.cleanupDomain() |
atse@12109 | 1238 self._stateSet(DOM_STATE_SHUTDOWN) |
emellor@7099 | 1239 return |
emellor@7099 | 1240 |
emellor@7180 | 1241 elif xeninfo['crashed']: |
ewan@12695 | 1242 if self.readDom('xend/shutdown_completed'): |
emellor@7461 | 1243 # We've seen this shutdown already, but we are preserving |
emellor@7461 | 1244 # the domain for debugging. Leave it alone. |
emellor@7461 | 1245 return |
emellor@7461 | 1246 |
emellor@7180 | 1247 log.warn('Domain has crashed: name=%s id=%d.', |
atse@12671 | 1248 self.info['name_label'], self.domid) |
ewan@13280 | 1249 self._writeVm(LAST_SHUTDOWN_REASON, 'crash') |
emellor@7099 | 1250 |
ewan@13444 | 1251 if xoptions.get_enable_dump(): |
ewan@13286 | 1252 try: |
ewan@13286 | 1253 self.dumpCore() |
ewan@13286 | 1254 except XendError: |
ewan@13286 | 1255 # This error has been logged -- there's nothing more |
ewan@13286 | 1256 # we can do in this context. |
ewan@13286 | 1257 pass |
emellor@7099 | 1258 |
emellor@7182 | 1259 restart_reason = 'crash' |
atse@12109 | 1260 self._stateSet(DOM_STATE_HALTED) |
emellor@7099 | 1261 |
emellor@7180 | 1262 elif xeninfo['shutdown']: |
atse@12109 | 1263 self._stateSet(DOM_STATE_SHUTDOWN) |
ewan@12695 | 1264 if self.readDom('xend/shutdown_completed'): |
emellor@7185 | 1265 # We've seen this shutdown already, but we are preserving |
emellor@7185 | 1266 # the domain for debugging. Leave it alone. |
emellor@7210 | 1267 return |
emellor@7210 | 1268 |
emellor@7185 | 1269 else: |
emellor@7185 | 1270 reason = shutdown_reason(xeninfo['shutdown_reason']) |
emellor@7099 | 1271 |
emellor@7185 | 1272 log.info('Domain has shutdown: name=%s id=%d reason=%s.', |
atse@12671 | 1273 self.info['name_label'], self.domid, reason) |
ewan@13280 | 1274 self._writeVm(LAST_SHUTDOWN_REASON, reason) |
emellor@7099 | 1275 |
atse@12109 | 1276 self._clearRestart() |
emellor@7185 | 1277 |
emellor@7185 | 1278 if reason == 'suspend': |
atse@12109 | 1279 self._stateSet(DOM_STATE_SUSPENDED) |
emellor@7185 | 1280 # Don't destroy the domain. XendCheckpoint will do |
emellor@8212 | 1281 # this once it has finished. However, stop watching |
emellor@8212 | 1282 # the VM path now, otherwise we will end up with one |
emellor@8212 | 1283 # watch for the old domain, and one for the new. |
atse@12109 | 1284 self._unwatchVm() |
atse@12524 | 1285 elif reason in ('poweroff', 'reboot'): |
emellor@7185 | 1286 restart_reason = reason |
emellor@7185 | 1287 else: |
emellor@7185 | 1288 self.destroy() |
emellor@7180 | 1289 |
emellor@7461 | 1290 elif self.dompath is None: |
emellor@7461 | 1291 # We have yet to manage to call introduceDomain on this |
emellor@7461 | 1292 # domain. This can happen if a restore is in progress, or has |
emellor@7461 | 1293 # failed. Ignore this domain. |
emellor@7461 | 1294 pass |
emellor@7099 | 1295 else: |
kfraser@15217 | 1296 # Domain is alive. If we are shutting it down, log a message |
kfraser@15217 | 1297 # if it seems unresponsive. |
atse@12253 | 1298 if xeninfo['paused']: |
atse@12253 | 1299 self._stateSet(DOM_STATE_PAUSED) |
atse@12253 | 1300 else: |
atse@12253 | 1301 self._stateSet(DOM_STATE_RUNNING) |
atse@12253 | 1302 |
kfraser@15210 | 1303 if self.shutdownStartTime: |
emellor@9163 | 1304 timeout = (SHUTDOWN_TIMEOUT - time.time() + |
emellor@9163 | 1305 self.shutdownStartTime) |
kfraser@15217 | 1306 if (timeout < 0 and not self.readDom('xend/unresponsive')): |
emellor@7180 | 1307 log.info( |
emellor@7180 | 1308 "Domain shutdown timeout expired: name=%s id=%s", |
atse@12671 | 1309 self.info['name_label'], self.domid) |
kfraser@15217 | 1310 self.storeDom('xend/unresponsive', 'True') |
emellor@7180 | 1311 finally: |
emellor@7180 | 1312 self.refresh_shutdown_lock.release() |
emellor@7180 | 1313 |
emellor@7180 | 1314 if restart_reason: |
ewan@13080 | 1315 threading.Thread(target = self._maybeRestart, |
ewan@13080 | 1316 args = (restart_reason,)).start() |
emellor@7099 | 1317 |
emellor@7099 | 1318 |
atse@12109 | 1319 # |
atse@12109 | 1320 # Restart functions - handling whether we come back up on shutdown. |
atse@12109 | 1321 # |
emellor@9163 | 1322 |
atse@12109 | 1323 def _clearRestart(self): |
atse@12109 | 1324 self._removeDom("xend/shutdown_start_time") |
emellor@9163 | 1325 |
emellor@9163 | 1326 |
atse@12109 | 1327 def _maybeRestart(self, reason): |
emellor@7182 | 1328 # Dispatch to the correct method based upon the configured on_{reason} |
emellor@7182 | 1329 # behaviour. |
atse@12671 | 1330 actions = {"destroy" : self.destroy, |
atse@12671 | 1331 "restart" : self._restart, |
atse@12671 | 1332 "preserve" : self._preserve, |
atse@12671 | 1333 "rename-restart" : self._renameRestart} |
emellor@7182 | 1334 |
atse@12671 | 1335 action_conf = { |
atse@12671 | 1336 'poweroff': 'actions_after_shutdown', |
atse@12671 | 1337 'reboot': 'actions_after_reboot', |
atse@12671 | 1338 'crash': 'actions_after_crash', |
atse@12671 | 1339 } |
atse@12671 | 1340 |
atse@12671 | 1341 action_target = self.info.get(action_conf.get(reason)) |
atse@12671 | 1342 func = actions.get(action_target, None) |
atse@12671 | 1343 if func and callable(func): |
atse@12671 | 1344 func() |
atse@12671 | 1345 else: |
atse@12671 | 1346 self.destroy() # default to destroy |
emellor@7182 | 1347 |
atse@12109 | 1348 def _renameRestart(self): |
atse@12109 | 1349 self._restart(True) |
atse@12109 | 1350 |
atse@12109 | 1351 def _restart(self, rename = False): |
atse@12109 | 1352 """Restart the domain after it has exited. |
atse@12109 | 1353 |
atse@12109 | 1354 @param rename True if the old domain is to be renamed and preserved, |
atse@12109 | 1355 False if it is to be destroyed. |
atse@12109 | 1356 """ |
atse@12109 | 1357 from xen.xend import XendDomain |
atse@12109 | 1358 |
atse@12109 | 1359 if self._readVm(RESTART_IN_PROGRESS): |
atse@12109 | 1360 log.error('Xend failed during restart of domain %s. ' |
atse@12109 | 1361 'Refusing to restart to avoid loops.', |
atse@12109 | 1362 str(self.domid)) |
atse@12109 | 1363 self.destroy() |
atse@12109 | 1364 return |
atse@12109 | 1365 |
ewan@12335 | 1366 old_domid = self.domid |
atse@12109 | 1367 self._writeVm(RESTART_IN_PROGRESS, 'True') |
atse@12109 | 1368 |
atse@12109 | 1369 now = time.time() |
atse@12109 | 1370 rst = self._readVm('xend/previous_restart_time') |
atse@12109 | 1371 if rst: |
atse@12109 | 1372 rst = float(rst) |
atse@12109 | 1373 timeout = now - rst |
atse@12109 | 1374 if timeout < MINIMUM_RESTART_TIME: |
atse@12109 | 1375 log.error( |
atse@12109 | 1376 'VM %s restarting too fast (%f seconds since the last ' |
atse@12109 | 1377 'restart). Refusing to restart to avoid loops.', |
atse@12671 | 1378 self.info['name_label'], timeout) |
atse@12109 | 1379 self.destroy() |
atse@12109 | 1380 return |
atse@12109 | 1381 |
atse@12109 | 1382 self._writeVm('xend/previous_restart_time', str(now)) |
atse@12109 | 1383 |
atse@12109 | 1384 try: |
atse@12109 | 1385 if rename: |
atse@12109 | 1386 self._preserveForRestart() |
atse@12109 | 1387 else: |
atse@12109 | 1388 self._unwatchVm() |
atse@12109 | 1389 self.destroyDomain() |
atse@12109 | 1390 |
atse@12109 | 1391 # new_dom's VM will be the same as this domain's VM, except where |
atse@12109 | 1392 # the rename flag has instructed us to call preserveForRestart. |
atse@12109 | 1393 # In that case, it is important that we remove the |
atse@12109 | 1394 # RESTART_IN_PROGRESS node from the new domain, not the old one, |
atse@12109 | 1395 # once the new one is available. |
atse@12109 | 1396 |
atse@12109 | 1397 new_dom = None |
atse@12109 | 1398 try: |
ewan@13166 | 1399 new_dom = XendDomain.instance().domain_create_from_dict( |
ewan@13166 | 1400 self.info) |
kfraser@15872 | 1401 new_dom.waitForDevices() |
atse@12109 | 1402 new_dom.unpause() |
atse@12109 | 1403 rst_cnt = self._readVm('xend/restart_count') |
atse@12109 | 1404 rst_cnt = int(rst_cnt) + 1 |
atse@12109 | 1405 self._writeVm('xend/restart_count', str(rst_cnt)) |
atse@12109 | 1406 new_dom._removeVm(RESTART_IN_PROGRESS) |
atse@12109 | 1407 except: |
atse@12109 | 1408 if new_dom: |
atse@12109 | 1409 new_dom._removeVm(RESTART_IN_PROGRESS) |
atse@12109 | 1410 new_dom.destroy() |
atse@12109 | 1411 else: |
atse@12109 | 1412 self._removeVm(RESTART_IN_PROGRESS) |
atse@12109 | 1413 raise |
atse@12109 | 1414 except: |
ewan@12335 | 1415 log.exception('Failed to restart domain %s.', str(old_domid)) |
emellor@7185 | 1416 |
atse@12109 | 1417 def _preserveForRestart(self): |
atse@12109 | 1418 """Preserve a domain that has been shut down, by giving it a new UUID, |
atse@12109 | 1419 cloning the VM details, and giving it a new name. This allows us to |
atse@12109 | 1420 keep this domain for debugging, but restart a new one in its place |
atse@12109 | 1421 preserving the restart semantics (name and UUID preserved). |
atse@12109 | 1422 """ |
atse@12109 | 1423 |
atse@12109 | 1424 new_uuid = uuid.createString() |
atse@12109 | 1425 new_name = 'Domain-%s' % new_uuid |
atse@12109 | 1426 log.info("Renaming dead domain %s (%d, %s) to %s (%s).", |
atse@12671 | 1427 self.info['name_label'], self.domid, self.info['uuid'], |
atse@12109 | 1428 new_name, new_uuid) |
atse@12109 | 1429 self._unwatchVm() |
atse@12109 | 1430 self._releaseDevices() |
atse@12671 | 1431 self.info['name_label'] = new_name |
atse@12109 | 1432 self.info['uuid'] = new_uuid |
atse@12109 | 1433 self.vmpath = XS_VMROOT + new_uuid |
atse@12109 | 1434 self._storeVmDetails() |
atse@12109 | 1435 self._preserve() |
atse@12109 | 1436 |
atse@12109 | 1437 |
atse@12109 | 1438 def _preserve(self): |
atse@12671 | 1439 log.info("Preserving dead domain %s (%d).", self.info['name_label'], |
atse@12109 | 1440 self.domid) |
atse@12109 | 1441 self._unwatchVm() |
ewan@12695 | 1442 self.storeDom('xend/shutdown_completed', 'True') |
atse@12109 | 1443 self._stateSet(DOM_STATE_HALTED) |
atse@12109 | 1444 |
atse@12109 | 1445 # |
atse@12109 | 1446 # Debugging .. |
atse@12109 | 1447 # |
atse@12109 | 1448 |
atse@12109 | 1449 def dumpCore(self, corefile = None): |
ewan@13286 | 1450 """Create a core dump for this domain. |
ewan@13286 | 1451 |
ewan@13286 | 1452 @raise: XendError if core dumping failed. |
ewan@13286 | 1453 """ |
emellor@7092 | 1454 |
emellor@7092 | 1455 try: |
root@11511 | 1456 if not corefile: |
root@11512 | 1457 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime()) |
root@11512 | 1458 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time, |
atse@12671 | 1459 self.info['name_label'], self.domid) |
atse@11621 | 1460 |
atse@11621 | 1461 if os.path.isdir(corefile): |
atse@11621 | 1462 raise XendError("Cannot dump core in a directory: %s" % |
atse@11621 | 1463 corefile) |
atse@11621 | 1464 |
emellor@7987 | 1465 xc.domain_dumpcore(self.domid, corefile) |
atse@11621 | 1466 except RuntimeError, ex: |
root@11512 | 1467 corefile_incomp = corefile+'-incomplete' |
root@11512 | 1468 os.rename(corefile, corefile_incomp) |
emellor@7236 | 1469 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s", |
atse@12671 | 1470 self.domid, self.info['name_label']) |
atse@11621 | 1471 raise XendError("Failed to dump core: %s" % str(ex)) |
emellor@7092 | 1472 |
atse@12109 | 1473 # |
atse@12109 | 1474 # Device creation/deletion functions |
atse@12109 | 1475 # |
mjw@1661 | 1476 |
atse@12109 | 1477 def _createDevice(self, deviceClass, devConfig): |
atse@12109 | 1478 return self.getDeviceController(deviceClass).createDevice(devConfig) |
emellor@7376 | 1479 |
atse@12109 | 1480 def _waitForDevice(self, deviceClass, devid): |
emellor@7689 | 1481 return self.getDeviceController(deviceClass).waitForDevice(devid) |
emellor@7689 | 1482 |
ewan@13080 | 1483 def _waitForDeviceUUID(self, dev_uuid): |
ewan@13080 | 1484 deviceClass, config = self.info['devices'].get(dev_uuid) |
ewan@13080 | 1485 self._waitForDevice(deviceClass, config['devid']) |
ewan@13080 | 1486 |
kfraser@15754 | 1487 def _waitForDevice_destroy(self, deviceClass, devid, backpath): |
kfraser@15754 | 1488 return self.getDeviceController(deviceClass).waitForDevice_destroy( |
kfraser@15754 | 1489 devid, backpath) |
kfraser@15754 | 1490 |
atse@12109 | 1491 def _reconfigureDevice(self, deviceClass, devid, devconfig): |
emellor@7276 | 1492 return self.getDeviceController(deviceClass).reconfigureDevice( |
emellor@6963 | 1493 devid, devconfig) |
mjw@4618 | 1494 |
atse@12109 | 1495 def _createDevices(self): |
atse@12109 | 1496 """Create the devices for a vm. |
mjw@4618 | 1497 |
atse@12109 | 1498 @raise: VmError for invalid devices |
atse@12109 | 1499 """ |
ewan@13633 | 1500 ordered_refs = self.info.ordered_device_refs() |
ewan@13633 | 1501 for dev_uuid in ordered_refs: |
ewan@13633 | 1502 devclass, config = self.info['devices'][dev_uuid] |
ewan@13633 | 1503 if devclass in XendDevices.valid_devices(): |
ewan@12816 | 1504 log.info("createDevice: %s : %s" % (devclass, scrub_password(config))) |
atse@13623 | 1505 dev_uuid = config.get('uuid') |
atse@13623 | 1506 devid = self._createDevice(devclass, config) |
atse@13623 | 1507 |
atse@13623 | 1508 # store devid in XendConfig for caching reasons |
atse@13623 | 1509 if dev_uuid in self.info['devices']: |
atse@13623 | 1510 self.info['devices'][dev_uuid][1]['devid'] = devid |
atse@12109 | 1511 |
atse@12109 | 1512 if self.image: |
atse@12109 | 1513 self.image.createDeviceModel() |
atse@12109 | 1514 |
Tim@13451 | 1515 def _releaseDevices(self, suspend = False): |
atse@12109 | 1516 """Release all domain's devices. Nothrow guarantee.""" |
keir@16266 | 1517 if self.image: |
keir@16266 | 1518 try: |
keir@16266 | 1519 log.debug("Destroying device model") |
keir@16266 | 1520 self.image.destroyDeviceModel() |
keir@16266 | 1521 except Exception, e: |
keir@16266 | 1522 log.exception("Device model destroy failed %s" % str(e)) |
keir@16266 | 1523 else: |
keir@16266 | 1524 log.debug("No device model") |
keir@16266 | 1525 |
keir@16266 | 1526 log.debug("Releasing devices") |
kfraser@15195 | 1527 t = xstransact("%s/device" % self.dompath) |
keir@16702 | 1528 try: |
keir@16702 | 1529 for devclass in XendDevices.valid_devices(): |
keir@16702 | 1530 for dev in t.list(devclass): |
keir@16702 | 1531 try: |
keir@16702 | 1532 log.debug("Removing %s", dev); |
keir@16702 | 1533 self.destroyDevice(devclass, dev, False); |
keir@16702 | 1534 except: |
keir@16702 | 1535 # Log and swallow any exceptions in removal -- |
keir@16702 | 1536 # there's nothing more we can do. |
kfraser@15195 | 1537 log.exception("Device release failed: %s; %s; %s", |
kfraser@15195 | 1538 self.info['name_label'], devclass, dev) |
keir@16702 | 1539 finally: |
keir@16702 | 1540 t.abort() |
atse@12109 | 1541 |
atse@12109 | 1542 def getDeviceController(self, name): |
atse@12109 | 1543 """Get the device controller for this domain, and if it |
atse@12109 | 1544 doesn't exist, create it. |
emellor@7276 | 1545 |
atse@12109 | 1546 @param name: device class name |
atse@12109 | 1547 @type name: string |
atse@12109 | 1548 @rtype: subclass of DevController |
atse@12109 | 1549 """ |
atse@12109 | 1550 if name not in self._deviceControllers: |
atse@12109 | 1551 devController = XendDevices.make_controller(name, self) |
atse@12109 | 1552 if not devController: |
atse@12109 | 1553 raise XendError("Unknown device type: %s" % name) |
atse@12109 | 1554 self._deviceControllers[name] = devController |
atse@12109 | 1555 |
atse@12109 | 1556 return self._deviceControllers[name] |
atse@12109 | 1557 |
atse@12109 | 1558 # |
atse@12109 | 1559 # Migration functions (public) |
atse@12109 | 1560 # |
atse@12109 | 1561 |
atse@12109 | 1562 def testMigrateDevices(self, network, dst): |
atse@12109 | 1563 """ Notify all device about intention of migration |
atse@12109 | 1564 @raise: XendError for a device that cannot be migrated |
atse@12109 | 1565 """ |
atse@12109 | 1566 for (n, c) in self.info.all_devices_sxpr(): |
atse@12109 | 1567 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST) |
atse@12109 | 1568 if rc != 0: |
atse@12109 | 1569 raise XendError("Device of type '%s' refuses migration." % n) |
emellor@6963 | 1570 |
atse@12109 | 1571 def migrateDevices(self, network, dst, step, domName=''): |
atse@12109 | 1572 """Notify the devices about migration |
atse@12109 | 1573 """ |
atse@12109 | 1574 ctr = 0 |
atse@12109 | 1575 try: |
atse@12109 | 1576 for (dev_type, dev_conf) in self.info.all_devices_sxpr(): |
atse@12109 | 1577 self.migrateDevice(dev_type, dev_conf, network, dst, |
atse@12109 | 1578 step, domName) |
atse@12109 | 1579 ctr = ctr + 1 |
atse@12109 | 1580 except: |
atse@12109 | 1581 for dev_type, dev_conf in self.info.all_devices_sxpr(): |
atse@12109 | 1582 if ctr == 0: |
atse@12109 | 1583 step = step - 1 |
atse@12109 | 1584 ctr = ctr - 1 |
atse@12109 | 1585 self._recoverMigrateDevice(dev_type, dev_conf, network, |
atse@12109 | 1586 dst, step, domName) |
atse@12109 | 1587 raise |
mjw@4618 | 1588 |
atse@12109 | 1589 def migrateDevice(self, deviceClass, deviceConfig, network, dst, |
atse@12109 | 1590 step, domName=''): |
atse@12109 | 1591 return self.getDeviceController(deviceClass).migrate(deviceConfig, |
atse@12109 | 1592 network, dst, step, domName) |
atse@12109 | 1593 |
atse@12109 | 1594 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network, |
atse@12109 | 1595 dst, step, domName=''): |
atse@12109 | 1596 return self.getDeviceController(deviceClass).recover_migrate( |
atse@12109 | 1597 deviceConfig, network, dst, step, domName) |
emellor@7110 | 1598 |
emellor@7110 | 1599 |
emellor@7438 | 1600 ## private: |
emellor@7438 | 1601 |
atse@12109 | 1602 def _constructDomain(self): |
emellor@7180 | 1603 """Construct the domain. |
mjw@2013 | 1604 |
mjw@2013 | 1605 @raise: VmError on error |
mjw@2013 | 1606 """ |
emellor@7099 | 1607 |
atse@12109 | 1608 log.debug('XendDomainInfo.constructDomain') |
emellor@7099 | 1609 |
ewan@13080 | 1610 self.shutdownStartTime = None |
ewan@13080 | 1611 |
ewan@14379 | 1612 hvm = self.info.is_hvm() |
kfraser@12234 | 1613 if hvm: |
kfraser@12234 | 1614 info = xc.xeninfo() |
atse@12671 | 1615 if 'hvm' not in info['xen_caps']: |
kfraser@12234 | 1616 raise VmError("HVM guest support is unavailable: is VT/AMD-V " |
kfraser@12234 | 1617 "supported by your CPU and enabled in your " |
kfraser@12234 | 1618 "BIOS?") |
kfraser@15029 | 1619 |
kfraser@15029 | 1620 # Hack to pre-reserve some memory for initial domain creation. |
kfraser@15029 | 1621 # There is an implicit memory overhead for any domain creation. This |
kfraser@15029 | 1622 # overhead is greater for some types of domain than others. For |
kfraser@15029 | 1623 # example, an x86 HVM domain will have a default shadow-pagetable |
kfraser@15029 | 1624 # allocation of 1MB. We free up 2MB here to be on the safe side. |
kfraser@15029 | 1625 balloon.free(2*1024) # 2MB should be plenty |
kfraser@12234 | 1626 |
kfraser@15684 | 1627 ssidref = 0 |
kfraser@15684 | 1628 if security.on(): |
kfraser@15684 | 1629 ssidref = security.calc_dom_ssidref_from_info(self.info) |
kfraser@15684 | 1630 if security.has_authorization(ssidref) == False: |
kfraser@15684 | 1631 raise VmError("VM is not authorized to run.") |
kfraser@15550 | 1632 |
kfraser@15550 | 1633 try: |
kfraser@15550 | 1634 self.domid = xc.domain_create( |
kfraser@15550 | 1635 domid = 0, |
kfraser@15550 | 1636 ssidref = ssidref, |
kfraser@15550 | 1637 handle = uuid.fromString(self.info['uuid']), |
kfraser@15550 | 1638 hvm = int(hvm)) |
kfraser@15550 | 1639 except Exception, e: |
kfraser@15550 | 1640 # may get here if due to ACM the operation is not permitted |
kfraser@15550 | 1641 if security.on(): |
kfraser@15550 | 1642 raise VmError('Domain in conflict set with running domain?') |
emellor@7099 | 1643 |
emellor@7260 | 1644 if self.domid < 0: |
emellor@7099 | 1645 raise VmError('Creating domain failed: name=%s' % |
atse@12671 | 1646 self.info['name_label']) |
emellor@7099 | 1647 |
emellor@7518 | 1648 self.dompath = GetDomainPath(self.domid) |
emellor@7518 | 1649 |
atse@12109 | 1650 self._recreateDom() |
emellor@7518 | 1651 |
keir@16275 | 1652 # Set timer configration of domain |
keir@16275 | 1653 if hvm: |
keir@16275 | 1654 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE, |
keir@16275 | 1655 long(self.info["platform"].get("timer_mode"))) |
keir@16275 | 1656 |
kaf24@7405 | 1657 # Set maximum number of vcpus in domain |
ewan@14519 | 1658 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max'])) |
emellor@7040 | 1659 |
keir@16630 | 1660 # Test whether the devices can be assigned with VT-d |
keir@16176 | 1661 pci_str = str(self.info["platform"].get("pci")) |
keir@16176 | 1662 if hvm and pci_str: |
keir@16630 | 1663 bdf = xc.test_assign_device(self.domid, pci_str) |
keir@16176 | 1664 if bdf != 0: |
keir@16176 | 1665 bus = (bdf >> 16) & 0xff |
keir@16176 | 1666 devfn = (bdf >> 8) & 0xff |
keir@16176 | 1667 dev = (devfn >> 3) & 0x1f |
keir@16176 | 1668 func = devfn & 0x7 |
keir@16176 | 1669 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is " |
keir@16176 | 1670 "not enabled, or the device is not exist, or it " |
keir@16176 | 1671 "has already been assigned to other domain" |
keir@16176 | 1672 % (bus, dev, func)) |
keir@16176 | 1673 |
Tim@13578 | 1674 # register the domain in the list |
Tim@13578 | 1675 from xen.xend import XendDomain |
Tim@13578 | 1676 XendDomain.instance().add_domain(self) |
emellor@7461 | 1677 |
atse@12109 | 1678 def _introduceDomain(self): |
emellor@7461 | 1679 assert self.domid is not None |
emellor@7461 | 1680 assert self.store_mfn is not None |
emellor@7461 | 1681 assert self.store_port is not None |
emellor@7669 | 1682 |
emellor@7669 | 1683 try: |
emellor@7669 | 1684 IntroduceDomain(self.domid, self.store_mfn, self.store_port) |
emellor@7669 | 1685 except RuntimeError, exn: |
emellor@7669 | 1686 raise XendError(str(exn)) |
emellor@7461 | 1687 |
emellor@7461 | 1688 |
atse@12109 | 1689 def _initDomain(self): |
smh22@7745 | 1690 log.debug('XendDomainInfo.initDomain: %s %s', |
emellor@7313 | 1691 self.domid, |
keir@16003 | 1692 self.info['vcpus_params']['weight']) |
emellor@7040 | 1693 |
ewan@13080 | 1694 self._configureBootloader() |
kaf24@9916 | 1695 |
emellor@7971 | 1696 try: |
ewan@14379 | 1697 self.image = image.create(self, self.info) |
emellor@7078 | 1698 |
keir@15086 | 1699 if self.info['platform'].get('localtime', 0): |
kaf24@10512 | 1700 xc.domain_set_time_offset(self.domid) |
kaf24@10512 | 1701 |
keir@16003 | 1702 xc.domain_setcpuweight(self.domid, \ |
keir@16003 | 1703 self.info['vcpus_params']['weight']) |
kaf24@7392 | 1704 |
emellor@8217 | 1705 # repin domain vcpus if a restricted cpus list is provided |
emellor@8217 | 1706 # this is done prior to memory allocation to aide in memory |
emellor@8217 | 1707 # distribution for NUMA systems. |
kfraser@11149 | 1708 if self.info['cpus'] is not None and len(self.info['cpus']) > 0: |
ewan@14519 | 1709 for v in range(0, self.info['VCPUs_max']): |
kfraser@11149 | 1710 xc.vcpu_setaffinity(self.domid, v, self.info['cpus']) |
emellor@7971 | 1711 |
ewan@11455 | 1712 # Use architecture- and image-specific calculations to determine |
ewan@11455 | 1713 # the various headrooms necessary, given the raw configured |
kfraser@12242 | 1714 # values. maxmem, memory, and shadow are all in KiB. |
ewan@14463 | 1715 # but memory_static_max etc are all stored in bytes now. |
kfraser@13029 | 1716 memory = self.image.getRequiredAvailableMemory( |
ewan@14471 | 1717 self.info['memory_dynamic_max'] / 1024) |
kfraser@13029 | 1718 maxmem = self.image.getRequiredAvailableMemory( |
ewan@14463 | 1719 self.info['memory_static_max'] / 1024) |
ewan@11455 | 1720 shadow = self.image.getRequiredShadowMemory( |
Tim@15911 | 1721 self.info['shadow_memory'] * 1024, |
ewan@14463 | 1722 self.info['memory_static_max'] / 1024) |
ewan@11455 | 1723 |
Tim@13451 | 1724 log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],) |
ewan@11455 | 1725 # Round shadow up to a multiple of a MiB, as shadow_mem_control |
ewan@11455 | 1726 # takes MiB and we must not round down and end up under-providing. |
ewan@11455 | 1727 shadow = ((shadow + 1023) / 1024) * 1024 |
ewan@11455 | 1728 |
ewan@11320 | 1729 # set memory limit |
ewan@11320 | 1730 xc.domain_setmaxmem(self.domid, maxmem) |
kfraser@10695 | 1731 |
tdeegan@11189 | 1732 # Make sure there's enough RAM available for the domain |
ewan@11455 | 1733 balloon.free(memory + shadow) |
tdeegan@11189 | 1734 |
tdeegan@11189 | 1735 # Set up the shadow memory |
ewan@11455 | 1736 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024) |
ewan@11320 | 1737 self.info['shadow_memory'] = shadow_cur |
kaf24@10106 | 1738 |
atse@12109 | 1739 self._createChannels() |
emellor@7040 | 1740 |
emellor@7971 | 1741 channel_details = self.image.createImage() |
emellor@7040 | 1742 |
emellor@7971 | 1743 self.store_mfn = channel_details['store_mfn'] |
emellor@7971 | 1744 if 'console_mfn' in channel_details: |
emellor@7971 | 1745 self.console_mfn = channel_details['console_mfn'] |
kfraser@14158 | 1746 if 'notes' in channel_details: |
kfraser@14159 | 1747 self.info.set_notes(channel_details['notes']) |
ian@15113 | 1748 if 'native_protocol' in channel_details: |
ian@15113 | 1749 self.native_protocol = channel_details['native_protocol']; |
emellor@7971 | 1750 |
atse@12109 | 1751 self._introduceDomain() |
emellor@7461 | 1752 |
atse@12109 | 1753 self._createDevices() |
emellor@7461 | 1754 |
ewan@13080 | 1755 self.image.cleanupBootloading() |
emellor@7461 | 1756 |
emellor@7971 | 1757 self.info['start_time'] = time.time() |
emellor@7820 | 1758 |
atse@12109 | 1759 self._stateSet(DOM_STATE_RUNNING) |
ewan@14380 | 1760 except VmError, exn: |
ewan@14380 | 1761 log.exception("XendDomainInfo.initDomain: exception occurred") |
ewan@14380 | 1762 if self.image: |
ewan@14380 | 1763 self.image.cleanupBootloading() |
ewan@14380 | 1764 raise exn |
ewan@14380 | 1765 except RuntimeError, exn: |
atse@12109 | 1766 log.exception("XendDomainInfo.initDomain: exception occurred") |
ewan@14379 | 1767 if self.image: |
ewan@14379 | 1768 self.image.cleanupBootloading() |
emellor@7971 | 1769 raise VmError(str(exn)) |
emellor@7022 | 1770 |
mjw@2580 | 1771 |
emellor@7180 | 1772 def cleanupDomain(self): |
emellor@7180 | 1773 """Cleanup domain resources; release devices. Idempotent. Nothrow |
emellor@7180 | 1774 guarantee.""" |
cl349@5426 | 1775 |
emellor@9398 | 1776 self.refresh_shutdown_lock.acquire() |
emellor@9398 | 1777 try: |
emellor@9398 | 1778 self.unwatchShutdown() |
atse@12109 | 1779 self._releaseDevices() |
Tim@13578 | 1780 bootloader_tidy(self) |
emellor@7099 | 1781 |
emellor@9398 | 1782 if self.image: |
emellor@9398 | 1783 self.image = None |
emellor@7099 | 1784 |
emellor@9398 | 1785 try: |
atse@12109 | 1786 self._removeDom() |
emellor@9398 | 1787 except: |
emellor@9398 | 1788 log.exception("Removing domain path failed.") |
emellor@7022 | 1789 |
atse@12109 | 1790 self._stateSet(DOM_STATE_HALTED) |
kfraser@15625 | 1791 self.domid = None # Do not push into _stateSet()! |
emellor@9398 | 1792 finally: |
emellor@9398 | 1793 self.refresh_shutdown_lock.release() |
emellor@7270 | 1794 |
emellor@7099 | 1795 |
emellor@9163 | 1796 def unwatchShutdown(self): |
emellor@9163 | 1797 """Remove the watch on the domain's control/shutdown node, if any. |
emellor@9398 | 1798 Idempotent. Nothrow guarantee. Expects to be protected by the |
emellor@9398 | 1799 refresh_shutdown_lock.""" |
emellor@9163 | 1800 |
emellor@9163 | 1801 try: |
emellor@9163 | 1802 try: |
emellor@9163 | 1803 if self.shutdownWatch: |
emellor@9163 | 1804 self.shutdownWatch.unwatch() |
emellor@9163 | 1805 finally: |
emellor@9163 | 1806 self.shutdownWatch = None |
emellor@9163 | 1807 except: |
emellor@9163 | 1808 log.exception("Unwatching control/shutdown failed.") |
emellor@9163 | 1809 |
atse@12109 | 1810 def waitForShutdown(self): |
atse@12109 | 1811 self.state_updated.acquire() |
atse@12109 | 1812 try: |
tom@14924 | 1813 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED): |
atse@12109 | 1814 self.state_updated.wait() |
atse@12109 | 1815 finally: |
atse@12109 | 1816 self.state_updated.release() |
emellor@9163 | 1817 |
atse@12109 | 1818 # |
atse@12109 | 1819 # TODO: recategorise - called from XendCheckpoint |
atse@12109 | 1820 # |
atse@12109 | 1821 |
atse@12109 | 1822 def completeRestore(self, store_mfn, console_mfn): |
atse@12109 | 1823 |
atse@12109 | 1824 log.debug("XendDomainInfo.completeRestore") |
atse@12109 | 1825 |
atse@12109 | 1826 self.store_mfn = store_mfn |
atse@12109 | 1827 self.console_mfn = console_mfn |
atse@12109 | 1828 |
atse@12109 | 1829 self._introduceDomain() |
keir@16265 | 1830 self.image = image.create(self, self.info) |
keir@16265 | 1831 if self.image: |
keir@16265 | 1832 self.image.createDeviceModel(True) |
atse@12109 | 1833 self._storeDomDetails() |
atse@12109 | 1834 self._registerWatches() |
atse@12275 | 1835 self.refreshShutdown() |
atse@12109 | 1836 |
atse@12109 | 1837 log.debug("XendDomainInfo.completeRestore done") |
atse@12109 | 1838 |
atse@12109 | 1839 |
atse@12109 | 1840 def _endRestore(self): |
atse@12109 | 1841 self.setResume(False) |
atse@12109 | 1842 |
atse@12109 | 1843 # |
atse@12109 | 1844 # VM Destroy |
atse@12109 | 1845 # |
emellor@7180 | 1846 |
wim@13865 | 1847 def _prepare_phantom_paths(self): |
wim@13865 | 1848 # get associated devices to destroy |
wim@13865 | 1849 # build list of phantom devices to be removed after normal devices |
wim@13865 | 1850 plist = [] |
wim@13987 | 1851 if self.domid is not None: |
wim@13987 | 1852 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid)) |
keir@16702 | 1853 try: |
keir@16702 | 1854 for dev in t.list(): |
keir@16702 | 1855 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \ |
keir@16702 | 1856 % (self.dompath, dev)) |
keir@16702 | 1857 if backend_phantom_vbd is not None: |
keir@16702 | 1858 frontend_phantom_vbd = xstransact.Read("%s/frontend" \ |
keir@16702 | 1859 % backend_phantom_vbd) |
keir@16702 | 1860 plist.append(backend_phantom_vbd) |
keir@16702 | 1861 plist.append(frontend_phantom_vbd) |
keir@16702 | 1862 finally: |
keir@16702 | 1863 t.abort() |
wim@13865 | 1864 return plist |
wim@13865 | 1865 |
wim@13865 | 1866 def _cleanup_phantom_devs(self, plist): |
wim@13865 | 1867 # remove phantom devices |
wim@13865 | 1868 if not plist == []: |
wim@13865 | 1869 time.sleep(2) |
wim@13865 | 1870 for paths in plist: |
wim@13865 | 1871 if paths.find('backend') != -1: |
wim@13865 | 1872 from xen.xend.server import DevController |
wim@13865 | 1873 # Modify online status /before/ updating state (latter is watched by |
wim@13865 | 1874 # drivers, so this ordering avoids a race). |
wim@13865 | 1875 xstransact.Write(paths, 'online', "0") |
wim@13865 | 1876 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing'])) |
wim@13865 | 1877 # force |
wim@13865 | 1878 xstransact.Remove(paths) |
wim@13865 | 1879 |
emellor@7180 | 1880 def destroy(self): |
emellor@7180 | 1881 """Cleanup VM and destroy domain. Nothrow guarantee.""" |
emellor@7101 | 1882 |
atse@12109 | 1883 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid)) |
emellor@7180 | 1884 |
wim@13865 | 1885 paths = self._prepare_phantom_paths() |
wim@13865 | 1886 |
atse@12109 | 1887 self._cleanupVm() |
emellor@7331 | 1888 if self.dompath is not None: |
atse@12109 | 1889 self.destroyDomain() |
emellor@7210 | 1890 |
wim@13865 | 1891 self._cleanup_phantom_devs(paths) |
emellor@7210 | 1892 |
ewan@14525 | 1893 if "transient" in self.info["other_config"] \ |
ewan@14525 | 1894 and bool(self.info["other_config"]["transient"]): |
ewan@14525 | 1895 from xen.xend import XendDomain |
ewan@14525 | 1896 XendDomain.instance().domain_delete_by_dominfo(self) |
ewan@14525 | 1897 |
ewan@14525 | 1898 |
emellor@7210 | 1899 def destroyDomain(self): |
atse@12109 | 1900 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid)) |
emellor@7210 | 1901 |
wim@13865 | 1902 paths = self._prepare_phantom_paths() |
wim@13865 | 1903 |
emellor@7101 | 1904 try: |
emellor@7099 | 1905 if self.domid is not None: |
keir@16626 | 1906 xc.domain_destroy_hook(self.domid) |
emellor@7987 | 1907 xc.domain_destroy(self.domid) |
atse@12109 | 1908 for state in DOM_STATES_OLD: |
atse@12109 | 1909 self.info[state] = 0 |
ewan@14525 | 1910 self._stateSet(DOM_STATE_HALTED) |
emellor@7236 | 1911 except: |
emellor@7099 | 1912 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.") |
emellor@7099 | 1913 |
Tim@13578 | 1914 from xen.xend import XendDomain |
ewan@14525 | 1915 XendDomain.instance().remove_domain(self) |
Tim@13578 | 1916 |
emellor@7291 | 1917 self.cleanupDomain() |
wim@13865 | 1918 self._cleanup_phantom_devs(paths) |
mjw@1661 | 1919 |
ewan@14525 | 1920 |
kfraser@13554 | 1921 def resumeDomain(self): |
kfraser@13554 | 1922 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid)) |
kfraser@13554 | 1923 |
kfraser@14198 | 1924 if self.domid is None: |
kfraser@14198 | 1925 return |
kfraser@13554 | 1926 try: |
kfraser@14198 | 1927 # could also fetch a parsed note from xenstore |
kfraser@14198 | 1928 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0 |
kfraser@14198 | 1929 if not fast: |
kfraser@14198 | 1930 self._releaseDevices() |
kfraser@14198 | 1931 self.testDeviceComplete() |
kfraser@14198 | 1932 self.testvifsComplete() |
kfraser@14198 | 1933 log.debug("XendDomainInfo.resumeDomain: devices released") |
kfraser@14198 | 1934 |
kfraser@14198 | 1935 self._resetChannels() |
kfraser@14198 | 1936 |
kfraser@14198 | 1937 self._removeDom('control/shutdown') |
kfraser@14198 | 1938 self._removeDom('device-misc/vif/nextDeviceID') |
kfraser@14198 | 1939 |
kfraser@14198 | 1940 self._createChannels() |
kfraser@14198 | 1941 self._introduceDomain() |
kfraser@14198 | 1942 self._storeDomDetails() |
kfraser@14198 | 1943 |
kfraser@14198 | 1944 self._createDevices() |
kfraser@14198 | 1945 log.debug("XendDomainInfo.resumeDomain: devices created") |
kfraser@14198 | 1946 |
kfraser@14198 | 1947 xc.domain_resume(self.domid, fast) |
kfraser@14198 | 1948 ResumeDomain(self.domid) |
kfraser@13554 | 1949 except: |
kfraser@13554 | 1950 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid))) |
keir@16266 | 1951 self.image.resumeDeviceModel() |
keir@16266 | 1952 log.debug("XendDomainInfo.resumeDomain: completed") |
Tim@15677 | 1953 |
kfraser@13554 | 1954 |
atse@12109 | 1955 # |
atse@12109 | 1956 # Channels for xenstore and console |
atse@12109 | 1957 # |
emellor@6963 | 1958 |
atse@12109 | 1959 def _createChannels(self): |
atse@12109 | 1960 """Create the channels to the domain. |
atse@12109 | 1961 """ |
atse@12109 | 1962 self.store_port = self._createChannel() |
atse@12109 | 1963 self.console_port = self._createChannel() |
emellor@6963 | 1964 |
emellor@7180 | 1965 |
atse@12109 | 1966 def _createChannel(self): |
emellor@7461 | 1967 """Create an event channel to the domain. |
emellor@7461 | 1968 """ |
kaf24@7274 | 1969 try: |
atse@13621 | 1970 if self.domid != None: |
atse@13621 | 1971 return xc.evtchn_alloc_unbound(domid = self.domid, |
atse@13621 | 1972 remote_dom = 0) |
kaf24@7274 | 1973 except: |
atse@13621 | 1974 log.exception("Exception in alloc_unbound(%s)", str(self.domid)) |
kaf24@7274 | 1975 raise |
kaf24@7274 | 1976 |
kfraser@13581 | 1977 def _resetChannels(self): |
kfraser@13581 | 1978 """Reset all event channels in the domain. |
kfraser@13581 | 1979 """ |
kfraser@13581 | 1980 try: |
atse@13621 | 1981 if self.domid != None: |
atse@13621 | 1982 return xc.evtchn_reset(dom = self.domid) |
kfraser@13581 | 1983 except: |
atse@13621 | 1984 log.exception("Exception in evtcnh_reset(%s)", str(self.domid)) |
kfraser@13581 | 1985 raise |
kfraser@13581 | 1986 |
kfraser@13581 | 1987 |
atse@12109 | 1988 # |
atse@12109 | 1989 # Bootloader configuration |
atse@12109 | 1990 # |
ewan@6808 | 1991 |
atse@12109 | 1992 def _configureBootloader(self): |
atse@12109 | 1993 """Run the bootloader if we're configured to do so.""" |
ewan@13080 | 1994 |
ewan@13080 | 1995 blexec = self.info['PV_bootloader'] |
ewan@13080 | 1996 bootloader_args = self.info['PV_bootloader_args'] |
ewan@13080 | 1997 kernel = self.info['PV_kernel'] |
ewan@13080 | 1998 ramdisk = self.info['PV_ramdisk'] |
ewan@13080 | 1999 args = self.info['PV_args'] |
ewan@13808 | 2000 boot = self.info['HVM_boot_policy'] |
atse@12805 | 2001 |
ewan@13080 | 2002 if boot: |
ewan@13080 | 2003 # HVM booting. |
ewan@14379 | 2004 pass |
ewan@13080 | 2005 elif not blexec and kernel: |
ewan@13080 | 2006 # Boot from dom0. Nothing left to do -- the kernel and ramdisk |
ewan@13080 | 2007 # will be picked up by image.py. |
ewan@13080 | 2008 pass |
ewan@13080 | 2009 else: |
ewan@13080 | 2010 # Boot using bootloader |
ewan@13080 | 2011 if not blexec or blexec == 'pygrub': |
Tim@13337 | 2012 blexec = osdep.pygrub_path |
ewan@13080 | 2013 |
ewan@13080 | 2014 blcfg = None |
ewan@13717 | 2015 disks = [x for x in self.info['vbd_refs'] |
ewan@13717 | 2016 if self.info['devices'][x][1]['bootable']] |
ewan@13717 | 2017 |
ewan@13717 | 2018 if not disks: |
ewan@13717 | 2019 msg = "Had a bootloader specified, but no disks are bootable" |
ewan@13717 | 2020 log.error(msg) |
ewan@13717 | 2021 raise VmError(msg) |
ewan@13717 | 2022 |
ewan@13722 | 2023 devinfo = self.info['devices'][disks[0]] |
ewan@13722 | 2024 devtype = devinfo[0] |
ewan@13722 | 2025 disk = devinfo[1]['uname'] |
ewan@13717 | 2026 |
ewan@13717 | 2027 fn = blkdev_uname_to_file(disk) |
markmc@14481 | 2028 taptype = blkdev_uname_to_taptype(disk) |
markmc@14481 | 2029 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev |
ewan@13717 | 2030 if mounted: |
ewan@13717 | 2031 # This is a file, not a device. pygrub can cope with a |
ewan@13717 | 2032 # file if it's raw, but if it's QCOW or other such formats |
ewan@13717 | 2033 # used through blktap, then we need to mount it first. |
atse@12805 | 2034 |
ewan@13717 | 2035 log.info("Mounting %s on %s." % |
ewan@13717 | 2036 (fn, BOOTLOADER_LOOPBACK_DEVICE)) |
ewan@13717 | 2037 |
ewan@13717 | 2038 vbd = { |
ewan@13717 | 2039 'mode': 'RO', |
ewan@13717 | 2040 'device': BOOTLOADER_LOOPBACK_DEVICE, |
ewan@13717 | 2041 } |
ewan@13717 | 2042 |
ewan@13717 | 2043 from xen.xend import XendDomain |
ewan@13717 | 2044 dom0 = XendDomain.instance().privilegedDomain() |
markmc@14481 | 2045 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk)) |
ewan@13717 | 2046 fn = BOOTLOADER_LOOPBACK_DEVICE |
ewan@13717 | 2047 |
ewan@13717 | 2048 try: |
ewan@13717 | 2049 blcfg = bootloader(blexec, fn, self, False, |
ewan@13717 | 2050 bootloader_args, kernel, ramdisk, args) |
ewan@13717 | 2051 finally: |
ewan@13080 | 2052 if mounted: |
ewan@13717 | 2053 log.info("Unmounting %s from %s." % |
ewan@13080 | 2054 (fn, BOOTLOADER_LOOPBACK_DEVICE)) |
ewan@13080 | 2055 |
kfraser@14985 | 2056 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE) |
ewan@13080 | 2057 |
ewan@13080 | 2058 if blcfg is None: |
ewan@13080 | 2059 msg = "Had a bootloader specified, but can't find disk" |
ewan@13080 | 2060 log.error(msg) |
ewan@13080 | 2061 raise VmError(msg) |
atse@12671 | 2062 |
ewan@13210 | 2063 self.info.update_with_image_sxp(blcfg, True) |
ewan@13080 | 2064 |
mjw@1661 | 2065 |
atse@12109 | 2066 # |
atse@12109 | 2067 # VM Functions |
atse@12109 | 2068 # |
atse@12109 | 2069 |
atse@12109 | 2070 def _readVMDetails(self, params): |
atse@12109 | 2071 """Read the specified parameters from the store. |
mjw@1661 | 2072 """ |
atse@12109 | 2073 try: |
atse@12109 | 2074 return self._gatherVm(*params) |
atse@12109 | 2075 except ValueError: |
atse@12109 | 2076 # One of the int/float entries in params has a corresponding store |
atse@12109 | 2077 # entry that is invalid. We recover, because older versions of |
atse@12109 | 2078 # Xend may have put the entry there (memory/target, for example), |
atse@12109 | 2079 # but this is in general a bad situation to have reached. |
atse@12109 | 2080 log.exception( |
atse@12109 | 2081 "Store corrupted at %s! Domain %d's configuration may be " |
atse@12109 | 2082 "affected.", self.vmpath, self.domid) |
atse@12109 | 2083 return [] |
emellor@7518 | 2084 |
atse@12109 | 2085 def _cleanupVm(self): |
atse@12109 | 2086 """Cleanup VM resources. Idempotent. Nothrow guarantee.""" |
atse@12109 | 2087 |
atse@12109 | 2088 self._unwatchVm() |
mjw@1661 | 2089 |
atse@12109 | 2090 try: |
atse@12109 | 2091 self._removeVm() |
atse@12109 | 2092 except: |
atse@12109 | 2093 log.exception("Removing VM path failed.") |
atse@12109 | 2094 |
emellor@9695 | 2095 |
tim@11472 | 2096 def checkLiveMigrateMemory(self): |
tim@11472 | 2097 """ Make sure there's enough memory to migrate this domain """ |
tim@11472 | 2098 overhead_kb = 0 |
tim@11472 | 2099 if arch.type == "x86": |
tim@11472 | 2100 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than |
tim@11472 | 2101 # the minimum that Xen would allocate if no value were given. |
ewan@14519 | 2102 overhead_kb = self.info['VCPUs_max'] * 1024 + \ |
ewan@14463 | 2103 (self.info['memory_static_max'] / 1024 / 1024) * 4 |
tim@11472 | 2104 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024 |
tim@11472 | 2105 # The domain might already have some shadow memory |
tim@11472 | 2106 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024 |
tim@11472 | 2107 if overhead_kb > 0: |
tim@11472 | 2108 balloon.free(overhead_kb) |
tim@11472 | 2109 |
atse@12109 | 2110 def _unwatchVm(self): |
atse@12109 | 2111 """Remove the watch on the VM path, if any. Idempotent. Nothrow |
atse@12109 | 2112 guarantee.""" |
atse@12524 | 2113 try: |
atse@12524 | 2114 try: |
atse@12524 | 2115 if self.vmWatch: |
atse@12524 | 2116 self.vmWatch.unwatch() |
atse@12524 | 2117 finally: |
atse@12524 | 2118 self.vmWatch = None |
atse@12524 | 2119 except: |
atse@12524 | 2120 log.exception("Unwatching VM path failed.") |
emellor@9695 | 2121 |
steven@11330 | 2122 def testDeviceComplete(self): |
jchesterfield@11328 | 2123 """ For Block IO migration safety we must ensure that |
jchesterfield@11328 | 2124 the device has shutdown correctly, i.e. all blocks are |
jchesterfield@11328 | 2125 flushed to disk |
jchesterfield@11328 | 2126 """ |
jchesterfield@11520 | 2127 start = time.time() |
jchesterfield@11328 | 2128 while True: |
jchesterfield@11328 | 2129 test = 0 |
jchesterfield@11520 | 2130 diff = time.time() - start |
jchesterfield@11328 | 2131 for i in self.getDeviceController('vbd').deviceIDs(): |
jchesterfield@11328 | 2132 test = 1 |
jchesterfield@11328 | 2133 log.info("Dev %s still active, looping...", i) |
jchesterfield@11328 | 2134 time.sleep(0.1) |
jchesterfield@11328 | 2135 |
jchesterfield@11328 | 2136 if test == 0: |
jchesterfield@11328 | 2137 break |
jchesterfield@11520 | 2138 if diff >= MIGRATE_TIMEOUT: |
jchesterfield@11520 | 2139 log.info("Dev still active but hit max loop timeout") |
jchesterfield@11520 | 2140 break |
jchesterfield@11328 | 2141 |
kfraser@13581 | 2142 def testvifsComplete(self): |
kfraser@13581 | 2143 """ In case vifs are released and then created for the same |
kfraser@13581 | 2144 domain, we need to wait the device shut down. |
kfraser@13581 | 2145 """ |
kfraser@13581 | 2146 start = time.time() |
kfraser@13581 | 2147 while True: |
kfraser@13581 | 2148 test = 0 |
kfraser@13581 | 2149 diff = time.time() - start |
kfraser@13581 | 2150 for i in self.getDeviceController('vif').deviceIDs(): |
kfraser@13581 | 2151 test = 1 |
kfraser@13581 | 2152 log.info("Dev %s still active, looping...", i) |
kfraser@13581 | 2153 time.sleep(0.1) |
kfraser@13581 | 2154 |
kfraser@13581 | 2155 if test == 0: |
kfraser@13581 | 2156 break |
kfraser@13581 | 2157 if diff >= MIGRATE_TIMEOUT: |
kfraser@13581 | 2158 log.info("Dev still active but hit max loop timeout") |
kfraser@13581 | 2159 break |
kfraser@13581 | 2160 |
atse@12109 | 2161 def _storeVmDetails(self): |
atse@12109 | 2162 to_store = {} |
atse@12109 | 2163 |
atse@12671 | 2164 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS: |
atse@12671 | 2165 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key) |
atse@12671 | 2166 if self._infoIsSet(info_key): |
atse@12671 | 2167 to_store[key] = str(self.info[info_key]) |
atse@12109 | 2168 |
ewan@14463 | 2169 if self._infoIsSet("static_memory_min"): |
ewan@14463 | 2170 to_store["memory"] = str(self.info["static_memory_min"]) |
ewan@14463 | 2171 if self._infoIsSet("static_memory_max"): |
ewan@14463 | 2172 to_store["maxmem"] = str(self.info["static_memory_max"]) |
ewan@14463 | 2173 |
ewan@14379 | 2174 image_sxpr = self.info.image_sxpr() |
ewan@14379 | 2175 if image_sxpr: |
ewan@14379 | 2176 to_store['image'] = sxp.to_string(image_sxpr) |
emellor@9695 | 2177 |
atse@12109 | 2178 if not self._readVm('xend/restart_count'): |
atse@12109 | 2179 to_store['xend/restart_count'] = str(0) |
emellor@7185 | 2180 |
ewan@12816 | 2181 log.debug("Storing VM details: %s", scrub_password(to_store)) |
emellor@7689 | 2182 |
atse@12109 | 2183 self._writeVm(to_store) |
atse@12109 | 2184 self._setVmPermissions() |
emellor@7689 | 2185 |
emellor@7689 | 2186 |
atse@12109 | 2187 def _setVmPermissions(self): |
atse@12109 | 2188 """Allow the guest domain to read its UUID. We don't allow it to |
atse@12109 | 2189 access any other entry, for security.""" |
atse@12109 | 2190 xstransact.SetPermissions('%s/uuid' % self.vmpath, |
atse@12109 | 2191 { 'dom' : self.domid, |
atse@12109 | 2192 'read' : True, |
atse@12109 | 2193 'write' : False }) |
atse@12109 | 2194 |
atse@12109 | 2195 # |
atse@12109 | 2196 # Utility functions |
atse@12109 | 2197 # |
mjw@1809 | 2198 |
tom@14924 | 2199 def __getattr__(self, name): |
tom@14924 | 2200 if name == "state": |
tom@14924 | 2201 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!") |
tom@14924 | 2202 log.warn("".join(traceback.format_stack())) |
tom@14924 | 2203 return self._stateGet() |
tom@14924 | 2204 else: |
tom@14924 | 2205 raise AttributeError() |
tom@14924 | 2206 |
tom@14924 | 2207 def __setattr__(self, name, value): |
tom@14924 | 2208 if name == "state": |
tom@14924 | 2209 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!") |
tom@14924 | 2210 log.warn("".join(traceback.format_stack())) |
tom@14924 | 2211 self._stateSet(value) |
tom@14924 | 2212 else: |
tom@14924 | 2213 self.__dict__[name] = value |
tom@14924 | 2214 |
atse@12109 | 2215 def _stateSet(self, state): |
atse@12109 | 2216 self.state_updated.acquire() |
atse@12109 | 2217 try: |
tom@14924 | 2218 # TODO Not sure this is correct... |
tom@14924 | 2219 # _stateGet is live now. Why not fire event |
tom@14924 | 2220 # even when it hasn't changed? |
tom@14924 | 2221 if self._stateGet() != state: |
atse@12109 | 2222 self.state_updated.notifyAll() |
ewan@14907 | 2223 import XendAPI |
ewan@14907 | 2224 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'], |
ewan@14907 | 2225 'power_state') |
atse@12109 | 2226 finally: |
atse@12109 | 2227 self.state_updated.release() |
mjw@2545 | 2228 |
tom@14924 | 2229 def _stateGet(self): |
tom@14924 | 2230 # Lets try and reconsitute the state from xc |
tom@14924 | 2231 # first lets try and get the domain info |
tom@14924 | 2232 # from xc - this will tell us if the domain |
tom@14924 | 2233 # exists |
tom@14924 | 2234 info = dom_get(self.getDomid()) |
tom@14924 | 2235 if info is None or info['shutdown']: |
tom@14924 | 2236 # We are either HALTED or SUSPENDED |
tom@14924 | 2237 # check saved image exists |
tom@14924 | 2238 from xen.xend import XendDomain |
tom@14924 | 2239 managed_config_path = \ |
tom@14924 | 2240 XendDomain.instance()._managed_check_point_path( \ |
tom@14924 | 2241 self.get_uuid()) |
tom@14924 | 2242 if os.path.exists(managed_config_path): |
tom@14924 | 2243 return XEN_API_VM_POWER_STATE_SUSPENDED |
tom@14924 | 2244 else: |
tom@14924 | 2245 return XEN_API_VM_POWER_STATE_HALTED |
tom@14924 | 2246 else: |
tom@14924 | 2247 # We are either RUNNING or PAUSED |
tom@14924 | 2248 if info['paused']: |
tom@14924 | 2249 return XEN_API_VM_POWER_STATE_PAUSED |
tom@14924 | 2250 else: |
tom@14924 | 2251 return XEN_API_VM_POWER_STATE_RUNNING |
tom@14924 | 2252 |
atse@12109 | 2253 def _infoIsSet(self, name): |
atse@12109 | 2254 return name in self.info and self.info[name] is not None |
vh249@4461 | 2255 |
atse@12109 | 2256 def _checkName(self, name): |
atse@12109 | 2257 """Check if a vm name is valid. Valid names contain alphabetic |
atse@12109 | 2258 characters, digits, or characters in '_-.:/+'. |
atse@12109 | 2259 The same name cannot be used for more than one vm at the same time. |
atse@12109 | 2260 |
atse@12109 | 2261 @param name: name |
atse@12109 | 2262 @raise: VmError if invalid |
vh249@4461 | 2263 """ |
atse@12109 | 2264 from xen.xend import XendDomain |
atse@12109 | 2265 |
atse@12109 | 2266 if name is None or name == '': |
atse@12109 | 2267 raise VmError('Missing VM Name') |
mjw@1809 | 2268 |
atse@12109 | 2269 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name): |
atse@12109 | 2270 raise VmError('Invalid VM Name') |
emellor@7461 | 2271 |
atse@12109 | 2272 dom = XendDomain.instance().domain_lookup_nr(name) |
keir@16011 | 2273 if dom and dom.info['uuid'] != self.info['uuid']: |
ewan@12763 | 2274 raise VmError("VM name '%s' already exists%s" % |
ewan@12763 | 2275 (name, |
ewan@12763 | 2276 dom.domid is not None and |
ewan@13040 | 2277 (" as domain %s" % str(dom.domid)) or "")) |
atse@12109 | 2278 |
mjw@4618 | 2279 |
keir@16298 | 2280 def update(self, info = None, refresh = True, transaction = None): |
atse@12109 | 2281 """Update with info from xc.domain_getinfo(). |
emellor@7185 | 2282 """ |
atse@12109 | 2283 log.trace("XendDomainInfo.update(%s) on domain %s", info, |
atse@12109 | 2284 str(self.domid)) |
atse@12109 | 2285 |
atse@12109 | 2286 if not info: |
atse@12109 | 2287 info = dom_get(self.domid) |
atse@12109 | 2288 if not info: |
atse@12109 | 2289 return |
ewan@14471 | 2290 |
ewan@14471 | 2291 if info["maxmem_kb"] < 0: |
ewan@14471 | 2292 info["maxmem_kb"] = XendNode.instance() \ |
ewan@14471 | 2293 .physinfo_dict()['total_memory'] * 1024 |
ewan@14471 | 2294 |
atse@12109 | 2295 #ssidref field not used any longer |
atse@12109 | 2296 if 'ssidref' in info: |
atse@12109 | 2297 info.pop('ssidref') |
emellor@7099 | 2298 |
atse@12109 | 2299 # make sure state is reset for info |
atse@12109 | 2300 # TODO: we should eventually get rid of old_dom_states |
atse@12109 | 2301 |
atse@12671 | 2302 self.info.update_config(info) |
keir@16298 | 2303 self._update_consoles(transaction) |
atse@13654 | 2304 |
atse@12109 | 2305 if refresh: |
atse@12275 | 2306 self.refreshShutdown(info) |
emellor@7099 | 2307 |
atse@12109 | 2308 log.trace("XendDomainInfo.update done on domain %s: %s", |
atse@12109 | 2309 str(self.domid), self.info) |
emellor@7482 | 2310 |
ewan@13108 | 2311 def sxpr(self, ignore_store = False, legacy_only = True): |
atse@12671 | 2312 result = self.info.to_sxp(domain = self, |
ewan@13108 | 2313 ignore_devices = ignore_store, |
ewan@13108 | 2314 legacy_only = legacy_only) |
ewan@12488 | 2315 |
atse@13654 | 2316 #if not ignore_store and self.dompath: |
atse@13654 | 2317 # vnc_port = self.readDom('console/vnc-port') |
atse@13654 | 2318 # if vnc_port is not None: |
atse@13654 | 2319 # result.append(['device', |
atse@13654 | 2320 # ['console', ['vnc-port', str(vnc_port)]]]) |
ewan@12488 | 2321 |
ewan@12488 | 2322 return result |
atse@12109 | 2323 |
atse@12109 | 2324 # Xen API |
atse@12109 | 2325 # ---------------------------------------------------------------- |
emellor@7482 | 2326 |
atse@12109 | 2327 def get_uuid(self): |
atse@12355 | 2328 dom_uuid = self.info.get('uuid') |
atse@12355 | 2329 if not dom_uuid: # if it doesn't exist, make one up |
atse@12355 | 2330 dom_uuid = uuid.createString() |
atse@12355 | 2331 self.info['uuid'] = dom_uuid |
atse@12355 | 2332 return dom_uuid |
atse@12355 | 2333 |
atse@12109 | 2334 def get_memory_static_max(self): |
atse@12826 | 2335 return self.info.get('memory_static_max', 0) |
atse@12109 | 2336 def get_memory_static_min(self): |
atse@12826 | 2337 return self.info.get('memory_static_min', 0) |
atse@12671 | 2338 def get_memory_dynamic_max(self): |
atse@12829 | 2339 return self.info.get('memory_dynamic_max', 0) |
atse@12671 | 2340 def get_memory_dynamic_min(self): |
atse@12829 | 2341 return self.info.get('memory_dynamic_min', 0) |
ewan@14575 | 2342 |
steven@15010 | 2343 # only update memory-related config values if they maintain sanity |
steven@15010 | 2344 def _safe_set_memory(self, key, newval): |
steven@15010 | 2345 oldval = self.info.get(key, 0) |
steven@15010 | 2346 try: |
steven@15010 | 2347 self.info[key] = newval |
steven@15010 | 2348 self.info._memory_sanity_check() |
steven@15010 | 2349 except Exception, ex: |
steven@15010 | 2350 self.info[key] = oldval |
steven@15010 | 2351 raise |
steven@15010 | 2352 |
ewan@14575 | 2353 def set_memory_static_max(self, val): |
steven@15010 | 2354 self._safe_set_memory('memory_static_max', val) |
ewan@14575 | 2355 def set_memory_static_min(self, val): |
steven@15010 | 2356 self._safe_set_memory('memory_static_min', val) |
ewan@14575 | 2357 def set_memory_dynamic_max(self, val): |
steven@15010 | 2358 self._safe_set_memory('memory_dynamic_max', val) |
ewan@14575 | 2359 def set_memory_dynamic_min(self, val): |
steven@15010 | 2360 self._safe_set_memory('memory_dynamic_min', val) |
ewan@14575 | 2361 |
atse@12109 | 2362 def get_vcpus_params(self): |
atse@13784 | 2363 if self.getDomid() is None: |
ewan@13821 | 2364 return self.info['vcpus_params'] |
atse@13784 | 2365 |
atse@13784 | 2366 retval = xc.sched_credit_domain_get(self.getDomid()) |
atse@13784 | 2367 return retval |
atse@12109 | 2368 def get_power_state(self): |
tom@14924 | 2369 return XEN_API_VM_POWER_STATE[self._stateGet()] |
ewan@14379 | 2370 def get_platform(self): |
ewan@14379 | 2371 return self.info.get('platform', {}) |
atse@12109 | 2372 def get_pci_bus(self): |
atse@13623 | 2373 return self.info.get('pci_bus', '') |
atse@12109 | 2374 def get_tools_version(self): |
atse@13623 | 2375 return self.info.get('tools_version', {}) |
ewan@14417 | 2376 def get_metrics(self): |
ewan@14417 | 2377 return self.metrics.get_uuid(); |
kfraser@15550 | 2378 |
kfraser@15550 | 2379 |
kfraser@15753 | 2380 def get_security_label(self, xspol=None): |
keir@15976 | 2381 import xen.util.xsm.xsm as security |
keir@15976 | 2382 label = security.get_security_label(self, xspol) |
kfraser@15550 | 2383 return label |
kfraser@15550 | 2384 |
kfraser@15753 | 2385 def set_security_label(self, seclab, old_seclab, xspol=None, |
kfraser@15753 | 2386 xspol_old=None): |
kfraser@15550 | 2387 """ |
kfraser@15550 | 2388 Set the security label of a domain from its old to |
kfraser@15550 | 2389 a new value. |
kfraser@15550 | 2390 @param seclab New security label formatted in the form |
kfraser@15550 | 2391 <policy type>:<policy name>:<vm label> |
kfraser@15550 | 2392 @param old_seclab The current security label that the |
kfraser@15550 | 2393 VM must have. |
kfraser@15550 | 2394 @param xspol An optional policy under which this |
kfraser@15550 | 2395 update should be done. If not given, |
kfraser@15550 | 2396 then the current active policy is used. |
kfraser@15753 | 2397 @param xspol_old The old policy; only to be passed during |
kfraser@15753 | 2398 the updating of a policy |
kfraser@15550 | 2399 @return Returns return code, a string with errors from |
kfraser@15550 | 2400 the hypervisor's operation, old label of the |
kfraser@15550 | 2401 domain |
kfraser@15550 | 2402 """ |
kfraser@15550 | 2403 rc = 0 |
kfraser@15550 | 2404 errors = "" |
kfraser@15550 | 2405 old_label = "" |
kfraser@15550 | 2406 new_ssidref = 0 |
kfraser@15550 | 2407 domid = self.getDomid() |
kfraser@15550 | 2408 res_labels = None |
kfraser@15753 | 2409 is_policy_update = (xspol_old != None) |
kfraser@15550 | 2410 |
kfraser@15550 | 2411 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance |
kfraser@15550 | 2412 from xen.util import xsconstants |
kfraser@15550 | 2413 |
kfraser@15550 | 2414 state = self._stateGet() |
kfraser@15550 | 2415 # Relabel only HALTED or RUNNING or PAUSED domains |
kfraser@15550 | 2416 if domid != 0 and \ |
kfraser@15550 | 2417 state not in \ |
kfraser@15550 | 2418 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \ |
kfraser@15550 | 2419 DOM_STATE_SUSPENDED ]: |
kfraser@15550 | 2420 log.warn("Relabeling domain not possible in state '%s'" % |
kfraser@15550 | 2421 DOM_STATES[state]) |
kfraser@15550 | 2422 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0) |
kfraser@15550 | 2423 |
kfraser@15550 | 2424 # Remove security label. Works only for halted domains |
kfraser@15550 | 2425 if not seclab or seclab == "": |
kfraser@15550 | 2426 if state not in [ DOM_STATE_HALTED ]: |
kfraser@15550 | 2427 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0) |
kfraser@15550 | 2428 |
kfraser@15550 | 2429 if self.info.has_key('security_label'): |
kfraser@15550 | 2430 old_label = self.info['security_label'] |
kfraser@15550 | 2431 # Check label against expected one. |
kfraser@15550 | 2432 if old_label != old_seclab: |
kfraser@15550 | 2433 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2434 del self.info['security_label'] |
kfraser@15550 | 2435 xen.xend.XendDomain.instance().managed_config_save(self) |
kfraser@15550 | 2436 return (xsconstants.XSERR_SUCCESS, "", "", 0) |
kfraser@15550 | 2437 |
kfraser@15550 | 2438 tmp = seclab.split(":") |
kfraser@15550 | 2439 if len(tmp) != 3: |
kfraser@15550 | 2440 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0) |
kfraser@15550 | 2441 typ, policy, label = tmp |
kfraser@15550 | 2442 |
kfraser@15550 | 2443 poladmin = XSPolicyAdminInstance() |
kfraser@15550 | 2444 if not xspol: |
kfraser@15550 | 2445 xspol = poladmin.get_policy_by_name(policy) |
kfraser@15550 | 2446 |
kfraser@15550 | 2447 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]: |
kfraser@15550 | 2448 #if domain is running or paused try to relabel in hypervisor |
kfraser@15550 | 2449 if not xspol: |
kfraser@15550 | 2450 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0) |
kfraser@15550 | 2451 |
kfraser@15550 | 2452 if typ != xspol.get_type_name() or \ |
kfraser@15550 | 2453 policy != xspol.get_name(): |
kfraser@15550 | 2454 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2455 |
kfraser@15550 | 2456 if typ == xsconstants.ACM_POLICY_ID: |
kfraser@15550 | 2457 new_ssidref = xspol.vmlabel_to_ssidref(label) |
kfraser@15550 | 2458 if new_ssidref == xsconstants.INVALID_SSIDREF: |
kfraser@15550 | 2459 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2460 |
kfraser@15550 | 2461 # Check that all used resources are accessible under the |
kfraser@15550 | 2462 # new label |
kfraser@15753 | 2463 if not is_policy_update and \ |
kfraser@15753 | 2464 not security.resources_compatible_with_vmlabel(xspol, |
kfraser@15550 | 2465 self, label): |
kfraser@15550 | 2466 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2467 |
keir@16559 | 2468 #Check label against expected one. Can only do this |
keir@16559 | 2469 # if the policy hasn't changed underneath in the meantime |
keir@16559 | 2470 if xspol_old == None: |
keir@16559 | 2471 old_label = self.get_security_label() |
keir@16559 | 2472 if old_label != old_seclab: |
keir@16559 | 2473 log.info("old_label != old_seclab: %s != %s" % |
keir@16559 | 2474 (old_label, old_seclab)) |
keir@16559 | 2475 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2476 |
kfraser@15550 | 2477 # relabel domain in the hypervisor |
kfraser@15550 | 2478 rc, errors = security.relabel_domains([[domid, new_ssidref]]) |
kfraser@15550 | 2479 log.info("rc from relabeling in HV: %d" % rc) |
kfraser@15550 | 2480 else: |
kfraser@15550 | 2481 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0) |
kfraser@15550 | 2482 |
kfraser@15550 | 2483 if rc == 0: |
kfraser@15550 | 2484 # HALTED, RUNNING or PAUSED |
kfraser@15550 | 2485 if domid == 0: |
kfraser@15550 | 2486 if xspol: |
keir@16559 | 2487 self.info['security_label'] = seclab |
kfraser@15550 | 2488 ssidref = poladmin.set_domain0_bootlabel(xspol, label) |
kfraser@15550 | 2489 else: |
kfraser@15550 | 2490 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0) |
kfraser@15550 | 2491 else: |
kfraser@15550 | 2492 if self.info.has_key('security_label'): |
kfraser@15550 | 2493 old_label = self.info['security_label'] |
kfraser@15550 | 2494 # Check label against expected one, unless wildcard |
kfraser@15550 | 2495 if old_label != old_seclab: |
kfraser@15550 | 2496 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0) |
kfraser@15550 | 2497 |
kfraser@15550 | 2498 self.info['security_label'] = seclab |
keir@16559 | 2499 |
kfraser@15550 | 2500 try: |
kfraser@15550 | 2501 xen.xend.XendDomain.instance().managed_config_save(self) |
kfraser@15550 | 2502 except: |
kfraser@15550 | 2503 pass |
kfraser@15550 | 2504 return (rc, errors, old_label, new_ssidref) |
kfraser@15550 | 2505 |
atse@12109 | 2506 def get_on_shutdown(self): |
kfraser@14278 | 2507 after_shutdown = self.info.get('actions_after_shutdown') |
atse@12140 | 2508 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT: |
atse@12140 | 2509 return XEN_API_ON_NORMAL_EXIT[-1] |
atse@12140 | 2510 return after_shutdown |
atse@12140 | 2511 |
atse@12109 | 2512 def get_on_reboot(self): |
kfraser@14278 | 2513 after_reboot = self.info.get('actions_after_reboot') |
atse@12140 | 2514 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT: |
atse@12140 | 2515 return XEN_API_ON_NORMAL_EXIT[-1] |
atse@12140 | 2516 return after_reboot |
emellor@7688 | 2517 |
atse@12109 | 2518 def get_on_suspend(self): |
atse@12671 | 2519 # TODO: not supported |
kfraser@14278 | 2520 after_suspend = self.info.get('actions_after_suspend') |
atse@12140 | 2521 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT: |
atse@12140 | 2522 return XEN_API_ON_NORMAL_EXIT[-1] |
atse@12140 | 2523 return after_suspend |
atse@12109 | 2524 |
atse@12109 | 2525 def get_on_crash(self): |
kfraser@14278 | 2526 after_crash = self.info.get('actions_after_crash') |
atse@12140 | 2527 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR: |
atse@12140 | 2528 return XEN_API_ON_CRASH_BEHAVIOUR[0] |
atse@12140 | 2529 return after_crash |
atse@12109 | 2530 |
atse@12135 | 2531 def get_dev_config_by_uuid(self, dev_class, dev_uuid): |
atse@12135 | 2532 """ Get's a device configuration either from XendConfig or |
atse@12151 | 2533 from the DevController. |
atse@12151 | 2534 |
atse@12151 | 2535 @param dev_class: device class, either, 'vbd' or 'vif' |
atse@12151 | 2536 @param dev_uuid: device UUID |
atse@12151 | 2537 |
atse@12151 | 2538 @rtype: dictionary |
atse@12151 | 2539 """ |
atse@13783 | 2540 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None)) |
atse@12151 | 2541 |
atse@12151 | 2542 # shortcut if the domain isn't started because |
atse@12151 | 2543 # the devcontrollers will have no better information |
atse@12151 | 2544 # than XendConfig. |
tom@14924 | 2545 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,): |
atse@13783 | 2546 if dev_config: |
atse@13783 | 2547 return copy.deepcopy(dev_config) |
atse@12135 | 2548 return None |
atse@12151 | 2549 |
atse@12151 | 2550 # instead of using dev_class, we use the dev_type |
atse@12151 | 2551 # that is from XendConfig. |
atse@12151 | 2552 controller = self.getDeviceController(dev_type) |
atse@12151 | 2553 if not controller: |
atse@12151 | 2554 return None |
atse@12137 | 2555 |
atse@12151 | 2556 all_configs = controller.getAllDeviceConfigurations() |
atse@12151 | 2557 if not all_configs: |
atse@12151 | 2558 return None |
atse@12137 | 2559 |
atse@13783 | 2560 updated_dev_config = copy.deepcopy(dev_config) |
atse@12151 | 2561 for _devid, _devcfg in all_configs.items(): |
atse@12151 | 2562 if _devcfg.get('uuid') == dev_uuid: |
atse@13783 | 2563 updated_dev_config.update(_devcfg) |
atse@13783 | 2564 updated_dev_config['id'] = _devid |
atse@13783 | 2565 return updated_dev_config |
atse@12109 | 2566 |
atse@13783 | 2567 return updated_dev_config |
atse@12135 | 2568 |
atse@12135 | 2569 def get_dev_xenapi_config(self, dev_class, dev_uuid): |
atse@12135 | 2570 config = self.get_dev_config_by_uuid(dev_class, dev_uuid) |
atse@12135 | 2571 if not config: |
atse@12135 | 2572 return {} |
atse@12135 | 2573 |
atse@12135 | 2574 config['VM'] = self.get_uuid() |
atse@12135 | 2575 |
atse@12135 | 2576 if dev_class == 'vif': |
atse@12135 | 2577 if not config.has_key('name'): |
atse@12135 | 2578 config['name'] = config.get('vifname', '') |
atse@12135 | 2579 if not config.has_key('MAC'): |
atse@12135 | 2580 config['MAC'] = config.get('mac', '') |
atse@12135 | 2581 if not config.has_key('type'): |
atse@12135 | 2582 config['type'] = 'paravirtualised' |
atse@12135 | 2583 if not config.has_key('device'): |
atse@12135 | 2584 devid = config.get('id') |
atse@12135 | 2585 if devid != None: |
atse@12135 | 2586 config['device'] = 'eth%d' % devid |
atse@12135 | 2587 else: |
atse@12135 | 2588 config['device'] = '' |
ewan@13206 | 2589 |
ewan@13206 | 2590 if not config.has_key('network'): |
ewan@13216 | 2591 try: |
tom@14970 | 2592 bridge = config.get('bridge', None) |
tom@14970 | 2593 if bridge is None: |
tom@14970 | 2594 from xen.util import Brctl |
tom@14970 | 2595 if_to_br = dict([(i,b) |
tom@14970 | 2596 for (b,ifs) in Brctl.get_state().items() |
tom@14970 | 2597 for i in ifs]) |
tom@14970 | 2598 vifname = "vif%s.%s" % (self.getDomid(), |
tom@14970 | 2599 config.get('id')) |
tom@14970 | 2600 bridge = if_to_br.get(vifname, None) |
ewan@13216 | 2601 config['network'] = \ |
ewan@13216 | 2602 XendNode.instance().bridge_to_network( |
tom@14970 | 2603 config.get('bridge')).get_uuid() |
ewan@13216 | 2604 except Exception: |
ewan@13216 | 2605 log.exception('bridge_to_network') |
ewan@13216 | 2606 # Ignore this for now -- it may happen if the device |
ewan@13216 | 2607 # has been specified using the legacy methods, but at |
ewan@13216 | 2608 # some point we're going to have to figure out how to |
ewan@13216 | 2609 # handle that properly. |
ewan@13206 | 2610 |
atse@12135 | 2611 config['MTU'] = 1500 # TODO |
atse@13616 | 2612 |
tom@14924 | 2613 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,): |
atse@13616 | 2614 xennode = XendNode.instance() |
atse@13616 | 2615 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid) |
atse@13616 | 2616 config['io_read_kbs'] = rx_bps/1024 |
atse@13616 | 2617 config['io_write_kbs'] = tx_bps/1024 |
keir@16438 | 2618 rx, tx = xennode.get_vif_stat(self.domid, devid) |
keir@16438 | 2619 config['io_total_read_kbs'] = rx/1024 |
keir@16438 | 2620 config['io_total_write_kbs'] = tx/1024 |
atse@13616 | 2621 else: |
atse@13616 | 2622 config['io_read_kbs'] = 0.0 |
keir@16438 | 2623 config['io_write_kbs'] = 0.0 |
keir@16438 | 2624 config['io_total_read_kbs'] = 0.0 |
keir@16438 | 2625 config['io_total_write_kbs'] = 0.0 |
atse@12109 | 2626 |
kfraser@15656 | 2627 config['security_label'] = config.get('security_label', '') |
kfraser@15656 | 2628 |
atse@12671 | 2629 if dev_class == 'vbd': |
atse@13616 | 2630 |
tom@14924 | 2631 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,): |
atse@13616 | 2632 controller = self.getDeviceController(dev_class) |
atse@13616 | 2633 devid, _1, _2 = controller.getDeviceDetails(config) |
atse@13616 | 2634 xennode = XendNode.instance() |
atse@13616 | 2635 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid) |
atse@13616 | 2636 config['io_read_kbs'] = rd_blkps |
atse@13616 | 2637 config['io_write_kbs'] = wr_blkps |
atse@13616 | 2638 else: |
atse@13616 | 2639 config['io_read_kbs'] = 0.0 |
atse@13616 | 2640 config['io_write_kbs'] = 0.0 |
atse@13616 | 2641 |
atse@12826 | 2642 config['VDI'] = config.get('VDI', '') |
atse@12135 | 2643 config['device'] = config.get('dev', '') |
atse@13615 | 2644 if ':' in config['device']: |
atse@13615 | 2645 vbd_name, vbd_type = config['device'].split(':', 1) |
atse@13615 | 2646 config['device'] = vbd_name |
atse@13615 | 2647 if vbd_type == 'cdrom': |
atse@13615 | 2648 config['type'] = XEN_API_VBD_TYPE[0] |
atse@13615 | 2649 else: |
atse@13615 | 2650 config['type'] = XEN_API_VBD_TYPE[1] |
atse@13615 | 2651 |
atse@12151 | 2652 config['driver'] = 'paravirtualised' # TODO |
atse@12151 | 2653 config['image'] = config.get('uname', '') |
atse@13616 | 2654 |
atse@13263 | 2655 if config.get('mode', 'r') == 'r': |
atse@12140 | 2656 config['mode'] = 'RO' |
atse@12140 | 2657 else: |
atse@12140 | 2658 config['mode'] = 'RW' |
atse@12109 | 2659 |
ewan@12382 | 2660 if dev_class == 'vtpm': |
ewan@13769 | 2661 if not config.has_key('type'): |
kaf24@13761 | 2662 config['type'] = 'paravirtualised' # TODO |
kaf24@13761 | 2663 if not config.has_key('backend'): |
kaf24@13761 | 2664 config['backend'] = "00000000-0000-0000-0000-000000000000" |
ewan@12382 | 2665 |
atse@12135 | 2666 return config |
emellor@7099 | 2667 |
atse@12135 | 2668 def get_dev_property(self, dev_class, dev_uuid, field): |
atse@12135 | 2669 config = self.get_dev_xenapi_config(dev_class, dev_uuid) |
atse@12135 | 2670 try: |
atse@12135 | 2671 return config[field] |
atse@12135 | 2672 except KeyError: |
atse@12135 | 2673 raise XendError('Invalid property for device: %s' % field) |
emellor@7210 | 2674 |
ewan@13717 | 2675 def set_dev_property(self, dev_class, dev_uuid, field, value): |
ewan@13717 | 2676 self.info['devices'][dev_uuid][1][field] = value |
ewan@13717 | 2677 |
atse@12109 | 2678 def get_vcpus_util(self): |
atse@12109 | 2679 vcpu_util = {} |
atse@13616 | 2680 xennode = XendNode.instance() |
ewan@14519 | 2681 if 'VCPUs_max' in self.info and self.domid != None: |
ewan@14519 | 2682 for i in range(0, self.info['VCPUs_max']): |
atse@13616 | 2683 util = xennode.get_vcpu_util(self.domid, i) |
atse@13616 | 2684 vcpu_util[str(i)] = util |
atse@12109 | 2685 |
atse@12109 | 2686 return vcpu_util |
atse@12109 | 2687 |
ewan@13079 | 2688 def get_consoles(self): |
ewan@13079 | 2689 return self.info.get('console_refs', []) |
ewan@13079 | 2690 |
atse@12109 | 2691 def get_vifs(self): |
atse@12124 | 2692 return self.info.get('vif_refs', []) |
emellor@7210 | 2693 |
atse@12109 | 2694 def get_vbds(self): |
atse@12124 | 2695 return self.info.get('vbd_refs', []) |
emellor@7185 | 2696 |
ewan@12203 | 2697 def get_vtpms(self): |
ewan@12203 | 2698 return self.info.get('vtpm_refs', []) |
ewan@12203 | 2699 |
atse@13624 | 2700 def create_vbd(self, xenapi_vbd, vdi_image_path): |
atse@12148 | 2701 """Create a VBD using a VDI from XendStorageRepository. |
atse@12148 | 2702 |
atse@12148 | 2703 @param xenapi_vbd: vbd struct from the Xen API |
atse@12148 | 2704 @param vdi_image_path: VDI UUID |
atse@12148 | 2705 @rtype: string |
atse@12148 | 2706 @return: uuid of the device |
atse@12148 | 2707 """ |
atse@12148 | 2708 xenapi_vbd['image'] = vdi_image_path |
atse@13624 | 2709 if vdi_image_path.startswith('tap'): |
atse@13624 | 2710 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd) |
atse@13624 | 2711 else: |
atse@13624 | 2712 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd) |
atse@13624 | 2713 |
atse@12148 | 2714 if not dev_uuid: |
atse@12148 | 2715 raise XendError('Failed to create device') |
atse@12148 | 2716 |
tom@14924 | 2717 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, |
tom@14924 | 2718 XEN_API_VM_POWER_STATE_PAUSED): |
ewan@13080 | 2719 _, config = self.info['devices'][dev_uuid] |
atse@13624 | 2720 |
atse@13624 | 2721 if vdi_image_path.startswith('tap'): |
ewan@14486 | 2722 dev_control = self.getDeviceController('tap') |
atse@13624 | 2723 else: |
atse@13624 | 2724 dev_control = self.getDeviceController('vbd') |
atse@12148 | 2725 |
ewan@14486 | 2726 try: |
ewan@14486 | 2727 devid = dev_control.createDevice(config) |
ewan@14486 | 2728 dev_control.waitForDevice(devid) |
ewan@14486 | 2729 self.info.device_update(dev_uuid, |
ewan@14486 | 2730 cfg_xenapi = {'devid': devid}) |
ewan@14486 | 2731 except Exception, exn: |
ewan@14486 | 2732 log.exception(exn) |
ewan@14486 | 2733 del self.info['devices'][dev_uuid] |
ewan@14486 | 2734 self.info['vbd_refs'].remove(dev_uuid) |
ewan@14486 | 2735 raise |
ewan@14486 | 2736 |
atse@12148 | 2737 return dev_uuid |
atse@12148 | 2738 |
wim@13865 | 2739 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path): |
wim@13865 | 2740 """Create a VBD using a VDI from XendStorageRepository. |
wim@13865 | 2741 |
wim@13865 | 2742 @param xenapi_vbd: vbd struct from the Xen API |
wim@13865 | 2743 @param vdi_image_path: VDI UUID |
wim@13865 | 2744 @rtype: string |
wim@13865 | 2745 @return: uuid of the device |
wim@13865 | 2746 """ |
wim@13865 | 2747 xenapi_vbd['image'] = vdi_image_path |
wim@13865 | 2748 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd) |
wim@13865 | 2749 if not dev_uuid: |
wim@13865 | 2750 raise XendError('Failed to create device') |
wim@13865 | 2751 |
tom@14924 | 2752 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING: |
wim@13865 | 2753 _, config = self.info['devices'][dev_uuid] |
wim@13865 | 2754 config['devid'] = self.getDeviceController('tap').createDevice(config) |
wim@13865 | 2755 |
wim@13865 | 2756 return config['devid'] |
wim@13865 | 2757 |
atse@12109 | 2758 def create_vif(self, xenapi_vif): |
atse@12148 | 2759 """Create VIF device from the passed struct in Xen API format. |
atse@12148 | 2760 |
atse@12148 | 2761 @param xenapi_vif: Xen API VIF Struct. |
atse@12148 | 2762 @rtype: string |
atse@12148 | 2763 @return: UUID |
atse@12148 | 2764 """ |
atse@12109 | 2765 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif) |
atse@12109 | 2766 if not dev_uuid: |
atse@12109 | 2767 raise XendError('Failed to create device') |
atse@12109 | 2768 |
tom@14924 | 2769 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, |
tom@14924 | 2770 XEN_API_VM_POWER_STATE_PAUSED): |
ewan@14491 | 2771 |
ewan@13080 | 2772 _, config = self.info['devices'][dev_uuid] |
ewan@14491 | 2773 dev_control = self.getDeviceController('vif') |
ewan@14491 | 2774 |
ewan@14491 | 2775 try: |
ewan@14491 | 2776 devid = dev_control.createDevice(config) |
ewan@14491 | 2777 dev_control.waitForDevice(devid) |
ewan@14491 | 2778 self.info.device_update(dev_uuid, |
ewan@14491 | 2779 cfg_xenapi = {'devid': devid}) |
ewan@14491 | 2780 except Exception, exn: |
ewan@14491 | 2781 log.exception(exn) |
ewan@14491 | 2782 del self.info['devices'][dev_uuid] |
ewan@14491 | 2783 self.info['vif_refs'].remove(dev_uuid) |
ewan@14491 | 2784 raise |
ewan@14491 | 2785 |
atse@12109 | 2786 return dev_uuid |
atse@12135 | 2787 |
ewan@12206 | 2788 def create_vtpm(self, xenapi_vtpm): |
ewan@12206 | 2789 """Create a VTPM device from the passed struct in Xen API format. |
ewan@12206 | 2790 |
ewan@12206 | 2791 @return: uuid of the device |
ewan@12206 | 2792 @rtype: string |
ewan@12206 | 2793 """ |
ewan@12206 | 2794 |
tom@14924 | 2795 if self._stateGet() not in (DOM_STATE_HALTED,): |
ewan@12382 | 2796 raise VmError("Can only add vTPM to a halted domain.") |
ewan@12382 | 2797 if self.get_vtpms() != []: |
ewan@12382 | 2798 raise VmError('Domain already has a vTPM.') |
ewan@12206 | 2799 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm) |
ewan@12206 | 2800 if not dev_uuid: |
ewan@12206 | 2801 raise XendError('Failed to create device') |
ewan@12206 | 2802 |
ewan@12206 | 2803 return dev_uuid |
ewan@12206 | 2804 |
atse@13783 | 2805 def create_console(self, xenapi_console): |
atse@13783 | 2806 """ Create a console device from a Xen API struct. |
atse@13783 | 2807 |
atse@13783 | 2808 @return: uuid of device |
atse@13783 | 2809 @rtype: string |
atse@13783 | 2810 """ |
tom@14924 | 2811 if self._stateGet() not in (DOM_STATE_HALTED,): |
atse@13783 | 2812 raise VmError("Can only add console to a halted domain.") |
atse@13783 | 2813 |
atse@13783 | 2814 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console) |
atse@13783 | 2815 if not dev_uuid: |
atse@13783 | 2816 raise XendError('Failed to create device') |
atse@13783 | 2817 |
atse@13783 | 2818 return dev_uuid |
atse@13783 | 2819 |
kfraser@15689 | 2820 def set_console_other_config(self, console_uuid, other_config): |
kfraser@15689 | 2821 self.info.console_update(console_uuid, 'other_config', other_config) |
kfraser@15689 | 2822 |
atse@13623 | 2823 def destroy_device_by_uuid(self, dev_type, dev_uuid): |
atse@13623 | 2824 if dev_uuid not in self.info['devices']: |
atse@13623 | 2825 raise XendError('Device does not exist') |
atse@13623 | 2826 |
atse@13623 | 2827 try: |
tom@14924 | 2828 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING, |
tom@14924 | 2829 XEN_API_VM_POWER_STATE_PAUSED): |
atse@13623 | 2830 _, config = self.info['devices'][dev_uuid] |
atse@13623 | 2831 devid = config.get('devid') |
atse@13623 | 2832 if devid != None: |
atse@13623 | 2833 self.getDeviceController(dev_type).destroyDevice(devid, force = False) |
atse@13623 | 2834 else: |
atse@13623 | 2835 raise XendError('Unable to get devid for device: %s:%s' % |
atse@13623 | 2836 (dev_type, dev_uuid)) |
atse@13623 | 2837 finally: |
atse@13623 | 2838 del self.info['devices'][dev_uuid] |
atse@13623 | 2839 self.info['%s_refs' % dev_type].remove(dev_uuid) |
atse@13623 | 2840 |
atse@13623 | 2841 def destroy_vbd(self, dev_uuid): |
atse@13623 | 2842 self.destroy_device_by_uuid('vbd', dev_uuid) |
atse@13623 | 2843 |
atse@13623 | 2844 def destroy_vif(self, dev_uuid): |
atse@13623 | 2845 self.destroy_device_by_uuid('vif', dev_uuid) |
atse@13623 | 2846 |
atse@13623 | 2847 def destroy_vtpm(self, dev_uuid): |
atse@13623 | 2848 self.destroy_device_by_uuid('vtpm', dev_uuid) |
atse@13623 | 2849 |
atse@12135 | 2850 def has_device(self, dev_class, dev_uuid): |
atse@12826 | 2851 return (dev_uuid in self.info['%s_refs' % dev_class.lower()]) |
atse@12109 | 2852 |
atse@12109 | 2853 def __str__(self): |
atse@12109 | 2854 return '<domain id=%s name=%s memory=%s state=%s>' % \ |
atse@12671 | 2855 (str(self.domid), self.info['name_label'], |
tom@14924 | 2856 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()]) |
atse@12109 | 2857 |
atse@12109 | 2858 __repr__ = __str__ |
atse@12109 | 2859 |