debuggers.hg
annotate tools/python/xen/xend/XendDomain.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>
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 | 7b4e560d6caf |
children | 666573856c59 |
rev | line source |
---|---|
kaf24@6102 | 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> |
cl349@5162 | 16 # Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk> |
emellor@7022 | 17 # Copyright (C) 2005 XenSource Ltd |
kaf24@6102 | 18 #============================================================================ |
mjw@1661 | 19 |
mjw@1661 | 20 """Handler for domain operations. |
mjw@1661 | 21 Nothing here is persistent (across reboots). |
mjw@1661 | 22 Needs to be persistent for one uptime. |
mjw@1661 | 23 """ |
emellor@7442 | 24 |
cl349@5182 | 25 import os |
ewan@12710 | 26 import stat |
atse@12109 | 27 import shutil |
emellor@8166 | 28 import socket |
ewan@13107 | 29 import tempfile |
emellor@7180 | 30 import threading |
mjw@1661 | 31 |
emellor@7042 | 32 import xen.lowlevel.xc |
mjw@1661 | 33 |
emellor@7205 | 34 |
ewan@14169 | 35 from xen.xend import XendOptions, XendCheckpoint, XendDomainInfo |
atse@12109 | 36 from xen.xend.PrettyPrint import prettyprint |
ewan@14525 | 37 from xen.xend import XendConfig |
atse@12524 | 38 from xen.xend.XendError import XendError, XendInvalidDomain, VmError |
ewan@13196 | 39 from xen.xend.XendError import VMBadState |
cl349@5374 | 40 from xen.xend.XendLogging import log |
ewan@12622 | 41 from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE |
atse@12109 | 42 from xen.xend.XendConstants import XS_VMROOT |
ewan@12622 | 43 from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_PAUSED |
ewan@12622 | 44 from xen.xend.XendConstants import DOM_STATE_RUNNING, DOM_STATE_SUSPENDED |
ewan@12622 | 45 from xen.xend.XendConstants import DOM_STATE_SHUTDOWN, DOM_STATE_UNKNOWN |
keir@14136 | 46 from xen.xend.XendConstants import TRIGGER_TYPE |
ewan@12537 | 47 from xen.xend.XendDevices import XendDevices |
tom@14924 | 48 from xen.xend.XendAPIConstants import * |
atse@12109 | 49 |
emellor@8209 | 50 from xen.xend.xenstore.xstransact import xstransact |
emellor@7858 | 51 from xen.xend.xenstore.xswatch import xswatch |
kfraser@15550 | 52 from xen.util import mkdir |
atse@12109 | 53 from xen.xend import uuid |
emellor@7042 | 54 |
emellor@7995 | 55 xc = xen.lowlevel.xc.xc() |
ewan@13444 | 56 xoptions = XendOptions.instance() |
emellor@7042 | 57 |
mjw@1661 | 58 __all__ = [ "XendDomain" ] |
mjw@4618 | 59 |
atse@12109 | 60 CACHED_CONFIG_FILE = 'config.sxp' |
atse@12109 | 61 CHECK_POINT_FILE = 'checkpoint.chk' |
atse@12109 | 62 DOM0_UUID = "00000000-0000-0000-0000-000000000000" |
atse@12109 | 63 DOM0_NAME = "Domain-0" |
atse@12109 | 64 DOM0_ID = 0 |
cl349@6938 | 65 |
ewan@12622 | 66 POWER_STATE_NAMES = dict([(x, XEN_API_VM_POWER_STATE[x]) |
ewan@12622 | 67 for x in [DOM_STATE_HALTED, |
ewan@12622 | 68 DOM_STATE_PAUSED, |
ewan@12622 | 69 DOM_STATE_RUNNING, |
ewan@12622 | 70 DOM_STATE_SUSPENDED, |
ewan@12622 | 71 DOM_STATE_SHUTDOWN, |
ewan@12622 | 72 DOM_STATE_UNKNOWN]]) |
ewan@12622 | 73 POWER_STATE_ALL = 'all' |
ewan@12622 | 74 |
ewan@12622 | 75 |
mjw@1661 | 76 class XendDomain: |
mjw@1661 | 77 """Index of all domains. Singleton. |
atse@12109 | 78 |
atse@12354 | 79 @ivar domains: map of domains indexed by domid |
atse@12109 | 80 @type domains: dict of XendDomainInfo |
atse@12354 | 81 @ivar managed_domains: domains that are not running and managed by Xend |
atse@12354 | 82 @type managed_domains: dict of XendDomainInfo indexed by uuid |
atse@12109 | 83 @ivar domains_lock: lock that must be held when manipulating self.domains |
atse@12109 | 84 @type domains_lock: threaading.RLock |
atse@12109 | 85 @ivar _allow_new_domains: Flag to set that allows creating of new domains. |
atse@12109 | 86 @type _allow_new_domains: boolean |
mjw@1661 | 87 """ |
mjw@1711 | 88 |
mjw@1661 | 89 def __init__(self): |
emellor@7205 | 90 self.domains = {} |
atse@12254 | 91 self.managed_domains = {} |
emellor@7376 | 92 self.domains_lock = threading.RLock() |
emellor@7858 | 93 |
atse@12109 | 94 # xen api instance vars |
atse@12109 | 95 # TODO: nothing uses this at the moment |
atse@12109 | 96 self._allow_new_domains = True |
emellor@8166 | 97 |
emellor@8166 | 98 # This must be called only the once, by instance() below. It is separate |
emellor@8166 | 99 # from the constructor because XendDomainInfo calls back into this class |
emellor@8166 | 100 # in order to check the uniqueness of domain names. This means that |
emellor@8166 | 101 # instance() must be able to return a valid instance of this class even |
emellor@8166 | 102 # during this initialisation. |
emellor@8166 | 103 def init(self): |
atse@12109 | 104 """Singleton initialisation function.""" |
atse@12109 | 105 |
ewan@12230 | 106 dom_path = self._managed_path() |
ewan@12710 | 107 mkdir.parents(dom_path, stat.S_IRWXU) |
ewan@12230 | 108 |
atse@12109 | 109 xstransact.Mkdir(XS_VMROOT) |
atse@12109 | 110 xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID}) |
emellor@8209 | 111 |
emellor@7205 | 112 self.domains_lock.acquire() |
emellor@7205 | 113 try: |
atse@12109 | 114 try: |
atse@12109 | 115 dom0info = [d for d in self._running_domains() \ |
ewan@12168 | 116 if d.get('domid') == DOM0_ID][0] |
atse@12109 | 117 |
atse@12165 | 118 dom0info['name'] = DOM0_NAME |
atse@12109 | 119 dom0 = XendDomainInfo.recreate(dom0info, True) |
atse@12109 | 120 except IndexError: |
atse@12109 | 121 raise XendError('Unable to find Domain 0') |
atse@12109 | 122 |
atse@12109 | 123 self._setDom0CPUCount() |
emellor@7882 | 124 |
emellor@7882 | 125 # This watch registration needs to be before the refresh call, so |
emellor@7882 | 126 # that we're sure that we haven't missed any releases, but inside |
emellor@7882 | 127 # the domains_lock, as we don't want the watch to fire until after |
emellor@7882 | 128 # the refresh call has completed. |
atse@12109 | 129 xswatch("@introduceDomain", self._on_domains_changed) |
atse@12109 | 130 xswatch("@releaseDomain", self._on_domains_changed) |
atse@12109 | 131 |
atse@12109 | 132 self._init_domains() |
emellor@7205 | 133 finally: |
emellor@7205 | 134 self.domains_lock.release() |
emellor@7205 | 135 |
atse@12109 | 136 |
atse@12109 | 137 def _on_domains_changed(self, _): |
atse@12109 | 138 """ Callback method when xenstore changes. |
mjw@1661 | 139 |
atse@12109 | 140 Calls refresh which will keep the local cache of domains |
atse@12109 | 141 in sync. |
cl349@5052 | 142 |
atse@12109 | 143 @rtype: int |
atse@12109 | 144 @return: 1 |
atse@12109 | 145 """ |
atse@12109 | 146 self.domains_lock.acquire() |
atse@12109 | 147 try: |
atse@12109 | 148 self._refresh() |
atse@12109 | 149 finally: |
atse@12109 | 150 self.domains_lock.release() |
atse@12109 | 151 return 1 |
atse@12109 | 152 |
atse@12109 | 153 def _init_domains(self): |
atse@12109 | 154 """Does the initial scan of managed and active domains to |
atse@12109 | 155 populate self.domains. |
atse@12109 | 156 |
atse@12109 | 157 Note: L{XendDomainInfo._checkName} will call back into XendDomain |
atse@12109 | 158 to make sure domain name is not a duplicate. |
atse@12109 | 159 |
cl349@5055 | 160 """ |
emellor@7205 | 161 self.domains_lock.acquire() |
emellor@7205 | 162 try: |
atse@12109 | 163 running = self._running_domains() |
atse@12109 | 164 managed = self._managed_domains() |
atse@12109 | 165 |
atse@12138 | 166 # add all active domains |
atse@12109 | 167 for dom in running: |
ewan@12349 | 168 if dom['dying'] == 1: |
ewan@12349 | 169 log.warn('Ignoring dying domain %d from now on' % |
ewan@12349 | 170 dom['domid']) |
ewan@12349 | 171 continue |
ewan@12349 | 172 |
atse@12109 | 173 if dom['domid'] != DOM0_ID: |
atse@12109 | 174 try: |
atse@12109 | 175 new_dom = XendDomainInfo.recreate(dom, False) |
atse@12109 | 176 except Exception: |
atse@12109 | 177 log.exception("Failed to create reference to running " |
atse@12109 | 178 "domain id: %d" % dom['domid']) |
atse@12109 | 179 |
atse@12109 | 180 # add all managed domains as dormant domains. |
atse@12109 | 181 for dom in managed: |
atse@12355 | 182 dom_uuid = dom.get('uuid') |
atse@12355 | 183 if not dom_uuid: |
atse@12355 | 184 continue |
atse@12355 | 185 |
ewan@12694 | 186 dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid) |
atse@12109 | 187 try: |
atse@12138 | 188 running_dom = self.domain_lookup_nr(dom_name) |
atse@12138 | 189 if not running_dom: |
atse@12138 | 190 # instantiate domain if not started. |
atse@12109 | 191 new_dom = XendDomainInfo.createDormant(dom) |
atse@12138 | 192 self._managed_domain_register(new_dom) |
ewan@12699 | 193 else: |
ewan@12699 | 194 self._managed_domain_register(running_dom) |
ewan@14525 | 195 for key in XendConfig.XENAPI_CFG_TYPES.keys(): |
ewan@14525 | 196 if key not in XendConfig.LEGACY_XENSTORE_VM_PARAMS and \ |
ewan@14525 | 197 key in dom: |
ewan@14525 | 198 running_dom.info[key] = dom[key] |
atse@12109 | 199 except Exception: |
atse@12109 | 200 log.exception("Failed to create reference to managed " |
atse@12109 | 201 "domain: %s" % dom_name) |
atse@12109 | 202 |
emellor@7205 | 203 finally: |
emellor@7205 | 204 self.domains_lock.release() |
emellor@7205 | 205 |
cl349@6605 | 206 |
atse@12109 | 207 # ----------------------------------------------------------------- |
atse@12109 | 208 # Getting managed domains storage path names |
atse@12109 | 209 |
atse@12109 | 210 def _managed_path(self, domuuid = None): |
atse@12109 | 211 """Returns the path of the directory where managed domain |
atse@12109 | 212 information is stored. |
atse@12109 | 213 |
atse@12109 | 214 @keyword domuuid: If not None, will return the path to the domain |
atse@12109 | 215 otherwise, will return the path containing |
atse@12109 | 216 the directories which represent each domain. |
atse@12109 | 217 @type: None or String. |
atse@12109 | 218 @rtype: String |
atse@12109 | 219 @return: Path. |
atse@12109 | 220 """ |
ewan@13444 | 221 dom_path = xoptions.get_xend_domains_path() |
atse@12109 | 222 if domuuid: |
atse@12109 | 223 dom_path = os.path.join(dom_path, domuuid) |
atse@12109 | 224 return dom_path |
atse@12109 | 225 |
atse@12109 | 226 def _managed_config_path(self, domuuid): |
atse@12109 | 227 """Returns the path to the configuration file of a managed domain. |
cl349@6605 | 228 |
atse@12109 | 229 @param domname: Domain uuid |
atse@12109 | 230 @type domname: String |
atse@12109 | 231 @rtype: String |
atse@12109 | 232 @return: path to config file. |
atse@12109 | 233 """ |
atse@12109 | 234 return os.path.join(self._managed_path(domuuid), CACHED_CONFIG_FILE) |
atse@12109 | 235 |
atse@12109 | 236 def _managed_check_point_path(self, domuuid): |
atse@12109 | 237 """Returns absolute path to check point file for managed domain. |
atse@12109 | 238 |
atse@12109 | 239 @param domuuid: Name of managed domain |
atse@12109 | 240 @type domname: String |
atse@12109 | 241 @rtype: String |
atse@12109 | 242 @return: Path |
atse@12109 | 243 """ |
atse@12109 | 244 return os.path.join(self._managed_path(domuuid), CHECK_POINT_FILE) |
atse@12109 | 245 |
atse@12109 | 246 def _managed_config_remove(self, domuuid): |
atse@12109 | 247 """Removes a domain configuration from managed list |
atse@12109 | 248 |
atse@12109 | 249 @param domuuid: Name of managed domain |
atse@12109 | 250 @type domname: String |
atse@12109 | 251 @raise XendError: fails to remove the domain. |
cl349@6605 | 252 """ |
atse@12109 | 253 config_path = self._managed_path(domuuid) |
atse@12109 | 254 try: |
atse@12109 | 255 if os.path.exists(config_path) and os.path.isdir(config_path): |
atse@12109 | 256 shutil.rmtree(config_path) |
atse@12109 | 257 except IOError: |
atse@12109 | 258 log.exception('managed_config_remove failed removing conf') |
atse@12109 | 259 raise XendError("Unable to remove managed configuration" |
atse@12109 | 260 " for domain: %s" % domuuid) |
cl349@6605 | 261 |
atse@12109 | 262 def managed_config_save(self, dominfo): |
atse@12109 | 263 """Save a domain's configuration to disk |
atse@12109 | 264 |
atse@12109 | 265 @param domninfo: Managed domain to save. |
atse@12109 | 266 @type dominfo: XendDomainInfo |
atse@12109 | 267 @raise XendError: fails to save configuration. |
atse@12109 | 268 @rtype: None |
cl349@6605 | 269 """ |
atse@12138 | 270 if not self.is_domain_managed(dominfo): |
atse@12138 | 271 return # refuse to save configuration this domain isn't managed |
atse@12138 | 272 |
atse@12109 | 273 if dominfo: |
atse@12109 | 274 domains_dir = self._managed_path() |
atse@12109 | 275 dom_uuid = dominfo.get_uuid() |
atse@12109 | 276 domain_config_dir = self._managed_path(dom_uuid) |
ewan@12710 | 277 |
ewan@12710 | 278 def make_or_raise(path): |
atse@12109 | 279 try: |
ewan@12710 | 280 mkdir.parents(path, stat.S_IRWXU) |
ewan@12710 | 281 except: |
ewan@12710 | 282 log.exception("%s could not be created." % path) |
ewan@12710 | 283 raise XendError("%s could not be created." % path) |
ewan@12710 | 284 |
ewan@12710 | 285 make_or_raise(domains_dir) |
ewan@12710 | 286 make_or_raise(domain_config_dir) |
ewan@12710 | 287 |
atse@12109 | 288 try: |
ewan@13107 | 289 fd, fn = tempfile.mkstemp() |
ewan@13107 | 290 f = os.fdopen(fd, 'w+b') |
ewan@13107 | 291 try: |
ewan@13108 | 292 prettyprint(dominfo.sxpr(legacy_only = False), f, |
ewan@13108 | 293 width = 78) |
ewan@13107 | 294 finally: |
ewan@13107 | 295 f.close() |
tom@15005 | 296 |
ewan@13107 | 297 try: |
tom@15005 | 298 shutil.move(fn, self._managed_config_path(dom_uuid)) |
ewan@13107 | 299 except: |
tom@15005 | 300 log.exception("Renaming %s to %s", fn, |
tom@15005 | 301 self._managed_config_path(dom_uuid)) |
ewan@13107 | 302 os.remove(fn) |
atse@12524 | 303 except: |
atse@12524 | 304 log.exception("Error occurred saving configuration file " + |
atse@12524 | 305 "to %s" % domain_config_dir) |
atse@12109 | 306 raise XendError("Failed to save configuration file to: %s" % |
atse@12109 | 307 domain_config_dir) |
atse@12109 | 308 else: |
atse@12109 | 309 log.warn("Trying to save configuration for invalid domain") |
cl349@6605 | 310 |
emellor@7180 | 311 |
atse@12109 | 312 def _managed_domains(self): |
atse@12109 | 313 """ Returns list of domains that are managed. |
atse@12109 | 314 |
atse@12109 | 315 Expects to be protected by domains_lock. |
cl349@6697 | 316 |
atse@12109 | 317 @rtype: list of XendConfig |
atse@12109 | 318 @return: List of domain configurations that are managed. |
mjw@1661 | 319 """ |
atse@12109 | 320 dom_path = self._managed_path() |
atse@12109 | 321 dom_uuids = os.listdir(dom_path) |
atse@12109 | 322 doms = [] |
atse@12109 | 323 for dom_uuid in dom_uuids: |
atse@12109 | 324 try: |
atse@12109 | 325 cfg_file = self._managed_config_path(dom_uuid) |
ewan@14525 | 326 cfg = XendConfig.XendConfig(filename = cfg_file) |
atse@12355 | 327 if cfg.get('uuid') != dom_uuid: |
atse@12355 | 328 # something is wrong with the SXP |
atse@12355 | 329 log.error("UUID mismatch in stored configuration: %s" % |
atse@12355 | 330 cfg_file) |
atse@12355 | 331 continue |
atse@12109 | 332 doms.append(cfg) |
atse@12109 | 333 except Exception: |
atse@12109 | 334 log.exception('Unable to open or parse config.sxp: %s' % \ |
atse@12109 | 335 cfg_file) |
mjw@4618 | 336 return doms |
mjw@4618 | 337 |
atse@12109 | 338 def _managed_domain_unregister(self, dom): |
atse@12109 | 339 try: |
atse@12254 | 340 if self.is_domain_managed(dom): |
atse@12366 | 341 self._managed_config_remove(dom.get_uuid()) |
atse@12254 | 342 del self.managed_domains[dom.get_uuid()] |
atse@12109 | 343 except ValueError: |
atse@12109 | 344 log.warn("Domain is not registered: %s" % dom.get_uuid()) |
emellor@7099 | 345 |
atse@12109 | 346 def _managed_domain_register(self, dom): |
atse@12254 | 347 self.managed_domains[dom.get_uuid()] = dom |
atse@12109 | 348 |
atse@12254 | 349 def is_domain_managed(self, dom = None): |
atse@12254 | 350 return (dom.get_uuid() in self.managed_domains) |
atse@12109 | 351 |
atse@12109 | 352 # End of Managed Domain Access |
atse@12109 | 353 # -------------------------------------------------------------------- |
atse@12109 | 354 |
atse@12109 | 355 def _running_domains(self): |
atse@12109 | 356 """Get table of domains indexed by id from xc. |
atse@12109 | 357 |
atse@12109 | 358 @requires: Expects to be protected by domains_lock. |
atse@12109 | 359 @rtype: list of dicts |
atse@12109 | 360 @return: A list of dicts representing the running domains. |
atse@12109 | 361 """ |
atse@12261 | 362 try: |
atse@12261 | 363 return xc.domain_getinfo() |
atse@12261 | 364 except RuntimeError, e: |
atse@12261 | 365 log.exception("Unable to get domain information.") |
atse@12261 | 366 return {} |
atse@12109 | 367 |
atse@12109 | 368 def _setDom0CPUCount(self): |
atse@12109 | 369 """Sets the number of VCPUs dom0 has. Retreived from the |
ewan@13444 | 370 Xend configuration, L{XendOptions}. |
atse@12109 | 371 |
atse@12109 | 372 @requires: Expects to be protected by domains_lock. |
atse@12109 | 373 @rtype: None |
atse@12109 | 374 """ |
atse@12109 | 375 dom0 = self.privilegedDomain() |
emellor@7438 | 376 |
emellor@7438 | 377 # get max number of vcpus to use for dom0 from config |
ewan@13444 | 378 target = int(xoptions.get_dom0_vcpus()) |
emellor@7438 | 379 log.debug("number of vcpus to use is %d", target) |
emellor@7438 | 380 |
emellor@7438 | 381 # target == 0 means use all processors |
emellor@7438 | 382 if target > 0: |
emellor@7454 | 383 dom0.setVCpuCount(target) |
cl349@5933 | 384 |
mjw@1661 | 385 |
ewan@13335 | 386 def _refresh(self, refresh_shutdown = True): |
atse@12109 | 387 """Refresh the domain list. Needs to be called when |
atse@12109 | 388 either xenstore has changed or when a method requires |
atse@12109 | 389 up to date information (like uptime, cputime stats). |
emellor@7269 | 390 |
ewan@12339 | 391 Expects to be protected by the domains_lock. |
ewan@12339 | 392 |
atse@12109 | 393 @rtype: None |
mjw@1667 | 394 """ |
ewan@12339 | 395 |
keir@16298 | 396 txn = xstransact() |
keir@16298 | 397 try: |
keir@16298 | 398 self._refreshTxn(txn, refresh_shutdown) |
keir@16298 | 399 txn.commit() |
keir@16298 | 400 except: |
keir@16298 | 401 txn.abort() |
keir@16298 | 402 raise |
keir@16298 | 403 |
keir@16298 | 404 def _refreshTxn(self, transaction, refresh_shutdown): |
ewan@12339 | 405 running = self._running_domains() |
atse@12524 | 406 # Add domains that are not already tracked but running in Xen, |
atse@12524 | 407 # and update domain state for those that are running and tracked. |
ewan@12339 | 408 for dom in running: |
ewan@12339 | 409 domid = dom['domid'] |
atse@12525 | 410 if domid in self.domains: |
keir@16298 | 411 self.domains[domid].update(dom, refresh_shutdown, transaction) |
atse@12524 | 412 elif domid not in self.domains and dom['dying'] != 1: |
atse@12524 | 413 try: |
atse@12524 | 414 new_dom = XendDomainInfo.recreate(dom, False) |
atse@12524 | 415 except VmError: |
atse@12524 | 416 log.exception("Unable to recreate domain") |
atse@12524 | 417 try: |
atse@12524 | 418 xc.domain_destroy(domid) |
atse@12524 | 419 except: |
atse@12524 | 420 log.exception("Hard destruction of domain failed: %d" % |
atse@12524 | 421 domid) |
ewan@12339 | 422 |
atse@12525 | 423 # update information for all running domains |
atse@12525 | 424 # - like cpu_time, status, dying, etc. |
atse@12525 | 425 # remove domains that are not running from active domain list. |
atse@12525 | 426 # The list might have changed by now, because the update call may |
atse@12525 | 427 # cause new domains to be added, if the domain has rebooted. We get |
atse@12525 | 428 # the list again. |
atse@12525 | 429 running = self._running_domains() |
atse@12525 | 430 running_domids = [d['domid'] for d in running if d['dying'] != 1] |
atse@12525 | 431 for domid, dom in self.domains.items(): |
atse@12525 | 432 if domid not in running_domids and domid != DOM0_ID: |
ewan@14525 | 433 self._remove_domain(dom, domid) |
atse@12525 | 434 |
atse@12525 | 435 |
Tim@13578 | 436 def add_domain(self, info): |
atse@12355 | 437 """Add a domain to the list of running domains |
atse@12109 | 438 |
atse@12109 | 439 @requires: Expects to be protected by the domains_lock. |
atse@12109 | 440 @param info: XendDomainInfo of a domain to be added. |
atse@12109 | 441 @type info: XendDomainInfo |
mjw@1782 | 442 """ |
atse@12254 | 443 log.debug("Adding Domain: %s" % info.getDomid()) |
atse@12254 | 444 self.domains[info.getDomid()] = info |
atse@12671 | 445 |
atse@12671 | 446 # update the managed domains with a new XendDomainInfo object |
atse@12671 | 447 # if we are keeping track of it. |
atse@12671 | 448 if info.get_uuid() in self.managed_domains: |
atse@12671 | 449 self._managed_domain_register(info) |
cl349@5374 | 450 |
Tim@13578 | 451 def remove_domain(self, info, domid = None): |
ewan@14525 | 452 """Remove the domain from the list of running domains, taking the |
ewan@14525 | 453 domains_lock first. |
ewan@14525 | 454 """ |
ewan@14525 | 455 self.domains_lock.acquire() |
ewan@14525 | 456 try: |
ewan@14525 | 457 self._remove_domain(info, domid) |
ewan@14525 | 458 finally: |
ewan@14525 | 459 self.domains_lock.release() |
ewan@14525 | 460 |
ewan@14525 | 461 def _remove_domain(self, info, domid = None): |
atse@12355 | 462 """Remove the domain from the list of running domains |
atse@12109 | 463 |
atse@12109 | 464 @requires: Expects to be protected by the domains_lock. |
atse@12109 | 465 @param info: XendDomainInfo of a domain to be removed. |
atse@12109 | 466 @type info: XendDomainInfo |
atse@12109 | 467 """ |
atse@12109 | 468 if info: |
atse@12254 | 469 if domid == None: |
atse@12254 | 470 domid = info.getDomid() |
mjw@1782 | 471 |
tom@14924 | 472 if info._stateGet() != DOM_STATE_HALTED: |
atse@12254 | 473 info.cleanupDomain() |
atse@12254 | 474 |
atse@12254 | 475 if domid in self.domains: |
atse@12254 | 476 del self.domains[domid] |
atse@12109 | 477 else: |
atse@12109 | 478 log.warning("Attempted to remove non-existent domain.") |
emellor@7210 | 479 |
emellor@7210 | 480 def restore_(self, config): |
emellor@7210 | 481 """Create a domain as part of the restore process. This is called |
atse@12109 | 482 only from L{XendCheckpoint}. |
emellor@7210 | 483 |
atse@12109 | 484 A restore request comes into XendDomain through L{domain_restore} |
atse@12109 | 485 or L{domain_restore_fd}. That request is |
emellor@7210 | 486 forwarded immediately to XendCheckpoint which, when it is ready, will |
emellor@7210 | 487 call this method. It is necessary to come through here rather than go |
atse@12109 | 488 directly to L{XendDomainInfo.restore} because we need to |
emellor@7210 | 489 serialise the domain creation process, but cannot lock |
emellor@7210 | 490 domain_restore_fd as a whole, otherwise we will deadlock waiting for |
emellor@7210 | 491 the old domain to die. |
atse@12109 | 492 |
atse@12109 | 493 @param config: Configuration of domain to restore |
atse@12109 | 494 @type config: SXP Object (eg. list of lists) |
emellor@7210 | 495 """ |
emellor@7210 | 496 self.domains_lock.acquire() |
emellor@7210 | 497 try: |
emellor@7210 | 498 dominfo = XendDomainInfo.restore(config) |
emellor@7210 | 499 return dominfo |
emellor@7210 | 500 finally: |
emellor@7210 | 501 self.domains_lock.release() |
emellor@7210 | 502 |
emellor@7022 | 503 |
emellor@7242 | 504 def domain_lookup(self, domid): |
atse@12109 | 505 """Look up given I{domid} in the list of managed and running |
atse@12109 | 506 domains. |
atse@12109 | 507 |
atse@12109 | 508 @note: Will cause a refresh before lookup up domains, for |
atse@12109 | 509 a version that does not need to re-read xenstore |
atse@12109 | 510 use L{domain_lookup_nr}. |
atse@12109 | 511 |
atse@12109 | 512 @param domid: Domain ID or Domain Name. |
atse@12109 | 513 @type domid: int or string |
atse@12109 | 514 @return: Found domain. |
atse@12109 | 515 @rtype: XendDomainInfo |
kfraser@14229 | 516 @raise XendInvalidDomain: If domain is not found. |
atse@12109 | 517 """ |
emellor@7205 | 518 self.domains_lock.acquire() |
emellor@7205 | 519 try: |
ewan@13335 | 520 self._refresh(refresh_shutdown = False) |
atse@12109 | 521 dom = self.domain_lookup_nr(domid) |
atse@12109 | 522 if not dom: |
ewan@14376 | 523 raise XendInvalidDomain(str(domid)) |
atse@12109 | 524 return dom |
emellor@7205 | 525 finally: |
emellor@7205 | 526 self.domains_lock.release() |
emellor@7205 | 527 |
emellor@7205 | 528 |
emellor@7242 | 529 def domain_lookup_nr(self, domid): |
atse@12109 | 530 """Look up given I{domid} in the list of managed and running |
atse@12109 | 531 domains. |
atse@12109 | 532 |
atse@12109 | 533 @param domid: Domain ID or Domain Name. |
atse@12109 | 534 @type domid: int or string |
atse@12109 | 535 @return: Found domain. |
atse@12109 | 536 @rtype: XendDomainInfo or None |
atse@12109 | 537 """ |
emellor@7205 | 538 self.domains_lock.acquire() |
emellor@7205 | 539 try: |
atse@12109 | 540 # lookup by name |
atse@12109 | 541 match = [dom for dom in self.domains.values() \ |
atse@12109 | 542 if dom.getName() == domid] |
atse@12109 | 543 if match: |
atse@12109 | 544 return match[0] |
atse@12109 | 545 |
atse@12354 | 546 match = [dom for dom in self.managed_domains.values() \ |
atse@12354 | 547 if dom.getName() == domid] |
atse@12354 | 548 if match: |
atse@12354 | 549 return match[0] |
atse@12354 | 550 |
atse@12109 | 551 # lookup by id |
atse@12109 | 552 try: |
atse@12254 | 553 if int(domid) in self.domains: |
atse@12254 | 554 return self.domains[int(domid)] |
atse@12254 | 555 except ValueError: |
atse@12109 | 556 pass |
atse@12109 | 557 |
atse@12354 | 558 # lookup by uuid for running domains |
atse@12354 | 559 match = [dom for dom in self.domains.values() \ |
atse@12354 | 560 if dom.get_uuid() == domid] |
atse@12354 | 561 if match: |
atse@12354 | 562 return match[0] |
atse@12354 | 563 |
atse@12354 | 564 # lookup by uuid for inactive managed domains |
atse@12354 | 565 if domid in self.managed_domains: |
atse@12354 | 566 return self.managed_domains[domid] |
atse@12354 | 567 |
atse@12109 | 568 return None |
atse@12109 | 569 finally: |
atse@12109 | 570 self.domains_lock.release() |
atse@12109 | 571 |
atse@12109 | 572 def privilegedDomain(self): |
atse@12109 | 573 """ Get the XendDomainInfo of a dom0 |
atse@12109 | 574 |
atse@12109 | 575 @rtype: XendDomainInfo |
atse@12109 | 576 """ |
atse@12109 | 577 self.domains_lock.acquire() |
atse@12109 | 578 try: |
atse@12254 | 579 return self.domains[DOM0_ID] |
atse@12109 | 580 finally: |
atse@12109 | 581 self.domains_lock.release() |
atse@12109 | 582 |
ewan@14636 | 583 def autostart_domains(self): |
ewan@14636 | 584 """ Autostart managed domains that are marked as such. """ |
ewan@14636 | 585 |
ewan@14636 | 586 need_starting = [] |
ewan@14636 | 587 |
ewan@14636 | 588 self.domains_lock.acquire() |
ewan@14636 | 589 try: |
ewan@14636 | 590 for dom_uuid, dom in self.managed_domains.items(): |
tom@14924 | 591 if dom and dom._stateGet() == DOM_STATE_HALTED: |
ewan@14636 | 592 on_xend_start = dom.info.get('on_xend_start', 'ignore') |
ewan@14636 | 593 auto_power_on = dom.info.get('auto_power_on', False) |
ewan@14636 | 594 should_start = (on_xend_start == 'start') or auto_power_on |
ewan@14636 | 595 if should_start: |
ewan@14636 | 596 need_starting.append(dom_uuid) |
ewan@14636 | 597 finally: |
ewan@14636 | 598 self.domains_lock.release() |
ewan@14636 | 599 |
ewan@14636 | 600 for dom_uuid in need_starting: |
ewan@14636 | 601 self.domain_start(dom_uuid, False) |
ewan@14636 | 602 |
atse@12109 | 603 def cleanup_domains(self): |
atse@12109 | 604 """Clean up domains that are marked as autostop. |
atse@12109 | 605 Should be called when Xend goes down. This is currently |
atse@12109 | 606 called from L{xen.xend.servers.XMLRPCServer}. |
atse@12109 | 607 |
atse@12109 | 608 """ |
atse@12109 | 609 log.debug('cleanup_domains') |
atse@12109 | 610 self.domains_lock.acquire() |
atse@12109 | 611 try: |
atse@12109 | 612 for dom in self.domains.values(): |
atse@12109 | 613 if dom.getName() == DOM0_NAME: |
atse@12109 | 614 continue |
atse@12109 | 615 |
keir@16625 | 616 try: |
keir@16625 | 617 if dom._stateGet() == DOM_STATE_RUNNING: |
keir@16625 | 618 shutdownAction = dom.info.get('on_xend_stop', 'ignore') |
keir@16625 | 619 if shutdownAction == 'shutdown': |
keir@16625 | 620 log.debug('Shutting down domain: %s' % dom.getName()) |
keir@16625 | 621 dom.shutdown("poweroff") |
keir@16625 | 622 elif shutdownAction == 'suspend': |
keir@16625 | 623 self.domain_suspend(dom.getName()) |
keir@16625 | 624 else: |
keir@16625 | 625 log.debug('Domain %s continues to run.' % dom.getName()) |
keir@16625 | 626 except: |
keir@16625 | 627 log.exception('Domain %s failed to %s.' % \ |
keir@16625 | 628 (dom.getName(), shutdownAction)) |
emellor@7205 | 629 finally: |
emellor@7205 | 630 self.domains_lock.release() |
emellor@7205 | 631 |
emellor@7205 | 632 |
atse@12109 | 633 |
atse@12109 | 634 # ---------------------------------------------------------------- |
atse@12109 | 635 # Xen API |
atse@12109 | 636 |
atse@12109 | 637 |
atse@12109 | 638 def set_allow_new_domains(self, allow_new_domains): |
atse@12109 | 639 self._allow_new_domains = allow_new_domains |
atse@12109 | 640 |
atse@12109 | 641 def allow_new_domains(self): |
atse@12109 | 642 return self._allow_new_domains |
atse@12109 | 643 |
atse@12109 | 644 def get_domain_refs(self): |
atse@12109 | 645 result = [] |
atse@12109 | 646 try: |
atse@12109 | 647 self.domains_lock.acquire() |
atse@12125 | 648 result = [d.get_uuid() for d in self.domains.values()] |
jfehlig@13044 | 649 for d in self.managed_domains.keys(): |
jfehlig@13044 | 650 if d not in result: |
jfehlig@13044 | 651 result.append(d) |
atse@12254 | 652 return result |
atse@12109 | 653 finally: |
atse@12109 | 654 self.domains_lock.release() |
atse@12109 | 655 |
ewan@13206 | 656 def get_all_vms(self): |
ewan@13206 | 657 self.domains_lock.acquire() |
ewan@13206 | 658 try: |
ewan@13206 | 659 result = self.domains.values() |
ewan@13206 | 660 result += [x for x in self.managed_domains.values() if |
ewan@13206 | 661 x not in result] |
ewan@13206 | 662 return result |
ewan@13206 | 663 finally: |
ewan@13206 | 664 self.domains_lock.release() |
ewan@13206 | 665 |
atse@12109 | 666 def get_vm_by_uuid(self, vm_uuid): |
emellor@7205 | 667 self.domains_lock.acquire() |
emellor@7205 | 668 try: |
atse@12254 | 669 for dom in self.domains.values(): |
atse@12254 | 670 if dom.get_uuid() == vm_uuid: |
atse@12254 | 671 return dom |
atse@12254 | 672 |
atse@12254 | 673 if vm_uuid in self.managed_domains: |
atse@12254 | 674 return self.managed_domains[vm_uuid] |
atse@12254 | 675 |
atse@12109 | 676 return None |
emellor@7205 | 677 finally: |
emellor@7205 | 678 self.domains_lock.release() |
emellor@7205 | 679 |
atse@12135 | 680 def get_vm_with_dev_uuid(self, klass, dev_uuid): |
atse@12135 | 681 self.domains_lock.acquire() |
atse@12109 | 682 try: |
atse@12254 | 683 for dom in self.domains.values() + self.managed_domains.values(): |
atse@12135 | 684 if dom.has_device(klass, dev_uuid): |
atse@12135 | 685 return dom |
atse@12135 | 686 return None |
atse@12135 | 687 finally: |
atse@12135 | 688 self.domains_lock.release() |
cl349@5372 | 689 |
atse@12135 | 690 def get_dev_property_by_uuid(self, klass, dev_uuid, field): |
atse@13594 | 691 value = None |
atse@12135 | 692 self.domains_lock.acquire() |
ewan@14574 | 693 |
atse@12135 | 694 try: |
ewan@14574 | 695 try: |
ewan@14574 | 696 dom = self.get_vm_with_dev_uuid(klass, dev_uuid) |
ewan@14574 | 697 if dom: |
ewan@14574 | 698 value = dom.get_dev_property(klass, dev_uuid, field) |
ewan@14574 | 699 except ValueError, e: |
ewan@14574 | 700 pass |
ewan@14574 | 701 finally: |
ewan@14574 | 702 self.domains_lock.release() |
atse@12109 | 703 |
atse@13594 | 704 return value |
atse@12109 | 705 |
kfraser@15656 | 706 def set_dev_property_by_uuid(self, klass, dev_uuid, field, value, |
kfraser@15656 | 707 old_val = None): |
kfraser@15656 | 708 rc = True |
kfraser@15656 | 709 self.domains_lock.acquire() |
kfraser@15656 | 710 |
kfraser@15656 | 711 try: |
kfraser@15656 | 712 try: |
kfraser@15656 | 713 dom = self.get_vm_with_dev_uuid(klass, dev_uuid) |
kfraser@15656 | 714 if dom: |
kfraser@15656 | 715 o_val = dom.get_dev_property(klass, dev_uuid, field) |
kfraser@15656 | 716 log.info("o_val=%s, old_val=%s" % (o_val, old_val)) |
kfraser@15656 | 717 if old_val and old_val != o_val: |
kfraser@15656 | 718 return False |
kfraser@15656 | 719 |
kfraser@15656 | 720 dom.set_dev_property(klass, dev_uuid, field, value) |
kfraser@15656 | 721 self.managed_config_save(dom) |
kfraser@15656 | 722 except ValueError, e: |
kfraser@15656 | 723 pass |
kfraser@15656 | 724 finally: |
kfraser@15656 | 725 self.domains_lock.release() |
kfraser@15656 | 726 |
kfraser@15656 | 727 return rc |
kfraser@15656 | 728 |
atse@12109 | 729 def is_valid_vm(self, vm_ref): |
atse@12109 | 730 return (self.get_vm_by_uuid(vm_ref) != None) |
atse@12109 | 731 |
atse@12109 | 732 def is_valid_dev(self, klass, dev_uuid): |
atse@12135 | 733 return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None) |
atse@12109 | 734 |
ewan@12639 | 735 def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs): |
ewan@12775 | 736 dom = self.uuid_to_dom(vm_uuid) |
ewan@12775 | 737 fn(dom, *args, **kwargs) |
ewan@12775 | 738 |
ewan@12775 | 739 def uuid_to_dom(self, vm_uuid): |
emellor@7205 | 740 self.domains_lock.acquire() |
emellor@7205 | 741 try: |
atse@12254 | 742 for domid, dom in self.domains.items(): |
ewan@12775 | 743 if dom.get_uuid() == vm_uuid: |
ewan@12775 | 744 return domid |
atse@12254 | 745 |
atse@12254 | 746 if vm_uuid in self.managed_domains: |
atse@12254 | 747 domid = self.managed_domains[vm_uuid].getDomid() |
ewan@12775 | 748 if domid is None: |
ewan@12775 | 749 return self.managed_domains[vm_uuid].getName() |
ewan@12775 | 750 else: |
ewan@12775 | 751 return domid |
atse@12254 | 752 |
ewan@14490 | 753 raise XendInvalidDomain(vm_uuid) |
atse@12109 | 754 finally: |
atse@12109 | 755 self.domains_lock.release() |
atse@12109 | 756 |
emellor@7205 | 757 |
atse@12109 | 758 def create_domain(self, xenapi_vm): |
atse@12109 | 759 self.domains_lock.acquire() |
atse@12109 | 760 try: |
atse@12109 | 761 try: |
ewan@14525 | 762 xeninfo = XendConfig.XendConfig(xapi = xenapi_vm) |
atse@12109 | 763 dominfo = XendDomainInfo.createDormant(xeninfo) |
atse@12109 | 764 log.debug("Creating new managed domain: %s: %s" % |
atse@12109 | 765 (dominfo.getName(), dominfo.get_uuid())) |
atse@12254 | 766 self._managed_domain_register(dominfo) |
atse@12109 | 767 self.managed_config_save(dominfo) |
atse@12109 | 768 return dominfo.get_uuid() |
atse@12109 | 769 except XendError, e: |
atse@12109 | 770 raise |
atse@12109 | 771 except Exception, e: |
atse@12109 | 772 raise XendError(str(e)) |
atse@12109 | 773 finally: |
atse@12109 | 774 self.domains_lock.release() |
atse@12109 | 775 |
atse@12109 | 776 def rename_domain(self, dom, new_name): |
atse@12109 | 777 self.domains_lock.acquire() |
atse@12109 | 778 try: |
atse@12109 | 779 old_name = dom.getName() |
atse@12109 | 780 dom.setName(new_name) |
atse@12109 | 781 |
atse@12109 | 782 finally: |
atse@12109 | 783 self.domains_lock.release() |
atse@12109 | 784 |
atse@12109 | 785 |
atse@12109 | 786 # |
atse@12109 | 787 # End of Xen API |
atse@12109 | 788 # ---------------------------------------------------------------- |
atse@12109 | 789 |
atse@12109 | 790 # ------------------------------------------------------------ |
atse@12109 | 791 # Xen Legacy API |
atse@12109 | 792 |
ewan@12622 | 793 def list(self, state = DOM_STATE_RUNNING): |
atse@12109 | 794 """Get list of domain objects. |
atse@12109 | 795 |
ewan@12622 | 796 @param: the state in which the VMs should be -- one of the |
ewan@12622 | 797 DOM_STATE_XYZ constants, or the corresponding name, or 'all'. |
atse@12109 | 798 @return: domains |
atse@12109 | 799 @rtype: list of XendDomainInfo |
atse@12109 | 800 """ |
ewan@12622 | 801 if type(state) == int: |
ewan@12622 | 802 state = POWER_STATE_NAMES[state] |
ewan@12622 | 803 state = state.lower() |
ewan@12622 | 804 |
atse@12109 | 805 self.domains_lock.acquire() |
atse@12109 | 806 try: |
ewan@13335 | 807 self._refresh(refresh_shutdown = False) |
atse@12254 | 808 |
atse@12254 | 809 # active domains |
atse@12254 | 810 active_domains = self.domains.values() |
atse@12254 | 811 active_uuids = [d.get_uuid() for d in active_domains] |
atse@12254 | 812 |
atse@12254 | 813 # inactive domains |
atse@12254 | 814 inactive_domains = [] |
atse@12254 | 815 for dom_uuid, dom in self.managed_domains.items(): |
atse@12254 | 816 if dom_uuid not in active_uuids: |
atse@12254 | 817 inactive_domains.append(dom) |
atse@12254 | 818 |
ewan@12622 | 819 if state == POWER_STATE_ALL: |
ewan@12622 | 820 return active_domains + inactive_domains |
ewan@12622 | 821 else: |
ewan@12622 | 822 return filter(lambda x: |
tom@14924 | 823 POWER_STATE_NAMES[x._stateGet()].lower() == state, |
ewan@12622 | 824 active_domains + inactive_domains) |
emellor@7205 | 825 finally: |
emellor@7205 | 826 self.domains_lock.release() |
emellor@7205 | 827 |
emellor@7205 | 828 |
ewan@12622 | 829 def list_sorted(self, state = DOM_STATE_RUNNING): |
atse@12109 | 830 """Get list of domain objects, sorted by name. |
atse@12109 | 831 |
ewan@12622 | 832 @param: the state in which the VMs should be -- one of the |
ewan@12622 | 833 DOM_STATE_XYZ constants, or the corresponding name, or 'all'. |
atse@12109 | 834 @return: domain objects |
atse@12109 | 835 @rtype: list of XendDomainInfo |
atse@12109 | 836 """ |
ewan@12622 | 837 doms = self.list(state) |
atse@12109 | 838 doms.sort(lambda x, y: cmp(x.getName(), y.getName())) |
atse@12109 | 839 return doms |
atse@12109 | 840 |
ewan@12622 | 841 def list_names(self, state = DOM_STATE_RUNNING): |
atse@12109 | 842 """Get list of domain names. |
atse@12109 | 843 |
ewan@12622 | 844 @param: the state in which the VMs should be -- one of the |
ewan@12622 | 845 DOM_STATE_XYZ constants, or the corresponding name, or 'all'. |
atse@12109 | 846 @return: domain names |
atse@12109 | 847 @rtype: list of strings. |
atse@12109 | 848 """ |
ewan@12622 | 849 return [d.getName() for d in self.list_sorted(state)] |
atse@12109 | 850 |
atse@12109 | 851 def domain_suspend(self, domname): |
atse@12109 | 852 """Suspends a domain that is persistently managed by Xend |
atse@12109 | 853 |
atse@12109 | 854 @param domname: Domain Name |
atse@12109 | 855 @type domname: string |
atse@12109 | 856 @rtype: None |
atse@12109 | 857 @raise XendError: Failure during checkpointing. |
atse@12109 | 858 """ |
atse@12109 | 859 |
atse@12109 | 860 try: |
atse@12109 | 861 dominfo = self.domain_lookup_nr(domname) |
atse@12109 | 862 if not dominfo: |
atse@12109 | 863 raise XendInvalidDomain(domname) |
atse@12109 | 864 |
atse@12109 | 865 if dominfo.getDomid() == DOM0_ID: |
atse@12109 | 866 raise XendError("Cannot save privileged domain %s" % domname) |
atse@12109 | 867 |
tom@14924 | 868 if dominfo._stateGet() != DOM_STATE_RUNNING: |
ewan@13196 | 869 raise VMBadState("Domain is not running", |
ewan@13196 | 870 POWER_STATE_NAMES[DOM_STATE_RUNNING], |
tom@14924 | 871 POWER_STATE_NAMES[dominfo._stateGet()]) |
atse@12109 | 872 |
ewan@12605 | 873 dom_uuid = dominfo.get_uuid() |
ewan@12605 | 874 |
ewan@12605 | 875 if not os.path.exists(self._managed_config_path(dom_uuid)): |
atse@12109 | 876 raise XendError("Domain is not managed by Xend lifecycle " + |
atse@12109 | 877 "support.") |
ewan@12605 | 878 |
ewan@12605 | 879 path = self._managed_check_point_path(dom_uuid) |
Tim@13863 | 880 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC |
Tim@13863 | 881 if hasattr(os, "O_LARGEFILE"): |
Tim@13863 | 882 oflags |= os.O_LARGEFILE |
Tim@13863 | 883 fd = os.open(path, oflags) |
atse@12109 | 884 try: |
atse@12109 | 885 # For now we don't support 'live checkpoint' |
atse@12109 | 886 XendCheckpoint.save(fd, dominfo, False, False, path) |
atse@12109 | 887 finally: |
atse@12109 | 888 os.close(fd) |
atse@12109 | 889 except OSError, ex: |
atse@12109 | 890 raise XendError("can't write guest state file %s: %s" % |
atse@12109 | 891 (path, ex[1])) |
atse@12109 | 892 |
ewan@12639 | 893 def domain_resume(self, domname, start_paused = False): |
atse@12109 | 894 """Resumes a domain that is persistently managed by Xend. |
atse@12109 | 895 |
atse@12109 | 896 @param domname: Domain Name |
atse@12109 | 897 @type domname: string |
atse@12109 | 898 @rtype: None |
atse@12109 | 899 @raise XendError: If failed to restore. |
atse@12109 | 900 """ |
ewan@12605 | 901 self.domains_lock.acquire() |
atse@12109 | 902 try: |
ewan@12605 | 903 try: |
keir@16144 | 904 fd = None |
ewan@12605 | 905 dominfo = self.domain_lookup_nr(domname) |
atse@12109 | 906 |
ewan@12605 | 907 if not dominfo: |
ewan@12605 | 908 raise XendInvalidDomain(domname) |
atse@12109 | 909 |
ewan@12605 | 910 if dominfo.getDomid() == DOM0_ID: |
ewan@12605 | 911 raise XendError("Cannot save privileged domain %s" % domname) |
ewan@12605 | 912 |
tom@14924 | 913 if dominfo._stateGet() != XEN_API_VM_POWER_STATE_SUSPENDED: |
tom@14924 | 914 raise XendError("Cannot resume domain that is not suspended.") |
atse@12109 | 915 |
keir@16675 | 916 dominfo.setResume(True) |
keir@16675 | 917 |
ewan@12605 | 918 dom_uuid = dominfo.get_uuid() |
ewan@12605 | 919 chkpath = self._managed_check_point_path(dom_uuid) |
ewan@12605 | 920 if not os.path.exists(chkpath): |
ewan@12605 | 921 raise XendError("Domain was not suspended by Xend") |
atse@12109 | 922 |
ewan@12605 | 923 # Restore that replaces the existing XendDomainInfo |
ewan@12605 | 924 try: |
tom@14924 | 925 log.debug('Current DomainInfo state: %d' % dominfo._stateGet()) |
Tim@13863 | 926 oflags = os.O_RDONLY |
Tim@13863 | 927 if hasattr(os, "O_LARGEFILE"): |
Tim@13863 | 928 oflags |= os.O_LARGEFILE |
keir@16144 | 929 fd = os.open(chkpath, oflags) |
ewan@12605 | 930 XendCheckpoint.restore(self, |
keir@16144 | 931 fd, |
ewan@12639 | 932 dominfo, |
ewan@12639 | 933 paused = start_paused) |
ewan@12605 | 934 os.unlink(chkpath) |
ewan@12605 | 935 except OSError, ex: |
ewan@12605 | 936 raise XendError("Failed to read stored checkpoint file") |
ewan@12605 | 937 except IOError, ex: |
ewan@12605 | 938 raise XendError("Failed to delete checkpoint file") |
ewan@12605 | 939 except Exception, ex: |
ewan@12605 | 940 log.exception("Exception occurred when resuming") |
ewan@12605 | 941 raise XendError("Error occurred when resuming: %s" % str(ex)) |
ewan@12605 | 942 finally: |
keir@16144 | 943 if fd is not None: |
keir@16144 | 944 os.close(fd) |
ewan@12605 | 945 self.domains_lock.release() |
atse@12109 | 946 |
atse@12109 | 947 |
atse@12109 | 948 def domain_create(self, config): |
atse@12109 | 949 """Create a domain from a configuration. |
atse@12109 | 950 |
atse@12109 | 951 @param config: configuration |
atse@12109 | 952 @type config: SXP Object (list of lists) |
atse@12109 | 953 @rtype: XendDomainInfo |
atse@12109 | 954 """ |
emellor@7205 | 955 self.domains_lock.acquire() |
emellor@7205 | 956 try: |
ewan@12336 | 957 self._refresh() |
ewan@12336 | 958 |
atse@12109 | 959 dominfo = XendDomainInfo.create(config) |
atse@12109 | 960 return dominfo |
emellor@7205 | 961 finally: |
emellor@7205 | 962 self.domains_lock.release() |
mjw@2013 | 963 |
emellor@7201 | 964 |
ewan@13166 | 965 def domain_create_from_dict(self, config_dict): |
ewan@13166 | 966 """Create a domain from a configuration dictionary. |
ewan@13166 | 967 |
ewan@13166 | 968 @param config_dict: configuration |
ewan@13166 | 969 @rtype: XendDomainInfo |
ewan@13166 | 970 """ |
ewan@13166 | 971 self.domains_lock.acquire() |
ewan@13166 | 972 try: |
ewan@13166 | 973 self._refresh() |
ewan@13166 | 974 |
ewan@13166 | 975 dominfo = XendDomainInfo.create_from_dict(config_dict) |
ewan@13166 | 976 return dominfo |
ewan@13166 | 977 finally: |
ewan@13166 | 978 self.domains_lock.release() |
ewan@13166 | 979 |
ewan@13166 | 980 |
atse@12109 | 981 def domain_new(self, config): |
atse@12109 | 982 """Create a domain from a configuration but do not start it. |
atse@12109 | 983 |
atse@12109 | 984 @param config: configuration |
atse@12109 | 985 @type config: SXP Object (list of lists) |
atse@12109 | 986 @rtype: XendDomainInfo |
atse@12109 | 987 """ |
emellor@7205 | 988 self.domains_lock.acquire() |
emellor@7205 | 989 try: |
atse@12109 | 990 try: |
ewan@14525 | 991 domconfig = XendConfig.XendConfig(sxp_obj = config) |
atse@12671 | 992 dominfo = XendDomainInfo.createDormant(domconfig) |
atse@12109 | 993 log.debug("Creating new managed domain: %s" % |
atse@12109 | 994 dominfo.getName()) |
atse@12254 | 995 self._managed_domain_register(dominfo) |
atse@12109 | 996 self.managed_config_save(dominfo) |
atse@12109 | 997 # no return value because it isn't meaningful for client |
atse@12109 | 998 except XendError, e: |
atse@12109 | 999 raise |
atse@12109 | 1000 except Exception, e: |
atse@12109 | 1001 raise XendError(str(e)) |
emellor@7205 | 1002 finally: |
emellor@7205 | 1003 self.domains_lock.release() |
emellor@7201 | 1004 |
ewan@12639 | 1005 def domain_start(self, domid, start_paused = True): |
atse@12109 | 1006 """Start a managed domain |
atse@12109 | 1007 |
atse@12109 | 1008 @require: Domain must not be running. |
atse@12109 | 1009 @param domid: Domain name or domain ID. |
atse@12109 | 1010 @type domid: string or int |
atse@12109 | 1011 @rtype: None |
atse@12109 | 1012 @raise XendError: If domain is still running |
atse@12109 | 1013 @rtype: None |
atse@12109 | 1014 """ |
atse@12109 | 1015 self.domains_lock.acquire() |
atse@12109 | 1016 try: |
ewan@12336 | 1017 self._refresh() |
ewan@12336 | 1018 |
atse@12109 | 1019 dominfo = self.domain_lookup_nr(domid) |
atse@12109 | 1020 if not dominfo: |
atse@12109 | 1021 raise XendInvalidDomain(str(domid)) |
atse@12109 | 1022 |
tom@14924 | 1023 if dominfo._stateGet() != DOM_STATE_HALTED: |
ewan@13196 | 1024 raise VMBadState("Domain is already running", |
ewan@13196 | 1025 POWER_STATE_NAMES[DOM_STATE_HALTED], |
tom@14924 | 1026 POWER_STATE_NAMES[dominfo._stateGet()]) |
atse@12109 | 1027 |
ewan@12640 | 1028 dominfo.start(is_managed = True) |
atse@12109 | 1029 finally: |
atse@12109 | 1030 self.domains_lock.release() |
kfraser@15191 | 1031 |
kfraser@15191 | 1032 try: |
kfraser@15191 | 1033 dominfo.waitForDevices() |
kfraser@15191 | 1034 except Exception, ex: |
kfraser@15191 | 1035 log.warn("Failed to setup devices for " + str(dominfo) + ": " + str(ex)) |
kfraser@15191 | 1036 dominfo.destroy() |
kfraser@15191 | 1037 raise |
kfraser@15191 | 1038 |
ewan@12640 | 1039 if not start_paused: |
ewan@12640 | 1040 dominfo.unpause() |
atse@12109 | 1041 |
atse@12109 | 1042 def domain_delete(self, domid): |
atse@12254 | 1043 """Remove a managed domain from database |
atse@12109 | 1044 |
atse@12109 | 1045 @require: Domain must not be running. |
atse@12109 | 1046 @param domid: Domain name or domain ID. |
atse@12109 | 1047 @type domid: string or int |
atse@12109 | 1048 @rtype: None |
atse@12109 | 1049 @raise XendError: If domain is still running |
atse@12109 | 1050 """ |
atse@12109 | 1051 self.domains_lock.acquire() |
atse@12109 | 1052 try: |
atse@12109 | 1053 try: |
atse@12109 | 1054 dominfo = self.domain_lookup_nr(domid) |
atse@12109 | 1055 if not dominfo: |
atse@12109 | 1056 raise XendInvalidDomain(str(domid)) |
atse@12109 | 1057 |
tom@14924 | 1058 if dominfo._stateGet() != XEN_API_VM_POWER_STATE_HALTED: |
tom@14924 | 1059 raise VMBadState("Domain is not halted.", |
ewan@13196 | 1060 POWER_STATE_NAMES[DOM_STATE_HALTED], |
tom@14924 | 1061 POWER_STATE_NAMES[dominfo._stateGet()]) |
ewan@14525 | 1062 |
ewan@14525 | 1063 self._domain_delete_by_info(dominfo) |
atse@12109 | 1064 except Exception, ex: |
atse@12109 | 1065 raise XendError(str(ex)) |
atse@12109 | 1066 finally: |
atse@12109 | 1067 self.domains_lock.release() |
ewan@14525 | 1068 |
ewan@14525 | 1069 |
ewan@14525 | 1070 def domain_delete_by_dominfo(self, dominfo): |
ewan@14525 | 1071 """Only for use by XendDomainInfo. |
ewan@14525 | 1072 """ |
ewan@14525 | 1073 self.domains_lock.acquire() |
ewan@14525 | 1074 try: |
ewan@14525 | 1075 self._domain_delete_by_info(dominfo) |
ewan@14525 | 1076 finally: |
ewan@14525 | 1077 self.domains_lock.release() |
ewan@14525 | 1078 |
ewan@14525 | 1079 |
ewan@14525 | 1080 def _domain_delete_by_info(self, dominfo): |
ewan@14525 | 1081 """Expects to be protected by domains_lock. |
ewan@14525 | 1082 """ |
ewan@14525 | 1083 log.info("Domain %s (%s) deleted." % |
ewan@14525 | 1084 (dominfo.getName(), dominfo.info.get('uuid'))) |
ewan@14525 | 1085 |
kfraser@15913 | 1086 dominfo.metrics.destroy() |
ewan@14525 | 1087 self._managed_domain_unregister(dominfo) |
ewan@14525 | 1088 self._remove_domain(dominfo) |
ewan@14525 | 1089 XendDevices.destroy_device_state(dominfo) |
ewan@14525 | 1090 |
atse@12109 | 1091 |
atse@12109 | 1092 def domain_configure(self, config): |
atse@12109 | 1093 """Configure an existing domain. |
atse@12109 | 1094 |
atse@12109 | 1095 @param vmconfig: vm configuration |
atse@12109 | 1096 @type vmconfig: SXP Object (list of lists) |
atse@12109 | 1097 @todo: Not implemented |
atse@12109 | 1098 """ |
atse@12109 | 1099 # !!! |
atse@12109 | 1100 raise XendError("Unsupported") |
atse@12109 | 1101 |
brendan@12559 | 1102 def domain_restore(self, src, paused=False): |
atse@12109 | 1103 """Restore a domain from file. |
kfraser@10690 | 1104 |
atse@12109 | 1105 @param src: filename of checkpoint file to restore from |
atse@12109 | 1106 @type src: string |
atse@12109 | 1107 @return: Restored domain |
atse@12109 | 1108 @rtype: XendDomainInfo |
atse@12109 | 1109 @raise XendError: Failure to restore domain |
atse@12109 | 1110 """ |
atse@12109 | 1111 try: |
Tim@13863 | 1112 oflags = os.O_RDONLY |
Tim@13863 | 1113 if hasattr(os, "O_LARGEFILE"): |
Tim@13863 | 1114 oflags |= os.O_LARGEFILE |
Tim@13863 | 1115 fd = os.open(src, oflags) |
atse@12109 | 1116 try: |
brendan@12559 | 1117 return self.domain_restore_fd(fd, paused=paused) |
atse@12109 | 1118 finally: |
atse@12109 | 1119 os.close(fd) |
atse@12109 | 1120 except OSError, ex: |
atse@12109 | 1121 raise XendError("can't read guest state file %s: %s" % |
atse@12109 | 1122 (src, ex[1])) |
atse@12109 | 1123 |
brendan@12559 | 1124 def domain_restore_fd(self, fd, paused=False): |
atse@12109 | 1125 """Restore a domain from the given file descriptor. |
atse@12109 | 1126 |
atse@12109 | 1127 @param fd: file descriptor of the checkpoint file |
atse@12109 | 1128 @type fd: File object |
atse@12109 | 1129 @rtype: XendDomainInfo |
atse@12109 | 1130 @raise XendError: if failed to restore |
atse@12109 | 1131 """ |
kfraser@10690 | 1132 |
mjw@2023 | 1133 try: |
brendan@12559 | 1134 return XendCheckpoint.restore(self, fd, paused=paused) |
kfraser@15205 | 1135 except XendError, e: |
kfraser@15205 | 1136 log.exception("Restore failed") |
kfraser@15205 | 1137 raise |
atse@12109 | 1138 except: |
atse@12109 | 1139 # I don't really want to log this exception here, but the error |
atse@12109 | 1140 # handling in the relocation-socket handling code (relocate.py) is |
atse@12109 | 1141 # poor, so we need to log this for debugging. |
atse@12109 | 1142 log.exception("Restore failed") |
atse@12109 | 1143 raise XendError("Restore failed") |
atse@12109 | 1144 |
atse@12109 | 1145 def domain_unpause(self, domid): |
atse@12109 | 1146 """Unpause domain execution. |
atse@12109 | 1147 |
atse@12109 | 1148 @param domid: Domain ID or Name |
atse@12109 | 1149 @type domid: int or string. |
atse@12109 | 1150 @rtype: None |
atse@12109 | 1151 @raise XendError: Failed to unpause |
atse@12109 | 1152 @raise XendInvalidDomain: Domain is not valid |
atse@12109 | 1153 """ |
atse@12109 | 1154 try: |
atse@12109 | 1155 dominfo = self.domain_lookup_nr(domid) |
atse@12109 | 1156 if not dominfo: |
atse@12109 | 1157 raise XendInvalidDomain(str(domid)) |
ewan@12499 | 1158 if dominfo.getDomid() == DOM0_ID: |
ewan@12499 | 1159 raise XendError("Cannot unpause privileged domain %s" % domid) |
kfraser@15557 | 1160 if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING): |
kfraser@15557 | 1161 raise VMBadState("Domain '%s' is not started" % domid, |
kfraser@15557 | 1162 POWER_STATE_NAMES[DOM_STATE_PAUSED], |
kfraser@15557 | 1163 POWER_STATE_NAMES[dominfo._stateGet()]) |
emellor@7244 | 1164 log.info("Domain %s (%d) unpaused.", dominfo.getName(), |
atse@12109 | 1165 int(dominfo.getDomid())) |
atse@12109 | 1166 dominfo.unpause() |
atse@12109 | 1167 except XendInvalidDomain: |
atse@12109 | 1168 log.exception("domain_unpause") |
atse@12109 | 1169 raise |
mjw@2023 | 1170 except Exception, ex: |
atse@12109 | 1171 log.exception("domain_unpause") |
mjw@2023 | 1172 raise XendError(str(ex)) |
emellor@7244 | 1173 |
kfraser@15926 | 1174 def domain_pause(self, domid, state=False): |
atse@12109 | 1175 """Pause domain execution. |
kfraser@10690 | 1176 |
atse@12109 | 1177 @param domid: Domain ID or Name |
atse@12109 | 1178 @type domid: int or string. |
kfraser@15926 | 1179 @keyword state: If True, will return the domain state before pause |
kfraser@15926 | 1180 @type state: bool |
kfraser@15926 | 1181 @rtype: int if state is True |
kfraser@15926 | 1182 @return: Domain state (DOM_STATE_*) |
kfraser@15926 | 1183 @rtype: None if state is False |
atse@12109 | 1184 @raise XendError: Failed to pause |
atse@12109 | 1185 @raise XendInvalidDomain: Domain is not valid |
atse@12109 | 1186 """ |
mjw@2023 | 1187 try: |
atse@12109 | 1188 dominfo = self.domain_lookup_nr(domid) |
atse@12109 | 1189 if not dominfo: |
atse@12109 | 1190 raise XendInvalidDomain(str(domid)) |
ewan@12499 | 1191 if dominfo.getDomid() == DOM0_ID: |
ewan@12499 | 1192 raise XendError("Cannot pause privileged domain %s" % domid) |
kfraser@15926 | 1193 ds = dominfo._stateGet() |
kfraser@15926 | 1194 if ds not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
kfraser@15557 | 1195 raise VMBadState("Domain '%s' is not started" % domid, |
kfraser@15557 | 1196 POWER_STATE_NAMES[DOM_STATE_RUNNING], |
kfraser@15926 | 1197 POWER_STATE_NAMES[ds]) |
emellor@7244 | 1198 log.info("Domain %s (%d) paused.", dominfo.getName(), |
atse@12109 | 1199 int(dominfo.getDomid())) |
atse@12109 | 1200 dominfo.pause() |
kfraser@15926 | 1201 if state: |
kfraser@15926 | 1202 return ds |
atse@12109 | 1203 except XendInvalidDomain: |
atse@12109 | 1204 log.exception("domain_pause") |
atse@12109 | 1205 raise |
mjw@2023 | 1206 except Exception, ex: |
atse@12109 | 1207 log.exception("domain_pause") |
mjw@2023 | 1208 raise XendError(str(ex)) |
emellor@7099 | 1209 |
root@11511 | 1210 def domain_dump(self, domid, filename, live, crash): |
root@11511 | 1211 """Dump domain core.""" |
root@11511 | 1212 |
atse@12172 | 1213 dominfo = self.domain_lookup_nr(domid) |
root@11511 | 1214 if not dominfo: |
root@11511 | 1215 raise XendInvalidDomain(str(domid)) |
root@11511 | 1216 |
atse@12172 | 1217 if dominfo.getDomid() == DOM0_ID: |
root@11511 | 1218 raise XendError("Cannot dump core for privileged domain %s" % domid) |
kfraser@15569 | 1219 if dominfo._stateGet() not in (DOM_STATE_PAUSED, DOM_STATE_RUNNING): |
kfraser@15569 | 1220 raise VMBadState("Domain '%s' is not started" % domid, |
kfraser@15569 | 1221 POWER_STATE_NAMES[DOM_STATE_PAUSED], |
kfraser@15569 | 1222 POWER_STATE_NAMES[dominfo._stateGet()]) |
root@11511 | 1223 |
root@11511 | 1224 try: |
atse@12172 | 1225 log.info("Domain core dump requested for domain %s (%d) " |
atse@12172 | 1226 "live=%d crash=%d.", |
root@11511 | 1227 dominfo.getName(), dominfo.getDomid(), live, crash) |
root@11511 | 1228 return dominfo.dumpCore(filename) |
root@11511 | 1229 except Exception, ex: |
root@11511 | 1230 raise XendError(str(ex)) |
emellor@7099 | 1231 |
emellor@7202 | 1232 def domain_destroy(self, domid): |
atse@12109 | 1233 """Terminate domain immediately. |
emellor@7031 | 1234 |
atse@12109 | 1235 @param domid: Domain ID or Name |
atse@12109 | 1236 @type domid: int or string. |
atse@12109 | 1237 @rtype: None |
atse@12109 | 1238 @raise XendError: Failed to destroy |
atse@12109 | 1239 @raise XendInvalidDomain: Domain is not valid |
atse@12109 | 1240 """ |
atse@12109 | 1241 |
atse@12109 | 1242 dominfo = self.domain_lookup_nr(domid) |
atse@12109 | 1243 if dominfo and dominfo.getDomid() == DOM0_ID: |
anthony@9441 | 1244 raise XendError("Cannot destroy privileged domain %s" % domid) |
anthony@9441 | 1245 |
cl349@6929 | 1246 if dominfo: |
cl349@6929 | 1247 val = dominfo.destroy() |
cl349@6929 | 1248 else: |
cl349@6929 | 1249 try: |
kaf24@11006 | 1250 val = xc.domain_destroy(int(domid)) |
atse@12109 | 1251 except ValueError: |
atse@12109 | 1252 raise XendInvalidDomain(domid) |
atse@12109 | 1253 except Exception, e: |
atse@12109 | 1254 raise XendError(str(e)) |
atse@12109 | 1255 |
cl349@6929 | 1256 return val |
mjw@1711 | 1257 |
vhanquez@8312 | 1258 def domain_migrate(self, domid, dst, live=False, resource=0, port=0): |
atse@12109 | 1259 """Start domain migration. |
atse@12109 | 1260 |
atse@12109 | 1261 @param domid: Domain ID or Name |
atse@12109 | 1262 @type domid: int or string. |
atse@12109 | 1263 @param dst: Destination IP address |
atse@12109 | 1264 @type dst: string |
atse@12109 | 1265 @keyword port: relocation port on destination |
atse@12109 | 1266 @type port: int |
atse@12109 | 1267 @keyword live: Live migration |
atse@12109 | 1268 @type live: bool |
atse@12109 | 1269 @keyword resource: not used?? |
atse@12109 | 1270 @rtype: None |
atse@12109 | 1271 @raise XendError: Failed to migrate |
atse@12109 | 1272 @raise XendInvalidDomain: Domain is not valid |
atse@12109 | 1273 """ |
mjw@1667 | 1274 |
atse@12109 | 1275 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1276 if not dominfo: |
emellor@9515 | 1277 raise XendInvalidDomain(str(domid)) |
cl349@5193 | 1278 |
atse@12109 | 1279 if dominfo.getDomid() == DOM0_ID: |
ewan@12498 | 1280 raise XendError("Cannot migrate privileged domain %s" % domid) |
emellor@8313 | 1281 |
emellor@9695 | 1282 """ The following call may raise a XendError exception """ |
kaf24@10009 | 1283 dominfo.testMigrateDevices(True, dst) |
emellor@9695 | 1284 |
tim@11472 | 1285 if live: |
tim@11472 | 1286 """ Make sure there's memory free for enabling shadow mode """ |
tim@11472 | 1287 dominfo.checkLiveMigrateMemory() |
tim@11472 | 1288 |
vhanquez@8312 | 1289 if port == 0: |
ewan@13444 | 1290 port = xoptions.get_xend_relocation_port() |
emellor@8166 | 1291 try: |
emellor@8166 | 1292 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
emellor@8166 | 1293 sock.connect((dst, port)) |
emellor@8166 | 1294 except socket.error, err: |
emellor@8166 | 1295 raise XendError("can't connect: %s" % err[1]) |
cl349@5193 | 1296 |
emellor@8166 | 1297 sock.send("receive\n") |
emellor@9695 | 1298 sock.recv(80) |
kaf24@10009 | 1299 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst) |
steven@11329 | 1300 sock.close() |
mjw@1661 | 1301 |
kfraser@15030 | 1302 def domain_save(self, domid, dst, checkpoint=False): |
mjw@1782 | 1303 """Start saving a domain to file. |
mjw@1667 | 1304 |
atse@12109 | 1305 @param domid: Domain ID or Name |
atse@12109 | 1306 @type domid: int or string. |
atse@12109 | 1307 @param dst: Destination filename |
atse@12109 | 1308 @type dst: string |
atse@12109 | 1309 @rtype: None |
atse@12109 | 1310 @raise XendError: Failed to save domain |
atse@12109 | 1311 @raise XendInvalidDomain: Domain is not valid |
mjw@1661 | 1312 """ |
cl349@5159 | 1313 try: |
atse@12109 | 1314 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1315 if not dominfo: |
emellor@9515 | 1316 raise XendInvalidDomain(str(domid)) |
cl349@5159 | 1317 |
atse@12109 | 1318 if dominfo.getDomid() == DOM0_ID: |
kfraser@15540 | 1319 raise XendError("Cannot save privileged domain %s" % str(domid)) |
kfraser@15540 | 1320 if dominfo._stateGet() != DOM_STATE_RUNNING: |
kfraser@15540 | 1321 raise VMBadState("Domain is not running", |
kfraser@15540 | 1322 POWER_STATE_NAMES[DOM_STATE_RUNNING], |
kfraser@15540 | 1323 POWER_STATE_NAMES[dominfo._stateGet()]) |
emellor@8313 | 1324 |
Tim@13863 | 1325 oflags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC |
Tim@13863 | 1326 if hasattr(os, "O_LARGEFILE"): |
Tim@13863 | 1327 oflags |= os.O_LARGEFILE |
Tim@13863 | 1328 fd = os.open(dst, oflags) |
emellor@7242 | 1329 try: |
kfraser@14199 | 1330 XendCheckpoint.save(fd, dominfo, False, False, dst, |
kfraser@14199 | 1331 checkpoint=checkpoint) |
keir@15475 | 1332 except Exception, e: |
emellor@7242 | 1333 os.close(fd) |
keir@15475 | 1334 raise e |
keir@15475 | 1335 os.close(fd) |
cl349@5183 | 1336 except OSError, ex: |
cl349@5183 | 1337 raise XendError("can't write guest state file %s: %s" % |
cl349@5183 | 1338 (dst, ex[1])) |
cl349@5159 | 1339 |
emellor@7242 | 1340 def domain_pincpu(self, domid, vcpu, cpumap): |
cl349@4883 | 1341 """Set which cpus vcpu can use |
mjw@1661 | 1342 |
atse@12109 | 1343 @param domid: Domain ID or Name |
atse@12109 | 1344 @type domid: int or string. |
atse@12109 | 1345 @param vcpu: vcpu to pin to |
atse@12109 | 1346 @type vcpu: int |
atse@12109 | 1347 @param cpumap: string repr of usable cpus |
atse@12109 | 1348 @type cpumap: string |
atse@12109 | 1349 @rtype: 0 |
mjw@1667 | 1350 """ |
atse@12109 | 1351 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1352 if not dominfo: |
emellor@9515 | 1353 raise XendInvalidDomain(str(domid)) |
emellor@9515 | 1354 |
ack@11657 | 1355 # if vcpu is keyword 'all', apply the cpumap to all vcpus |
ack@11657 | 1356 vcpus = [ vcpu ] |
ack@11657 | 1357 if str(vcpu).lower() == "all": |
ack@11657 | 1358 vcpus = range(0, int(dominfo.getVCpuCount())) |
ack@11657 | 1359 |
ack@11657 | 1360 # set the same cpumask for all vcpus |
ack@11657 | 1361 rc = 0 |
ack@11657 | 1362 for v in vcpus: |
ack@11657 | 1363 try: |
ack@11657 | 1364 rc = xc.vcpu_setaffinity(dominfo.getDomid(), int(v), cpumap) |
ack@11657 | 1365 except Exception, ex: |
ewan@14519 | 1366 log.exception(ex) |
ewan@14519 | 1367 raise XendError("Cannot pin vcpu: %s to cpu: %s - %s" % \ |
ewan@14519 | 1368 (v, cpumap, str(ex))) |
ack@11657 | 1369 return rc |
mjw@1661 | 1370 |
emellor@7242 | 1371 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime, |
emellor@7242 | 1372 weight): |
sd386@3611 | 1373 """Set Simple EDF scheduler parameters for a domain. |
atse@12109 | 1374 |
atse@12109 | 1375 @param domid: Domain ID or Name |
atse@12109 | 1376 @type domid: int or string. |
atse@12109 | 1377 @rtype: 0 |
sd386@3487 | 1378 """ |
atse@12109 | 1379 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1380 if not dominfo: |
emellor@9515 | 1381 raise XendInvalidDomain(str(domid)) |
sd386@3487 | 1382 try: |
emellor@7242 | 1383 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_, |
emellor@6959 | 1384 latency, extratime, weight) |
sd386@3487 | 1385 except Exception, ex: |
sd386@3487 | 1386 raise XendError(str(ex)) |
sd386@3487 | 1387 |
emellor@7242 | 1388 def domain_cpu_sedf_get(self, domid): |
cl349@5372 | 1389 """Get Simple EDF scheduler parameters for a domain. |
atse@12109 | 1390 |
atse@12109 | 1391 @param domid: Domain ID or Name |
atse@12109 | 1392 @type domid: int or string. |
atse@12109 | 1393 @rtype: SXP object |
atse@12109 | 1394 @return: The parameters for Simple EDF schedule for a domain. |
sd386@3487 | 1395 """ |
atse@12109 | 1396 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1397 if not dominfo: |
emellor@9515 | 1398 raise XendInvalidDomain(str(domid)) |
sd386@3487 | 1399 try: |
emellor@9231 | 1400 sedf_info = xc.sedf_domain_get(dominfo.getDomid()) |
emellor@9231 | 1401 # return sxpr |
emellor@9231 | 1402 return ['sedf', |
atse@12394 | 1403 ['domid', sedf_info['domid']], |
emellor@9231 | 1404 ['period', sedf_info['period']], |
emellor@9231 | 1405 ['slice', sedf_info['slice']], |
emellor@9231 | 1406 ['latency', sedf_info['latency']], |
emellor@9231 | 1407 ['extratime', sedf_info['extratime']], |
emellor@9231 | 1408 ['weight', sedf_info['weight']]] |
emellor@9231 | 1409 |
sd386@3487 | 1410 except Exception, ex: |
sd386@3487 | 1411 raise XendError(str(ex)) |
cl349@5342 | 1412 |
tdeegan@11189 | 1413 def domain_shadow_control(self, domid, op): |
atse@12109 | 1414 """Shadow page control. |
atse@12109 | 1415 |
atse@12109 | 1416 @param domid: Domain ID or Name |
atse@12109 | 1417 @type domid: int or string. |
atse@12109 | 1418 @param op: operation |
atse@12109 | 1419 @type op: int |
atse@12109 | 1420 @rtype: 0 |
atse@12109 | 1421 """ |
tdeegan@11189 | 1422 dominfo = self.domain_lookup(domid) |
tdeegan@11189 | 1423 try: |
tdeegan@11189 | 1424 return xc.shadow_control(dominfo.getDomid(), op) |
tdeegan@11189 | 1425 except Exception, ex: |
tdeegan@11189 | 1426 raise XendError(str(ex)) |
tdeegan@11189 | 1427 |
tdeegan@11189 | 1428 def domain_shadow_mem_get(self, domid): |
atse@12109 | 1429 """Get shadow pagetable memory allocation. |
atse@12109 | 1430 |
atse@12109 | 1431 @param domid: Domain ID or Name |
atse@12109 | 1432 @type domid: int or string. |
atse@12109 | 1433 @rtype: int |
atse@12109 | 1434 @return: shadow memory in MB |
atse@12109 | 1435 """ |
tdeegan@11189 | 1436 dominfo = self.domain_lookup(domid) |
tdeegan@11189 | 1437 try: |
tdeegan@11189 | 1438 return xc.shadow_mem_control(dominfo.getDomid()) |
tdeegan@11189 | 1439 except Exception, ex: |
tdeegan@11189 | 1440 raise XendError(str(ex)) |
tdeegan@11189 | 1441 |
tdeegan@11189 | 1442 def domain_shadow_mem_set(self, domid, mb): |
atse@12109 | 1443 """Set shadow pagetable memory allocation. |
atse@12109 | 1444 |
atse@12109 | 1445 @param domid: Domain ID or Name |
atse@12109 | 1446 @type domid: int or string. |
atse@12109 | 1447 @param mb: shadow memory to set in MB |
atse@12109 | 1448 @type: mb: int |
atse@12109 | 1449 @rtype: int |
atse@12109 | 1450 @return: shadow memory in MB |
atse@12109 | 1451 """ |
tdeegan@11189 | 1452 dominfo = self.domain_lookup(domid) |
tdeegan@11189 | 1453 try: |
tdeegan@11189 | 1454 return xc.shadow_mem_control(dominfo.getDomid(), mb=mb) |
tdeegan@11189 | 1455 except Exception, ex: |
tdeegan@11189 | 1456 raise XendError(str(ex)) |
tdeegan@11189 | 1457 |
kaf24@10212 | 1458 def domain_sched_credit_get(self, domid): |
ack@10206 | 1459 """Get credit scheduler parameters for a domain. |
atse@12109 | 1460 |
atse@12109 | 1461 @param domid: Domain ID or Name |
atse@12109 | 1462 @type domid: int or string. |
atse@12109 | 1463 @rtype: dict with keys 'weight' and 'cap' |
atse@12109 | 1464 @return: credit scheduler parameters |
ack@10206 | 1465 """ |
atse@12109 | 1466 dominfo = self.domain_lookup_nr(domid) |
ack@10206 | 1467 if not dominfo: |
ack@10206 | 1468 raise XendInvalidDomain(str(domid)) |
kfraser@15510 | 1469 |
kfraser@15510 | 1470 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
kfraser@15510 | 1471 try: |
kfraser@15510 | 1472 return xc.sched_credit_domain_get(dominfo.getDomid()) |
kfraser@15510 | 1473 except Exception, ex: |
kfraser@15510 | 1474 raise XendError(str(ex)) |
kfraser@15510 | 1475 else: |
kfraser@15510 | 1476 return {'weight' : dominfo.getWeight(), |
kfraser@15510 | 1477 'cap' : dominfo.getCap()} |
ack@10206 | 1478 |
ack@11593 | 1479 def domain_sched_credit_set(self, domid, weight = None, cap = None): |
ack@10206 | 1480 """Set credit scheduler parameters for a domain. |
atse@12109 | 1481 |
atse@12109 | 1482 @param domid: Domain ID or Name |
atse@12109 | 1483 @type domid: int or string. |
atse@12109 | 1484 @type weight: int |
atse@12109 | 1485 @type cap: int |
atse@12109 | 1486 @rtype: 0 |
ack@10206 | 1487 """ |
kfraser@15187 | 1488 set_weight = False |
kfraser@15187 | 1489 set_cap = False |
atse@12109 | 1490 dominfo = self.domain_lookup_nr(domid) |
ack@10206 | 1491 if not dominfo: |
ack@10206 | 1492 raise XendInvalidDomain(str(domid)) |
ack@10206 | 1493 try: |
ack@11593 | 1494 if weight is None: |
ack@11593 | 1495 weight = int(0) |
ack@11593 | 1496 elif weight < 1 or weight > 65535: |
ack@11593 | 1497 raise XendError("weight is out of range") |
kfraser@15187 | 1498 else: |
kfraser@15187 | 1499 set_weight = True |
ack@11593 | 1500 |
ack@11593 | 1501 if cap is None: |
ack@11593 | 1502 cap = int(~0) |
ack@11593 | 1503 elif cap < 0 or cap > dominfo.getVCpuCount() * 100: |
ack@11593 | 1504 raise XendError("cap is out of range") |
kfraser@15187 | 1505 else: |
kfraser@15187 | 1506 set_cap = True |
ack@11593 | 1507 |
ewan@12339 | 1508 assert type(weight) == int |
ewan@12339 | 1509 assert type(cap) == int |
ewan@12339 | 1510 |
kfraser@15510 | 1511 rc = 0 |
kfraser@15510 | 1512 if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
kfraser@15510 | 1513 rc = xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap) |
kfraser@15187 | 1514 if rc == 0: |
kfraser@15187 | 1515 if set_weight: |
kfraser@15187 | 1516 dominfo.setWeight(weight) |
kfraser@15187 | 1517 if set_cap: |
kfraser@15187 | 1518 dominfo.setCap(cap) |
kfraser@15487 | 1519 self.managed_config_save(dominfo) |
kfraser@15187 | 1520 return rc |
ack@10206 | 1521 except Exception, ex: |
ewan@12339 | 1522 log.exception(ex) |
ack@10206 | 1523 raise XendError(str(ex)) |
ack@10206 | 1524 |
emellor@7242 | 1525 def domain_maxmem_set(self, domid, mem): |
mjw@1966 | 1526 """Set the memory limit for a domain. |
mjw@1966 | 1527 |
atse@12109 | 1528 @param domid: Domain ID or Name |
atse@12109 | 1529 @type domid: int or string. |
emellor@7092 | 1530 @param mem: memory limit (in MiB) |
atse@12109 | 1531 @type mem: int |
atse@12109 | 1532 @raise XendError: fail to set memory |
atse@12109 | 1533 @rtype: 0 |
mjw@1966 | 1534 """ |
atse@12109 | 1535 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1536 if not dominfo: |
emellor@9515 | 1537 raise XendInvalidDomain(str(domid)) |
ewan@13099 | 1538 dominfo.setMemoryMaximum(mem) |
mjw@1966 | 1539 |
kaf24@7667 | 1540 def domain_ioport_range_enable(self, domid, first, last): |
kaf24@7667 | 1541 """Enable access to a range of IO ports for a domain |
kaf24@7667 | 1542 |
kaf24@7667 | 1543 @param first: first IO port |
kaf24@7667 | 1544 @param last: last IO port |
atse@12109 | 1545 @raise XendError: failed to set range |
atse@12109 | 1546 @rtype: 0 |
kaf24@7667 | 1547 """ |
atse@12109 | 1548 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1549 if not dominfo: |
emellor@9515 | 1550 raise XendInvalidDomain(str(domid)) |
kaf24@7667 | 1551 nr_ports = last - first + 1 |
kaf24@7667 | 1552 try: |
kaf24@7667 | 1553 return xc.domain_ioport_permission(dominfo.getDomid(), |
kaf24@7667 | 1554 first_port = first, |
kaf24@7667 | 1555 nr_ports = nr_ports, |
kaf24@7667 | 1556 allow_access = 1) |
kaf24@7667 | 1557 except Exception, ex: |
kaf24@7667 | 1558 raise XendError(str(ex)) |
kaf24@7667 | 1559 |
kaf24@7667 | 1560 def domain_ioport_range_disable(self, domid, first, last): |
kaf24@7667 | 1561 """Disable access to a range of IO ports for a domain |
kaf24@7667 | 1562 |
kaf24@7667 | 1563 @param first: first IO port |
kaf24@7667 | 1564 @param last: last IO port |
atse@12109 | 1565 @raise XendError: failed to set range |
atse@12109 | 1566 @rtype: 0 |
kaf24@7667 | 1567 """ |
atse@12109 | 1568 dominfo = self.domain_lookup_nr(domid) |
emellor@9515 | 1569 if not dominfo: |
emellor@9515 | 1570 raise XendInvalidDomain(str(domid)) |
kaf24@7667 | 1571 nr_ports = last - first + 1 |
kaf24@7667 | 1572 try: |
kaf24@7667 | 1573 return xc.domain_ioport_permission(dominfo.getDomid(), |
kaf24@7667 | 1574 first_port = first, |
kaf24@7667 | 1575 nr_ports = nr_ports, |
kaf24@7667 | 1576 allow_access = 0) |
kaf24@7667 | 1577 except Exception, ex: |
kaf24@7667 | 1578 raise XendError(str(ex)) |
kaf24@7667 | 1579 |
keir@14136 | 1580 def domain_send_trigger(self, domid, trigger_name, vcpu = 0): |
keir@14136 | 1581 """Send trigger to a domain. |
keir@14136 | 1582 |
keir@14136 | 1583 @param domid: Domain ID or Name |
keir@14136 | 1584 @type domid: int or string. |
keir@14136 | 1585 @param trigger_name: trigger type name |
keir@14136 | 1586 @type trigger_name: string |
keir@14136 | 1587 @param vcpu: VCPU to send trigger (default is 0) |
keir@14136 | 1588 @type vcpu: int |
keir@14136 | 1589 @raise XendError: failed to send trigger |
keir@14136 | 1590 @raise XendInvalidDomain: Domain is not valid |
keir@14136 | 1591 @rtype: 0 |
keir@14136 | 1592 """ |
keir@14136 | 1593 dominfo = self.domain_lookup_nr(domid) |
keir@14136 | 1594 if not dominfo: |
keir@14136 | 1595 raise XendInvalidDomain(str(domid)) |
kfraser@15570 | 1596 if dominfo._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED): |
kfraser@15570 | 1597 raise VMBadState("Domain '%s' is not started" % domid, |
kfraser@15570 | 1598 POWER_STATE_NAMES[DOM_STATE_RUNNING], |
kfraser@15570 | 1599 POWER_STATE_NAMES[dominfo._stateGet()]) |
kfraser@15873 | 1600 if trigger_name.lower() in TRIGGER_TYPE.keys(): |
keir@14136 | 1601 trigger = TRIGGER_TYPE[trigger_name.lower()] |
keir@14136 | 1602 else: |
kfraser@15873 | 1603 raise XendError("Invalid trigger: %s" % trigger_name) |
keir@14136 | 1604 try: |
keir@14136 | 1605 return xc.domain_send_trigger(dominfo.getDomid(), |
keir@14136 | 1606 trigger, |
keir@14136 | 1607 vcpu) |
keir@14136 | 1608 except Exception, ex: |
keir@14136 | 1609 raise XendError(str(ex)) |
keir@14136 | 1610 |
emellor@7092 | 1611 |
mjw@1661 | 1612 def instance(): |
mjw@1667 | 1613 """Singleton constructor. Use this instead of the class constructor. |
mjw@1667 | 1614 """ |
mjw@1661 | 1615 global inst |
mjw@1661 | 1616 try: |
mjw@1661 | 1617 inst |
mjw@1661 | 1618 except: |
mjw@1661 | 1619 inst = XendDomain() |
emellor@8166 | 1620 inst.init() |
mjw@1661 | 1621 return inst |