debuggers.hg

annotate tools/python/xen/xend/XendDomainInfo.py @ 16675:9b37cabe0485

xend: Indicate a resume operation

Indicate that the domain is created as part of a resume operation
rather than a 'create'.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 19 14:45:04 2007 +0000 (2007-12-19)
parents ef83b50fc4a4
children 9fe92a88912b
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)
kfraser@15195 1528 for devclass in XendDevices.valid_devices():
kfraser@15195 1529 for dev in t.list(devclass):
kfraser@15195 1530 try:
kfraser@15195 1531 log.debug("Removing %s", dev);
kfraser@15195 1532 self.destroyDevice(devclass, dev, False);
kfraser@15195 1533 except:
kfraser@15195 1534 # Log and swallow any exceptions in removal --
kfraser@15195 1535 # there's nothing more we can do.
kfraser@15195 1536 log.exception("Device release failed: %s; %s; %s",
kfraser@15195 1537 self.info['name_label'], devclass, dev)
kfraser@15195 1538
kfraser@15195 1539
atse@12109 1540
atse@12109 1541 def getDeviceController(self, name):
atse@12109 1542 """Get the device controller for this domain, and if it
atse@12109 1543 doesn't exist, create it.
emellor@7276 1544
atse@12109 1545 @param name: device class name
atse@12109 1546 @type name: string
atse@12109 1547 @rtype: subclass of DevController
atse@12109 1548 """
atse@12109 1549 if name not in self._deviceControllers:
atse@12109 1550 devController = XendDevices.make_controller(name, self)
atse@12109 1551 if not devController:
atse@12109 1552 raise XendError("Unknown device type: %s" % name)
atse@12109 1553 self._deviceControllers[name] = devController
atse@12109 1554
atse@12109 1555 return self._deviceControllers[name]
atse@12109 1556
atse@12109 1557 #
atse@12109 1558 # Migration functions (public)
atse@12109 1559 #
atse@12109 1560
atse@12109 1561 def testMigrateDevices(self, network, dst):
atse@12109 1562 """ Notify all device about intention of migration
atse@12109 1563 @raise: XendError for a device that cannot be migrated
atse@12109 1564 """
atse@12109 1565 for (n, c) in self.info.all_devices_sxpr():
atse@12109 1566 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
atse@12109 1567 if rc != 0:
atse@12109 1568 raise XendError("Device of type '%s' refuses migration." % n)
emellor@6963 1569
atse@12109 1570 def migrateDevices(self, network, dst, step, domName=''):
atse@12109 1571 """Notify the devices about migration
atse@12109 1572 """
atse@12109 1573 ctr = 0
atse@12109 1574 try:
atse@12109 1575 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
atse@12109 1576 self.migrateDevice(dev_type, dev_conf, network, dst,
atse@12109 1577 step, domName)
atse@12109 1578 ctr = ctr + 1
atse@12109 1579 except:
atse@12109 1580 for dev_type, dev_conf in self.info.all_devices_sxpr():
atse@12109 1581 if ctr == 0:
atse@12109 1582 step = step - 1
atse@12109 1583 ctr = ctr - 1
atse@12109 1584 self._recoverMigrateDevice(dev_type, dev_conf, network,
atse@12109 1585 dst, step, domName)
atse@12109 1586 raise
mjw@4618 1587
atse@12109 1588 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
atse@12109 1589 step, domName=''):
atse@12109 1590 return self.getDeviceController(deviceClass).migrate(deviceConfig,
atse@12109 1591 network, dst, step, domName)
atse@12109 1592
atse@12109 1593 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
atse@12109 1594 dst, step, domName=''):
atse@12109 1595 return self.getDeviceController(deviceClass).recover_migrate(
atse@12109 1596 deviceConfig, network, dst, step, domName)
emellor@7110 1597
emellor@7110 1598
emellor@7438 1599 ## private:
emellor@7438 1600
atse@12109 1601 def _constructDomain(self):
emellor@7180 1602 """Construct the domain.
mjw@2013 1603
mjw@2013 1604 @raise: VmError on error
mjw@2013 1605 """
emellor@7099 1606
atse@12109 1607 log.debug('XendDomainInfo.constructDomain')
emellor@7099 1608
ewan@13080 1609 self.shutdownStartTime = None
ewan@13080 1610
ewan@14379 1611 hvm = self.info.is_hvm()
kfraser@12234 1612 if hvm:
kfraser@12234 1613 info = xc.xeninfo()
atse@12671 1614 if 'hvm' not in info['xen_caps']:
kfraser@12234 1615 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
kfraser@12234 1616 "supported by your CPU and enabled in your "
kfraser@12234 1617 "BIOS?")
kfraser@15029 1618
kfraser@15029 1619 # Hack to pre-reserve some memory for initial domain creation.
kfraser@15029 1620 # There is an implicit memory overhead for any domain creation. This
kfraser@15029 1621 # overhead is greater for some types of domain than others. For
kfraser@15029 1622 # example, an x86 HVM domain will have a default shadow-pagetable
kfraser@15029 1623 # allocation of 1MB. We free up 2MB here to be on the safe side.
kfraser@15029 1624 balloon.free(2*1024) # 2MB should be plenty
kfraser@12234 1625
kfraser@15684 1626 ssidref = 0
kfraser@15684 1627 if security.on():
kfraser@15684 1628 ssidref = security.calc_dom_ssidref_from_info(self.info)
kfraser@15684 1629 if security.has_authorization(ssidref) == False:
kfraser@15684 1630 raise VmError("VM is not authorized to run.")
kfraser@15550 1631
kfraser@15550 1632 try:
kfraser@15550 1633 self.domid = xc.domain_create(
kfraser@15550 1634 domid = 0,
kfraser@15550 1635 ssidref = ssidref,
kfraser@15550 1636 handle = uuid.fromString(self.info['uuid']),
kfraser@15550 1637 hvm = int(hvm))
kfraser@15550 1638 except Exception, e:
kfraser@15550 1639 # may get here if due to ACM the operation is not permitted
kfraser@15550 1640 if security.on():
kfraser@15550 1641 raise VmError('Domain in conflict set with running domain?')
emellor@7099 1642
emellor@7260 1643 if self.domid < 0:
emellor@7099 1644 raise VmError('Creating domain failed: name=%s' %
atse@12671 1645 self.info['name_label'])
emellor@7099 1646
emellor@7518 1647 self.dompath = GetDomainPath(self.domid)
emellor@7518 1648
atse@12109 1649 self._recreateDom()
emellor@7518 1650
keir@16275 1651 # Set timer configration of domain
keir@16275 1652 if hvm:
keir@16275 1653 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
keir@16275 1654 long(self.info["platform"].get("timer_mode")))
keir@16275 1655
kaf24@7405 1656 # Set maximum number of vcpus in domain
ewan@14519 1657 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
emellor@7040 1658
keir@16630 1659 # Test whether the devices can be assigned with VT-d
keir@16176 1660 pci_str = str(self.info["platform"].get("pci"))
keir@16176 1661 if hvm and pci_str:
keir@16630 1662 bdf = xc.test_assign_device(self.domid, pci_str)
keir@16176 1663 if bdf != 0:
keir@16176 1664 bus = (bdf >> 16) & 0xff
keir@16176 1665 devfn = (bdf >> 8) & 0xff
keir@16176 1666 dev = (devfn >> 3) & 0x1f
keir@16176 1667 func = devfn & 0x7
keir@16176 1668 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
keir@16176 1669 "not enabled, or the device is not exist, or it "
keir@16176 1670 "has already been assigned to other domain"
keir@16176 1671 % (bus, dev, func))
keir@16176 1672
Tim@13578 1673 # register the domain in the list
Tim@13578 1674 from xen.xend import XendDomain
Tim@13578 1675 XendDomain.instance().add_domain(self)
emellor@7461 1676
atse@12109 1677 def _introduceDomain(self):
emellor@7461 1678 assert self.domid is not None
emellor@7461 1679 assert self.store_mfn is not None
emellor@7461 1680 assert self.store_port is not None
emellor@7669 1681
emellor@7669 1682 try:
emellor@7669 1683 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
emellor@7669 1684 except RuntimeError, exn:
emellor@7669 1685 raise XendError(str(exn))
emellor@7461 1686
emellor@7461 1687
atse@12109 1688 def _initDomain(self):
smh22@7745 1689 log.debug('XendDomainInfo.initDomain: %s %s',
emellor@7313 1690 self.domid,
keir@16003 1691 self.info['vcpus_params']['weight'])
emellor@7040 1692
ewan@13080 1693 self._configureBootloader()
kaf24@9916 1694
emellor@7971 1695 try:
ewan@14379 1696 self.image = image.create(self, self.info)
emellor@7078 1697
keir@15086 1698 if self.info['platform'].get('localtime', 0):
kaf24@10512 1699 xc.domain_set_time_offset(self.domid)
kaf24@10512 1700
keir@16003 1701 xc.domain_setcpuweight(self.domid, \
keir@16003 1702 self.info['vcpus_params']['weight'])
kaf24@7392 1703
emellor@8217 1704 # repin domain vcpus if a restricted cpus list is provided
emellor@8217 1705 # this is done prior to memory allocation to aide in memory
emellor@8217 1706 # distribution for NUMA systems.
kfraser@11149 1707 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
ewan@14519 1708 for v in range(0, self.info['VCPUs_max']):
kfraser@11149 1709 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
emellor@7971 1710
ewan@11455 1711 # Use architecture- and image-specific calculations to determine
ewan@11455 1712 # the various headrooms necessary, given the raw configured
kfraser@12242 1713 # values. maxmem, memory, and shadow are all in KiB.
ewan@14463 1714 # but memory_static_max etc are all stored in bytes now.
kfraser@13029 1715 memory = self.image.getRequiredAvailableMemory(
ewan@14471 1716 self.info['memory_dynamic_max'] / 1024)
kfraser@13029 1717 maxmem = self.image.getRequiredAvailableMemory(
ewan@14463 1718 self.info['memory_static_max'] / 1024)
ewan@11455 1719 shadow = self.image.getRequiredShadowMemory(
Tim@15911 1720 self.info['shadow_memory'] * 1024,
ewan@14463 1721 self.info['memory_static_max'] / 1024)
ewan@11455 1722
Tim@13451 1723 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 1724 # Round shadow up to a multiple of a MiB, as shadow_mem_control
ewan@11455 1725 # takes MiB and we must not round down and end up under-providing.
ewan@11455 1726 shadow = ((shadow + 1023) / 1024) * 1024
ewan@11455 1727
ewan@11320 1728 # set memory limit
ewan@11320 1729 xc.domain_setmaxmem(self.domid, maxmem)
kfraser@10695 1730
tdeegan@11189 1731 # Make sure there's enough RAM available for the domain
ewan@11455 1732 balloon.free(memory + shadow)
tdeegan@11189 1733
tdeegan@11189 1734 # Set up the shadow memory
ewan@11455 1735 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
ewan@11320 1736 self.info['shadow_memory'] = shadow_cur
kaf24@10106 1737
atse@12109 1738 self._createChannels()
emellor@7040 1739
emellor@7971 1740 channel_details = self.image.createImage()
emellor@7040 1741
emellor@7971 1742 self.store_mfn = channel_details['store_mfn']
emellor@7971 1743 if 'console_mfn' in channel_details:
emellor@7971 1744 self.console_mfn = channel_details['console_mfn']
kfraser@14158 1745 if 'notes' in channel_details:
kfraser@14159 1746 self.info.set_notes(channel_details['notes'])
ian@15113 1747 if 'native_protocol' in channel_details:
ian@15113 1748 self.native_protocol = channel_details['native_protocol'];
emellor@7971 1749
atse@12109 1750 self._introduceDomain()
emellor@7461 1751
atse@12109 1752 self._createDevices()
emellor@7461 1753
ewan@13080 1754 self.image.cleanupBootloading()
emellor@7461 1755
emellor@7971 1756 self.info['start_time'] = time.time()
emellor@7820 1757
atse@12109 1758 self._stateSet(DOM_STATE_RUNNING)
ewan@14380 1759 except VmError, exn:
ewan@14380 1760 log.exception("XendDomainInfo.initDomain: exception occurred")
ewan@14380 1761 if self.image:
ewan@14380 1762 self.image.cleanupBootloading()
ewan@14380 1763 raise exn
ewan@14380 1764 except RuntimeError, exn:
atse@12109 1765 log.exception("XendDomainInfo.initDomain: exception occurred")
ewan@14379 1766 if self.image:
ewan@14379 1767 self.image.cleanupBootloading()
emellor@7971 1768 raise VmError(str(exn))
emellor@7022 1769
mjw@2580 1770
emellor@7180 1771 def cleanupDomain(self):
emellor@7180 1772 """Cleanup domain resources; release devices. Idempotent. Nothrow
emellor@7180 1773 guarantee."""
cl349@5426 1774
emellor@9398 1775 self.refresh_shutdown_lock.acquire()
emellor@9398 1776 try:
emellor@9398 1777 self.unwatchShutdown()
atse@12109 1778 self._releaseDevices()
Tim@13578 1779 bootloader_tidy(self)
emellor@7099 1780
emellor@9398 1781 if self.image:
emellor@9398 1782 self.image = None
emellor@7099 1783
emellor@9398 1784 try:
atse@12109 1785 self._removeDom()
emellor@9398 1786 except:
emellor@9398 1787 log.exception("Removing domain path failed.")
emellor@7022 1788
atse@12109 1789 self._stateSet(DOM_STATE_HALTED)
kfraser@15625 1790 self.domid = None # Do not push into _stateSet()!
emellor@9398 1791 finally:
emellor@9398 1792 self.refresh_shutdown_lock.release()
emellor@7270 1793
emellor@7099 1794
emellor@9163 1795 def unwatchShutdown(self):
emellor@9163 1796 """Remove the watch on the domain's control/shutdown node, if any.
emellor@9398 1797 Idempotent. Nothrow guarantee. Expects to be protected by the
emellor@9398 1798 refresh_shutdown_lock."""
emellor@9163 1799
emellor@9163 1800 try:
emellor@9163 1801 try:
emellor@9163 1802 if self.shutdownWatch:
emellor@9163 1803 self.shutdownWatch.unwatch()
emellor@9163 1804 finally:
emellor@9163 1805 self.shutdownWatch = None
emellor@9163 1806 except:
emellor@9163 1807 log.exception("Unwatching control/shutdown failed.")
emellor@9163 1808
atse@12109 1809 def waitForShutdown(self):
atse@12109 1810 self.state_updated.acquire()
atse@12109 1811 try:
tom@14924 1812 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
atse@12109 1813 self.state_updated.wait()
atse@12109 1814 finally:
atse@12109 1815 self.state_updated.release()
emellor@9163 1816
atse@12109 1817 #
atse@12109 1818 # TODO: recategorise - called from XendCheckpoint
atse@12109 1819 #
atse@12109 1820
atse@12109 1821 def completeRestore(self, store_mfn, console_mfn):
atse@12109 1822
atse@12109 1823 log.debug("XendDomainInfo.completeRestore")
atse@12109 1824
atse@12109 1825 self.store_mfn = store_mfn
atse@12109 1826 self.console_mfn = console_mfn
atse@12109 1827
atse@12109 1828 self._introduceDomain()
keir@16265 1829 self.image = image.create(self, self.info)
keir@16265 1830 if self.image:
keir@16265 1831 self.image.createDeviceModel(True)
atse@12109 1832 self._storeDomDetails()
atse@12109 1833 self._registerWatches()
atse@12275 1834 self.refreshShutdown()
atse@12109 1835
atse@12109 1836 log.debug("XendDomainInfo.completeRestore done")
atse@12109 1837
atse@12109 1838
atse@12109 1839 def _endRestore(self):
atse@12109 1840 self.setResume(False)
atse@12109 1841
atse@12109 1842 #
atse@12109 1843 # VM Destroy
atse@12109 1844 #
emellor@7180 1845
wim@13865 1846 def _prepare_phantom_paths(self):
wim@13865 1847 # get associated devices to destroy
wim@13865 1848 # build list of phantom devices to be removed after normal devices
wim@13865 1849 plist = []
wim@13987 1850 if self.domid is not None:
wim@13987 1851 from xen.xend.xenstore.xstransact import xstransact
wim@13987 1852 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
wim@13987 1853 for dev in t.list():
wim@13987 1854 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
wim@13987 1855 % (self.dompath, dev))
wim@13987 1856 if backend_phantom_vbd is not None:
wim@13987 1857 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
wim@13987 1858 % backend_phantom_vbd)
wim@13987 1859 plist.append(backend_phantom_vbd)
wim@13987 1860 plist.append(frontend_phantom_vbd)
wim@13865 1861 return plist
wim@13865 1862
wim@13865 1863 def _cleanup_phantom_devs(self, plist):
wim@13865 1864 # remove phantom devices
wim@13865 1865 if not plist == []:
wim@13865 1866 time.sleep(2)
wim@13865 1867 for paths in plist:
wim@13865 1868 if paths.find('backend') != -1:
wim@13865 1869 from xen.xend.server import DevController
wim@13865 1870 # Modify online status /before/ updating state (latter is watched by
wim@13865 1871 # drivers, so this ordering avoids a race).
wim@13865 1872 xstransact.Write(paths, 'online', "0")
wim@13865 1873 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
wim@13865 1874 # force
wim@13865 1875 xstransact.Remove(paths)
wim@13865 1876
emellor@7180 1877 def destroy(self):
emellor@7180 1878 """Cleanup VM and destroy domain. Nothrow guarantee."""
emellor@7101 1879
atse@12109 1880 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
emellor@7180 1881
wim@13865 1882 paths = self._prepare_phantom_paths()
wim@13865 1883
atse@12109 1884 self._cleanupVm()
emellor@7331 1885 if self.dompath is not None:
atse@12109 1886 self.destroyDomain()
emellor@7210 1887
wim@13865 1888 self._cleanup_phantom_devs(paths)
emellor@7210 1889
ewan@14525 1890 if "transient" in self.info["other_config"] \
ewan@14525 1891 and bool(self.info["other_config"]["transient"]):
ewan@14525 1892 from xen.xend import XendDomain
ewan@14525 1893 XendDomain.instance().domain_delete_by_dominfo(self)
ewan@14525 1894
ewan@14525 1895
emellor@7210 1896 def destroyDomain(self):
atse@12109 1897 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
emellor@7210 1898
wim@13865 1899 paths = self._prepare_phantom_paths()
wim@13865 1900
emellor@7101 1901 try:
emellor@7099 1902 if self.domid is not None:
keir@16626 1903 xc.domain_destroy_hook(self.domid)
emellor@7987 1904 xc.domain_destroy(self.domid)
atse@12109 1905 for state in DOM_STATES_OLD:
atse@12109 1906 self.info[state] = 0
ewan@14525 1907 self._stateSet(DOM_STATE_HALTED)
emellor@7236 1908 except:
emellor@7099 1909 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
emellor@7099 1910
Tim@13578 1911 from xen.xend import XendDomain
ewan@14525 1912 XendDomain.instance().remove_domain(self)
Tim@13578 1913
emellor@7291 1914 self.cleanupDomain()
wim@13865 1915 self._cleanup_phantom_devs(paths)
mjw@1661 1916
ewan@14525 1917
kfraser@13554 1918 def resumeDomain(self):
kfraser@13554 1919 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
kfraser@13554 1920
kfraser@14198 1921 if self.domid is None:
kfraser@14198 1922 return
kfraser@13554 1923 try:
kfraser@14198 1924 # could also fetch a parsed note from xenstore
kfraser@14198 1925 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
kfraser@14198 1926 if not fast:
kfraser@14198 1927 self._releaseDevices()
kfraser@14198 1928 self.testDeviceComplete()
kfraser@14198 1929 self.testvifsComplete()
kfraser@14198 1930 log.debug("XendDomainInfo.resumeDomain: devices released")
kfraser@14198 1931
kfraser@14198 1932 self._resetChannels()
kfraser@14198 1933
kfraser@14198 1934 self._removeDom('control/shutdown')
kfraser@14198 1935 self._removeDom('device-misc/vif/nextDeviceID')
kfraser@14198 1936
kfraser@14198 1937 self._createChannels()
kfraser@14198 1938 self._introduceDomain()
kfraser@14198 1939 self._storeDomDetails()
kfraser@14198 1940
kfraser@14198 1941 self._createDevices()
kfraser@14198 1942 log.debug("XendDomainInfo.resumeDomain: devices created")
kfraser@14198 1943
kfraser@14198 1944 xc.domain_resume(self.domid, fast)
kfraser@14198 1945 ResumeDomain(self.domid)
kfraser@13554 1946 except:
kfraser@13554 1947 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
keir@16266 1948 self.image.resumeDeviceModel()
keir@16266 1949 log.debug("XendDomainInfo.resumeDomain: completed")
Tim@15677 1950
kfraser@13554 1951
atse@12109 1952 #
atse@12109 1953 # Channels for xenstore and console
atse@12109 1954 #
emellor@6963 1955
atse@12109 1956 def _createChannels(self):
atse@12109 1957 """Create the channels to the domain.
atse@12109 1958 """
atse@12109 1959 self.store_port = self._createChannel()
atse@12109 1960 self.console_port = self._createChannel()
emellor@6963 1961
emellor@7180 1962
atse@12109 1963 def _createChannel(self):
emellor@7461 1964 """Create an event channel to the domain.
emellor@7461 1965 """
kaf24@7274 1966 try:
atse@13621 1967 if self.domid != None:
atse@13621 1968 return xc.evtchn_alloc_unbound(domid = self.domid,
atse@13621 1969 remote_dom = 0)
kaf24@7274 1970 except:
atse@13621 1971 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
kaf24@7274 1972 raise
kaf24@7274 1973
kfraser@13581 1974 def _resetChannels(self):
kfraser@13581 1975 """Reset all event channels in the domain.
kfraser@13581 1976 """
kfraser@13581 1977 try:
atse@13621 1978 if self.domid != None:
atse@13621 1979 return xc.evtchn_reset(dom = self.domid)
kfraser@13581 1980 except:
atse@13621 1981 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
kfraser@13581 1982 raise
kfraser@13581 1983
kfraser@13581 1984
atse@12109 1985 #
atse@12109 1986 # Bootloader configuration
atse@12109 1987 #
ewan@6808 1988
atse@12109 1989 def _configureBootloader(self):
atse@12109 1990 """Run the bootloader if we're configured to do so."""
ewan@13080 1991
ewan@13080 1992 blexec = self.info['PV_bootloader']
ewan@13080 1993 bootloader_args = self.info['PV_bootloader_args']
ewan@13080 1994 kernel = self.info['PV_kernel']
ewan@13080 1995 ramdisk = self.info['PV_ramdisk']
ewan@13080 1996 args = self.info['PV_args']
ewan@13808 1997 boot = self.info['HVM_boot_policy']
atse@12805 1998
ewan@13080 1999 if boot:
ewan@13080 2000 # HVM booting.
ewan@14379 2001 pass
ewan@13080 2002 elif not blexec and kernel:
ewan@13080 2003 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
ewan@13080 2004 # will be picked up by image.py.
ewan@13080 2005 pass
ewan@13080 2006 else:
ewan@13080 2007 # Boot using bootloader
ewan@13080 2008 if not blexec or blexec == 'pygrub':
Tim@13337 2009 blexec = osdep.pygrub_path
ewan@13080 2010
ewan@13080 2011 blcfg = None
ewan@13717 2012 disks = [x for x in self.info['vbd_refs']
ewan@13717 2013 if self.info['devices'][x][1]['bootable']]
ewan@13717 2014
ewan@13717 2015 if not disks:
ewan@13717 2016 msg = "Had a bootloader specified, but no disks are bootable"
ewan@13717 2017 log.error(msg)
ewan@13717 2018 raise VmError(msg)
ewan@13717 2019
ewan@13722 2020 devinfo = self.info['devices'][disks[0]]
ewan@13722 2021 devtype = devinfo[0]
ewan@13722 2022 disk = devinfo[1]['uname']
ewan@13717 2023
ewan@13717 2024 fn = blkdev_uname_to_file(disk)
markmc@14481 2025 taptype = blkdev_uname_to_taptype(disk)
markmc@14481 2026 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
ewan@13717 2027 if mounted:
ewan@13717 2028 # This is a file, not a device. pygrub can cope with a
ewan@13717 2029 # file if it's raw, but if it's QCOW or other such formats
ewan@13717 2030 # used through blktap, then we need to mount it first.
atse@12805 2031
ewan@13717 2032 log.info("Mounting %s on %s." %
ewan@13717 2033 (fn, BOOTLOADER_LOOPBACK_DEVICE))
ewan@13717 2034
ewan@13717 2035 vbd = {
ewan@13717 2036 'mode': 'RO',
ewan@13717 2037 'device': BOOTLOADER_LOOPBACK_DEVICE,
ewan@13717 2038 }
ewan@13717 2039
ewan@13717 2040 from xen.xend import XendDomain
ewan@13717 2041 dom0 = XendDomain.instance().privilegedDomain()
markmc@14481 2042 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
ewan@13717 2043 fn = BOOTLOADER_LOOPBACK_DEVICE
ewan@13717 2044
ewan@13717 2045 try:
ewan@13717 2046 blcfg = bootloader(blexec, fn, self, False,
ewan@13717 2047 bootloader_args, kernel, ramdisk, args)
ewan@13717 2048 finally:
ewan@13080 2049 if mounted:
ewan@13717 2050 log.info("Unmounting %s from %s." %
ewan@13080 2051 (fn, BOOTLOADER_LOOPBACK_DEVICE))
ewan@13080 2052
kfraser@14985 2053 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
ewan@13080 2054
ewan@13080 2055 if blcfg is None:
ewan@13080 2056 msg = "Had a bootloader specified, but can't find disk"
ewan@13080 2057 log.error(msg)
ewan@13080 2058 raise VmError(msg)
atse@12671 2059
ewan@13210 2060 self.info.update_with_image_sxp(blcfg, True)
ewan@13080 2061
mjw@1661 2062
atse@12109 2063 #
atse@12109 2064 # VM Functions
atse@12109 2065 #
atse@12109 2066
atse@12109 2067 def _readVMDetails(self, params):
atse@12109 2068 """Read the specified parameters from the store.
mjw@1661 2069 """
atse@12109 2070 try:
atse@12109 2071 return self._gatherVm(*params)
atse@12109 2072 except ValueError:
atse@12109 2073 # One of the int/float entries in params has a corresponding store
atse@12109 2074 # entry that is invalid. We recover, because older versions of
atse@12109 2075 # Xend may have put the entry there (memory/target, for example),
atse@12109 2076 # but this is in general a bad situation to have reached.
atse@12109 2077 log.exception(
atse@12109 2078 "Store corrupted at %s! Domain %d's configuration may be "
atse@12109 2079 "affected.", self.vmpath, self.domid)
atse@12109 2080 return []
emellor@7518 2081
atse@12109 2082 def _cleanupVm(self):
atse@12109 2083 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
atse@12109 2084
atse@12109 2085 self._unwatchVm()
mjw@1661 2086
atse@12109 2087 try:
atse@12109 2088 self._removeVm()
atse@12109 2089 except:
atse@12109 2090 log.exception("Removing VM path failed.")
atse@12109 2091
emellor@9695 2092
tim@11472 2093 def checkLiveMigrateMemory(self):
tim@11472 2094 """ Make sure there's enough memory to migrate this domain """
tim@11472 2095 overhead_kb = 0
tim@11472 2096 if arch.type == "x86":
tim@11472 2097 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
tim@11472 2098 # the minimum that Xen would allocate if no value were given.
ewan@14519 2099 overhead_kb = self.info['VCPUs_max'] * 1024 + \
ewan@14463 2100 (self.info['memory_static_max'] / 1024 / 1024) * 4
tim@11472 2101 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
tim@11472 2102 # The domain might already have some shadow memory
tim@11472 2103 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
tim@11472 2104 if overhead_kb > 0:
tim@11472 2105 balloon.free(overhead_kb)
tim@11472 2106
atse@12109 2107 def _unwatchVm(self):
atse@12109 2108 """Remove the watch on the VM path, if any. Idempotent. Nothrow
atse@12109 2109 guarantee."""
atse@12524 2110 try:
atse@12524 2111 try:
atse@12524 2112 if self.vmWatch:
atse@12524 2113 self.vmWatch.unwatch()
atse@12524 2114 finally:
atse@12524 2115 self.vmWatch = None
atse@12524 2116 except:
atse@12524 2117 log.exception("Unwatching VM path failed.")
emellor@9695 2118
steven@11330 2119 def testDeviceComplete(self):
jchesterfield@11328 2120 """ For Block IO migration safety we must ensure that
jchesterfield@11328 2121 the device has shutdown correctly, i.e. all blocks are
jchesterfield@11328 2122 flushed to disk
jchesterfield@11328 2123 """
jchesterfield@11520 2124 start = time.time()
jchesterfield@11328 2125 while True:
jchesterfield@11328 2126 test = 0
jchesterfield@11520 2127 diff = time.time() - start
jchesterfield@11328 2128 for i in self.getDeviceController('vbd').deviceIDs():
jchesterfield@11328 2129 test = 1
jchesterfield@11328 2130 log.info("Dev %s still active, looping...", i)
jchesterfield@11328 2131 time.sleep(0.1)
jchesterfield@11328 2132
jchesterfield@11328 2133 if test == 0:
jchesterfield@11328 2134 break
jchesterfield@11520 2135 if diff >= MIGRATE_TIMEOUT:
jchesterfield@11520 2136 log.info("Dev still active but hit max loop timeout")
jchesterfield@11520 2137 break
jchesterfield@11328 2138
kfraser@13581 2139 def testvifsComplete(self):
kfraser@13581 2140 """ In case vifs are released and then created for the same
kfraser@13581 2141 domain, we need to wait the device shut down.
kfraser@13581 2142 """
kfraser@13581 2143 start = time.time()
kfraser@13581 2144 while True:
kfraser@13581 2145 test = 0
kfraser@13581 2146 diff = time.time() - start
kfraser@13581 2147 for i in self.getDeviceController('vif').deviceIDs():
kfraser@13581 2148 test = 1
kfraser@13581 2149 log.info("Dev %s still active, looping...", i)
kfraser@13581 2150 time.sleep(0.1)
kfraser@13581 2151
kfraser@13581 2152 if test == 0:
kfraser@13581 2153 break
kfraser@13581 2154 if diff >= MIGRATE_TIMEOUT:
kfraser@13581 2155 log.info("Dev still active but hit max loop timeout")
kfraser@13581 2156 break
kfraser@13581 2157
atse@12109 2158 def _storeVmDetails(self):
atse@12109 2159 to_store = {}
atse@12109 2160
atse@12671 2161 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
atse@12671 2162 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
atse@12671 2163 if self._infoIsSet(info_key):
atse@12671 2164 to_store[key] = str(self.info[info_key])
atse@12109 2165
ewan@14463 2166 if self._infoIsSet("static_memory_min"):
ewan@14463 2167 to_store["memory"] = str(self.info["static_memory_min"])
ewan@14463 2168 if self._infoIsSet("static_memory_max"):
ewan@14463 2169 to_store["maxmem"] = str(self.info["static_memory_max"])
ewan@14463 2170
ewan@14379 2171 image_sxpr = self.info.image_sxpr()
ewan@14379 2172 if image_sxpr:
ewan@14379 2173 to_store['image'] = sxp.to_string(image_sxpr)
emellor@9695 2174
atse@12109 2175 if not self._readVm('xend/restart_count'):
atse@12109 2176 to_store['xend/restart_count'] = str(0)
emellor@7185 2177
ewan@12816 2178 log.debug("Storing VM details: %s", scrub_password(to_store))
emellor@7689 2179
atse@12109 2180 self._writeVm(to_store)
atse@12109 2181 self._setVmPermissions()
emellor@7689 2182
emellor@7689 2183
atse@12109 2184 def _setVmPermissions(self):
atse@12109 2185 """Allow the guest domain to read its UUID. We don't allow it to
atse@12109 2186 access any other entry, for security."""
atse@12109 2187 xstransact.SetPermissions('%s/uuid' % self.vmpath,
atse@12109 2188 { 'dom' : self.domid,
atse@12109 2189 'read' : True,
atse@12109 2190 'write' : False })
atse@12109 2191
atse@12109 2192 #
atse@12109 2193 # Utility functions
atse@12109 2194 #
mjw@1809 2195
tom@14924 2196 def __getattr__(self, name):
tom@14924 2197 if name == "state":
tom@14924 2198 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
tom@14924 2199 log.warn("".join(traceback.format_stack()))
tom@14924 2200 return self._stateGet()
tom@14924 2201 else:
tom@14924 2202 raise AttributeError()
tom@14924 2203
tom@14924 2204 def __setattr__(self, name, value):
tom@14924 2205 if name == "state":
tom@14924 2206 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
tom@14924 2207 log.warn("".join(traceback.format_stack()))
tom@14924 2208 self._stateSet(value)
tom@14924 2209 else:
tom@14924 2210 self.__dict__[name] = value
tom@14924 2211
atse@12109 2212 def _stateSet(self, state):
atse@12109 2213 self.state_updated.acquire()
atse@12109 2214 try:
tom@14924 2215 # TODO Not sure this is correct...
tom@14924 2216 # _stateGet is live now. Why not fire event
tom@14924 2217 # even when it hasn't changed?
tom@14924 2218 if self._stateGet() != state:
atse@12109 2219 self.state_updated.notifyAll()
ewan@14907 2220 import XendAPI
ewan@14907 2221 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
ewan@14907 2222 'power_state')
atse@12109 2223 finally:
atse@12109 2224 self.state_updated.release()
mjw@2545 2225
tom@14924 2226 def _stateGet(self):
tom@14924 2227 # Lets try and reconsitute the state from xc
tom@14924 2228 # first lets try and get the domain info
tom@14924 2229 # from xc - this will tell us if the domain
tom@14924 2230 # exists
tom@14924 2231 info = dom_get(self.getDomid())
tom@14924 2232 if info is None or info['shutdown']:
tom@14924 2233 # We are either HALTED or SUSPENDED
tom@14924 2234 # check saved image exists
tom@14924 2235 from xen.xend import XendDomain
tom@14924 2236 managed_config_path = \
tom@14924 2237 XendDomain.instance()._managed_check_point_path( \
tom@14924 2238 self.get_uuid())
tom@14924 2239 if os.path.exists(managed_config_path):
tom@14924 2240 return XEN_API_VM_POWER_STATE_SUSPENDED
tom@14924 2241 else:
tom@14924 2242 return XEN_API_VM_POWER_STATE_HALTED
tom@14924 2243 else:
tom@14924 2244 # We are either RUNNING or PAUSED
tom@14924 2245 if info['paused']:
tom@14924 2246 return XEN_API_VM_POWER_STATE_PAUSED
tom@14924 2247 else:
tom@14924 2248 return XEN_API_VM_POWER_STATE_RUNNING
tom@14924 2249
atse@12109 2250 def _infoIsSet(self, name):
atse@12109 2251 return name in self.info and self.info[name] is not None
vh249@4461 2252
atse@12109 2253 def _checkName(self, name):
atse@12109 2254 """Check if a vm name is valid. Valid names contain alphabetic
atse@12109 2255 characters, digits, or characters in '_-.:/+'.
atse@12109 2256 The same name cannot be used for more than one vm at the same time.
atse@12109 2257
atse@12109 2258 @param name: name
atse@12109 2259 @raise: VmError if invalid
vh249@4461 2260 """
atse@12109 2261 from xen.xend import XendDomain
atse@12109 2262
atse@12109 2263 if name is None or name == '':
atse@12109 2264 raise VmError('Missing VM Name')
mjw@1809 2265
atse@12109 2266 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
atse@12109 2267 raise VmError('Invalid VM Name')
emellor@7461 2268
atse@12109 2269 dom = XendDomain.instance().domain_lookup_nr(name)
keir@16011 2270 if dom and dom.info['uuid'] != self.info['uuid']:
ewan@12763 2271 raise VmError("VM name '%s' already exists%s" %
ewan@12763 2272 (name,
ewan@12763 2273 dom.domid is not None and
ewan@13040 2274 (" as domain %s" % str(dom.domid)) or ""))
atse@12109 2275
mjw@4618 2276
keir@16298 2277 def update(self, info = None, refresh = True, transaction = None):
atse@12109 2278 """Update with info from xc.domain_getinfo().
emellor@7185 2279 """
atse@12109 2280 log.trace("XendDomainInfo.update(%s) on domain %s", info,
atse@12109 2281 str(self.domid))
atse@12109 2282
atse@12109 2283 if not info:
atse@12109 2284 info = dom_get(self.domid)
atse@12109 2285 if not info:
atse@12109 2286 return
ewan@14471 2287
ewan@14471 2288 if info["maxmem_kb"] < 0:
ewan@14471 2289 info["maxmem_kb"] = XendNode.instance() \
ewan@14471 2290 .physinfo_dict()['total_memory'] * 1024
ewan@14471 2291
atse@12109 2292 #ssidref field not used any longer
atse@12109 2293 if 'ssidref' in info:
atse@12109 2294 info.pop('ssidref')
emellor@7099 2295
atse@12109 2296 # make sure state is reset for info
atse@12109 2297 # TODO: we should eventually get rid of old_dom_states
atse@12109 2298
atse@12671 2299 self.info.update_config(info)
keir@16298 2300 self._update_consoles(transaction)
atse@13654 2301
atse@12109 2302 if refresh:
atse@12275 2303 self.refreshShutdown(info)
emellor@7099 2304
atse@12109 2305 log.trace("XendDomainInfo.update done on domain %s: %s",
atse@12109 2306 str(self.domid), self.info)
emellor@7482 2307
ewan@13108 2308 def sxpr(self, ignore_store = False, legacy_only = True):
atse@12671 2309 result = self.info.to_sxp(domain = self,
ewan@13108 2310 ignore_devices = ignore_store,
ewan@13108 2311 legacy_only = legacy_only)
ewan@12488 2312
atse@13654 2313 #if not ignore_store and self.dompath:
atse@13654 2314 # vnc_port = self.readDom('console/vnc-port')
atse@13654 2315 # if vnc_port is not None:
atse@13654 2316 # result.append(['device',
atse@13654 2317 # ['console', ['vnc-port', str(vnc_port)]]])
ewan@12488 2318
ewan@12488 2319 return result
atse@12109 2320
atse@12109 2321 # Xen API
atse@12109 2322 # ----------------------------------------------------------------
emellor@7482 2323
atse@12109 2324 def get_uuid(self):
atse@12355 2325 dom_uuid = self.info.get('uuid')
atse@12355 2326 if not dom_uuid: # if it doesn't exist, make one up
atse@12355 2327 dom_uuid = uuid.createString()
atse@12355 2328 self.info['uuid'] = dom_uuid
atse@12355 2329 return dom_uuid
atse@12355 2330
atse@12109 2331 def get_memory_static_max(self):
atse@12826 2332 return self.info.get('memory_static_max', 0)
atse@12109 2333 def get_memory_static_min(self):
atse@12826 2334 return self.info.get('memory_static_min', 0)
atse@12671 2335 def get_memory_dynamic_max(self):
atse@12829 2336 return self.info.get('memory_dynamic_max', 0)
atse@12671 2337 def get_memory_dynamic_min(self):
atse@12829 2338 return self.info.get('memory_dynamic_min', 0)
ewan@14575 2339
steven@15010 2340 # only update memory-related config values if they maintain sanity
steven@15010 2341 def _safe_set_memory(self, key, newval):
steven@15010 2342 oldval = self.info.get(key, 0)
steven@15010 2343 try:
steven@15010 2344 self.info[key] = newval
steven@15010 2345 self.info._memory_sanity_check()
steven@15010 2346 except Exception, ex:
steven@15010 2347 self.info[key] = oldval
steven@15010 2348 raise
steven@15010 2349
ewan@14575 2350 def set_memory_static_max(self, val):
steven@15010 2351 self._safe_set_memory('memory_static_max', val)
ewan@14575 2352 def set_memory_static_min(self, val):
steven@15010 2353 self._safe_set_memory('memory_static_min', val)
ewan@14575 2354 def set_memory_dynamic_max(self, val):
steven@15010 2355 self._safe_set_memory('memory_dynamic_max', val)
ewan@14575 2356 def set_memory_dynamic_min(self, val):
steven@15010 2357 self._safe_set_memory('memory_dynamic_min', val)
ewan@14575 2358
atse@12109 2359 def get_vcpus_params(self):
atse@13784 2360 if self.getDomid() is None:
ewan@13821 2361 return self.info['vcpus_params']
atse@13784 2362
atse@13784 2363 retval = xc.sched_credit_domain_get(self.getDomid())
atse@13784 2364 return retval
atse@12109 2365 def get_power_state(self):
tom@14924 2366 return XEN_API_VM_POWER_STATE[self._stateGet()]
ewan@14379 2367 def get_platform(self):
ewan@14379 2368 return self.info.get('platform', {})
atse@12109 2369 def get_pci_bus(self):
atse@13623 2370 return self.info.get('pci_bus', '')
atse@12109 2371 def get_tools_version(self):
atse@13623 2372 return self.info.get('tools_version', {})
ewan@14417 2373 def get_metrics(self):
ewan@14417 2374 return self.metrics.get_uuid();
kfraser@15550 2375
kfraser@15550 2376
kfraser@15753 2377 def get_security_label(self, xspol=None):
keir@15976 2378 import xen.util.xsm.xsm as security
keir@15976 2379 label = security.get_security_label(self, xspol)
kfraser@15550 2380 return label
kfraser@15550 2381
kfraser@15753 2382 def set_security_label(self, seclab, old_seclab, xspol=None,
kfraser@15753 2383 xspol_old=None):
kfraser@15550 2384 """
kfraser@15550 2385 Set the security label of a domain from its old to
kfraser@15550 2386 a new value.
kfraser@15550 2387 @param seclab New security label formatted in the form
kfraser@15550 2388 <policy type>:<policy name>:<vm label>
kfraser@15550 2389 @param old_seclab The current security label that the
kfraser@15550 2390 VM must have.
kfraser@15550 2391 @param xspol An optional policy under which this
kfraser@15550 2392 update should be done. If not given,
kfraser@15550 2393 then the current active policy is used.
kfraser@15753 2394 @param xspol_old The old policy; only to be passed during
kfraser@15753 2395 the updating of a policy
kfraser@15550 2396 @return Returns return code, a string with errors from
kfraser@15550 2397 the hypervisor's operation, old label of the
kfraser@15550 2398 domain
kfraser@15550 2399 """
kfraser@15550 2400 rc = 0
kfraser@15550 2401 errors = ""
kfraser@15550 2402 old_label = ""
kfraser@15550 2403 new_ssidref = 0
kfraser@15550 2404 domid = self.getDomid()
kfraser@15550 2405 res_labels = None
kfraser@15753 2406 is_policy_update = (xspol_old != None)
kfraser@15550 2407
kfraser@15550 2408 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
kfraser@15550 2409 from xen.util import xsconstants
kfraser@15550 2410
kfraser@15550 2411 state = self._stateGet()
kfraser@15550 2412 # Relabel only HALTED or RUNNING or PAUSED domains
kfraser@15550 2413 if domid != 0 and \
kfraser@15550 2414 state not in \
kfraser@15550 2415 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
kfraser@15550 2416 DOM_STATE_SUSPENDED ]:
kfraser@15550 2417 log.warn("Relabeling domain not possible in state '%s'" %
kfraser@15550 2418 DOM_STATES[state])
kfraser@15550 2419 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
kfraser@15550 2420
kfraser@15550 2421 # Remove security label. Works only for halted domains
kfraser@15550 2422 if not seclab or seclab == "":
kfraser@15550 2423 if state not in [ DOM_STATE_HALTED ]:
kfraser@15550 2424 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
kfraser@15550 2425
kfraser@15550 2426 if self.info.has_key('security_label'):
kfraser@15550 2427 old_label = self.info['security_label']
kfraser@15550 2428 # Check label against expected one.
kfraser@15550 2429 if old_label != old_seclab:
kfraser@15550 2430 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2431 del self.info['security_label']
kfraser@15550 2432 xen.xend.XendDomain.instance().managed_config_save(self)
kfraser@15550 2433 return (xsconstants.XSERR_SUCCESS, "", "", 0)
kfraser@15550 2434
kfraser@15550 2435 tmp = seclab.split(":")
kfraser@15550 2436 if len(tmp) != 3:
kfraser@15550 2437 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
kfraser@15550 2438 typ, policy, label = tmp
kfraser@15550 2439
kfraser@15550 2440 poladmin = XSPolicyAdminInstance()
kfraser@15550 2441 if not xspol:
kfraser@15550 2442 xspol = poladmin.get_policy_by_name(policy)
kfraser@15550 2443
kfraser@15550 2444 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
kfraser@15550 2445 #if domain is running or paused try to relabel in hypervisor
kfraser@15550 2446 if not xspol:
kfraser@15550 2447 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
kfraser@15550 2448
kfraser@15550 2449 if typ != xspol.get_type_name() or \
kfraser@15550 2450 policy != xspol.get_name():
kfraser@15550 2451 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2452
kfraser@15550 2453 if typ == xsconstants.ACM_POLICY_ID:
kfraser@15550 2454 new_ssidref = xspol.vmlabel_to_ssidref(label)
kfraser@15550 2455 if new_ssidref == xsconstants.INVALID_SSIDREF:
kfraser@15550 2456 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2457
kfraser@15550 2458 # Check that all used resources are accessible under the
kfraser@15550 2459 # new label
kfraser@15753 2460 if not is_policy_update and \
kfraser@15753 2461 not security.resources_compatible_with_vmlabel(xspol,
kfraser@15550 2462 self, label):
kfraser@15550 2463 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2464
keir@16559 2465 #Check label against expected one. Can only do this
keir@16559 2466 # if the policy hasn't changed underneath in the meantime
keir@16559 2467 if xspol_old == None:
keir@16559 2468 old_label = self.get_security_label()
keir@16559 2469 if old_label != old_seclab:
keir@16559 2470 log.info("old_label != old_seclab: %s != %s" %
keir@16559 2471 (old_label, old_seclab))
keir@16559 2472 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2473
kfraser@15550 2474 # relabel domain in the hypervisor
kfraser@15550 2475 rc, errors = security.relabel_domains([[domid, new_ssidref]])
kfraser@15550 2476 log.info("rc from relabeling in HV: %d" % rc)
kfraser@15550 2477 else:
kfraser@15550 2478 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
kfraser@15550 2479
kfraser@15550 2480 if rc == 0:
kfraser@15550 2481 # HALTED, RUNNING or PAUSED
kfraser@15550 2482 if domid == 0:
kfraser@15550 2483 if xspol:
keir@16559 2484 self.info['security_label'] = seclab
kfraser@15550 2485 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
kfraser@15550 2486 else:
kfraser@15550 2487 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
kfraser@15550 2488 else:
kfraser@15550 2489 if self.info.has_key('security_label'):
kfraser@15550 2490 old_label = self.info['security_label']
kfraser@15550 2491 # Check label against expected one, unless wildcard
kfraser@15550 2492 if old_label != old_seclab:
kfraser@15550 2493 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
kfraser@15550 2494
kfraser@15550 2495 self.info['security_label'] = seclab
keir@16559 2496
kfraser@15550 2497 try:
kfraser@15550 2498 xen.xend.XendDomain.instance().managed_config_save(self)
kfraser@15550 2499 except:
kfraser@15550 2500 pass
kfraser@15550 2501 return (rc, errors, old_label, new_ssidref)
kfraser@15550 2502
atse@12109 2503 def get_on_shutdown(self):
kfraser@14278 2504 after_shutdown = self.info.get('actions_after_shutdown')
atse@12140 2505 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
atse@12140 2506 return XEN_API_ON_NORMAL_EXIT[-1]
atse@12140 2507 return after_shutdown
atse@12140 2508
atse@12109 2509 def get_on_reboot(self):
kfraser@14278 2510 after_reboot = self.info.get('actions_after_reboot')
atse@12140 2511 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
atse@12140 2512 return XEN_API_ON_NORMAL_EXIT[-1]
atse@12140 2513 return after_reboot
emellor@7688 2514
atse@12109 2515 def get_on_suspend(self):
atse@12671 2516 # TODO: not supported
kfraser@14278 2517 after_suspend = self.info.get('actions_after_suspend')
atse@12140 2518 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
atse@12140 2519 return XEN_API_ON_NORMAL_EXIT[-1]
atse@12140 2520 return after_suspend
atse@12109 2521
atse@12109 2522 def get_on_crash(self):
kfraser@14278 2523 after_crash = self.info.get('actions_after_crash')
atse@12140 2524 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
atse@12140 2525 return XEN_API_ON_CRASH_BEHAVIOUR[0]
atse@12140 2526 return after_crash
atse@12109 2527
atse@12135 2528 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
atse@12135 2529 """ Get's a device configuration either from XendConfig or
atse@12151 2530 from the DevController.
atse@12151 2531
atse@12151 2532 @param dev_class: device class, either, 'vbd' or 'vif'
atse@12151 2533 @param dev_uuid: device UUID
atse@12151 2534
atse@12151 2535 @rtype: dictionary
atse@12151 2536 """
atse@13783 2537 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
atse@12151 2538
atse@12151 2539 # shortcut if the domain isn't started because
atse@12151 2540 # the devcontrollers will have no better information
atse@12151 2541 # than XendConfig.
tom@14924 2542 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
atse@13783 2543 if dev_config:
atse@13783 2544 return copy.deepcopy(dev_config)
atse@12135 2545 return None
atse@12151 2546
atse@12151 2547 # instead of using dev_class, we use the dev_type
atse@12151 2548 # that is from XendConfig.
atse@12151 2549 controller = self.getDeviceController(dev_type)
atse@12151 2550 if not controller:
atse@12151 2551 return None
atse@12137 2552
atse@12151 2553 all_configs = controller.getAllDeviceConfigurations()
atse@12151 2554 if not all_configs:
atse@12151 2555 return None
atse@12137 2556
atse@13783 2557 updated_dev_config = copy.deepcopy(dev_config)
atse@12151 2558 for _devid, _devcfg in all_configs.items():
atse@12151 2559 if _devcfg.get('uuid') == dev_uuid:
atse@13783 2560 updated_dev_config.update(_devcfg)
atse@13783 2561 updated_dev_config['id'] = _devid
atse@13783 2562 return updated_dev_config
atse@12109 2563
atse@13783 2564 return updated_dev_config
atse@12135 2565
atse@12135 2566 def get_dev_xenapi_config(self, dev_class, dev_uuid):
atse@12135 2567 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
atse@12135 2568 if not config:
atse@12135 2569 return {}
atse@12135 2570
atse@12135 2571 config['VM'] = self.get_uuid()
atse@12135 2572
atse@12135 2573 if dev_class == 'vif':
atse@12135 2574 if not config.has_key('name'):
atse@12135 2575 config['name'] = config.get('vifname', '')
atse@12135 2576 if not config.has_key('MAC'):
atse@12135 2577 config['MAC'] = config.get('mac', '')
atse@12135 2578 if not config.has_key('type'):
atse@12135 2579 config['type'] = 'paravirtualised'
atse@12135 2580 if not config.has_key('device'):
atse@12135 2581 devid = config.get('id')
atse@12135 2582 if devid != None:
atse@12135 2583 config['device'] = 'eth%d' % devid
atse@12135 2584 else:
atse@12135 2585 config['device'] = ''
ewan@13206 2586
ewan@13206 2587 if not config.has_key('network'):
ewan@13216 2588 try:
tom@14970 2589 bridge = config.get('bridge', None)
tom@14970 2590 if bridge is None:
tom@14970 2591 from xen.util import Brctl
tom@14970 2592 if_to_br = dict([(i,b)
tom@14970 2593 for (b,ifs) in Brctl.get_state().items()
tom@14970 2594 for i in ifs])
tom@14970 2595 vifname = "vif%s.%s" % (self.getDomid(),
tom@14970 2596 config.get('id'))
tom@14970 2597 bridge = if_to_br.get(vifname, None)
ewan@13216 2598 config['network'] = \
ewan@13216 2599 XendNode.instance().bridge_to_network(
tom@14970 2600 config.get('bridge')).get_uuid()
ewan@13216 2601 except Exception:
ewan@13216 2602 log.exception('bridge_to_network')
ewan@13216 2603 # Ignore this for now -- it may happen if the device
ewan@13216 2604 # has been specified using the legacy methods, but at
ewan@13216 2605 # some point we're going to have to figure out how to
ewan@13216 2606 # handle that properly.
ewan@13206 2607
atse@12135 2608 config['MTU'] = 1500 # TODO
atse@13616 2609
tom@14924 2610 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
atse@13616 2611 xennode = XendNode.instance()
atse@13616 2612 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
atse@13616 2613 config['io_read_kbs'] = rx_bps/1024
atse@13616 2614 config['io_write_kbs'] = tx_bps/1024
keir@16438 2615 rx, tx = xennode.get_vif_stat(self.domid, devid)
keir@16438 2616 config['io_total_read_kbs'] = rx/1024
keir@16438 2617 config['io_total_write_kbs'] = tx/1024
atse@13616 2618 else:
atse@13616 2619 config['io_read_kbs'] = 0.0
keir@16438 2620 config['io_write_kbs'] = 0.0
keir@16438 2621 config['io_total_read_kbs'] = 0.0
keir@16438 2622 config['io_total_write_kbs'] = 0.0
atse@12109 2623
kfraser@15656 2624 config['security_label'] = config.get('security_label', '')
kfraser@15656 2625
atse@12671 2626 if dev_class == 'vbd':
atse@13616 2627
tom@14924 2628 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
atse@13616 2629 controller = self.getDeviceController(dev_class)
atse@13616 2630 devid, _1, _2 = controller.getDeviceDetails(config)
atse@13616 2631 xennode = XendNode.instance()
atse@13616 2632 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
atse@13616 2633 config['io_read_kbs'] = rd_blkps
atse@13616 2634 config['io_write_kbs'] = wr_blkps
atse@13616 2635 else:
atse@13616 2636 config['io_read_kbs'] = 0.0
atse@13616 2637 config['io_write_kbs'] = 0.0
atse@13616 2638
atse@12826 2639 config['VDI'] = config.get('VDI', '')
atse@12135 2640 config['device'] = config.get('dev', '')
atse@13615 2641 if ':' in config['device']:
atse@13615 2642 vbd_name, vbd_type = config['device'].split(':', 1)
atse@13615 2643 config['device'] = vbd_name
atse@13615 2644 if vbd_type == 'cdrom':
atse@13615 2645 config['type'] = XEN_API_VBD_TYPE[0]
atse@13615 2646 else:
atse@13615 2647 config['type'] = XEN_API_VBD_TYPE[1]
atse@13615 2648
atse@12151 2649 config['driver'] = 'paravirtualised' # TODO
atse@12151 2650 config['image'] = config.get('uname', '')
atse@13616 2651
atse@13263 2652 if config.get('mode', 'r') == 'r':
atse@12140 2653 config['mode'] = 'RO'
atse@12140 2654 else:
atse@12140 2655 config['mode'] = 'RW'
atse@12109 2656
ewan@12382 2657 if dev_class == 'vtpm':
ewan@13769 2658 if not config.has_key('type'):
kaf24@13761 2659 config['type'] = 'paravirtualised' # TODO
kaf24@13761 2660 if not config.has_key('backend'):
kaf24@13761 2661 config['backend'] = "00000000-0000-0000-0000-000000000000"
ewan@12382 2662
atse@12135 2663 return config
emellor@7099 2664
atse@12135 2665 def get_dev_property(self, dev_class, dev_uuid, field):
atse@12135 2666 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
atse@12135 2667 try:
atse@12135 2668 return config[field]
atse@12135 2669 except KeyError:
atse@12135 2670 raise XendError('Invalid property for device: %s' % field)
emellor@7210 2671
ewan@13717 2672 def set_dev_property(self, dev_class, dev_uuid, field, value):
ewan@13717 2673 self.info['devices'][dev_uuid][1][field] = value
ewan@13717 2674
atse@12109 2675 def get_vcpus_util(self):
atse@12109 2676 vcpu_util = {}
atse@13616 2677 xennode = XendNode.instance()
ewan@14519 2678 if 'VCPUs_max' in self.info and self.domid != None:
ewan@14519 2679 for i in range(0, self.info['VCPUs_max']):
atse@13616 2680 util = xennode.get_vcpu_util(self.domid, i)
atse@13616 2681 vcpu_util[str(i)] = util
atse@12109 2682
atse@12109 2683 return vcpu_util
atse@12109 2684
ewan@13079 2685 def get_consoles(self):
ewan@13079 2686 return self.info.get('console_refs', [])
ewan@13079 2687
atse@12109 2688 def get_vifs(self):
atse@12124 2689 return self.info.get('vif_refs', [])
emellor@7210 2690
atse@12109 2691 def get_vbds(self):
atse@12124 2692 return self.info.get('vbd_refs', [])
emellor@7185 2693
ewan@12203 2694 def get_vtpms(self):
ewan@12203 2695 return self.info.get('vtpm_refs', [])
ewan@12203 2696
atse@13624 2697 def create_vbd(self, xenapi_vbd, vdi_image_path):
atse@12148 2698 """Create a VBD using a VDI from XendStorageRepository.
atse@12148 2699
atse@12148 2700 @param xenapi_vbd: vbd struct from the Xen API
atse@12148 2701 @param vdi_image_path: VDI UUID
atse@12148 2702 @rtype: string
atse@12148 2703 @return: uuid of the device
atse@12148 2704 """
atse@12148 2705 xenapi_vbd['image'] = vdi_image_path
atse@13624 2706 if vdi_image_path.startswith('tap'):
atse@13624 2707 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
atse@13624 2708 else:
atse@13624 2709 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
atse@13624 2710
atse@12148 2711 if not dev_uuid:
atse@12148 2712 raise XendError('Failed to create device')
atse@12148 2713
tom@14924 2714 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
tom@14924 2715 XEN_API_VM_POWER_STATE_PAUSED):
ewan@13080 2716 _, config = self.info['devices'][dev_uuid]
atse@13624 2717
atse@13624 2718 if vdi_image_path.startswith('tap'):
ewan@14486 2719 dev_control = self.getDeviceController('tap')
atse@13624 2720 else:
atse@13624 2721 dev_control = self.getDeviceController('vbd')
atse@12148 2722
ewan@14486 2723 try:
ewan@14486 2724 devid = dev_control.createDevice(config)
ewan@14486 2725 dev_control.waitForDevice(devid)
ewan@14486 2726 self.info.device_update(dev_uuid,
ewan@14486 2727 cfg_xenapi = {'devid': devid})
ewan@14486 2728 except Exception, exn:
ewan@14486 2729 log.exception(exn)
ewan@14486 2730 del self.info['devices'][dev_uuid]
ewan@14486 2731 self.info['vbd_refs'].remove(dev_uuid)
ewan@14486 2732 raise
ewan@14486 2733
atse@12148 2734 return dev_uuid
atse@12148 2735
wim@13865 2736 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
wim@13865 2737 """Create a VBD using a VDI from XendStorageRepository.
wim@13865 2738
wim@13865 2739 @param xenapi_vbd: vbd struct from the Xen API
wim@13865 2740 @param vdi_image_path: VDI UUID
wim@13865 2741 @rtype: string
wim@13865 2742 @return: uuid of the device
wim@13865 2743 """
wim@13865 2744 xenapi_vbd['image'] = vdi_image_path
wim@13865 2745 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
wim@13865 2746 if not dev_uuid:
wim@13865 2747 raise XendError('Failed to create device')
wim@13865 2748
tom@14924 2749 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
wim@13865 2750 _, config = self.info['devices'][dev_uuid]
wim@13865 2751 config['devid'] = self.getDeviceController('tap').createDevice(config)
wim@13865 2752
wim@13865 2753 return config['devid']
wim@13865 2754
atse@12109 2755 def create_vif(self, xenapi_vif):
atse@12148 2756 """Create VIF device from the passed struct in Xen API format.
atse@12148 2757
atse@12148 2758 @param xenapi_vif: Xen API VIF Struct.
atse@12148 2759 @rtype: string
atse@12148 2760 @return: UUID
atse@12148 2761 """
atse@12109 2762 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
atse@12109 2763 if not dev_uuid:
atse@12109 2764 raise XendError('Failed to create device')
atse@12109 2765
tom@14924 2766 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
tom@14924 2767 XEN_API_VM_POWER_STATE_PAUSED):
ewan@14491 2768
ewan@13080 2769 _, config = self.info['devices'][dev_uuid]
ewan@14491 2770 dev_control = self.getDeviceController('vif')
ewan@14491 2771
ewan@14491 2772 try:
ewan@14491 2773 devid = dev_control.createDevice(config)
ewan@14491 2774 dev_control.waitForDevice(devid)
ewan@14491 2775 self.info.device_update(dev_uuid,
ewan@14491 2776 cfg_xenapi = {'devid': devid})
ewan@14491 2777 except Exception, exn:
ewan@14491 2778 log.exception(exn)
ewan@14491 2779 del self.info['devices'][dev_uuid]
ewan@14491 2780 self.info['vif_refs'].remove(dev_uuid)
ewan@14491 2781 raise
ewan@14491 2782
atse@12109 2783 return dev_uuid
atse@12135 2784
ewan@12206 2785 def create_vtpm(self, xenapi_vtpm):
ewan@12206 2786 """Create a VTPM device from the passed struct in Xen API format.
ewan@12206 2787
ewan@12206 2788 @return: uuid of the device
ewan@12206 2789 @rtype: string
ewan@12206 2790 """
ewan@12206 2791
tom@14924 2792 if self._stateGet() not in (DOM_STATE_HALTED,):
ewan@12382 2793 raise VmError("Can only add vTPM to a halted domain.")
ewan@12382 2794 if self.get_vtpms() != []:
ewan@12382 2795 raise VmError('Domain already has a vTPM.')
ewan@12206 2796 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
ewan@12206 2797 if not dev_uuid:
ewan@12206 2798 raise XendError('Failed to create device')
ewan@12206 2799
ewan@12206 2800 return dev_uuid
ewan@12206 2801
atse@13783 2802 def create_console(self, xenapi_console):
atse@13783 2803 """ Create a console device from a Xen API struct.
atse@13783 2804
atse@13783 2805 @return: uuid of device
atse@13783 2806 @rtype: string
atse@13783 2807 """
tom@14924 2808 if self._stateGet() not in (DOM_STATE_HALTED,):
atse@13783 2809 raise VmError("Can only add console to a halted domain.")
atse@13783 2810
atse@13783 2811 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
atse@13783 2812 if not dev_uuid:
atse@13783 2813 raise XendError('Failed to create device')
atse@13783 2814
atse@13783 2815 return dev_uuid
atse@13783 2816
kfraser@15689 2817 def set_console_other_config(self, console_uuid, other_config):
kfraser@15689 2818 self.info.console_update(console_uuid, 'other_config', other_config)
kfraser@15689 2819
atse@13623 2820 def destroy_device_by_uuid(self, dev_type, dev_uuid):
atse@13623 2821 if dev_uuid not in self.info['devices']:
atse@13623 2822 raise XendError('Device does not exist')
atse@13623 2823
atse@13623 2824 try:
tom@14924 2825 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
tom@14924 2826 XEN_API_VM_POWER_STATE_PAUSED):
atse@13623 2827 _, config = self.info['devices'][dev_uuid]
atse@13623 2828 devid = config.get('devid')
atse@13623 2829 if devid != None:
atse@13623 2830 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
atse@13623 2831 else:
atse@13623 2832 raise XendError('Unable to get devid for device: %s:%s' %
atse@13623 2833 (dev_type, dev_uuid))
atse@13623 2834 finally:
atse@13623 2835 del self.info['devices'][dev_uuid]
atse@13623 2836 self.info['%s_refs' % dev_type].remove(dev_uuid)
atse@13623 2837
atse@13623 2838 def destroy_vbd(self, dev_uuid):
atse@13623 2839 self.destroy_device_by_uuid('vbd', dev_uuid)
atse@13623 2840
atse@13623 2841 def destroy_vif(self, dev_uuid):
atse@13623 2842 self.destroy_device_by_uuid('vif', dev_uuid)
atse@13623 2843
atse@13623 2844 def destroy_vtpm(self, dev_uuid):
atse@13623 2845 self.destroy_device_by_uuid('vtpm', dev_uuid)
atse@13623 2846
atse@12135 2847 def has_device(self, dev_class, dev_uuid):
atse@12826 2848 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
atse@12109 2849
atse@12109 2850 def __str__(self):
atse@12109 2851 return '<domain id=%s name=%s memory=%s state=%s>' % \
atse@12671 2852 (str(self.domid), self.info['name_label'],
tom@14924 2853 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
atse@12109 2854
atse@12109 2855 __repr__ = __str__
atse@12109 2856