debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 16981:625c923f7b4a

xend: fix external-device-migrate step 0

Add domain name to a migrateDevice call, so the helper script
(external-device-migrate example) does not fail at step 0
(MIGRATION_TEST).

Signed-off-by: Pascal Bouchareine <pascal@gandi.net>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jan 30 14:24:20 2008 +0000 (2008-01-30)
parents 47b7ec3b4055
children c4a06902febf
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, SetTarget, 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, XEN_API_VM_POWER_STATE_CRASHED):
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 del self.info['devices'][dev_uuid]
539 if dev_type == 'tap':
540 self.info['vbd_refs'].remove(dev_uuid)
541 else:
542 self.info['%s_refs' % dev_type].remove(dev_uuid)
543 raise ex
544 else:
545 devid = None
547 xen.xend.XendDomain.instance().managed_config_save(self)
548 return self.getDeviceController(dev_type).sxpr(devid)
550 def device_configure(self, dev_sxp, devid = None):
551 """Configure an existing device.
553 @param dev_config: device configuration
554 @type dev_config: SXP object (parsed config)
555 @param devid: device id
556 @type devid: int
557 @return: Returns True if successfully updated device
558 @rtype: boolean
559 """
561 # convert device sxp to a dict
562 dev_class = sxp.name(dev_sxp)
563 dev_config = {}
564 for opt_val in dev_sxp[1:]:
565 try:
566 dev_config[opt_val[0]] = opt_val[1]
567 except IndexError:
568 pass
570 # use DevController.reconfigureDevice to change device config
571 dev_control = self.getDeviceController(dev_class)
572 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
574 # update XendConfig with new device info
575 if dev_uuid:
576 self.info.device_update(dev_uuid, dev_sxp)
578 return True
580 def waitForDevices(self):
581 """Wait for this domain's configured devices to connect.
583 @raise VmError: if any device fails to initialise.
584 """
585 for devclass in XendDevices.valid_devices():
586 self.getDeviceController(devclass).waitForDevices()
588 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
589 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
590 deviceClass, devid)
592 if rm_cfg:
593 # Convert devid to device number. A device number is
594 # needed to remove its configuration.
595 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
597 # Save current sxprs. A device number and a backend
598 # path are needed to remove its configuration but sxprs
599 # do not have those after calling destroyDevice.
600 sxprs = self.getDeviceSxprs(deviceClass)
602 rc = None
603 if self.domid is not None:
604 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
605 if not force and rm_cfg:
606 # The backend path, other than the device itself,
607 # has to be passed because its accompanied frontend
608 # path may be void until its removal is actually
609 # issued. It is probable because destroyDevice is
610 # issued first.
611 for dev_num, dev_info in sxprs:
612 dev_num = int(dev_num)
613 if dev_num == dev:
614 for x in dev_info:
615 if x[0] == 'backend':
616 backend = x[1]
617 break
618 break
619 self._waitForDevice_destroy(deviceClass, devid, backend)
621 if rm_cfg:
622 if deviceClass == 'vif':
623 if self.domid is not None:
624 for dev_num, dev_info in sxprs:
625 dev_num = int(dev_num)
626 if dev_num == dev:
627 for x in dev_info:
628 if x[0] == 'mac':
629 mac = x[1]
630 break
631 break
632 dev_info = self._getDeviceInfo_vif(mac)
633 else:
634 _, dev_info = sxprs[dev]
635 else: # 'vbd' or 'tap'
636 dev_info = self._getDeviceInfo_vbd(dev)
637 # To remove the UUID of the device from refs,
638 # deviceClass must be always 'vbd'.
639 deviceClass = 'vbd'
640 if dev_info is None:
641 raise XendError("Device %s is not defined" % devid)
643 dev_uuid = sxp.child_value(dev_info, 'uuid')
644 del self.info['devices'][dev_uuid]
645 self.info['%s_refs' % deviceClass].remove(dev_uuid)
646 xen.xend.XendDomain.instance().managed_config_save(self)
648 return rc
650 def getDeviceSxprs(self, deviceClass):
651 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
652 return self.getDeviceController(deviceClass).sxprs()
653 else:
654 sxprs = []
655 dev_num = 0
656 for dev_type, dev_info in self.info.all_devices_sxpr():
657 if dev_type == deviceClass:
658 sxprs.append([dev_num, dev_info])
659 dev_num += 1
660 return sxprs
662 def getBlockDeviceClass(self, devid):
663 # To get a device number from the devid,
664 # we temporarily use the device controller of VBD.
665 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
666 dev_info = self._getDeviceInfo_vbd(dev)
667 if dev_info:
668 return dev_info[0]
670 def _getDeviceInfo_vif(self, mac):
671 for dev_type, dev_info in self.info.all_devices_sxpr():
672 if dev_type != 'vif':
673 continue
674 if mac == sxp.child_value(dev_info, 'mac'):
675 return dev_info
677 def _getDeviceInfo_vbd(self, devid):
678 for dev_type, dev_info in self.info.all_devices_sxpr():
679 if dev_type != 'vbd' and dev_type != 'tap':
680 continue
681 dev = sxp.child_value(dev_info, 'dev')
682 dev = dev.split(':')[0]
683 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
684 if devid == dev:
685 return dev_info
688 def setMemoryTarget(self, target):
689 """Set the memory target of this domain.
690 @param target: In MiB.
691 """
692 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
693 self.info['name_label'], str(self.domid), target)
695 MiB = 1024 * 1024
696 self._safe_set_memory('memory_dynamic_min', target * MiB)
697 self._safe_set_memory('memory_dynamic_max', target * MiB)
699 if self.domid >= 0:
700 self.storeVm("memory", target)
701 self.storeDom("memory/target", target << 10)
702 xen.xend.XendDomain.instance().managed_config_save(self)
704 def setMemoryMaximum(self, limit):
705 """Set the maximum memory limit of this domain
706 @param limit: In MiB.
707 """
708 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
709 self.info['name_label'], str(self.domid), limit)
711 if limit <= 0:
712 raise XendError('Invalid memory size')
714 MiB = 1024 * 1024
715 self._safe_set_memory('memory_static_max', limit * MiB)
717 if self.domid >= 0:
718 maxmem = int(limit) * 1024
719 try:
720 return xc.domain_setmaxmem(self.domid, maxmem)
721 except Exception, ex:
722 raise XendError(str(ex))
723 xen.xend.XendDomain.instance().managed_config_save(self)
726 def getVCPUInfo(self):
727 try:
728 # We include the domain name and ID, to help xm.
729 sxpr = ['domain',
730 ['domid', self.domid],
731 ['name', self.info['name_label']],
732 ['vcpu_count', self.info['VCPUs_max']]]
734 for i in range(0, self.info['VCPUs_max']):
735 if self.domid is not None:
736 info = xc.vcpu_getinfo(self.domid, i)
738 sxpr.append(['vcpu',
739 ['number', i],
740 ['online', info['online']],
741 ['blocked', info['blocked']],
742 ['running', info['running']],
743 ['cpu_time', info['cpu_time'] / 1e9],
744 ['cpu', info['cpu']],
745 ['cpumap', info['cpumap']]])
746 else:
747 sxpr.append(['vcpu',
748 ['number', i],
749 ['online', 0],
750 ['blocked', 0],
751 ['running', 0],
752 ['cpu_time', 0.0],
753 ['cpu', -1],
754 ['cpumap', self.info['cpus'] and \
755 self.info['cpus'] or range(64)]])
757 return sxpr
759 except RuntimeError, exn:
760 raise XendError(str(exn))
763 def getDomInfo(self):
764 return dom_get(self.domid)
766 #
767 # internal functions ... TODO: re-categorised
768 #
770 def _augmentInfo(self, priv):
771 """Augment self.info, as given to us through L{recreate}, with
772 values taken from the store. This recovers those values known
773 to xend but not to the hypervisor.
774 """
775 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
776 if priv:
777 augment_entries.remove('memory')
778 augment_entries.remove('maxmem')
779 augment_entries.remove('vcpus')
780 augment_entries.remove('vcpu_avail')
782 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
783 for k in augment_entries])
785 # make returned lists into a dictionary
786 vm_config = dict(zip(augment_entries, vm_config))
788 for arg in augment_entries:
789 val = vm_config[arg]
790 if val != None:
791 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
792 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
793 self.info[xapiarg] = val
794 elif arg == "memory":
795 self.info["static_memory_min"] = val
796 elif arg == "maxmem":
797 self.info["static_memory_max"] = val
798 else:
799 self.info[arg] = val
801 # For dom0, we ignore any stored value for the vcpus fields, and
802 # read the current value from Xen instead. This allows boot-time
803 # settings to take precedence over any entries in the store.
804 if priv:
805 xeninfo = dom_get(self.domid)
806 self.info['VCPUs_max'] = xeninfo['online_vcpus']
807 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
809 # read image value
810 image_sxp = self._readVm('image')
811 if image_sxp:
812 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
814 # read devices
815 devices = []
816 for devclass in XendDevices.valid_devices():
817 devconfig = self.getDeviceController(devclass).configurations()
818 if devconfig:
819 devices.extend(devconfig)
821 if not self.info['devices'] and devices is not None:
822 for device in devices:
823 self.info.device_add(device[0], cfg_sxp = device)
825 self._update_consoles()
827 def _update_consoles(self, transaction = None):
828 if self.domid == None or self.domid == 0:
829 return
831 # Update VT100 port if it exists
832 if transaction is None:
833 self.console_port = self.readDom('console/port')
834 else:
835 self.console_port = self.readDomTxn(transaction, 'console/port')
836 if self.console_port is not None:
837 serial_consoles = self.info.console_get_all('vt100')
838 if not serial_consoles:
839 cfg = self.info.console_add('vt100', self.console_port)
840 self._createDevice('console', cfg)
841 else:
842 console_uuid = serial_consoles[0].get('uuid')
843 self.info.console_update(console_uuid, 'location',
844 self.console_port)
847 # Update VNC port if it exists and write to xenstore
848 if transaction is None:
849 vnc_port = self.readDom('console/vnc-port')
850 else:
851 vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
852 if vnc_port is not None:
853 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
854 if dev_type == 'vfb':
855 old_location = dev_info.get('location')
856 listen_host = dev_info.get('vnclisten', 'localhost')
857 new_location = '%s:%s' % (listen_host, str(vnc_port))
858 if old_location == new_location:
859 break
861 dev_info['location'] = new_location
862 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
863 vfb_ctrl = self.getDeviceController('vfb')
864 vfb_ctrl.reconfigureDevice(0, dev_info)
865 break
867 #
868 # Function to update xenstore /vm/*
869 #
871 def _readVm(self, *args):
872 return xstransact.Read(self.vmpath, *args)
874 def _writeVm(self, *args):
875 return xstransact.Write(self.vmpath, *args)
877 def _removeVm(self, *args):
878 return xstransact.Remove(self.vmpath, *args)
880 def _gatherVm(self, *args):
881 return xstransact.Gather(self.vmpath, *args)
883 def storeVm(self, *args):
884 return xstransact.Store(self.vmpath, *args)
886 def permissionsVm(self, *args):
887 return xstransact.SetPermissions(self.vmpath, *args)
890 def _readVmTxn(self, transaction, *args):
891 paths = map(lambda x: self.vmpath + "/" + x, args)
892 return transaction.read(*paths)
894 def _writeVmTxn(self, transaction, *args):
895 paths = map(lambda x: self.vmpath + "/" + x, args)
896 return transaction.write(*paths)
898 def _removeVmTxn(self, transaction, *args):
899 paths = map(lambda x: self.vmpath + "/" + x, args)
900 return transaction.remove(*paths)
902 def _gatherVmTxn(self, transaction, *args):
903 paths = map(lambda x: self.vmpath + "/" + x, args)
904 return transaction.gather(paths)
906 def storeVmTxn(self, transaction, *args):
907 paths = map(lambda x: self.vmpath + "/" + x, args)
908 return transaction.store(*paths)
910 def permissionsVmTxn(self, transaction, *args):
911 paths = map(lambda x: self.vmpath + "/" + x, args)
912 return transaction.set_permissions(*paths)
914 #
915 # Function to update xenstore /dom/*
916 #
918 def readDom(self, *args):
919 return xstransact.Read(self.dompath, *args)
921 def gatherDom(self, *args):
922 return xstransact.Gather(self.dompath, *args)
924 def _writeDom(self, *args):
925 return xstransact.Write(self.dompath, *args)
927 def _removeDom(self, *args):
928 return xstransact.Remove(self.dompath, *args)
930 def storeDom(self, *args):
931 return xstransact.Store(self.dompath, *args)
934 def readDomTxn(self, transaction, *args):
935 paths = map(lambda x: self.dompath + "/" + x, args)
936 return transaction.read(*paths)
938 def gatherDomTxn(self, transaction, *args):
939 paths = map(lambda x: self.dompath + "/" + x, args)
940 return transaction.gather(*paths)
942 def _writeDomTxn(self, transaction, *args):
943 paths = map(lambda x: self.dompath + "/" + x, args)
944 return transaction.write(*paths)
946 def _removeDomTxn(self, transaction, *args):
947 paths = map(lambda x: self.dompath + "/" + x, args)
948 return transaction.remove(*paths)
950 def storeDomTxn(self, transaction, *args):
951 paths = map(lambda x: self.dompath + "/" + x, args)
952 return transaction.store(*paths)
955 def _recreateDom(self):
956 complete(self.dompath, lambda t: self._recreateDomFunc(t))
958 def _recreateDomFunc(self, t):
959 t.remove()
960 t.mkdir()
961 t.set_permissions({'dom' : self.domid})
962 t.write('vm', self.vmpath)
964 def _storeDomDetails(self):
965 to_store = {
966 'domid': str(self.domid),
967 'vm': self.vmpath,
968 'name': self.info['name_label'],
969 'console/limit': str(xoptions.get_console_limit() * 1024),
970 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
971 }
973 def f(n, v):
974 if v is not None:
975 if type(v) == bool:
976 to_store[n] = v and "1" or "0"
977 else:
978 to_store[n] = str(v)
980 # Figure out if we need to tell xenconsoled to ignore this guest's
981 # console - device model will handle console if it is running
982 constype = "ioemu"
983 if 'device_model' not in self.info['platform']:
984 constype = "xenconsoled"
986 f('console/port', self.console_port)
987 f('console/ring-ref', self.console_mfn)
988 f('console/type', constype)
989 f('store/port', self.store_port)
990 f('store/ring-ref', self.store_mfn)
992 if arch.type == "x86":
993 f('control/platform-feature-multiprocessor-suspend', True)
995 # elfnotes
996 for n, v in self.info.get_notes().iteritems():
997 n = n.lower().replace('_', '-')
998 if n == 'features':
999 for v in v.split('|'):
1000 v = v.replace('_', '-')
1001 if v.startswith('!'):
1002 f('image/%s/%s' % (n, v[1:]), False)
1003 else:
1004 f('image/%s/%s' % (n, v), True)
1005 else:
1006 f('image/%s' % n, v)
1008 if self.info.has_key('security_label'):
1009 f('security_label', self.info['security_label'])
1011 to_store.update(self._vcpuDomDetails())
1013 log.debug("Storing domain details: %s", scrub_password(to_store))
1015 self._writeDom(to_store)
1017 def _vcpuDomDetails(self):
1018 def availability(n):
1019 if self.info['vcpu_avail'] & (1 << n):
1020 return 'online'
1021 else:
1022 return 'offline'
1024 result = {}
1025 for v in range(0, self.info['VCPUs_max']):
1026 result["cpu/%d/availability" % v] = availability(v)
1027 return result
1030 # xenstore watches
1033 def _registerWatches(self):
1034 """Register a watch on this VM's entries in the store, and the
1035 domain's control/shutdown node, so that when they are changed
1036 externally, we keep up to date. This should only be called by {@link
1037 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1038 details have been written, but before the new instance is returned."""
1039 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1040 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1041 self._handleShutdownWatch)
1043 def _storeChanged(self, _):
1044 log.trace("XendDomainInfo.storeChanged");
1046 changed = False
1048 # Check whether values in the configuration have
1049 # changed in Xenstore.
1051 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1052 'rtc/timeoffset']
1054 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1055 for k in cfg_vm])
1057 # convert two lists into a python dictionary
1058 vm_details = dict(zip(cfg_vm, vm_details))
1060 if vm_details['rtc/timeoffset'] == None:
1061 vm_details['rtc/timeoffset'] = "0"
1063 for arg, val in vm_details.items():
1064 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1065 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1066 if val != None and val != self.info[xapiarg]:
1067 self.info[xapiarg] = val
1068 changed = True
1069 elif arg == "memory":
1070 if val != None and val != self.info["static_memory_min"]:
1071 self.info["static_memory_min"] = val
1072 changed = True
1073 elif arg == "maxmem":
1074 if val != None and val != self.info["static_memory_max"]:
1075 self.info["static_memory_max"] = val
1076 changed = True
1078 # Check whether image definition has been updated
1079 image_sxp = self._readVm('image')
1080 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1081 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1082 changed = True
1084 # Check if the rtc offset has changes
1085 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1086 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1087 changed = True
1089 if changed:
1090 # Update the domain section of the store, as this contains some
1091 # parameters derived from the VM configuration.
1092 self._storeDomDetails()
1094 return 1
1096 def _handleShutdownWatch(self, _):
1097 log.debug('XendDomainInfo.handleShutdownWatch')
1099 reason = self.readDom('control/shutdown')
1101 if reason and reason != 'suspend':
1102 sst = self.readDom('xend/shutdown_start_time')
1103 now = time.time()
1104 if sst:
1105 self.shutdownStartTime = float(sst)
1106 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1107 else:
1108 self.shutdownStartTime = now
1109 self.storeDom('xend/shutdown_start_time', now)
1110 timeout = SHUTDOWN_TIMEOUT
1112 log.trace(
1113 "Scheduling refreshShutdown on domain %d in %ds.",
1114 self.domid, timeout)
1115 threading.Timer(timeout, self.refreshShutdown).start()
1117 return True
1121 # Public Attributes for the VM
1125 def getDomid(self):
1126 return self.domid
1128 def setName(self, name, to_store = True):
1129 self._checkName(name)
1130 self.info['name_label'] = name
1131 if to_store:
1132 self.storeVm("name", name)
1134 def getName(self):
1135 return self.info['name_label']
1137 def getDomainPath(self):
1138 return self.dompath
1140 def getShutdownReason(self):
1141 return self.readDom('control/shutdown')
1143 def getStorePort(self):
1144 """For use only by image.py and XendCheckpoint.py."""
1145 return self.store_port
1147 def getConsolePort(self):
1148 """For use only by image.py and XendCheckpoint.py"""
1149 return self.console_port
1151 def getFeatures(self):
1152 """For use only by image.py."""
1153 return self.info['features']
1155 def getVCpuCount(self):
1156 return self.info['VCPUs_max']
1158 def setVCpuCount(self, vcpus):
1159 if vcpus <= 0:
1160 raise XendError('Invalid VCPUs')
1162 self.info['vcpu_avail'] = (1 << vcpus) - 1
1163 if self.domid >= 0:
1164 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1165 # update dom differently depending on whether we are adjusting
1166 # vcpu number up or down, otherwise _vcpuDomDetails does not
1167 # disable the vcpus
1168 if self.info['VCPUs_max'] > vcpus:
1169 # decreasing
1170 self._writeDom(self._vcpuDomDetails())
1171 self.info['VCPUs_live'] = vcpus
1172 else:
1173 # same or increasing
1174 self.info['VCPUs_live'] = vcpus
1175 self._writeDom(self._vcpuDomDetails())
1176 else:
1177 self.info['VCPUs_max'] = vcpus
1178 xen.xend.XendDomain.instance().managed_config_save(self)
1179 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1180 vcpus)
1182 def getMemoryTarget(self):
1183 """Get this domain's target memory size, in KB."""
1184 return self.info['memory_dynamic_max'] / 1024
1186 def getMemoryMaximum(self):
1187 """Get this domain's maximum memory size, in KB."""
1188 # remember, info now stores memory in bytes
1189 return self.info['memory_static_max'] / 1024
1191 def getResume(self):
1192 return str(self._resume)
1194 def setResume(self, isresume):
1195 self._resume = isresume
1197 def getCap(self):
1198 return self.info['vcpus_params']['cap']
1200 def setCap(self, cpu_cap):
1201 self.info['vcpus_params']['cap'] = cpu_cap
1203 def getWeight(self):
1204 return self.info['vcpus_params']['weight']
1206 def setWeight(self, cpu_weight):
1207 self.info['vcpus_params']['weight'] = cpu_weight
1209 def setResume(self, state):
1210 self._resume = state
1212 def getRestartCount(self):
1213 return self._readVm('xend/restart_count')
1215 def refreshShutdown(self, xeninfo = None):
1216 """ Checks the domain for whether a shutdown is required.
1218 Called from XendDomainInfo and also image.py for HVM images.
1219 """
1221 # If set at the end of this method, a restart is required, with the
1222 # given reason. This restart has to be done out of the scope of
1223 # refresh_shutdown_lock.
1224 restart_reason = None
1226 self.refresh_shutdown_lock.acquire()
1227 try:
1228 if xeninfo is None:
1229 xeninfo = dom_get(self.domid)
1230 if xeninfo is None:
1231 # The domain no longer exists. This will occur if we have
1232 # scheduled a timer to check for shutdown timeouts and the
1233 # shutdown succeeded. It will also occur if someone
1234 # destroys a domain beneath us. We clean up the domain,
1235 # just in case, but we can't clean up the VM, because that
1236 # VM may have migrated to a different domain on this
1237 # machine.
1238 self.cleanupDomain()
1239 self._stateSet(DOM_STATE_HALTED)
1240 return
1242 if xeninfo['dying']:
1243 # Dying means that a domain has been destroyed, but has not
1244 # yet been cleaned up by Xen. This state could persist
1245 # indefinitely if, for example, another domain has some of its
1246 # pages mapped. We might like to diagnose this problem in the
1247 # future, but for now all we do is make sure that it's not us
1248 # holding the pages, by calling cleanupDomain. We can't
1249 # clean up the VM, as above.
1250 self.cleanupDomain()
1251 self._stateSet(DOM_STATE_SHUTDOWN)
1252 return
1254 elif xeninfo['crashed']:
1255 if self.readDom('xend/shutdown_completed'):
1256 # We've seen this shutdown already, but we are preserving
1257 # the domain for debugging. Leave it alone.
1258 return
1260 log.warn('Domain has crashed: name=%s id=%d.',
1261 self.info['name_label'], self.domid)
1262 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1264 if xoptions.get_enable_dump():
1265 try:
1266 self.dumpCore()
1267 except XendError:
1268 # This error has been logged -- there's nothing more
1269 # we can do in this context.
1270 pass
1272 restart_reason = 'crash'
1273 self._stateSet(DOM_STATE_HALTED)
1275 elif xeninfo['shutdown']:
1276 self._stateSet(DOM_STATE_SHUTDOWN)
1277 if self.readDom('xend/shutdown_completed'):
1278 # We've seen this shutdown already, but we are preserving
1279 # the domain for debugging. Leave it alone.
1280 return
1282 else:
1283 reason = shutdown_reason(xeninfo['shutdown_reason'])
1285 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1286 self.info['name_label'], self.domid, reason)
1287 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1289 self._clearRestart()
1291 if reason == 'suspend':
1292 self._stateSet(DOM_STATE_SUSPENDED)
1293 # Don't destroy the domain. XendCheckpoint will do
1294 # this once it has finished. However, stop watching
1295 # the VM path now, otherwise we will end up with one
1296 # watch for the old domain, and one for the new.
1297 self._unwatchVm()
1298 elif reason in ('poweroff', 'reboot'):
1299 restart_reason = reason
1300 else:
1301 self.destroy()
1303 elif self.dompath is None:
1304 # We have yet to manage to call introduceDomain on this
1305 # domain. This can happen if a restore is in progress, or has
1306 # failed. Ignore this domain.
1307 pass
1308 else:
1309 # Domain is alive. If we are shutting it down, log a message
1310 # if it seems unresponsive.
1311 if xeninfo['paused']:
1312 self._stateSet(DOM_STATE_PAUSED)
1313 else:
1314 self._stateSet(DOM_STATE_RUNNING)
1316 if self.shutdownStartTime:
1317 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1318 self.shutdownStartTime)
1319 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1320 log.info(
1321 "Domain shutdown timeout expired: name=%s id=%s",
1322 self.info['name_label'], self.domid)
1323 self.storeDom('xend/unresponsive', 'True')
1324 finally:
1325 self.refresh_shutdown_lock.release()
1327 if restart_reason:
1328 threading.Thread(target = self._maybeRestart,
1329 args = (restart_reason,)).start()
1333 # Restart functions - handling whether we come back up on shutdown.
1336 def _clearRestart(self):
1337 self._removeDom("xend/shutdown_start_time")
1340 def _maybeRestart(self, reason):
1341 # Dispatch to the correct method based upon the configured on_{reason}
1342 # behaviour.
1343 actions = {"destroy" : self.destroy,
1344 "restart" : self._restart,
1345 "preserve" : self._preserve,
1346 "rename-restart" : self._renameRestart}
1348 action_conf = {
1349 'poweroff': 'actions_after_shutdown',
1350 'reboot': 'actions_after_reboot',
1351 'crash': 'actions_after_crash',
1354 action_target = self.info.get(action_conf.get(reason))
1355 func = actions.get(action_target, None)
1356 if func and callable(func):
1357 func()
1358 else:
1359 self.destroy() # default to destroy
1361 def _renameRestart(self):
1362 self._restart(True)
1364 def _restart(self, rename = False):
1365 """Restart the domain after it has exited.
1367 @param rename True if the old domain is to be renamed and preserved,
1368 False if it is to be destroyed.
1369 """
1370 from xen.xend import XendDomain
1372 if self._readVm(RESTART_IN_PROGRESS):
1373 log.error('Xend failed during restart of domain %s. '
1374 'Refusing to restart to avoid loops.',
1375 str(self.domid))
1376 self.destroy()
1377 return
1379 old_domid = self.domid
1380 self._writeVm(RESTART_IN_PROGRESS, 'True')
1382 now = time.time()
1383 rst = self._readVm('xend/previous_restart_time')
1384 if rst:
1385 rst = float(rst)
1386 timeout = now - rst
1387 if timeout < MINIMUM_RESTART_TIME:
1388 log.error(
1389 'VM %s restarting too fast (%f seconds since the last '
1390 'restart). Refusing to restart to avoid loops.',
1391 self.info['name_label'], timeout)
1392 self.destroy()
1393 return
1395 self._writeVm('xend/previous_restart_time', str(now))
1397 new_dom_info = self.info
1398 try:
1399 if rename:
1400 new_dom_info = self._preserveForRestart()
1401 else:
1402 self._unwatchVm()
1403 self.destroy()
1405 # new_dom's VM will be the same as this domain's VM, except where
1406 # the rename flag has instructed us to call preserveForRestart.
1407 # In that case, it is important that we remove the
1408 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1409 # once the new one is available.
1411 new_dom = None
1412 try:
1413 new_dom = XendDomain.instance().domain_create_from_dict(
1414 new_dom_info)
1415 new_dom.waitForDevices()
1416 new_dom.unpause()
1417 new_dom._removeVm(RESTART_IN_PROGRESS)
1418 except:
1419 if new_dom:
1420 new_dom._removeVm(RESTART_IN_PROGRESS)
1421 new_dom.destroy()
1422 else:
1423 self._removeVm(RESTART_IN_PROGRESS)
1424 raise
1425 except:
1426 log.exception('Failed to restart domain %s.', str(old_domid))
1428 def _preserveForRestart(self):
1429 """Preserve a domain that has been shut down, by giving it a new UUID,
1430 cloning the VM details, and giving it a new name. This allows us to
1431 keep this domain for debugging, but restart a new one in its place
1432 preserving the restart semantics (name and UUID preserved).
1433 """
1435 new_uuid = uuid.createString()
1436 new_name = 'Domain-%s' % new_uuid
1437 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1438 self.info['name_label'], self.domid, self.info['uuid'],
1439 new_name, new_uuid)
1440 self._unwatchVm()
1441 self._releaseDevices()
1442 # Remove existing vm node in xenstore
1443 self._removeVm()
1444 new_dom_info = self.info.copy()
1445 new_dom_info['name_label'] = self.info['name_label']
1446 new_dom_info['uuid'] = self.info['uuid']
1447 self.info['name_label'] = new_name
1448 self.info['uuid'] = new_uuid
1449 self.vmpath = XS_VMROOT + new_uuid
1450 # Write out new vm node to xenstore
1451 self._storeVmDetails()
1452 rst_cnt = self._readVm('xend/restart_count')
1453 rst_cnt = int(rst_cnt) + 1
1454 self._writeVm('xend/restart_count', str(rst_cnt))
1455 self._preserve()
1456 return new_dom_info
1459 def _preserve(self):
1460 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1461 self.domid)
1462 self._unwatchVm()
1463 self.storeDom('xend/shutdown_completed', 'True')
1464 self._stateSet(DOM_STATE_HALTED)
1467 # Debugging ..
1470 def dumpCore(self, corefile = None):
1471 """Create a core dump for this domain.
1473 @raise: XendError if core dumping failed.
1474 """
1476 try:
1477 if not corefile:
1478 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1479 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1480 self.info['name_label'], self.domid)
1482 if os.path.isdir(corefile):
1483 raise XendError("Cannot dump core in a directory: %s" %
1484 corefile)
1486 xc.domain_dumpcore(self.domid, corefile)
1487 except RuntimeError, ex:
1488 corefile_incomp = corefile+'-incomplete'
1489 os.rename(corefile, corefile_incomp)
1490 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1491 self.domid, self.info['name_label'])
1492 raise XendError("Failed to dump core: %s" % str(ex))
1495 # Device creation/deletion functions
1498 def _createDevice(self, deviceClass, devConfig):
1499 return self.getDeviceController(deviceClass).createDevice(devConfig)
1501 def _waitForDevice(self, deviceClass, devid):
1502 return self.getDeviceController(deviceClass).waitForDevice(devid)
1504 def _waitForDeviceUUID(self, dev_uuid):
1505 deviceClass, config = self.info['devices'].get(dev_uuid)
1506 self._waitForDevice(deviceClass, config['devid'])
1508 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1509 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1510 devid, backpath)
1512 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1513 return self.getDeviceController(deviceClass).reconfigureDevice(
1514 devid, devconfig)
1516 def _createDevices(self):
1517 """Create the devices for a vm.
1519 @raise: VmError for invalid devices
1520 """
1521 ordered_refs = self.info.ordered_device_refs()
1522 for dev_uuid in ordered_refs:
1523 devclass, config = self.info['devices'][dev_uuid]
1524 if devclass in XendDevices.valid_devices():
1525 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1526 dev_uuid = config.get('uuid')
1527 devid = self._createDevice(devclass, config)
1529 # store devid in XendConfig for caching reasons
1530 if dev_uuid in self.info['devices']:
1531 self.info['devices'][dev_uuid][1]['devid'] = devid
1533 if self.image:
1534 self.image.createDeviceModel()
1536 def _releaseDevices(self, suspend = False):
1537 """Release all domain's devices. Nothrow guarantee."""
1538 if self.image:
1539 try:
1540 log.debug("Destroying device model")
1541 self.image.destroyDeviceModel()
1542 except Exception, e:
1543 log.exception("Device model destroy failed %s" % str(e))
1544 else:
1545 log.debug("No device model")
1547 log.debug("Releasing devices")
1548 t = xstransact("%s/device" % self.dompath)
1549 try:
1550 for devclass in XendDevices.valid_devices():
1551 for dev in t.list(devclass):
1552 try:
1553 log.debug("Removing %s", dev);
1554 self.destroyDevice(devclass, dev, False);
1555 except:
1556 # Log and swallow any exceptions in removal --
1557 # there's nothing more we can do.
1558 log.exception("Device release failed: %s; %s; %s",
1559 self.info['name_label'], devclass, dev)
1560 finally:
1561 t.abort()
1563 def getDeviceController(self, name):
1564 """Get the device controller for this domain, and if it
1565 doesn't exist, create it.
1567 @param name: device class name
1568 @type name: string
1569 @rtype: subclass of DevController
1570 """
1571 if name not in self._deviceControllers:
1572 devController = XendDevices.make_controller(name, self)
1573 if not devController:
1574 raise XendError("Unknown device type: %s" % name)
1575 self._deviceControllers[name] = devController
1577 return self._deviceControllers[name]
1580 # Migration functions (public)
1583 def testMigrateDevices(self, network, dst):
1584 """ Notify all device about intention of migration
1585 @raise: XendError for a device that cannot be migrated
1586 """
1587 for (n, c) in self.info.all_devices_sxpr():
1588 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1589 if rc != 0:
1590 raise XendError("Device of type '%s' refuses migration." % n)
1592 def migrateDevices(self, network, dst, step, domName=''):
1593 """Notify the devices about migration
1594 """
1595 ctr = 0
1596 try:
1597 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1598 self.migrateDevice(dev_type, dev_conf, network, dst,
1599 step, domName)
1600 ctr = ctr + 1
1601 except:
1602 for dev_type, dev_conf in self.info.all_devices_sxpr():
1603 if ctr == 0:
1604 step = step - 1
1605 ctr = ctr - 1
1606 self._recoverMigrateDevice(dev_type, dev_conf, network,
1607 dst, step, domName)
1608 raise
1610 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1611 step, domName=''):
1612 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1613 network, dst, step, domName)
1615 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1616 dst, step, domName=''):
1617 return self.getDeviceController(deviceClass).recover_migrate(
1618 deviceConfig, network, dst, step, domName)
1621 ## private:
1623 def _constructDomain(self):
1624 """Construct the domain.
1626 @raise: VmError on error
1627 """
1629 log.debug('XendDomainInfo.constructDomain')
1631 self.shutdownStartTime = None
1633 hap = 0
1634 hvm = self.info.is_hvm()
1635 if hvm:
1636 hap = self.info.is_hap()
1637 info = xc.xeninfo()
1638 if 'hvm' not in info['xen_caps']:
1639 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1640 "supported by your CPU and enabled in your "
1641 "BIOS?")
1643 # Hack to pre-reserve some memory for initial domain creation.
1644 # There is an implicit memory overhead for any domain creation. This
1645 # overhead is greater for some types of domain than others. For
1646 # example, an x86 HVM domain will have a default shadow-pagetable
1647 # allocation of 1MB. We free up 2MB here to be on the safe side.
1648 balloon.free(2*1024) # 2MB should be plenty
1650 ssidref = 0
1651 if security.on():
1652 ssidref = security.calc_dom_ssidref_from_info(self.info)
1653 if security.has_authorization(ssidref) == False:
1654 raise VmError("VM is not authorized to run.")
1656 try:
1657 self.domid = xc.domain_create(
1658 domid = 0,
1659 ssidref = ssidref,
1660 handle = uuid.fromString(self.info['uuid']),
1661 flags = (int(hvm) << 0) | (int(hap) << 1),
1662 target = self.info.target())
1663 except Exception, e:
1664 # may get here if due to ACM the operation is not permitted
1665 if security.on():
1666 raise VmError('Domain in conflict set with running domain?')
1668 if self.domid < 0:
1669 raise VmError('Creating domain failed: name=%s' %
1670 self.info['name_label'])
1672 self.dompath = GetDomainPath(self.domid)
1674 self._recreateDom()
1676 # Set timer configration of domain
1677 timer_mode = self.info["platform"].get("timer_mode")
1678 if hvm and timer_mode is not None:
1679 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
1680 long(timer_mode))
1682 # Set maximum number of vcpus in domain
1683 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1685 # Test whether the devices can be assigned with VT-d
1686 pci_str = str(self.info["platform"].get("pci"))
1687 if hvm and pci_str:
1688 bdf = xc.test_assign_device(self.domid, pci_str)
1689 if bdf != 0:
1690 bus = (bdf >> 16) & 0xff
1691 devfn = (bdf >> 8) & 0xff
1692 dev = (devfn >> 3) & 0x1f
1693 func = devfn & 0x7
1694 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
1695 "not enabled, or the device is not exist, or it "
1696 "has already been assigned to other domain"
1697 % (bus, dev, func))
1699 # register the domain in the list
1700 from xen.xend import XendDomain
1701 XendDomain.instance().add_domain(self)
1703 def _introduceDomain(self):
1704 assert self.domid is not None
1705 assert self.store_mfn is not None
1706 assert self.store_port is not None
1708 try:
1709 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1710 except RuntimeError, exn:
1711 raise XendError(str(exn))
1713 def _setTarget(self, target):
1714 assert self.domid is not None
1716 try:
1717 SetTarget(self.domid, target)
1718 self.storeDom('target', target)
1719 except RuntimeError, exn:
1720 raise XendError(str(exn))
1723 def _initDomain(self):
1724 log.debug('XendDomainInfo.initDomain: %s %s',
1725 self.domid,
1726 self.info['vcpus_params']['weight'])
1728 self._configureBootloader()
1730 try:
1731 if self.info['platform'].get('localtime', 0):
1732 t = time.time()
1733 loc = time.localtime(t)
1734 utc = time.gmtime(t)
1735 timeoffset = int(time.mktime(loc) - time.mktime(utc))
1736 self.info['platform']['rtc_timeoffset'] = timeoffset
1738 self.image = image.create(self, self.info)
1740 xc.domain_setcpuweight(self.domid, \
1741 self.info['vcpus_params']['weight'])
1743 # repin domain vcpus if a restricted cpus list is provided
1744 # this is done prior to memory allocation to aide in memory
1745 # distribution for NUMA systems.
1746 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1747 for v in range(0, self.info['VCPUs_max']):
1748 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1750 # Use architecture- and image-specific calculations to determine
1751 # the various headrooms necessary, given the raw configured
1752 # values. maxmem, memory, and shadow are all in KiB.
1753 # but memory_static_max etc are all stored in bytes now.
1754 memory = self.image.getRequiredAvailableMemory(
1755 self.info['memory_dynamic_max'] / 1024)
1756 maxmem = self.image.getRequiredAvailableMemory(
1757 self.info['memory_static_max'] / 1024)
1758 shadow = self.image.getRequiredShadowMemory(
1759 self.info['shadow_memory'] * 1024,
1760 self.info['memory_static_max'] / 1024)
1762 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'],)
1763 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1764 # takes MiB and we must not round down and end up under-providing.
1765 shadow = ((shadow + 1023) / 1024) * 1024
1767 # set memory limit
1768 xc.domain_setmaxmem(self.domid, maxmem)
1770 # Make sure there's enough RAM available for the domain
1771 balloon.free(memory + shadow)
1773 # Set up the shadow memory
1774 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1775 self.info['shadow_memory'] = shadow_cur
1777 self._createChannels()
1779 channel_details = self.image.createImage()
1781 self.store_mfn = channel_details['store_mfn']
1782 if 'console_mfn' in channel_details:
1783 self.console_mfn = channel_details['console_mfn']
1784 if 'notes' in channel_details:
1785 self.info.set_notes(channel_details['notes'])
1786 if 'native_protocol' in channel_details:
1787 self.native_protocol = channel_details['native_protocol'];
1789 self._introduceDomain()
1790 if self.info.target():
1791 self._setTarget(self.info.target())
1793 self._createDevices()
1795 self.image.cleanupBootloading()
1797 self.info['start_time'] = time.time()
1799 self._stateSet(DOM_STATE_RUNNING)
1800 except VmError, exn:
1801 log.exception("XendDomainInfo.initDomain: exception occurred")
1802 if self.image:
1803 self.image.cleanupBootloading()
1804 raise exn
1805 except RuntimeError, exn:
1806 log.exception("XendDomainInfo.initDomain: exception occurred")
1807 if self.image:
1808 self.image.cleanupBootloading()
1809 raise VmError(str(exn))
1812 def cleanupDomain(self):
1813 """Cleanup domain resources; release devices. Idempotent. Nothrow
1814 guarantee."""
1816 self.refresh_shutdown_lock.acquire()
1817 try:
1818 self.unwatchShutdown()
1819 self._releaseDevices()
1820 bootloader_tidy(self)
1822 if self.image:
1823 self.image = None
1825 try:
1826 self._removeDom()
1827 except:
1828 log.exception("Removing domain path failed.")
1830 self._stateSet(DOM_STATE_HALTED)
1831 self.domid = None # Do not push into _stateSet()!
1832 finally:
1833 self.refresh_shutdown_lock.release()
1836 def unwatchShutdown(self):
1837 """Remove the watch on the domain's control/shutdown node, if any.
1838 Idempotent. Nothrow guarantee. Expects to be protected by the
1839 refresh_shutdown_lock."""
1841 try:
1842 try:
1843 if self.shutdownWatch:
1844 self.shutdownWatch.unwatch()
1845 finally:
1846 self.shutdownWatch = None
1847 except:
1848 log.exception("Unwatching control/shutdown failed.")
1850 def waitForShutdown(self):
1851 self.state_updated.acquire()
1852 try:
1853 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1854 self.state_updated.wait()
1855 finally:
1856 self.state_updated.release()
1859 # TODO: recategorise - called from XendCheckpoint
1862 def completeRestore(self, store_mfn, console_mfn):
1864 log.debug("XendDomainInfo.completeRestore")
1866 self.store_mfn = store_mfn
1867 self.console_mfn = console_mfn
1869 self._introduceDomain()
1870 self.image = image.create(self, self.info)
1871 if self.image:
1872 self.image.createDeviceModel(True)
1873 self._storeDomDetails()
1874 self._registerWatches()
1875 self.refreshShutdown()
1877 log.debug("XendDomainInfo.completeRestore done")
1880 def _endRestore(self):
1881 self.setResume(False)
1884 # VM Destroy
1887 def _prepare_phantom_paths(self):
1888 # get associated devices to destroy
1889 # build list of phantom devices to be removed after normal devices
1890 plist = []
1891 if self.domid is not None:
1892 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1893 try:
1894 for dev in t.list():
1895 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1896 % (self.dompath, dev))
1897 if backend_phantom_vbd is not None:
1898 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1899 % backend_phantom_vbd)
1900 plist.append(backend_phantom_vbd)
1901 plist.append(frontend_phantom_vbd)
1902 finally:
1903 t.abort()
1904 return plist
1906 def _cleanup_phantom_devs(self, plist):
1907 # remove phantom devices
1908 if not plist == []:
1909 time.sleep(2)
1910 for paths in plist:
1911 if paths.find('backend') != -1:
1912 from xen.xend.server import DevController
1913 # Modify online status /before/ updating state (latter is watched by
1914 # drivers, so this ordering avoids a race).
1915 xstransact.Write(paths, 'online', "0")
1916 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1917 # force
1918 xstransact.Remove(paths)
1920 def destroy(self):
1921 """Cleanup VM and destroy domain. Nothrow guarantee."""
1923 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1925 paths = self._prepare_phantom_paths()
1927 self._cleanupVm()
1928 if self.dompath is not None:
1929 self.destroyDomain()
1931 self._cleanup_phantom_devs(paths)
1933 if "transient" in self.info["other_config"] \
1934 and bool(self.info["other_config"]["transient"]):
1935 from xen.xend import XendDomain
1936 XendDomain.instance().domain_delete_by_dominfo(self)
1939 def destroyDomain(self):
1940 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1942 paths = self._prepare_phantom_paths()
1944 try:
1945 if self.domid is not None:
1946 xc.domain_destroy_hook(self.domid)
1947 xc.domain_destroy(self.domid)
1948 for state in DOM_STATES_OLD:
1949 self.info[state] = 0
1950 self._stateSet(DOM_STATE_HALTED)
1951 except:
1952 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1954 from xen.xend import XendDomain
1955 XendDomain.instance().remove_domain(self)
1957 self.cleanupDomain()
1958 self._cleanup_phantom_devs(paths)
1961 def resumeDomain(self):
1962 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1964 if self.domid is None:
1965 return
1966 try:
1967 # could also fetch a parsed note from xenstore
1968 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1969 if not fast:
1970 self._releaseDevices()
1971 self.testDeviceComplete()
1972 self.testvifsComplete()
1973 log.debug("XendDomainInfo.resumeDomain: devices released")
1975 self._resetChannels()
1977 self._removeDom('control/shutdown')
1978 self._removeDom('device-misc/vif/nextDeviceID')
1980 self._createChannels()
1981 self._introduceDomain()
1982 self._storeDomDetails()
1984 self._createDevices()
1985 log.debug("XendDomainInfo.resumeDomain: devices created")
1987 xc.domain_resume(self.domid, fast)
1988 ResumeDomain(self.domid)
1989 except:
1990 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1991 self.image.resumeDeviceModel()
1992 log.debug("XendDomainInfo.resumeDomain: completed")
1996 # Channels for xenstore and console
1999 def _createChannels(self):
2000 """Create the channels to the domain.
2001 """
2002 self.store_port = self._createChannel()
2003 self.console_port = self._createChannel()
2006 def _createChannel(self):
2007 """Create an event channel to the domain.
2008 """
2009 try:
2010 if self.domid != None:
2011 return xc.evtchn_alloc_unbound(domid = self.domid,
2012 remote_dom = 0)
2013 except:
2014 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2015 raise
2017 def _resetChannels(self):
2018 """Reset all event channels in the domain.
2019 """
2020 try:
2021 if self.domid != None:
2022 return xc.evtchn_reset(dom = self.domid)
2023 except:
2024 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2025 raise
2029 # Bootloader configuration
2032 def _configureBootloader(self):
2033 """Run the bootloader if we're configured to do so."""
2035 blexec = self.info['PV_bootloader']
2036 bootloader_args = self.info['PV_bootloader_args']
2037 kernel = self.info['PV_kernel']
2038 ramdisk = self.info['PV_ramdisk']
2039 args = self.info['PV_args']
2040 boot = self.info['HVM_boot_policy']
2042 if boot:
2043 # HVM booting.
2044 pass
2045 elif not blexec and kernel:
2046 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2047 # will be picked up by image.py.
2048 pass
2049 else:
2050 # Boot using bootloader
2051 if not blexec or blexec == 'pygrub':
2052 blexec = osdep.pygrub_path
2054 blcfg = None
2055 disks = [x for x in self.info['vbd_refs']
2056 if self.info['devices'][x][1]['bootable']]
2058 if not disks:
2059 msg = "Had a bootloader specified, but no disks are bootable"
2060 log.error(msg)
2061 raise VmError(msg)
2063 devinfo = self.info['devices'][disks[0]]
2064 devtype = devinfo[0]
2065 disk = devinfo[1]['uname']
2067 fn = blkdev_uname_to_file(disk)
2068 taptype = blkdev_uname_to_taptype(disk)
2069 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2070 if mounted:
2071 # This is a file, not a device. pygrub can cope with a
2072 # file if it's raw, but if it's QCOW or other such formats
2073 # used through blktap, then we need to mount it first.
2075 log.info("Mounting %s on %s." %
2076 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2078 vbd = {
2079 'mode': 'RO',
2080 'device': BOOTLOADER_LOOPBACK_DEVICE,
2083 from xen.xend import XendDomain
2084 dom0 = XendDomain.instance().privilegedDomain()
2085 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2086 fn = BOOTLOADER_LOOPBACK_DEVICE
2088 try:
2089 blcfg = bootloader(blexec, fn, self, False,
2090 bootloader_args, kernel, ramdisk, args)
2091 finally:
2092 if mounted:
2093 log.info("Unmounting %s from %s." %
2094 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2096 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2098 if blcfg is None:
2099 msg = "Had a bootloader specified, but can't find disk"
2100 log.error(msg)
2101 raise VmError(msg)
2103 self.info.update_with_image_sxp(blcfg, True)
2107 # VM Functions
2110 def _readVMDetails(self, params):
2111 """Read the specified parameters from the store.
2112 """
2113 try:
2114 return self._gatherVm(*params)
2115 except ValueError:
2116 # One of the int/float entries in params has a corresponding store
2117 # entry that is invalid. We recover, because older versions of
2118 # Xend may have put the entry there (memory/target, for example),
2119 # but this is in general a bad situation to have reached.
2120 log.exception(
2121 "Store corrupted at %s! Domain %d's configuration may be "
2122 "affected.", self.vmpath, self.domid)
2123 return []
2125 def _cleanupVm(self):
2126 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2128 self._unwatchVm()
2130 try:
2131 self._removeVm()
2132 except:
2133 log.exception("Removing VM path failed.")
2136 def checkLiveMigrateMemory(self):
2137 """ Make sure there's enough memory to migrate this domain """
2138 overhead_kb = 0
2139 if arch.type == "x86":
2140 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2141 # the minimum that Xen would allocate if no value were given.
2142 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2143 (self.info['memory_static_max'] / 1024 / 1024) * 4
2144 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2145 # The domain might already have some shadow memory
2146 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2147 if overhead_kb > 0:
2148 balloon.free(overhead_kb)
2150 def _unwatchVm(self):
2151 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2152 guarantee."""
2153 try:
2154 try:
2155 if self.vmWatch:
2156 self.vmWatch.unwatch()
2157 finally:
2158 self.vmWatch = None
2159 except:
2160 log.exception("Unwatching VM path failed.")
2162 def testDeviceComplete(self):
2163 """ For Block IO migration safety we must ensure that
2164 the device has shutdown correctly, i.e. all blocks are
2165 flushed to disk
2166 """
2167 start = time.time()
2168 while True:
2169 test = 0
2170 diff = time.time() - start
2171 for i in self.getDeviceController('vbd').deviceIDs():
2172 test = 1
2173 log.info("Dev %s still active, looping...", i)
2174 time.sleep(0.1)
2176 if test == 0:
2177 break
2178 if diff >= MIGRATE_TIMEOUT:
2179 log.info("Dev still active but hit max loop timeout")
2180 break
2182 def testvifsComplete(self):
2183 """ In case vifs are released and then created for the same
2184 domain, we need to wait the device shut down.
2185 """
2186 start = time.time()
2187 while True:
2188 test = 0
2189 diff = time.time() - start
2190 for i in self.getDeviceController('vif').deviceIDs():
2191 test = 1
2192 log.info("Dev %s still active, looping...", i)
2193 time.sleep(0.1)
2195 if test == 0:
2196 break
2197 if diff >= MIGRATE_TIMEOUT:
2198 log.info("Dev still active but hit max loop timeout")
2199 break
2201 def _storeVmDetails(self):
2202 to_store = {}
2204 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2205 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2206 if self._infoIsSet(info_key):
2207 to_store[key] = str(self.info[info_key])
2209 if self._infoIsSet("static_memory_min"):
2210 to_store["memory"] = str(self.info["static_memory_min"])
2211 if self._infoIsSet("static_memory_max"):
2212 to_store["maxmem"] = str(self.info["static_memory_max"])
2214 image_sxpr = self.info.image_sxpr()
2215 if image_sxpr:
2216 to_store['image'] = sxp.to_string(image_sxpr)
2218 if not self._readVm('xend/restart_count'):
2219 to_store['xend/restart_count'] = str(0)
2221 log.debug("Storing VM details: %s", scrub_password(to_store))
2223 self._writeVm(to_store)
2224 self._setVmPermissions()
2227 def _setVmPermissions(self):
2228 """Allow the guest domain to read its UUID. We don't allow it to
2229 access any other entry, for security."""
2230 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2231 { 'dom' : self.domid,
2232 'read' : True,
2233 'write' : False })
2236 # Utility functions
2239 def __getattr__(self, name):
2240 if name == "state":
2241 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2242 log.warn("".join(traceback.format_stack()))
2243 return self._stateGet()
2244 else:
2245 raise AttributeError()
2247 def __setattr__(self, name, value):
2248 if name == "state":
2249 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2250 log.warn("".join(traceback.format_stack()))
2251 self._stateSet(value)
2252 else:
2253 self.__dict__[name] = value
2255 def _stateSet(self, state):
2256 self.state_updated.acquire()
2257 try:
2258 # TODO Not sure this is correct...
2259 # _stateGet is live now. Why not fire event
2260 # even when it hasn't changed?
2261 if self._stateGet() != state:
2262 self.state_updated.notifyAll()
2263 import XendAPI
2264 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2265 'power_state')
2266 finally:
2267 self.state_updated.release()
2269 def _stateGet(self):
2270 # Lets try and reconsitute the state from xc
2271 # first lets try and get the domain info
2272 # from xc - this will tell us if the domain
2273 # exists
2274 info = dom_get(self.getDomid())
2275 if info is None or info['shutdown']:
2276 # We are either HALTED or SUSPENDED
2277 # check saved image exists
2278 from xen.xend import XendDomain
2279 managed_config_path = \
2280 XendDomain.instance()._managed_check_point_path( \
2281 self.get_uuid())
2282 if os.path.exists(managed_config_path):
2283 return XEN_API_VM_POWER_STATE_SUSPENDED
2284 else:
2285 return XEN_API_VM_POWER_STATE_HALTED
2286 elif info['crashed']:
2287 # Crashed
2288 return XEN_API_VM_POWER_STATE_CRASHED
2289 else:
2290 # We are either RUNNING or PAUSED
2291 if info['paused']:
2292 return XEN_API_VM_POWER_STATE_PAUSED
2293 else:
2294 return XEN_API_VM_POWER_STATE_RUNNING
2296 def _infoIsSet(self, name):
2297 return name in self.info and self.info[name] is not None
2299 def _checkName(self, name):
2300 """Check if a vm name is valid. Valid names contain alphabetic
2301 characters, digits, or characters in '_-.:/+'.
2302 The same name cannot be used for more than one vm at the same time.
2304 @param name: name
2305 @raise: VmError if invalid
2306 """
2307 from xen.xend import XendDomain
2309 if name is None or name == '':
2310 raise VmError('Missing VM Name')
2312 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2313 raise VmError('Invalid VM Name')
2315 dom = XendDomain.instance().domain_lookup_nr(name)
2316 if dom and dom.info['uuid'] != self.info['uuid']:
2317 raise VmError("VM name '%s' already exists%s" %
2318 (name,
2319 dom.domid is not None and
2320 (" as domain %s" % str(dom.domid)) or ""))
2323 def update(self, info = None, refresh = True, transaction = None):
2324 """Update with info from xc.domain_getinfo().
2325 """
2326 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2327 str(self.domid))
2329 if not info:
2330 info = dom_get(self.domid)
2331 if not info:
2332 return
2334 if info["maxmem_kb"] < 0:
2335 info["maxmem_kb"] = XendNode.instance() \
2336 .physinfo_dict()['total_memory'] * 1024
2338 #ssidref field not used any longer
2339 if 'ssidref' in info:
2340 info.pop('ssidref')
2342 # make sure state is reset for info
2343 # TODO: we should eventually get rid of old_dom_states
2345 self.info.update_config(info)
2346 self._update_consoles(transaction)
2348 if refresh:
2349 self.refreshShutdown(info)
2351 log.trace("XendDomainInfo.update done on domain %s: %s",
2352 str(self.domid), self.info)
2354 def sxpr(self, ignore_store = False, legacy_only = True):
2355 result = self.info.to_sxp(domain = self,
2356 ignore_devices = ignore_store,
2357 legacy_only = legacy_only)
2359 #if not ignore_store and self.dompath:
2360 # vnc_port = self.readDom('console/vnc-port')
2361 # if vnc_port is not None:
2362 # result.append(['device',
2363 # ['console', ['vnc-port', str(vnc_port)]]])
2365 return result
2367 # Xen API
2368 # ----------------------------------------------------------------
2370 def get_uuid(self):
2371 dom_uuid = self.info.get('uuid')
2372 if not dom_uuid: # if it doesn't exist, make one up
2373 dom_uuid = uuid.createString()
2374 self.info['uuid'] = dom_uuid
2375 return dom_uuid
2377 def get_memory_static_max(self):
2378 return self.info.get('memory_static_max', 0)
2379 def get_memory_static_min(self):
2380 return self.info.get('memory_static_min', 0)
2381 def get_memory_dynamic_max(self):
2382 return self.info.get('memory_dynamic_max', 0)
2383 def get_memory_dynamic_min(self):
2384 return self.info.get('memory_dynamic_min', 0)
2386 # only update memory-related config values if they maintain sanity
2387 def _safe_set_memory(self, key, newval):
2388 oldval = self.info.get(key, 0)
2389 try:
2390 self.info[key] = newval
2391 self.info._memory_sanity_check()
2392 except Exception, ex:
2393 self.info[key] = oldval
2394 raise
2396 def set_memory_static_max(self, val):
2397 self._safe_set_memory('memory_static_max', val)
2398 def set_memory_static_min(self, val):
2399 self._safe_set_memory('memory_static_min', val)
2400 def set_memory_dynamic_max(self, val):
2401 self._safe_set_memory('memory_dynamic_max', val)
2402 def set_memory_dynamic_min(self, val):
2403 self._safe_set_memory('memory_dynamic_min', val)
2405 def get_vcpus_params(self):
2406 if self.getDomid() is None:
2407 return self.info['vcpus_params']
2409 retval = xc.sched_credit_domain_get(self.getDomid())
2410 return retval
2411 def get_power_state(self):
2412 return XEN_API_VM_POWER_STATE[self._stateGet()]
2413 def get_platform(self):
2414 return self.info.get('platform', {})
2415 def get_pci_bus(self):
2416 return self.info.get('pci_bus', '')
2417 def get_tools_version(self):
2418 return self.info.get('tools_version', {})
2419 def get_metrics(self):
2420 return self.metrics.get_uuid();
2423 def get_security_label(self, xspol=None):
2424 import xen.util.xsm.xsm as security
2425 label = security.get_security_label(self, xspol)
2426 return label
2428 def set_security_label(self, seclab, old_seclab, xspol=None,
2429 xspol_old=None):
2430 """
2431 Set the security label of a domain from its old to
2432 a new value.
2433 @param seclab New security label formatted in the form
2434 <policy type>:<policy name>:<vm label>
2435 @param old_seclab The current security label that the
2436 VM must have.
2437 @param xspol An optional policy under which this
2438 update should be done. If not given,
2439 then the current active policy is used.
2440 @param xspol_old The old policy; only to be passed during
2441 the updating of a policy
2442 @return Returns return code, a string with errors from
2443 the hypervisor's operation, old label of the
2444 domain
2445 """
2446 rc = 0
2447 errors = ""
2448 old_label = ""
2449 new_ssidref = 0
2450 domid = self.getDomid()
2451 res_labels = None
2452 is_policy_update = (xspol_old != None)
2454 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2455 from xen.util import xsconstants
2457 state = self._stateGet()
2458 # Relabel only HALTED or RUNNING or PAUSED domains
2459 if domid != 0 and \
2460 state not in \
2461 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2462 DOM_STATE_SUSPENDED ]:
2463 log.warn("Relabeling domain not possible in state '%s'" %
2464 DOM_STATES[state])
2465 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2467 # Remove security label. Works only for halted domains
2468 if not seclab or seclab == "":
2469 if state not in [ DOM_STATE_HALTED ]:
2470 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2472 if self.info.has_key('security_label'):
2473 old_label = self.info['security_label']
2474 # Check label against expected one.
2475 if old_label != old_seclab:
2476 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2477 del self.info['security_label']
2478 xen.xend.XendDomain.instance().managed_config_save(self)
2479 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2481 tmp = seclab.split(":")
2482 if len(tmp) != 3:
2483 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2484 typ, policy, label = tmp
2486 poladmin = XSPolicyAdminInstance()
2487 if not xspol:
2488 xspol = poladmin.get_policy_by_name(policy)
2490 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2491 #if domain is running or paused try to relabel in hypervisor
2492 if not xspol:
2493 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2495 if typ != xspol.get_type_name() or \
2496 policy != xspol.get_name():
2497 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2499 if typ == xsconstants.ACM_POLICY_ID:
2500 new_ssidref = xspol.vmlabel_to_ssidref(label)
2501 if new_ssidref == xsconstants.INVALID_SSIDREF:
2502 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2504 # Check that all used resources are accessible under the
2505 # new label
2506 if not is_policy_update and \
2507 not security.resources_compatible_with_vmlabel(xspol,
2508 self, label):
2509 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2511 #Check label against expected one. Can only do this
2512 # if the policy hasn't changed underneath in the meantime
2513 if xspol_old == None:
2514 old_label = self.get_security_label()
2515 if old_label != old_seclab:
2516 log.info("old_label != old_seclab: %s != %s" %
2517 (old_label, old_seclab))
2518 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2520 # relabel domain in the hypervisor
2521 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2522 log.info("rc from relabeling in HV: %d" % rc)
2523 else:
2524 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2526 if rc == 0:
2527 # HALTED, RUNNING or PAUSED
2528 if domid == 0:
2529 if xspol:
2530 self.info['security_label'] = seclab
2531 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2532 else:
2533 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2534 else:
2535 if self.info.has_key('security_label'):
2536 old_label = self.info['security_label']
2537 # Check label against expected one, unless wildcard
2538 if old_label != old_seclab:
2539 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2541 self.info['security_label'] = seclab
2543 try:
2544 xen.xend.XendDomain.instance().managed_config_save(self)
2545 except:
2546 pass
2547 return (rc, errors, old_label, new_ssidref)
2549 def get_on_shutdown(self):
2550 after_shutdown = self.info.get('actions_after_shutdown')
2551 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2552 return XEN_API_ON_NORMAL_EXIT[-1]
2553 return after_shutdown
2555 def get_on_reboot(self):
2556 after_reboot = self.info.get('actions_after_reboot')
2557 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2558 return XEN_API_ON_NORMAL_EXIT[-1]
2559 return after_reboot
2561 def get_on_suspend(self):
2562 # TODO: not supported
2563 after_suspend = self.info.get('actions_after_suspend')
2564 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2565 return XEN_API_ON_NORMAL_EXIT[-1]
2566 return after_suspend
2568 def get_on_crash(self):
2569 after_crash = self.info.get('actions_after_crash')
2570 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2571 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2572 return after_crash
2574 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2575 """ Get's a device configuration either from XendConfig or
2576 from the DevController.
2578 @param dev_class: device class, either, 'vbd' or 'vif'
2579 @param dev_uuid: device UUID
2581 @rtype: dictionary
2582 """
2583 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2585 # shortcut if the domain isn't started because
2586 # the devcontrollers will have no better information
2587 # than XendConfig.
2588 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2589 if dev_config:
2590 return copy.deepcopy(dev_config)
2591 return None
2593 # instead of using dev_class, we use the dev_type
2594 # that is from XendConfig.
2595 controller = self.getDeviceController(dev_type)
2596 if not controller:
2597 return None
2599 all_configs = controller.getAllDeviceConfigurations()
2600 if not all_configs:
2601 return None
2603 updated_dev_config = copy.deepcopy(dev_config)
2604 for _devid, _devcfg in all_configs.items():
2605 if _devcfg.get('uuid') == dev_uuid:
2606 updated_dev_config.update(_devcfg)
2607 updated_dev_config['id'] = _devid
2608 return updated_dev_config
2610 return updated_dev_config
2612 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2613 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2614 if not config:
2615 return {}
2617 config['VM'] = self.get_uuid()
2619 if dev_class == 'vif':
2620 if not config.has_key('name'):
2621 config['name'] = config.get('vifname', '')
2622 if not config.has_key('MAC'):
2623 config['MAC'] = config.get('mac', '')
2624 if not config.has_key('type'):
2625 config['type'] = 'paravirtualised'
2626 if not config.has_key('device'):
2627 devid = config.get('id')
2628 if devid != None:
2629 config['device'] = 'eth%d' % devid
2630 else:
2631 config['device'] = ''
2633 if not config.has_key('network'):
2634 try:
2635 bridge = config.get('bridge', None)
2636 if bridge is None:
2637 from xen.util import Brctl
2638 if_to_br = dict([(i,b)
2639 for (b,ifs) in Brctl.get_state().items()
2640 for i in ifs])
2641 vifname = "vif%s.%s" % (self.getDomid(),
2642 config.get('id'))
2643 bridge = if_to_br.get(vifname, None)
2644 config['network'] = \
2645 XendNode.instance().bridge_to_network(
2646 config.get('bridge')).get_uuid()
2647 except Exception:
2648 log.exception('bridge_to_network')
2649 # Ignore this for now -- it may happen if the device
2650 # has been specified using the legacy methods, but at
2651 # some point we're going to have to figure out how to
2652 # handle that properly.
2654 config['MTU'] = 1500 # TODO
2656 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2657 xennode = XendNode.instance()
2658 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2659 config['io_read_kbs'] = rx_bps/1024
2660 config['io_write_kbs'] = tx_bps/1024
2661 rx, tx = xennode.get_vif_stat(self.domid, devid)
2662 config['io_total_read_kbs'] = rx/1024
2663 config['io_total_write_kbs'] = tx/1024
2664 else:
2665 config['io_read_kbs'] = 0.0
2666 config['io_write_kbs'] = 0.0
2667 config['io_total_read_kbs'] = 0.0
2668 config['io_total_write_kbs'] = 0.0
2670 config['security_label'] = config.get('security_label', '')
2672 if dev_class == 'vbd':
2674 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2675 controller = self.getDeviceController(dev_class)
2676 devid, _1, _2 = controller.getDeviceDetails(config)
2677 xennode = XendNode.instance()
2678 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2679 config['io_read_kbs'] = rd_blkps
2680 config['io_write_kbs'] = wr_blkps
2681 else:
2682 config['io_read_kbs'] = 0.0
2683 config['io_write_kbs'] = 0.0
2685 config['VDI'] = config.get('VDI', '')
2686 config['device'] = config.get('dev', '')
2687 if ':' in config['device']:
2688 vbd_name, vbd_type = config['device'].split(':', 1)
2689 config['device'] = vbd_name
2690 if vbd_type == 'cdrom':
2691 config['type'] = XEN_API_VBD_TYPE[0]
2692 else:
2693 config['type'] = XEN_API_VBD_TYPE[1]
2695 config['driver'] = 'paravirtualised' # TODO
2696 config['image'] = config.get('uname', '')
2698 if config.get('mode', 'r') == 'r':
2699 config['mode'] = 'RO'
2700 else:
2701 config['mode'] = 'RW'
2703 if dev_class == 'vtpm':
2704 if not config.has_key('type'):
2705 config['type'] = 'paravirtualised' # TODO
2706 if not config.has_key('backend'):
2707 config['backend'] = "00000000-0000-0000-0000-000000000000"
2709 return config
2711 def get_dev_property(self, dev_class, dev_uuid, field):
2712 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2713 try:
2714 return config[field]
2715 except KeyError:
2716 raise XendError('Invalid property for device: %s' % field)
2718 def set_dev_property(self, dev_class, dev_uuid, field, value):
2719 self.info['devices'][dev_uuid][1][field] = value
2721 def get_vcpus_util(self):
2722 vcpu_util = {}
2723 xennode = XendNode.instance()
2724 if 'VCPUs_max' in self.info and self.domid != None:
2725 for i in range(0, self.info['VCPUs_max']):
2726 util = xennode.get_vcpu_util(self.domid, i)
2727 vcpu_util[str(i)] = util
2729 return vcpu_util
2731 def get_consoles(self):
2732 return self.info.get('console_refs', [])
2734 def get_vifs(self):
2735 return self.info.get('vif_refs', [])
2737 def get_vbds(self):
2738 return self.info.get('vbd_refs', [])
2740 def get_vtpms(self):
2741 return self.info.get('vtpm_refs', [])
2743 def create_vbd(self, xenapi_vbd, vdi_image_path):
2744 """Create a VBD using a VDI from XendStorageRepository.
2746 @param xenapi_vbd: vbd struct from the Xen API
2747 @param vdi_image_path: VDI UUID
2748 @rtype: string
2749 @return: uuid of the device
2750 """
2751 xenapi_vbd['image'] = vdi_image_path
2752 if vdi_image_path.startswith('tap'):
2753 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2754 else:
2755 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2757 if not dev_uuid:
2758 raise XendError('Failed to create device')
2760 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2761 XEN_API_VM_POWER_STATE_PAUSED):
2762 _, config = self.info['devices'][dev_uuid]
2764 if vdi_image_path.startswith('tap'):
2765 dev_control = self.getDeviceController('tap')
2766 else:
2767 dev_control = self.getDeviceController('vbd')
2769 try:
2770 devid = dev_control.createDevice(config)
2771 dev_control.waitForDevice(devid)
2772 self.info.device_update(dev_uuid,
2773 cfg_xenapi = {'devid': devid})
2774 except Exception, exn:
2775 log.exception(exn)
2776 del self.info['devices'][dev_uuid]
2777 self.info['vbd_refs'].remove(dev_uuid)
2778 raise
2780 return dev_uuid
2782 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2783 """Create a VBD using a VDI from XendStorageRepository.
2785 @param xenapi_vbd: vbd struct from the Xen API
2786 @param vdi_image_path: VDI UUID
2787 @rtype: string
2788 @return: uuid of the device
2789 """
2790 xenapi_vbd['image'] = vdi_image_path
2791 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2792 if not dev_uuid:
2793 raise XendError('Failed to create device')
2795 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2796 _, config = self.info['devices'][dev_uuid]
2797 config['devid'] = self.getDeviceController('tap').createDevice(config)
2799 return config['devid']
2801 def create_vif(self, xenapi_vif):
2802 """Create VIF device from the passed struct in Xen API format.
2804 @param xenapi_vif: Xen API VIF Struct.
2805 @rtype: string
2806 @return: UUID
2807 """
2808 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2809 if not dev_uuid:
2810 raise XendError('Failed to create device')
2812 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2813 XEN_API_VM_POWER_STATE_PAUSED):
2815 _, config = self.info['devices'][dev_uuid]
2816 dev_control = self.getDeviceController('vif')
2818 try:
2819 devid = dev_control.createDevice(config)
2820 dev_control.waitForDevice(devid)
2821 self.info.device_update(dev_uuid,
2822 cfg_xenapi = {'devid': devid})
2823 except Exception, exn:
2824 log.exception(exn)
2825 del self.info['devices'][dev_uuid]
2826 self.info['vif_refs'].remove(dev_uuid)
2827 raise
2829 return dev_uuid
2831 def create_vtpm(self, xenapi_vtpm):
2832 """Create a VTPM device from the passed struct in Xen API format.
2834 @return: uuid of the device
2835 @rtype: string
2836 """
2838 if self._stateGet() not in (DOM_STATE_HALTED,):
2839 raise VmError("Can only add vTPM to a halted domain.")
2840 if self.get_vtpms() != []:
2841 raise VmError('Domain already has a vTPM.')
2842 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2843 if not dev_uuid:
2844 raise XendError('Failed to create device')
2846 return dev_uuid
2848 def create_console(self, xenapi_console):
2849 """ Create a console device from a Xen API struct.
2851 @return: uuid of device
2852 @rtype: string
2853 """
2854 if self._stateGet() not in (DOM_STATE_HALTED,):
2855 raise VmError("Can only add console to a halted domain.")
2857 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2858 if not dev_uuid:
2859 raise XendError('Failed to create device')
2861 return dev_uuid
2863 def set_console_other_config(self, console_uuid, other_config):
2864 self.info.console_update(console_uuid, 'other_config', other_config)
2866 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2867 if dev_uuid not in self.info['devices']:
2868 raise XendError('Device does not exist')
2870 try:
2871 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2872 XEN_API_VM_POWER_STATE_PAUSED):
2873 _, config = self.info['devices'][dev_uuid]
2874 devid = config.get('devid')
2875 if devid != None:
2876 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2877 else:
2878 raise XendError('Unable to get devid for device: %s:%s' %
2879 (dev_type, dev_uuid))
2880 finally:
2881 del self.info['devices'][dev_uuid]
2882 self.info['%s_refs' % dev_type].remove(dev_uuid)
2884 def destroy_vbd(self, dev_uuid):
2885 self.destroy_device_by_uuid('vbd', dev_uuid)
2887 def destroy_vif(self, dev_uuid):
2888 self.destroy_device_by_uuid('vif', dev_uuid)
2890 def destroy_vtpm(self, dev_uuid):
2891 self.destroy_device_by_uuid('vtpm', dev_uuid)
2893 def has_device(self, dev_class, dev_uuid):
2894 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2896 def __str__(self):
2897 return '<domain id=%s name=%s memory=%s state=%s>' % \
2898 (str(self.domid), self.info['name_label'],
2899 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2901 __repr__ = __str__