ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 16884:367902a19412

Fix 'on_*=rename-restart' domain configuration option.

When setting e.g. 'on_crash=rename-restart' option in domain config
and crashing guest OS running in the domain, the new domain is
restarted with same name as renamed domain.

jfehlig4: # xm li
Name ID Mem VCPUs State Time(s)
Domain-0 0 1233 4 r----- 937.9
Domain-e64b12a0-0493-44d7-afde-55c776513426 21 384 1 ---c- 14.3
Domain-e64b12a0-0493-44d7-afde-55c776513426 22 384 1 r----- 7.3

This patch copies the domain info prior to setting new name and uuid
in the crashed domain info and uses the copied domain info to
construct the restarted domain.

Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jan 25 13:29:07 2008 +0000 (2008-01-25)
parents 666573856c59
children 7f9646fcffe8
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):
1129 self._checkName(name)
1130 self.info['name_label'] = name
1131 self.storeVm("name", name)
1133 def getName(self):
1134 return self.info['name_label']
1136 def getDomainPath(self):
1137 return self.dompath
1139 def getShutdownReason(self):
1140 return self.readDom('control/shutdown')
1142 def getStorePort(self):
1143 """For use only by image.py and XendCheckpoint.py."""
1144 return self.store_port
1146 def getConsolePort(self):
1147 """For use only by image.py and XendCheckpoint.py"""
1148 return self.console_port
1150 def getFeatures(self):
1151 """For use only by image.py."""
1152 return self.info['features']
1154 def getVCpuCount(self):
1155 return self.info['VCPUs_max']
1157 def setVCpuCount(self, vcpus):
1158 if vcpus <= 0:
1159 raise XendError('Invalid VCPUs')
1161 self.info['vcpu_avail'] = (1 << vcpus) - 1
1162 if self.domid >= 0:
1163 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1164 # update dom differently depending on whether we are adjusting
1165 # vcpu number up or down, otherwise _vcpuDomDetails does not
1166 # disable the vcpus
1167 if self.info['VCPUs_max'] > vcpus:
1168 # decreasing
1169 self._writeDom(self._vcpuDomDetails())
1170 self.info['VCPUs_live'] = vcpus
1171 else:
1172 # same or increasing
1173 self.info['VCPUs_live'] = vcpus
1174 self._writeDom(self._vcpuDomDetails())
1175 else:
1176 self.info['VCPUs_max'] = vcpus
1177 xen.xend.XendDomain.instance().managed_config_save(self)
1178 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1179 vcpus)
1181 def getMemoryTarget(self):
1182 """Get this domain's target memory size, in KB."""
1183 return self.info['memory_dynamic_max'] / 1024
1185 def getMemoryMaximum(self):
1186 """Get this domain's maximum memory size, in KB."""
1187 # remember, info now stores memory in bytes
1188 return self.info['memory_static_max'] / 1024
1190 def getResume(self):
1191 return str(self._resume)
1193 def setResume(self, isresume):
1194 self._resume = isresume
1196 def getCap(self):
1197 return self.info['vcpus_params']['cap']
1199 def setCap(self, cpu_cap):
1200 self.info['vcpus_params']['cap'] = cpu_cap
1202 def getWeight(self):
1203 return self.info['vcpus_params']['weight']
1205 def setWeight(self, cpu_weight):
1206 self.info['vcpus_params']['weight'] = cpu_weight
1208 def setResume(self, state):
1209 self._resume = state
1211 def getRestartCount(self):
1212 return self._readVm('xend/restart_count')
1214 def refreshShutdown(self, xeninfo = None):
1215 """ Checks the domain for whether a shutdown is required.
1217 Called from XendDomainInfo and also image.py for HVM images.
1218 """
1220 # If set at the end of this method, a restart is required, with the
1221 # given reason. This restart has to be done out of the scope of
1222 # refresh_shutdown_lock.
1223 restart_reason = None
1225 self.refresh_shutdown_lock.acquire()
1226 try:
1227 if xeninfo is None:
1228 xeninfo = dom_get(self.domid)
1229 if xeninfo is None:
1230 # The domain no longer exists. This will occur if we have
1231 # scheduled a timer to check for shutdown timeouts and the
1232 # shutdown succeeded. It will also occur if someone
1233 # destroys a domain beneath us. We clean up the domain,
1234 # just in case, but we can't clean up the VM, because that
1235 # VM may have migrated to a different domain on this
1236 # machine.
1237 self.cleanupDomain()
1238 self._stateSet(DOM_STATE_HALTED)
1239 return
1241 if xeninfo['dying']:
1242 # Dying means that a domain has been destroyed, but has not
1243 # yet been cleaned up by Xen. This state could persist
1244 # indefinitely if, for example, another domain has some of its
1245 # pages mapped. We might like to diagnose this problem in the
1246 # future, but for now all we do is make sure that it's not us
1247 # holding the pages, by calling cleanupDomain. We can't
1248 # clean up the VM, as above.
1249 self.cleanupDomain()
1250 self._stateSet(DOM_STATE_SHUTDOWN)
1251 return
1253 elif xeninfo['crashed']:
1254 if self.readDom('xend/shutdown_completed'):
1255 # We've seen this shutdown already, but we are preserving
1256 # the domain for debugging. Leave it alone.
1257 return
1259 log.warn('Domain has crashed: name=%s id=%d.',
1260 self.info['name_label'], self.domid)
1261 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1263 if xoptions.get_enable_dump():
1264 try:
1265 self.dumpCore()
1266 except XendError:
1267 # This error has been logged -- there's nothing more
1268 # we can do in this context.
1269 pass
1271 restart_reason = 'crash'
1272 self._stateSet(DOM_STATE_HALTED)
1274 elif xeninfo['shutdown']:
1275 self._stateSet(DOM_STATE_SHUTDOWN)
1276 if self.readDom('xend/shutdown_completed'):
1277 # We've seen this shutdown already, but we are preserving
1278 # the domain for debugging. Leave it alone.
1279 return
1281 else:
1282 reason = shutdown_reason(xeninfo['shutdown_reason'])
1284 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1285 self.info['name_label'], self.domid, reason)
1286 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1288 self._clearRestart()
1290 if reason == 'suspend':
1291 self._stateSet(DOM_STATE_SUSPENDED)
1292 # Don't destroy the domain. XendCheckpoint will do
1293 # this once it has finished. However, stop watching
1294 # the VM path now, otherwise we will end up with one
1295 # watch for the old domain, and one for the new.
1296 self._unwatchVm()
1297 elif reason in ('poweroff', 'reboot'):
1298 restart_reason = reason
1299 else:
1300 self.destroy()
1302 elif self.dompath is None:
1303 # We have yet to manage to call introduceDomain on this
1304 # domain. This can happen if a restore is in progress, or has
1305 # failed. Ignore this domain.
1306 pass
1307 else:
1308 # Domain is alive. If we are shutting it down, log a message
1309 # if it seems unresponsive.
1310 if xeninfo['paused']:
1311 self._stateSet(DOM_STATE_PAUSED)
1312 else:
1313 self._stateSet(DOM_STATE_RUNNING)
1315 if self.shutdownStartTime:
1316 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1317 self.shutdownStartTime)
1318 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1319 log.info(
1320 "Domain shutdown timeout expired: name=%s id=%s",
1321 self.info['name_label'], self.domid)
1322 self.storeDom('xend/unresponsive', 'True')
1323 finally:
1324 self.refresh_shutdown_lock.release()
1326 if restart_reason:
1327 threading.Thread(target = self._maybeRestart,
1328 args = (restart_reason,)).start()
1332 # Restart functions - handling whether we come back up on shutdown.
1335 def _clearRestart(self):
1336 self._removeDom("xend/shutdown_start_time")
1339 def _maybeRestart(self, reason):
1340 # Dispatch to the correct method based upon the configured on_{reason}
1341 # behaviour.
1342 actions = {"destroy" : self.destroy,
1343 "restart" : self._restart,
1344 "preserve" : self._preserve,
1345 "rename-restart" : self._renameRestart}
1347 action_conf = {
1348 'poweroff': 'actions_after_shutdown',
1349 'reboot': 'actions_after_reboot',
1350 'crash': 'actions_after_crash',
1353 action_target = self.info.get(action_conf.get(reason))
1354 func = actions.get(action_target, None)
1355 if func and callable(func):
1356 func()
1357 else:
1358 self.destroy() # default to destroy
1360 def _renameRestart(self):
1361 self._restart(True)
1363 def _restart(self, rename = False):
1364 """Restart the domain after it has exited.
1366 @param rename True if the old domain is to be renamed and preserved,
1367 False if it is to be destroyed.
1368 """
1369 from xen.xend import XendDomain
1371 if self._readVm(RESTART_IN_PROGRESS):
1372 log.error('Xend failed during restart of domain %s. '
1373 'Refusing to restart to avoid loops.',
1374 str(self.domid))
1375 self.destroy()
1376 return
1378 old_domid = self.domid
1379 self._writeVm(RESTART_IN_PROGRESS, 'True')
1381 now = time.time()
1382 rst = self._readVm('xend/previous_restart_time')
1383 if rst:
1384 rst = float(rst)
1385 timeout = now - rst
1386 if timeout < MINIMUM_RESTART_TIME:
1387 log.error(
1388 'VM %s restarting too fast (%f seconds since the last '
1389 'restart). Refusing to restart to avoid loops.',
1390 self.info['name_label'], timeout)
1391 self.destroy()
1392 return
1394 self._writeVm('xend/previous_restart_time', str(now))
1396 new_dom_info = self.info
1397 try:
1398 if rename:
1399 new_dom_info = self._preserveForRestart()
1400 else:
1401 self._unwatchVm()
1402 self.destroyDomain()
1404 # new_dom's VM will be the same as this domain's VM, except where
1405 # the rename flag has instructed us to call preserveForRestart.
1406 # In that case, it is important that we remove the
1407 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1408 # once the new one is available.
1410 new_dom = None
1411 try:
1412 new_dom = XendDomain.instance().domain_create_from_dict(
1413 new_dom_info)
1414 new_dom.waitForDevices()
1415 new_dom.unpause()
1416 rst_cnt = self._readVm('xend/restart_count')
1417 rst_cnt = int(rst_cnt) + 1
1418 self._writeVm('xend/restart_count', str(rst_cnt))
1419 new_dom._removeVm(RESTART_IN_PROGRESS)
1420 except:
1421 if new_dom:
1422 new_dom._removeVm(RESTART_IN_PROGRESS)
1423 new_dom.destroy()
1424 else:
1425 self._removeVm(RESTART_IN_PROGRESS)
1426 raise
1427 except:
1428 log.exception('Failed to restart domain %s.', str(old_domid))
1430 def _preserveForRestart(self):
1431 """Preserve a domain that has been shut down, by giving it a new UUID,
1432 cloning the VM details, and giving it a new name. This allows us to
1433 keep this domain for debugging, but restart a new one in its place
1434 preserving the restart semantics (name and UUID preserved).
1435 """
1437 new_uuid = uuid.createString()
1438 new_name = 'Domain-%s' % new_uuid
1439 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1440 self.info['name_label'], self.domid, self.info['uuid'],
1441 new_name, new_uuid)
1442 self._unwatchVm()
1443 self._releaseDevices()
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 self._storeVmDetails()
1451 self._preserve()
1452 return new_dom_info
1455 def _preserve(self):
1456 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1457 self.domid)
1458 self._unwatchVm()
1459 self.storeDom('xend/shutdown_completed', 'True')
1460 self._stateSet(DOM_STATE_HALTED)
1463 # Debugging ..
1466 def dumpCore(self, corefile = None):
1467 """Create a core dump for this domain.
1469 @raise: XendError if core dumping failed.
1470 """
1472 try:
1473 if not corefile:
1474 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1475 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1476 self.info['name_label'], self.domid)
1478 if os.path.isdir(corefile):
1479 raise XendError("Cannot dump core in a directory: %s" %
1480 corefile)
1482 xc.domain_dumpcore(self.domid, corefile)
1483 except RuntimeError, ex:
1484 corefile_incomp = corefile+'-incomplete'
1485 os.rename(corefile, corefile_incomp)
1486 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1487 self.domid, self.info['name_label'])
1488 raise XendError("Failed to dump core: %s" % str(ex))
1491 # Device creation/deletion functions
1494 def _createDevice(self, deviceClass, devConfig):
1495 return self.getDeviceController(deviceClass).createDevice(devConfig)
1497 def _waitForDevice(self, deviceClass, devid):
1498 return self.getDeviceController(deviceClass).waitForDevice(devid)
1500 def _waitForDeviceUUID(self, dev_uuid):
1501 deviceClass, config = self.info['devices'].get(dev_uuid)
1502 self._waitForDevice(deviceClass, config['devid'])
1504 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1505 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1506 devid, backpath)
1508 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1509 return self.getDeviceController(deviceClass).reconfigureDevice(
1510 devid, devconfig)
1512 def _createDevices(self):
1513 """Create the devices for a vm.
1515 @raise: VmError for invalid devices
1516 """
1517 ordered_refs = self.info.ordered_device_refs()
1518 for dev_uuid in ordered_refs:
1519 devclass, config = self.info['devices'][dev_uuid]
1520 if devclass in XendDevices.valid_devices():
1521 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1522 dev_uuid = config.get('uuid')
1523 devid = self._createDevice(devclass, config)
1525 # store devid in XendConfig for caching reasons
1526 if dev_uuid in self.info['devices']:
1527 self.info['devices'][dev_uuid][1]['devid'] = devid
1529 if self.image:
1530 self.image.createDeviceModel()
1532 def _releaseDevices(self, suspend = False):
1533 """Release all domain's devices. Nothrow guarantee."""
1534 if self.image:
1535 try:
1536 log.debug("Destroying device model")
1537 self.image.destroyDeviceModel()
1538 except Exception, e:
1539 log.exception("Device model destroy failed %s" % str(e))
1540 else:
1541 log.debug("No device model")
1543 log.debug("Releasing devices")
1544 t = xstransact("%s/device" % self.dompath)
1545 try:
1546 for devclass in XendDevices.valid_devices():
1547 for dev in t.list(devclass):
1548 try:
1549 log.debug("Removing %s", dev);
1550 self.destroyDevice(devclass, dev, False);
1551 except:
1552 # Log and swallow any exceptions in removal --
1553 # there's nothing more we can do.
1554 log.exception("Device release failed: %s; %s; %s",
1555 self.info['name_label'], devclass, dev)
1556 finally:
1557 t.abort()
1559 def getDeviceController(self, name):
1560 """Get the device controller for this domain, and if it
1561 doesn't exist, create it.
1563 @param name: device class name
1564 @type name: string
1565 @rtype: subclass of DevController
1566 """
1567 if name not in self._deviceControllers:
1568 devController = XendDevices.make_controller(name, self)
1569 if not devController:
1570 raise XendError("Unknown device type: %s" % name)
1571 self._deviceControllers[name] = devController
1573 return self._deviceControllers[name]
1576 # Migration functions (public)
1579 def testMigrateDevices(self, network, dst):
1580 """ Notify all device about intention of migration
1581 @raise: XendError for a device that cannot be migrated
1582 """
1583 for (n, c) in self.info.all_devices_sxpr():
1584 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1585 if rc != 0:
1586 raise XendError("Device of type '%s' refuses migration." % n)
1588 def migrateDevices(self, network, dst, step, domName=''):
1589 """Notify the devices about migration
1590 """
1591 ctr = 0
1592 try:
1593 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1594 self.migrateDevice(dev_type, dev_conf, network, dst,
1595 step, domName)
1596 ctr = ctr + 1
1597 except:
1598 for dev_type, dev_conf in self.info.all_devices_sxpr():
1599 if ctr == 0:
1600 step = step - 1
1601 ctr = ctr - 1
1602 self._recoverMigrateDevice(dev_type, dev_conf, network,
1603 dst, step, domName)
1604 raise
1606 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1607 step, domName=''):
1608 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1609 network, dst, step, domName)
1611 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1612 dst, step, domName=''):
1613 return self.getDeviceController(deviceClass).recover_migrate(
1614 deviceConfig, network, dst, step, domName)
1617 ## private:
1619 def _constructDomain(self):
1620 """Construct the domain.
1622 @raise: VmError on error
1623 """
1625 log.debug('XendDomainInfo.constructDomain')
1627 self.shutdownStartTime = None
1629 hvm = self.info.is_hvm()
1630 if hvm:
1631 info = xc.xeninfo()
1632 if 'hvm' not in info['xen_caps']:
1633 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1634 "supported by your CPU and enabled in your "
1635 "BIOS?")
1637 # Hack to pre-reserve some memory for initial domain creation.
1638 # There is an implicit memory overhead for any domain creation. This
1639 # overhead is greater for some types of domain than others. For
1640 # example, an x86 HVM domain will have a default shadow-pagetable
1641 # allocation of 1MB. We free up 2MB here to be on the safe side.
1642 balloon.free(2*1024) # 2MB should be plenty
1644 ssidref = 0
1645 if security.on():
1646 ssidref = security.calc_dom_ssidref_from_info(self.info)
1647 if security.has_authorization(ssidref) == False:
1648 raise VmError("VM is not authorized to run.")
1650 try:
1651 self.domid = xc.domain_create(
1652 domid = 0,
1653 ssidref = ssidref,
1654 handle = uuid.fromString(self.info['uuid']),
1655 hvm = int(hvm),
1656 target = self.info.target())
1657 except Exception, e:
1658 # may get here if due to ACM the operation is not permitted
1659 if security.on():
1660 raise VmError('Domain in conflict set with running domain?')
1662 if self.domid < 0:
1663 raise VmError('Creating domain failed: name=%s' %
1664 self.info['name_label'])
1666 self.dompath = GetDomainPath(self.domid)
1668 self._recreateDom()
1670 # Set timer configration of domain
1671 timer_mode = self.info["platform"].get("timer_mode")
1672 if hvm and timer_mode is not None:
1673 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
1674 long(timer_mode))
1676 # Set maximum number of vcpus in domain
1677 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1679 # Test whether the devices can be assigned with VT-d
1680 pci_str = str(self.info["platform"].get("pci"))
1681 if hvm and pci_str:
1682 bdf = xc.test_assign_device(self.domid, pci_str)
1683 if bdf != 0:
1684 bus = (bdf >> 16) & 0xff
1685 devfn = (bdf >> 8) & 0xff
1686 dev = (devfn >> 3) & 0x1f
1687 func = devfn & 0x7
1688 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
1689 "not enabled, or the device is not exist, or it "
1690 "has already been assigned to other domain"
1691 % (bus, dev, func))
1693 # register the domain in the list
1694 from xen.xend import XendDomain
1695 XendDomain.instance().add_domain(self)
1697 def _introduceDomain(self):
1698 assert self.domid is not None
1699 assert self.store_mfn is not None
1700 assert self.store_port is not None
1702 try:
1703 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1704 except RuntimeError, exn:
1705 raise XendError(str(exn))
1707 def _setTarget(self, target):
1708 assert self.domid is not None
1710 try:
1711 SetTarget(self.domid, target)
1712 self.storeDom('target', target)
1713 except RuntimeError, exn:
1714 raise XendError(str(exn))
1717 def _initDomain(self):
1718 log.debug('XendDomainInfo.initDomain: %s %s',
1719 self.domid,
1720 self.info['vcpus_params']['weight'])
1722 self._configureBootloader()
1724 try:
1725 self.image = image.create(self, self.info)
1727 if self.info['platform'].get('localtime', 0):
1728 xc.domain_set_time_offset(self.domid)
1730 xc.domain_setcpuweight(self.domid, \
1731 self.info['vcpus_params']['weight'])
1733 # repin domain vcpus if a restricted cpus list is provided
1734 # this is done prior to memory allocation to aide in memory
1735 # distribution for NUMA systems.
1736 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1737 for v in range(0, self.info['VCPUs_max']):
1738 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1740 # Use architecture- and image-specific calculations to determine
1741 # the various headrooms necessary, given the raw configured
1742 # values. maxmem, memory, and shadow are all in KiB.
1743 # but memory_static_max etc are all stored in bytes now.
1744 memory = self.image.getRequiredAvailableMemory(
1745 self.info['memory_dynamic_max'] / 1024)
1746 maxmem = self.image.getRequiredAvailableMemory(
1747 self.info['memory_static_max'] / 1024)
1748 shadow = self.image.getRequiredShadowMemory(
1749 self.info['shadow_memory'] * 1024,
1750 self.info['memory_static_max'] / 1024)
1752 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'],)
1753 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1754 # takes MiB and we must not round down and end up under-providing.
1755 shadow = ((shadow + 1023) / 1024) * 1024
1757 # set memory limit
1758 xc.domain_setmaxmem(self.domid, maxmem)
1760 # Make sure there's enough RAM available for the domain
1761 balloon.free(memory + shadow)
1763 # Set up the shadow memory
1764 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1765 self.info['shadow_memory'] = shadow_cur
1767 self._createChannels()
1769 channel_details = self.image.createImage()
1771 self.store_mfn = channel_details['store_mfn']
1772 if 'console_mfn' in channel_details:
1773 self.console_mfn = channel_details['console_mfn']
1774 if 'notes' in channel_details:
1775 self.info.set_notes(channel_details['notes'])
1776 if 'native_protocol' in channel_details:
1777 self.native_protocol = channel_details['native_protocol'];
1779 self._introduceDomain()
1780 if self.info.target():
1781 self._setTarget(self.info.target())
1783 self._createDevices()
1785 self.image.cleanupBootloading()
1787 self.info['start_time'] = time.time()
1789 self._stateSet(DOM_STATE_RUNNING)
1790 except VmError, exn:
1791 log.exception("XendDomainInfo.initDomain: exception occurred")
1792 if self.image:
1793 self.image.cleanupBootloading()
1794 raise exn
1795 except RuntimeError, exn:
1796 log.exception("XendDomainInfo.initDomain: exception occurred")
1797 if self.image:
1798 self.image.cleanupBootloading()
1799 raise VmError(str(exn))
1802 def cleanupDomain(self):
1803 """Cleanup domain resources; release devices. Idempotent. Nothrow
1804 guarantee."""
1806 self.refresh_shutdown_lock.acquire()
1807 try:
1808 self.unwatchShutdown()
1809 self._releaseDevices()
1810 bootloader_tidy(self)
1812 if self.image:
1813 self.image = None
1815 try:
1816 self._removeDom()
1817 except:
1818 log.exception("Removing domain path failed.")
1820 self._stateSet(DOM_STATE_HALTED)
1821 self.domid = None # Do not push into _stateSet()!
1822 finally:
1823 self.refresh_shutdown_lock.release()
1826 def unwatchShutdown(self):
1827 """Remove the watch on the domain's control/shutdown node, if any.
1828 Idempotent. Nothrow guarantee. Expects to be protected by the
1829 refresh_shutdown_lock."""
1831 try:
1832 try:
1833 if self.shutdownWatch:
1834 self.shutdownWatch.unwatch()
1835 finally:
1836 self.shutdownWatch = None
1837 except:
1838 log.exception("Unwatching control/shutdown failed.")
1840 def waitForShutdown(self):
1841 self.state_updated.acquire()
1842 try:
1843 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1844 self.state_updated.wait()
1845 finally:
1846 self.state_updated.release()
1849 # TODO: recategorise - called from XendCheckpoint
1852 def completeRestore(self, store_mfn, console_mfn):
1854 log.debug("XendDomainInfo.completeRestore")
1856 self.store_mfn = store_mfn
1857 self.console_mfn = console_mfn
1859 self._introduceDomain()
1860 self.image = image.create(self, self.info)
1861 if self.image:
1862 self.image.createDeviceModel(True)
1863 self._storeDomDetails()
1864 self._registerWatches()
1865 self.refreshShutdown()
1867 log.debug("XendDomainInfo.completeRestore done")
1870 def _endRestore(self):
1871 self.setResume(False)
1874 # VM Destroy
1877 def _prepare_phantom_paths(self):
1878 # get associated devices to destroy
1879 # build list of phantom devices to be removed after normal devices
1880 plist = []
1881 if self.domid is not None:
1882 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1883 try:
1884 for dev in t.list():
1885 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1886 % (self.dompath, dev))
1887 if backend_phantom_vbd is not None:
1888 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1889 % backend_phantom_vbd)
1890 plist.append(backend_phantom_vbd)
1891 plist.append(frontend_phantom_vbd)
1892 finally:
1893 t.abort()
1894 return plist
1896 def _cleanup_phantom_devs(self, plist):
1897 # remove phantom devices
1898 if not plist == []:
1899 time.sleep(2)
1900 for paths in plist:
1901 if paths.find('backend') != -1:
1902 from xen.xend.server import DevController
1903 # Modify online status /before/ updating state (latter is watched by
1904 # drivers, so this ordering avoids a race).
1905 xstransact.Write(paths, 'online', "0")
1906 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1907 # force
1908 xstransact.Remove(paths)
1910 def destroy(self):
1911 """Cleanup VM and destroy domain. Nothrow guarantee."""
1913 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1915 paths = self._prepare_phantom_paths()
1917 self._cleanupVm()
1918 if self.dompath is not None:
1919 self.destroyDomain()
1921 self._cleanup_phantom_devs(paths)
1923 if "transient" in self.info["other_config"] \
1924 and bool(self.info["other_config"]["transient"]):
1925 from xen.xend import XendDomain
1926 XendDomain.instance().domain_delete_by_dominfo(self)
1929 def destroyDomain(self):
1930 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1932 paths = self._prepare_phantom_paths()
1934 try:
1935 if self.domid is not None:
1936 xc.domain_destroy_hook(self.domid)
1937 xc.domain_destroy(self.domid)
1938 for state in DOM_STATES_OLD:
1939 self.info[state] = 0
1940 self._stateSet(DOM_STATE_HALTED)
1941 except:
1942 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1944 from xen.xend import XendDomain
1945 XendDomain.instance().remove_domain(self)
1947 self.cleanupDomain()
1948 self._cleanup_phantom_devs(paths)
1951 def resumeDomain(self):
1952 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1954 if self.domid is None:
1955 return
1956 try:
1957 # could also fetch a parsed note from xenstore
1958 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1959 if not fast:
1960 self._releaseDevices()
1961 self.testDeviceComplete()
1962 self.testvifsComplete()
1963 log.debug("XendDomainInfo.resumeDomain: devices released")
1965 self._resetChannels()
1967 self._removeDom('control/shutdown')
1968 self._removeDom('device-misc/vif/nextDeviceID')
1970 self._createChannels()
1971 self._introduceDomain()
1972 self._storeDomDetails()
1974 self._createDevices()
1975 log.debug("XendDomainInfo.resumeDomain: devices created")
1977 xc.domain_resume(self.domid, fast)
1978 ResumeDomain(self.domid)
1979 except:
1980 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1981 self.image.resumeDeviceModel()
1982 log.debug("XendDomainInfo.resumeDomain: completed")
1986 # Channels for xenstore and console
1989 def _createChannels(self):
1990 """Create the channels to the domain.
1991 """
1992 self.store_port = self._createChannel()
1993 self.console_port = self._createChannel()
1996 def _createChannel(self):
1997 """Create an event channel to the domain.
1998 """
1999 try:
2000 if self.domid != None:
2001 return xc.evtchn_alloc_unbound(domid = self.domid,
2002 remote_dom = 0)
2003 except:
2004 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2005 raise
2007 def _resetChannels(self):
2008 """Reset all event channels in the domain.
2009 """
2010 try:
2011 if self.domid != None:
2012 return xc.evtchn_reset(dom = self.domid)
2013 except:
2014 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2015 raise
2019 # Bootloader configuration
2022 def _configureBootloader(self):
2023 """Run the bootloader if we're configured to do so."""
2025 blexec = self.info['PV_bootloader']
2026 bootloader_args = self.info['PV_bootloader_args']
2027 kernel = self.info['PV_kernel']
2028 ramdisk = self.info['PV_ramdisk']
2029 args = self.info['PV_args']
2030 boot = self.info['HVM_boot_policy']
2032 if boot:
2033 # HVM booting.
2034 pass
2035 elif not blexec and kernel:
2036 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2037 # will be picked up by image.py.
2038 pass
2039 else:
2040 # Boot using bootloader
2041 if not blexec or blexec == 'pygrub':
2042 blexec = osdep.pygrub_path
2044 blcfg = None
2045 disks = [x for x in self.info['vbd_refs']
2046 if self.info['devices'][x][1]['bootable']]
2048 if not disks:
2049 msg = "Had a bootloader specified, but no disks are bootable"
2050 log.error(msg)
2051 raise VmError(msg)
2053 devinfo = self.info['devices'][disks[0]]
2054 devtype = devinfo[0]
2055 disk = devinfo[1]['uname']
2057 fn = blkdev_uname_to_file(disk)
2058 taptype = blkdev_uname_to_taptype(disk)
2059 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2060 if mounted:
2061 # This is a file, not a device. pygrub can cope with a
2062 # file if it's raw, but if it's QCOW or other such formats
2063 # used through blktap, then we need to mount it first.
2065 log.info("Mounting %s on %s." %
2066 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2068 vbd = {
2069 'mode': 'RO',
2070 'device': BOOTLOADER_LOOPBACK_DEVICE,
2073 from xen.xend import XendDomain
2074 dom0 = XendDomain.instance().privilegedDomain()
2075 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2076 fn = BOOTLOADER_LOOPBACK_DEVICE
2078 try:
2079 blcfg = bootloader(blexec, fn, self, False,
2080 bootloader_args, kernel, ramdisk, args)
2081 finally:
2082 if mounted:
2083 log.info("Unmounting %s from %s." %
2084 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2086 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2088 if blcfg is None:
2089 msg = "Had a bootloader specified, but can't find disk"
2090 log.error(msg)
2091 raise VmError(msg)
2093 self.info.update_with_image_sxp(blcfg, True)
2097 # VM Functions
2100 def _readVMDetails(self, params):
2101 """Read the specified parameters from the store.
2102 """
2103 try:
2104 return self._gatherVm(*params)
2105 except ValueError:
2106 # One of the int/float entries in params has a corresponding store
2107 # entry that is invalid. We recover, because older versions of
2108 # Xend may have put the entry there (memory/target, for example),
2109 # but this is in general a bad situation to have reached.
2110 log.exception(
2111 "Store corrupted at %s! Domain %d's configuration may be "
2112 "affected.", self.vmpath, self.domid)
2113 return []
2115 def _cleanupVm(self):
2116 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2118 self._unwatchVm()
2120 try:
2121 self._removeVm()
2122 except:
2123 log.exception("Removing VM path failed.")
2126 def checkLiveMigrateMemory(self):
2127 """ Make sure there's enough memory to migrate this domain """
2128 overhead_kb = 0
2129 if arch.type == "x86":
2130 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2131 # the minimum that Xen would allocate if no value were given.
2132 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2133 (self.info['memory_static_max'] / 1024 / 1024) * 4
2134 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2135 # The domain might already have some shadow memory
2136 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2137 if overhead_kb > 0:
2138 balloon.free(overhead_kb)
2140 def _unwatchVm(self):
2141 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2142 guarantee."""
2143 try:
2144 try:
2145 if self.vmWatch:
2146 self.vmWatch.unwatch()
2147 finally:
2148 self.vmWatch = None
2149 except:
2150 log.exception("Unwatching VM path failed.")
2152 def testDeviceComplete(self):
2153 """ For Block IO migration safety we must ensure that
2154 the device has shutdown correctly, i.e. all blocks are
2155 flushed to disk
2156 """
2157 start = time.time()
2158 while True:
2159 test = 0
2160 diff = time.time() - start
2161 for i in self.getDeviceController('vbd').deviceIDs():
2162 test = 1
2163 log.info("Dev %s still active, looping...", i)
2164 time.sleep(0.1)
2166 if test == 0:
2167 break
2168 if diff >= MIGRATE_TIMEOUT:
2169 log.info("Dev still active but hit max loop timeout")
2170 break
2172 def testvifsComplete(self):
2173 """ In case vifs are released and then created for the same
2174 domain, we need to wait the device shut down.
2175 """
2176 start = time.time()
2177 while True:
2178 test = 0
2179 diff = time.time() - start
2180 for i in self.getDeviceController('vif').deviceIDs():
2181 test = 1
2182 log.info("Dev %s still active, looping...", i)
2183 time.sleep(0.1)
2185 if test == 0:
2186 break
2187 if diff >= MIGRATE_TIMEOUT:
2188 log.info("Dev still active but hit max loop timeout")
2189 break
2191 def _storeVmDetails(self):
2192 to_store = {}
2194 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2195 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2196 if self._infoIsSet(info_key):
2197 to_store[key] = str(self.info[info_key])
2199 if self._infoIsSet("static_memory_min"):
2200 to_store["memory"] = str(self.info["static_memory_min"])
2201 if self._infoIsSet("static_memory_max"):
2202 to_store["maxmem"] = str(self.info["static_memory_max"])
2204 image_sxpr = self.info.image_sxpr()
2205 if image_sxpr:
2206 to_store['image'] = sxp.to_string(image_sxpr)
2208 if not self._readVm('xend/restart_count'):
2209 to_store['xend/restart_count'] = str(0)
2211 log.debug("Storing VM details: %s", scrub_password(to_store))
2213 self._writeVm(to_store)
2214 self._setVmPermissions()
2217 def _setVmPermissions(self):
2218 """Allow the guest domain to read its UUID. We don't allow it to
2219 access any other entry, for security."""
2220 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2221 { 'dom' : self.domid,
2222 'read' : True,
2223 'write' : False })
2226 # Utility functions
2229 def __getattr__(self, name):
2230 if name == "state":
2231 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2232 log.warn("".join(traceback.format_stack()))
2233 return self._stateGet()
2234 else:
2235 raise AttributeError()
2237 def __setattr__(self, name, value):
2238 if name == "state":
2239 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2240 log.warn("".join(traceback.format_stack()))
2241 self._stateSet(value)
2242 else:
2243 self.__dict__[name] = value
2245 def _stateSet(self, state):
2246 self.state_updated.acquire()
2247 try:
2248 # TODO Not sure this is correct...
2249 # _stateGet is live now. Why not fire event
2250 # even when it hasn't changed?
2251 if self._stateGet() != state:
2252 self.state_updated.notifyAll()
2253 import XendAPI
2254 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2255 'power_state')
2256 finally:
2257 self.state_updated.release()
2259 def _stateGet(self):
2260 # Lets try and reconsitute the state from xc
2261 # first lets try and get the domain info
2262 # from xc - this will tell us if the domain
2263 # exists
2264 info = dom_get(self.getDomid())
2265 if info is None or info['shutdown']:
2266 # We are either HALTED or SUSPENDED
2267 # check saved image exists
2268 from xen.xend import XendDomain
2269 managed_config_path = \
2270 XendDomain.instance()._managed_check_point_path( \
2271 self.get_uuid())
2272 if os.path.exists(managed_config_path):
2273 return XEN_API_VM_POWER_STATE_SUSPENDED
2274 else:
2275 return XEN_API_VM_POWER_STATE_HALTED
2276 elif info['crashed']:
2277 # Crashed
2278 return XEN_API_VM_POWER_STATE_CRASHED
2279 else:
2280 # We are either RUNNING or PAUSED
2281 if info['paused']:
2282 return XEN_API_VM_POWER_STATE_PAUSED
2283 else:
2284 return XEN_API_VM_POWER_STATE_RUNNING
2286 def _infoIsSet(self, name):
2287 return name in self.info and self.info[name] is not None
2289 def _checkName(self, name):
2290 """Check if a vm name is valid. Valid names contain alphabetic
2291 characters, digits, or characters in '_-.:/+'.
2292 The same name cannot be used for more than one vm at the same time.
2294 @param name: name
2295 @raise: VmError if invalid
2296 """
2297 from xen.xend import XendDomain
2299 if name is None or name == '':
2300 raise VmError('Missing VM Name')
2302 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2303 raise VmError('Invalid VM Name')
2305 dom = XendDomain.instance().domain_lookup_nr(name)
2306 if dom and dom.info['uuid'] != self.info['uuid']:
2307 raise VmError("VM name '%s' already exists%s" %
2308 (name,
2309 dom.domid is not None and
2310 (" as domain %s" % str(dom.domid)) or ""))
2313 def update(self, info = None, refresh = True, transaction = None):
2314 """Update with info from xc.domain_getinfo().
2315 """
2316 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2317 str(self.domid))
2319 if not info:
2320 info = dom_get(self.domid)
2321 if not info:
2322 return
2324 if info["maxmem_kb"] < 0:
2325 info["maxmem_kb"] = XendNode.instance() \
2326 .physinfo_dict()['total_memory'] * 1024
2328 #ssidref field not used any longer
2329 if 'ssidref' in info:
2330 info.pop('ssidref')
2332 # make sure state is reset for info
2333 # TODO: we should eventually get rid of old_dom_states
2335 self.info.update_config(info)
2336 self._update_consoles(transaction)
2338 if refresh:
2339 self.refreshShutdown(info)
2341 log.trace("XendDomainInfo.update done on domain %s: %s",
2342 str(self.domid), self.info)
2344 def sxpr(self, ignore_store = False, legacy_only = True):
2345 result = self.info.to_sxp(domain = self,
2346 ignore_devices = ignore_store,
2347 legacy_only = legacy_only)
2349 #if not ignore_store and self.dompath:
2350 # vnc_port = self.readDom('console/vnc-port')
2351 # if vnc_port is not None:
2352 # result.append(['device',
2353 # ['console', ['vnc-port', str(vnc_port)]]])
2355 return result
2357 # Xen API
2358 # ----------------------------------------------------------------
2360 def get_uuid(self):
2361 dom_uuid = self.info.get('uuid')
2362 if not dom_uuid: # if it doesn't exist, make one up
2363 dom_uuid = uuid.createString()
2364 self.info['uuid'] = dom_uuid
2365 return dom_uuid
2367 def get_memory_static_max(self):
2368 return self.info.get('memory_static_max', 0)
2369 def get_memory_static_min(self):
2370 return self.info.get('memory_static_min', 0)
2371 def get_memory_dynamic_max(self):
2372 return self.info.get('memory_dynamic_max', 0)
2373 def get_memory_dynamic_min(self):
2374 return self.info.get('memory_dynamic_min', 0)
2376 # only update memory-related config values if they maintain sanity
2377 def _safe_set_memory(self, key, newval):
2378 oldval = self.info.get(key, 0)
2379 try:
2380 self.info[key] = newval
2381 self.info._memory_sanity_check()
2382 except Exception, ex:
2383 self.info[key] = oldval
2384 raise
2386 def set_memory_static_max(self, val):
2387 self._safe_set_memory('memory_static_max', val)
2388 def set_memory_static_min(self, val):
2389 self._safe_set_memory('memory_static_min', val)
2390 def set_memory_dynamic_max(self, val):
2391 self._safe_set_memory('memory_dynamic_max', val)
2392 def set_memory_dynamic_min(self, val):
2393 self._safe_set_memory('memory_dynamic_min', val)
2395 def get_vcpus_params(self):
2396 if self.getDomid() is None:
2397 return self.info['vcpus_params']
2399 retval = xc.sched_credit_domain_get(self.getDomid())
2400 return retval
2401 def get_power_state(self):
2402 return XEN_API_VM_POWER_STATE[self._stateGet()]
2403 def get_platform(self):
2404 return self.info.get('platform', {})
2405 def get_pci_bus(self):
2406 return self.info.get('pci_bus', '')
2407 def get_tools_version(self):
2408 return self.info.get('tools_version', {})
2409 def get_metrics(self):
2410 return self.metrics.get_uuid();
2413 def get_security_label(self, xspol=None):
2414 import xen.util.xsm.xsm as security
2415 label = security.get_security_label(self, xspol)
2416 return label
2418 def set_security_label(self, seclab, old_seclab, xspol=None,
2419 xspol_old=None):
2420 """
2421 Set the security label of a domain from its old to
2422 a new value.
2423 @param seclab New security label formatted in the form
2424 <policy type>:<policy name>:<vm label>
2425 @param old_seclab The current security label that the
2426 VM must have.
2427 @param xspol An optional policy under which this
2428 update should be done. If not given,
2429 then the current active policy is used.
2430 @param xspol_old The old policy; only to be passed during
2431 the updating of a policy
2432 @return Returns return code, a string with errors from
2433 the hypervisor's operation, old label of the
2434 domain
2435 """
2436 rc = 0
2437 errors = ""
2438 old_label = ""
2439 new_ssidref = 0
2440 domid = self.getDomid()
2441 res_labels = None
2442 is_policy_update = (xspol_old != None)
2444 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2445 from xen.util import xsconstants
2447 state = self._stateGet()
2448 # Relabel only HALTED or RUNNING or PAUSED domains
2449 if domid != 0 and \
2450 state not in \
2451 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2452 DOM_STATE_SUSPENDED ]:
2453 log.warn("Relabeling domain not possible in state '%s'" %
2454 DOM_STATES[state])
2455 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2457 # Remove security label. Works only for halted domains
2458 if not seclab or seclab == "":
2459 if state not in [ DOM_STATE_HALTED ]:
2460 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2462 if self.info.has_key('security_label'):
2463 old_label = self.info['security_label']
2464 # Check label against expected one.
2465 if old_label != old_seclab:
2466 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2467 del self.info['security_label']
2468 xen.xend.XendDomain.instance().managed_config_save(self)
2469 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2471 tmp = seclab.split(":")
2472 if len(tmp) != 3:
2473 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2474 typ, policy, label = tmp
2476 poladmin = XSPolicyAdminInstance()
2477 if not xspol:
2478 xspol = poladmin.get_policy_by_name(policy)
2480 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2481 #if domain is running or paused try to relabel in hypervisor
2482 if not xspol:
2483 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2485 if typ != xspol.get_type_name() or \
2486 policy != xspol.get_name():
2487 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2489 if typ == xsconstants.ACM_POLICY_ID:
2490 new_ssidref = xspol.vmlabel_to_ssidref(label)
2491 if new_ssidref == xsconstants.INVALID_SSIDREF:
2492 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2494 # Check that all used resources are accessible under the
2495 # new label
2496 if not is_policy_update and \
2497 not security.resources_compatible_with_vmlabel(xspol,
2498 self, label):
2499 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2501 #Check label against expected one. Can only do this
2502 # if the policy hasn't changed underneath in the meantime
2503 if xspol_old == None:
2504 old_label = self.get_security_label()
2505 if old_label != old_seclab:
2506 log.info("old_label != old_seclab: %s != %s" %
2507 (old_label, old_seclab))
2508 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2510 # relabel domain in the hypervisor
2511 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2512 log.info("rc from relabeling in HV: %d" % rc)
2513 else:
2514 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2516 if rc == 0:
2517 # HALTED, RUNNING or PAUSED
2518 if domid == 0:
2519 if xspol:
2520 self.info['security_label'] = seclab
2521 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2522 else:
2523 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2524 else:
2525 if self.info.has_key('security_label'):
2526 old_label = self.info['security_label']
2527 # Check label against expected one, unless wildcard
2528 if old_label != old_seclab:
2529 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2531 self.info['security_label'] = seclab
2533 try:
2534 xen.xend.XendDomain.instance().managed_config_save(self)
2535 except:
2536 pass
2537 return (rc, errors, old_label, new_ssidref)
2539 def get_on_shutdown(self):
2540 after_shutdown = self.info.get('actions_after_shutdown')
2541 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2542 return XEN_API_ON_NORMAL_EXIT[-1]
2543 return after_shutdown
2545 def get_on_reboot(self):
2546 after_reboot = self.info.get('actions_after_reboot')
2547 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2548 return XEN_API_ON_NORMAL_EXIT[-1]
2549 return after_reboot
2551 def get_on_suspend(self):
2552 # TODO: not supported
2553 after_suspend = self.info.get('actions_after_suspend')
2554 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2555 return XEN_API_ON_NORMAL_EXIT[-1]
2556 return after_suspend
2558 def get_on_crash(self):
2559 after_crash = self.info.get('actions_after_crash')
2560 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2561 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2562 return after_crash
2564 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2565 """ Get's a device configuration either from XendConfig or
2566 from the DevController.
2568 @param dev_class: device class, either, 'vbd' or 'vif'
2569 @param dev_uuid: device UUID
2571 @rtype: dictionary
2572 """
2573 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2575 # shortcut if the domain isn't started because
2576 # the devcontrollers will have no better information
2577 # than XendConfig.
2578 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2579 if dev_config:
2580 return copy.deepcopy(dev_config)
2581 return None
2583 # instead of using dev_class, we use the dev_type
2584 # that is from XendConfig.
2585 controller = self.getDeviceController(dev_type)
2586 if not controller:
2587 return None
2589 all_configs = controller.getAllDeviceConfigurations()
2590 if not all_configs:
2591 return None
2593 updated_dev_config = copy.deepcopy(dev_config)
2594 for _devid, _devcfg in all_configs.items():
2595 if _devcfg.get('uuid') == dev_uuid:
2596 updated_dev_config.update(_devcfg)
2597 updated_dev_config['id'] = _devid
2598 return updated_dev_config
2600 return updated_dev_config
2602 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2603 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2604 if not config:
2605 return {}
2607 config['VM'] = self.get_uuid()
2609 if dev_class == 'vif':
2610 if not config.has_key('name'):
2611 config['name'] = config.get('vifname', '')
2612 if not config.has_key('MAC'):
2613 config['MAC'] = config.get('mac', '')
2614 if not config.has_key('type'):
2615 config['type'] = 'paravirtualised'
2616 if not config.has_key('device'):
2617 devid = config.get('id')
2618 if devid != None:
2619 config['device'] = 'eth%d' % devid
2620 else:
2621 config['device'] = ''
2623 if not config.has_key('network'):
2624 try:
2625 bridge = config.get('bridge', None)
2626 if bridge is None:
2627 from xen.util import Brctl
2628 if_to_br = dict([(i,b)
2629 for (b,ifs) in Brctl.get_state().items()
2630 for i in ifs])
2631 vifname = "vif%s.%s" % (self.getDomid(),
2632 config.get('id'))
2633 bridge = if_to_br.get(vifname, None)
2634 config['network'] = \
2635 XendNode.instance().bridge_to_network(
2636 config.get('bridge')).get_uuid()
2637 except Exception:
2638 log.exception('bridge_to_network')
2639 # Ignore this for now -- it may happen if the device
2640 # has been specified using the legacy methods, but at
2641 # some point we're going to have to figure out how to
2642 # handle that properly.
2644 config['MTU'] = 1500 # TODO
2646 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2647 xennode = XendNode.instance()
2648 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2649 config['io_read_kbs'] = rx_bps/1024
2650 config['io_write_kbs'] = tx_bps/1024
2651 rx, tx = xennode.get_vif_stat(self.domid, devid)
2652 config['io_total_read_kbs'] = rx/1024
2653 config['io_total_write_kbs'] = tx/1024
2654 else:
2655 config['io_read_kbs'] = 0.0
2656 config['io_write_kbs'] = 0.0
2657 config['io_total_read_kbs'] = 0.0
2658 config['io_total_write_kbs'] = 0.0
2660 config['security_label'] = config.get('security_label', '')
2662 if dev_class == 'vbd':
2664 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2665 controller = self.getDeviceController(dev_class)
2666 devid, _1, _2 = controller.getDeviceDetails(config)
2667 xennode = XendNode.instance()
2668 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2669 config['io_read_kbs'] = rd_blkps
2670 config['io_write_kbs'] = wr_blkps
2671 else:
2672 config['io_read_kbs'] = 0.0
2673 config['io_write_kbs'] = 0.0
2675 config['VDI'] = config.get('VDI', '')
2676 config['device'] = config.get('dev', '')
2677 if ':' in config['device']:
2678 vbd_name, vbd_type = config['device'].split(':', 1)
2679 config['device'] = vbd_name
2680 if vbd_type == 'cdrom':
2681 config['type'] = XEN_API_VBD_TYPE[0]
2682 else:
2683 config['type'] = XEN_API_VBD_TYPE[1]
2685 config['driver'] = 'paravirtualised' # TODO
2686 config['image'] = config.get('uname', '')
2688 if config.get('mode', 'r') == 'r':
2689 config['mode'] = 'RO'
2690 else:
2691 config['mode'] = 'RW'
2693 if dev_class == 'vtpm':
2694 if not config.has_key('type'):
2695 config['type'] = 'paravirtualised' # TODO
2696 if not config.has_key('backend'):
2697 config['backend'] = "00000000-0000-0000-0000-000000000000"
2699 return config
2701 def get_dev_property(self, dev_class, dev_uuid, field):
2702 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2703 try:
2704 return config[field]
2705 except KeyError:
2706 raise XendError('Invalid property for device: %s' % field)
2708 def set_dev_property(self, dev_class, dev_uuid, field, value):
2709 self.info['devices'][dev_uuid][1][field] = value
2711 def get_vcpus_util(self):
2712 vcpu_util = {}
2713 xennode = XendNode.instance()
2714 if 'VCPUs_max' in self.info and self.domid != None:
2715 for i in range(0, self.info['VCPUs_max']):
2716 util = xennode.get_vcpu_util(self.domid, i)
2717 vcpu_util[str(i)] = util
2719 return vcpu_util
2721 def get_consoles(self):
2722 return self.info.get('console_refs', [])
2724 def get_vifs(self):
2725 return self.info.get('vif_refs', [])
2727 def get_vbds(self):
2728 return self.info.get('vbd_refs', [])
2730 def get_vtpms(self):
2731 return self.info.get('vtpm_refs', [])
2733 def create_vbd(self, xenapi_vbd, vdi_image_path):
2734 """Create a VBD using a VDI from XendStorageRepository.
2736 @param xenapi_vbd: vbd struct from the Xen API
2737 @param vdi_image_path: VDI UUID
2738 @rtype: string
2739 @return: uuid of the device
2740 """
2741 xenapi_vbd['image'] = vdi_image_path
2742 if vdi_image_path.startswith('tap'):
2743 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2744 else:
2745 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2747 if not dev_uuid:
2748 raise XendError('Failed to create device')
2750 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2751 XEN_API_VM_POWER_STATE_PAUSED):
2752 _, config = self.info['devices'][dev_uuid]
2754 if vdi_image_path.startswith('tap'):
2755 dev_control = self.getDeviceController('tap')
2756 else:
2757 dev_control = self.getDeviceController('vbd')
2759 try:
2760 devid = dev_control.createDevice(config)
2761 dev_control.waitForDevice(devid)
2762 self.info.device_update(dev_uuid,
2763 cfg_xenapi = {'devid': devid})
2764 except Exception, exn:
2765 log.exception(exn)
2766 del self.info['devices'][dev_uuid]
2767 self.info['vbd_refs'].remove(dev_uuid)
2768 raise
2770 return dev_uuid
2772 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2773 """Create a VBD using a VDI from XendStorageRepository.
2775 @param xenapi_vbd: vbd struct from the Xen API
2776 @param vdi_image_path: VDI UUID
2777 @rtype: string
2778 @return: uuid of the device
2779 """
2780 xenapi_vbd['image'] = vdi_image_path
2781 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2782 if not dev_uuid:
2783 raise XendError('Failed to create device')
2785 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2786 _, config = self.info['devices'][dev_uuid]
2787 config['devid'] = self.getDeviceController('tap').createDevice(config)
2789 return config['devid']
2791 def create_vif(self, xenapi_vif):
2792 """Create VIF device from the passed struct in Xen API format.
2794 @param xenapi_vif: Xen API VIF Struct.
2795 @rtype: string
2796 @return: UUID
2797 """
2798 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2799 if not dev_uuid:
2800 raise XendError('Failed to create device')
2802 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2803 XEN_API_VM_POWER_STATE_PAUSED):
2805 _, config = self.info['devices'][dev_uuid]
2806 dev_control = self.getDeviceController('vif')
2808 try:
2809 devid = dev_control.createDevice(config)
2810 dev_control.waitForDevice(devid)
2811 self.info.device_update(dev_uuid,
2812 cfg_xenapi = {'devid': devid})
2813 except Exception, exn:
2814 log.exception(exn)
2815 del self.info['devices'][dev_uuid]
2816 self.info['vif_refs'].remove(dev_uuid)
2817 raise
2819 return dev_uuid
2821 def create_vtpm(self, xenapi_vtpm):
2822 """Create a VTPM device from the passed struct in Xen API format.
2824 @return: uuid of the device
2825 @rtype: string
2826 """
2828 if self._stateGet() not in (DOM_STATE_HALTED,):
2829 raise VmError("Can only add vTPM to a halted domain.")
2830 if self.get_vtpms() != []:
2831 raise VmError('Domain already has a vTPM.')
2832 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2833 if not dev_uuid:
2834 raise XendError('Failed to create device')
2836 return dev_uuid
2838 def create_console(self, xenapi_console):
2839 """ Create a console device from a Xen API struct.
2841 @return: uuid of device
2842 @rtype: string
2843 """
2844 if self._stateGet() not in (DOM_STATE_HALTED,):
2845 raise VmError("Can only add console to a halted domain.")
2847 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2848 if not dev_uuid:
2849 raise XendError('Failed to create device')
2851 return dev_uuid
2853 def set_console_other_config(self, console_uuid, other_config):
2854 self.info.console_update(console_uuid, 'other_config', other_config)
2856 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2857 if dev_uuid not in self.info['devices']:
2858 raise XendError('Device does not exist')
2860 try:
2861 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2862 XEN_API_VM_POWER_STATE_PAUSED):
2863 _, config = self.info['devices'][dev_uuid]
2864 devid = config.get('devid')
2865 if devid != None:
2866 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2867 else:
2868 raise XendError('Unable to get devid for device: %s:%s' %
2869 (dev_type, dev_uuid))
2870 finally:
2871 del self.info['devices'][dev_uuid]
2872 self.info['%s_refs' % dev_type].remove(dev_uuid)
2874 def destroy_vbd(self, dev_uuid):
2875 self.destroy_device_by_uuid('vbd', dev_uuid)
2877 def destroy_vif(self, dev_uuid):
2878 self.destroy_device_by_uuid('vif', dev_uuid)
2880 def destroy_vtpm(self, dev_uuid):
2881 self.destroy_device_by_uuid('vtpm', dev_uuid)
2883 def has_device(self, dev_class, dev_uuid):
2884 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2886 def __str__(self):
2887 return '<domain id=%s name=%s memory=%s state=%s>' % \
2888 (str(self.domid), self.info['name_label'],
2889 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2891 __repr__ = __str__