debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 16559:5255eac35270

Implement legacy XML-RPC interface for ACM commands.

This patch implements a (non Xen-API) legacy XML-RPC interface for the
ACM commands and funnels the calls into code introduced by the Xen-API
support for ACM security management. Since some of the functionality
has changed, also the xm applications have changed. In particular the
following old commands have been removed along with some tools the
have become obsolete now:

- loadpolicy (included in: setpolicy)
- makepolicy (included in: setpolicy)
- cfgbootpolicy (included in: setpolicy)

and the following commands been introduced:

- setpolicy
- getpolicy
- resetpolicy

All tools have been adapted to work in Xen-API and legacy XML-RPC
mode. Both modes support the same functionality.

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