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>
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