debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 16991:c4a06902febf

Fix xm shutdown/reboot for HVM domain of IA64

xc.domain_destroy_hook() is called twice when we execute
xm shutdown/reboot command to an HVM domain without PV drivers.
The first calling is from shutdown() in XendDomainInfo.py.
The second calling is from destroyDomain() in XendDomainInfo.py.
The first calling is not necessary, so this patch removes it.

A discussion about this patch is as follows.
http://lists.xensource.com/archives/html/xen-ia64-devel/2008-01/msg00232.html

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jan 31 09:43:05 2008 +0000 (2008-01-31)
parents 625c923f7b4a
children def2adbce510
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 log.info("HVM save:remote shutdown dom %d!", self.domid)
485 xc.domain_shutdown(self.domid, code)
487 def pause(self):
488 """Pause domain
490 @raise XendError: Failed pausing a domain
491 """
492 try:
493 xc.domain_pause(self.domid)
494 self._stateSet(DOM_STATE_PAUSED)
495 except Exception, ex:
496 log.exception(ex)
497 raise XendError("Domain unable to be paused: %s" % str(ex))
499 def unpause(self):
500 """Unpause domain
502 @raise XendError: Failed unpausing a domain
503 """
504 try:
505 xc.domain_unpause(self.domid)
506 self._stateSet(DOM_STATE_RUNNING)
507 except Exception, ex:
508 log.exception(ex)
509 raise XendError("Domain unable to be unpaused: %s" % str(ex))
511 def send_sysrq(self, key):
512 """ Send a Sysrq equivalent key via xenstored."""
513 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
514 raise XendError("Domain '%s' is not started" % self.info['name_label'])
516 asserts.isCharConvertible(key)
517 self.storeDom("control/sysrq", '%c' % key)
519 def device_create(self, dev_config):
520 """Create a new device.
522 @param dev_config: device configuration
523 @type dev_config: SXP object (parsed config)
524 """
525 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
526 dev_type = sxp.name(dev_config)
527 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
528 dev_config_dict = self.info['devices'][dev_uuid][1]
529 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
531 if self.domid is not None:
532 try:
533 dev_config_dict['devid'] = devid = \
534 self._createDevice(dev_type, dev_config_dict)
535 self._waitForDevice(dev_type, devid)
536 except VmError, ex:
537 del self.info['devices'][dev_uuid]
538 if dev_type == 'tap':
539 self.info['vbd_refs'].remove(dev_uuid)
540 else:
541 self.info['%s_refs' % dev_type].remove(dev_uuid)
542 raise ex
543 else:
544 devid = None
546 xen.xend.XendDomain.instance().managed_config_save(self)
547 return self.getDeviceController(dev_type).sxpr(devid)
549 def device_configure(self, dev_sxp, devid = None):
550 """Configure an existing device.
552 @param dev_config: device configuration
553 @type dev_config: SXP object (parsed config)
554 @param devid: device id
555 @type devid: int
556 @return: Returns True if successfully updated device
557 @rtype: boolean
558 """
560 # convert device sxp to a dict
561 dev_class = sxp.name(dev_sxp)
562 dev_config = {}
563 for opt_val in dev_sxp[1:]:
564 try:
565 dev_config[opt_val[0]] = opt_val[1]
566 except IndexError:
567 pass
569 # use DevController.reconfigureDevice to change device config
570 dev_control = self.getDeviceController(dev_class)
571 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
573 # update XendConfig with new device info
574 if dev_uuid:
575 self.info.device_update(dev_uuid, dev_sxp)
577 return True
579 def waitForDevices(self):
580 """Wait for this domain's configured devices to connect.
582 @raise VmError: if any device fails to initialise.
583 """
584 for devclass in XendDevices.valid_devices():
585 self.getDeviceController(devclass).waitForDevices()
587 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
588 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
589 deviceClass, devid)
591 if rm_cfg:
592 # Convert devid to device number. A device number is
593 # needed to remove its configuration.
594 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
596 # Save current sxprs. A device number and a backend
597 # path are needed to remove its configuration but sxprs
598 # do not have those after calling destroyDevice.
599 sxprs = self.getDeviceSxprs(deviceClass)
601 rc = None
602 if self.domid is not None:
603 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
604 if not force and rm_cfg:
605 # The backend path, other than the device itself,
606 # has to be passed because its accompanied frontend
607 # path may be void until its removal is actually
608 # issued. It is probable because destroyDevice is
609 # issued first.
610 for dev_num, dev_info in sxprs:
611 dev_num = int(dev_num)
612 if dev_num == dev:
613 for x in dev_info:
614 if x[0] == 'backend':
615 backend = x[1]
616 break
617 break
618 self._waitForDevice_destroy(deviceClass, devid, backend)
620 if rm_cfg:
621 if deviceClass == 'vif':
622 if self.domid is not None:
623 for dev_num, dev_info in sxprs:
624 dev_num = int(dev_num)
625 if dev_num == dev:
626 for x in dev_info:
627 if x[0] == 'mac':
628 mac = x[1]
629 break
630 break
631 dev_info = self._getDeviceInfo_vif(mac)
632 else:
633 _, dev_info = sxprs[dev]
634 else: # 'vbd' or 'tap'
635 dev_info = self._getDeviceInfo_vbd(dev)
636 # To remove the UUID of the device from refs,
637 # deviceClass must be always 'vbd'.
638 deviceClass = 'vbd'
639 if dev_info is None:
640 raise XendError("Device %s is not defined" % devid)
642 dev_uuid = sxp.child_value(dev_info, 'uuid')
643 del self.info['devices'][dev_uuid]
644 self.info['%s_refs' % deviceClass].remove(dev_uuid)
645 xen.xend.XendDomain.instance().managed_config_save(self)
647 return rc
649 def getDeviceSxprs(self, deviceClass):
650 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
651 return self.getDeviceController(deviceClass).sxprs()
652 else:
653 sxprs = []
654 dev_num = 0
655 for dev_type, dev_info in self.info.all_devices_sxpr():
656 if dev_type == deviceClass:
657 sxprs.append([dev_num, dev_info])
658 dev_num += 1
659 return sxprs
661 def getBlockDeviceClass(self, devid):
662 # To get a device number from the devid,
663 # we temporarily use the device controller of VBD.
664 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
665 dev_info = self._getDeviceInfo_vbd(dev)
666 if dev_info:
667 return dev_info[0]
669 def _getDeviceInfo_vif(self, mac):
670 for dev_type, dev_info in self.info.all_devices_sxpr():
671 if dev_type != 'vif':
672 continue
673 if mac == sxp.child_value(dev_info, 'mac'):
674 return dev_info
676 def _getDeviceInfo_vbd(self, devid):
677 for dev_type, dev_info in self.info.all_devices_sxpr():
678 if dev_type != 'vbd' and dev_type != 'tap':
679 continue
680 dev = sxp.child_value(dev_info, 'dev')
681 dev = dev.split(':')[0]
682 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
683 if devid == dev:
684 return dev_info
687 def setMemoryTarget(self, target):
688 """Set the memory target of this domain.
689 @param target: In MiB.
690 """
691 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
692 self.info['name_label'], str(self.domid), target)
694 MiB = 1024 * 1024
695 self._safe_set_memory('memory_dynamic_min', target * MiB)
696 self._safe_set_memory('memory_dynamic_max', target * MiB)
698 if self.domid >= 0:
699 self.storeVm("memory", target)
700 self.storeDom("memory/target", target << 10)
701 xen.xend.XendDomain.instance().managed_config_save(self)
703 def setMemoryMaximum(self, limit):
704 """Set the maximum memory limit of this domain
705 @param limit: In MiB.
706 """
707 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
708 self.info['name_label'], str(self.domid), limit)
710 if limit <= 0:
711 raise XendError('Invalid memory size')
713 MiB = 1024 * 1024
714 self._safe_set_memory('memory_static_max', limit * MiB)
716 if self.domid >= 0:
717 maxmem = int(limit) * 1024
718 try:
719 return xc.domain_setmaxmem(self.domid, maxmem)
720 except Exception, ex:
721 raise XendError(str(ex))
722 xen.xend.XendDomain.instance().managed_config_save(self)
725 def getVCPUInfo(self):
726 try:
727 # We include the domain name and ID, to help xm.
728 sxpr = ['domain',
729 ['domid', self.domid],
730 ['name', self.info['name_label']],
731 ['vcpu_count', self.info['VCPUs_max']]]
733 for i in range(0, self.info['VCPUs_max']):
734 if self.domid is not None:
735 info = xc.vcpu_getinfo(self.domid, i)
737 sxpr.append(['vcpu',
738 ['number', i],
739 ['online', info['online']],
740 ['blocked', info['blocked']],
741 ['running', info['running']],
742 ['cpu_time', info['cpu_time'] / 1e9],
743 ['cpu', info['cpu']],
744 ['cpumap', info['cpumap']]])
745 else:
746 sxpr.append(['vcpu',
747 ['number', i],
748 ['online', 0],
749 ['blocked', 0],
750 ['running', 0],
751 ['cpu_time', 0.0],
752 ['cpu', -1],
753 ['cpumap', self.info['cpus'] and \
754 self.info['cpus'] or range(64)]])
756 return sxpr
758 except RuntimeError, exn:
759 raise XendError(str(exn))
762 def getDomInfo(self):
763 return dom_get(self.domid)
765 #
766 # internal functions ... TODO: re-categorised
767 #
769 def _augmentInfo(self, priv):
770 """Augment self.info, as given to us through L{recreate}, with
771 values taken from the store. This recovers those values known
772 to xend but not to the hypervisor.
773 """
774 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
775 if priv:
776 augment_entries.remove('memory')
777 augment_entries.remove('maxmem')
778 augment_entries.remove('vcpus')
779 augment_entries.remove('vcpu_avail')
781 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
782 for k in augment_entries])
784 # make returned lists into a dictionary
785 vm_config = dict(zip(augment_entries, vm_config))
787 for arg in augment_entries:
788 val = vm_config[arg]
789 if val != None:
790 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
791 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
792 self.info[xapiarg] = val
793 elif arg == "memory":
794 self.info["static_memory_min"] = val
795 elif arg == "maxmem":
796 self.info["static_memory_max"] = val
797 else:
798 self.info[arg] = val
800 # For dom0, we ignore any stored value for the vcpus fields, and
801 # read the current value from Xen instead. This allows boot-time
802 # settings to take precedence over any entries in the store.
803 if priv:
804 xeninfo = dom_get(self.domid)
805 self.info['VCPUs_max'] = xeninfo['online_vcpus']
806 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
808 # read image value
809 image_sxp = self._readVm('image')
810 if image_sxp:
811 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
813 # read devices
814 devices = []
815 for devclass in XendDevices.valid_devices():
816 devconfig = self.getDeviceController(devclass).configurations()
817 if devconfig:
818 devices.extend(devconfig)
820 if not self.info['devices'] and devices is not None:
821 for device in devices:
822 self.info.device_add(device[0], cfg_sxp = device)
824 self._update_consoles()
826 def _update_consoles(self, transaction = None):
827 if self.domid == None or self.domid == 0:
828 return
830 # Update VT100 port if it exists
831 if transaction is None:
832 self.console_port = self.readDom('console/port')
833 else:
834 self.console_port = self.readDomTxn(transaction, 'console/port')
835 if self.console_port is not None:
836 serial_consoles = self.info.console_get_all('vt100')
837 if not serial_consoles:
838 cfg = self.info.console_add('vt100', self.console_port)
839 self._createDevice('console', cfg)
840 else:
841 console_uuid = serial_consoles[0].get('uuid')
842 self.info.console_update(console_uuid, 'location',
843 self.console_port)
846 # Update VNC port if it exists and write to xenstore
847 if transaction is None:
848 vnc_port = self.readDom('console/vnc-port')
849 else:
850 vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
851 if vnc_port is not None:
852 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
853 if dev_type == 'vfb':
854 old_location = dev_info.get('location')
855 listen_host = dev_info.get('vnclisten', 'localhost')
856 new_location = '%s:%s' % (listen_host, str(vnc_port))
857 if old_location == new_location:
858 break
860 dev_info['location'] = new_location
861 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
862 vfb_ctrl = self.getDeviceController('vfb')
863 vfb_ctrl.reconfigureDevice(0, dev_info)
864 break
866 #
867 # Function to update xenstore /vm/*
868 #
870 def _readVm(self, *args):
871 return xstransact.Read(self.vmpath, *args)
873 def _writeVm(self, *args):
874 return xstransact.Write(self.vmpath, *args)
876 def _removeVm(self, *args):
877 return xstransact.Remove(self.vmpath, *args)
879 def _gatherVm(self, *args):
880 return xstransact.Gather(self.vmpath, *args)
882 def storeVm(self, *args):
883 return xstransact.Store(self.vmpath, *args)
885 def permissionsVm(self, *args):
886 return xstransact.SetPermissions(self.vmpath, *args)
889 def _readVmTxn(self, transaction, *args):
890 paths = map(lambda x: self.vmpath + "/" + x, args)
891 return transaction.read(*paths)
893 def _writeVmTxn(self, transaction, *args):
894 paths = map(lambda x: self.vmpath + "/" + x, args)
895 return transaction.write(*paths)
897 def _removeVmTxn(self, transaction, *args):
898 paths = map(lambda x: self.vmpath + "/" + x, args)
899 return transaction.remove(*paths)
901 def _gatherVmTxn(self, transaction, *args):
902 paths = map(lambda x: self.vmpath + "/" + x, args)
903 return transaction.gather(paths)
905 def storeVmTxn(self, transaction, *args):
906 paths = map(lambda x: self.vmpath + "/" + x, args)
907 return transaction.store(*paths)
909 def permissionsVmTxn(self, transaction, *args):
910 paths = map(lambda x: self.vmpath + "/" + x, args)
911 return transaction.set_permissions(*paths)
913 #
914 # Function to update xenstore /dom/*
915 #
917 def readDom(self, *args):
918 return xstransact.Read(self.dompath, *args)
920 def gatherDom(self, *args):
921 return xstransact.Gather(self.dompath, *args)
923 def _writeDom(self, *args):
924 return xstransact.Write(self.dompath, *args)
926 def _removeDom(self, *args):
927 return xstransact.Remove(self.dompath, *args)
929 def storeDom(self, *args):
930 return xstransact.Store(self.dompath, *args)
933 def readDomTxn(self, transaction, *args):
934 paths = map(lambda x: self.dompath + "/" + x, args)
935 return transaction.read(*paths)
937 def gatherDomTxn(self, transaction, *args):
938 paths = map(lambda x: self.dompath + "/" + x, args)
939 return transaction.gather(*paths)
941 def _writeDomTxn(self, transaction, *args):
942 paths = map(lambda x: self.dompath + "/" + x, args)
943 return transaction.write(*paths)
945 def _removeDomTxn(self, transaction, *args):
946 paths = map(lambda x: self.dompath + "/" + x, args)
947 return transaction.remove(*paths)
949 def storeDomTxn(self, transaction, *args):
950 paths = map(lambda x: self.dompath + "/" + x, args)
951 return transaction.store(*paths)
954 def _recreateDom(self):
955 complete(self.dompath, lambda t: self._recreateDomFunc(t))
957 def _recreateDomFunc(self, t):
958 t.remove()
959 t.mkdir()
960 t.set_permissions({'dom' : self.domid})
961 t.write('vm', self.vmpath)
963 def _storeDomDetails(self):
964 to_store = {
965 'domid': str(self.domid),
966 'vm': self.vmpath,
967 'name': self.info['name_label'],
968 'console/limit': str(xoptions.get_console_limit() * 1024),
969 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
970 }
972 def f(n, v):
973 if v is not None:
974 if type(v) == bool:
975 to_store[n] = v and "1" or "0"
976 else:
977 to_store[n] = str(v)
979 # Figure out if we need to tell xenconsoled to ignore this guest's
980 # console - device model will handle console if it is running
981 constype = "ioemu"
982 if 'device_model' not in self.info['platform']:
983 constype = "xenconsoled"
985 f('console/port', self.console_port)
986 f('console/ring-ref', self.console_mfn)
987 f('console/type', constype)
988 f('store/port', self.store_port)
989 f('store/ring-ref', self.store_mfn)
991 if arch.type == "x86":
992 f('control/platform-feature-multiprocessor-suspend', True)
994 # elfnotes
995 for n, v in self.info.get_notes().iteritems():
996 n = n.lower().replace('_', '-')
997 if n == 'features':
998 for v in v.split('|'):
999 v = v.replace('_', '-')
1000 if v.startswith('!'):
1001 f('image/%s/%s' % (n, v[1:]), False)
1002 else:
1003 f('image/%s/%s' % (n, v), True)
1004 else:
1005 f('image/%s' % n, v)
1007 if self.info.has_key('security_label'):
1008 f('security_label', self.info['security_label'])
1010 to_store.update(self._vcpuDomDetails())
1012 log.debug("Storing domain details: %s", scrub_password(to_store))
1014 self._writeDom(to_store)
1016 def _vcpuDomDetails(self):
1017 def availability(n):
1018 if self.info['vcpu_avail'] & (1 << n):
1019 return 'online'
1020 else:
1021 return 'offline'
1023 result = {}
1024 for v in range(0, self.info['VCPUs_max']):
1025 result["cpu/%d/availability" % v] = availability(v)
1026 return result
1029 # xenstore watches
1032 def _registerWatches(self):
1033 """Register a watch on this VM's entries in the store, and the
1034 domain's control/shutdown node, so that when they are changed
1035 externally, we keep up to date. This should only be called by {@link
1036 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1037 details have been written, but before the new instance is returned."""
1038 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1039 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1040 self._handleShutdownWatch)
1042 def _storeChanged(self, _):
1043 log.trace("XendDomainInfo.storeChanged");
1045 changed = False
1047 # Check whether values in the configuration have
1048 # changed in Xenstore.
1050 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1051 'rtc/timeoffset']
1053 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1054 for k in cfg_vm])
1056 # convert two lists into a python dictionary
1057 vm_details = dict(zip(cfg_vm, vm_details))
1059 if vm_details['rtc/timeoffset'] == None:
1060 vm_details['rtc/timeoffset'] = "0"
1062 for arg, val in vm_details.items():
1063 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1064 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1065 if val != None and val != self.info[xapiarg]:
1066 self.info[xapiarg] = val
1067 changed = True
1068 elif arg == "memory":
1069 if val != None and val != self.info["static_memory_min"]:
1070 self.info["static_memory_min"] = val
1071 changed = True
1072 elif arg == "maxmem":
1073 if val != None and val != self.info["static_memory_max"]:
1074 self.info["static_memory_max"] = val
1075 changed = True
1077 # Check whether image definition has been updated
1078 image_sxp = self._readVm('image')
1079 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1080 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1081 changed = True
1083 # Check if the rtc offset has changes
1084 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1085 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1086 changed = True
1088 if changed:
1089 # Update the domain section of the store, as this contains some
1090 # parameters derived from the VM configuration.
1091 self._storeDomDetails()
1093 return 1
1095 def _handleShutdownWatch(self, _):
1096 log.debug('XendDomainInfo.handleShutdownWatch')
1098 reason = self.readDom('control/shutdown')
1100 if reason and reason != 'suspend':
1101 sst = self.readDom('xend/shutdown_start_time')
1102 now = time.time()
1103 if sst:
1104 self.shutdownStartTime = float(sst)
1105 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1106 else:
1107 self.shutdownStartTime = now
1108 self.storeDom('xend/shutdown_start_time', now)
1109 timeout = SHUTDOWN_TIMEOUT
1111 log.trace(
1112 "Scheduling refreshShutdown on domain %d in %ds.",
1113 self.domid, timeout)
1114 threading.Timer(timeout, self.refreshShutdown).start()
1116 return True
1120 # Public Attributes for the VM
1124 def getDomid(self):
1125 return self.domid
1127 def setName(self, name, to_store = True):
1128 self._checkName(name)
1129 self.info['name_label'] = name
1130 if to_store:
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.destroy()
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 new_dom._removeVm(RESTART_IN_PROGRESS)
1417 except:
1418 if new_dom:
1419 new_dom._removeVm(RESTART_IN_PROGRESS)
1420 new_dom.destroy()
1421 else:
1422 self._removeVm(RESTART_IN_PROGRESS)
1423 raise
1424 except:
1425 log.exception('Failed to restart domain %s.', str(old_domid))
1427 def _preserveForRestart(self):
1428 """Preserve a domain that has been shut down, by giving it a new UUID,
1429 cloning the VM details, and giving it a new name. This allows us to
1430 keep this domain for debugging, but restart a new one in its place
1431 preserving the restart semantics (name and UUID preserved).
1432 """
1434 new_uuid = uuid.createString()
1435 new_name = 'Domain-%s' % new_uuid
1436 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1437 self.info['name_label'], self.domid, self.info['uuid'],
1438 new_name, new_uuid)
1439 self._unwatchVm()
1440 self._releaseDevices()
1441 # Remove existing vm node in xenstore
1442 self._removeVm()
1443 new_dom_info = self.info.copy()
1444 new_dom_info['name_label'] = self.info['name_label']
1445 new_dom_info['uuid'] = self.info['uuid']
1446 self.info['name_label'] = new_name
1447 self.info['uuid'] = new_uuid
1448 self.vmpath = XS_VMROOT + new_uuid
1449 # Write out new vm node to xenstore
1450 self._storeVmDetails()
1451 rst_cnt = self._readVm('xend/restart_count')
1452 rst_cnt = int(rst_cnt) + 1
1453 self._writeVm('xend/restart_count', str(rst_cnt))
1454 self._preserve()
1455 return new_dom_info
1458 def _preserve(self):
1459 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1460 self.domid)
1461 self._unwatchVm()
1462 self.storeDom('xend/shutdown_completed', 'True')
1463 self._stateSet(DOM_STATE_HALTED)
1466 # Debugging ..
1469 def dumpCore(self, corefile = None):
1470 """Create a core dump for this domain.
1472 @raise: XendError if core dumping failed.
1473 """
1475 try:
1476 if not corefile:
1477 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1478 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1479 self.info['name_label'], self.domid)
1481 if os.path.isdir(corefile):
1482 raise XendError("Cannot dump core in a directory: %s" %
1483 corefile)
1485 xc.domain_dumpcore(self.domid, corefile)
1486 except RuntimeError, ex:
1487 corefile_incomp = corefile+'-incomplete'
1488 os.rename(corefile, corefile_incomp)
1489 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1490 self.domid, self.info['name_label'])
1491 raise XendError("Failed to dump core: %s" % str(ex))
1494 # Device creation/deletion functions
1497 def _createDevice(self, deviceClass, devConfig):
1498 return self.getDeviceController(deviceClass).createDevice(devConfig)
1500 def _waitForDevice(self, deviceClass, devid):
1501 return self.getDeviceController(deviceClass).waitForDevice(devid)
1503 def _waitForDeviceUUID(self, dev_uuid):
1504 deviceClass, config = self.info['devices'].get(dev_uuid)
1505 self._waitForDevice(deviceClass, config['devid'])
1507 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1508 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1509 devid, backpath)
1511 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1512 return self.getDeviceController(deviceClass).reconfigureDevice(
1513 devid, devconfig)
1515 def _createDevices(self):
1516 """Create the devices for a vm.
1518 @raise: VmError for invalid devices
1519 """
1520 ordered_refs = self.info.ordered_device_refs()
1521 for dev_uuid in ordered_refs:
1522 devclass, config = self.info['devices'][dev_uuid]
1523 if devclass in XendDevices.valid_devices():
1524 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1525 dev_uuid = config.get('uuid')
1526 devid = self._createDevice(devclass, config)
1528 # store devid in XendConfig for caching reasons
1529 if dev_uuid in self.info['devices']:
1530 self.info['devices'][dev_uuid][1]['devid'] = devid
1532 if self.image:
1533 self.image.createDeviceModel()
1535 def _releaseDevices(self, suspend = False):
1536 """Release all domain's devices. Nothrow guarantee."""
1537 if self.image:
1538 try:
1539 log.debug("Destroying device model")
1540 self.image.destroyDeviceModel()
1541 except Exception, e:
1542 log.exception("Device model destroy failed %s" % str(e))
1543 else:
1544 log.debug("No device model")
1546 log.debug("Releasing devices")
1547 t = xstransact("%s/device" % self.dompath)
1548 try:
1549 for devclass in XendDevices.valid_devices():
1550 for dev in t.list(devclass):
1551 try:
1552 log.debug("Removing %s", dev);
1553 self.destroyDevice(devclass, dev, False);
1554 except:
1555 # Log and swallow any exceptions in removal --
1556 # there's nothing more we can do.
1557 log.exception("Device release failed: %s; %s; %s",
1558 self.info['name_label'], devclass, dev)
1559 finally:
1560 t.abort()
1562 def getDeviceController(self, name):
1563 """Get the device controller for this domain, and if it
1564 doesn't exist, create it.
1566 @param name: device class name
1567 @type name: string
1568 @rtype: subclass of DevController
1569 """
1570 if name not in self._deviceControllers:
1571 devController = XendDevices.make_controller(name, self)
1572 if not devController:
1573 raise XendError("Unknown device type: %s" % name)
1574 self._deviceControllers[name] = devController
1576 return self._deviceControllers[name]
1579 # Migration functions (public)
1582 def testMigrateDevices(self, network, dst):
1583 """ Notify all device about intention of migration
1584 @raise: XendError for a device that cannot be migrated
1585 """
1586 for (n, c) in self.info.all_devices_sxpr():
1587 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1588 if rc != 0:
1589 raise XendError("Device of type '%s' refuses migration." % n)
1591 def migrateDevices(self, network, dst, step, domName=''):
1592 """Notify the devices about migration
1593 """
1594 ctr = 0
1595 try:
1596 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1597 self.migrateDevice(dev_type, dev_conf, network, dst,
1598 step, domName)
1599 ctr = ctr + 1
1600 except:
1601 for dev_type, dev_conf in self.info.all_devices_sxpr():
1602 if ctr == 0:
1603 step = step - 1
1604 ctr = ctr - 1
1605 self._recoverMigrateDevice(dev_type, dev_conf, network,
1606 dst, step, domName)
1607 raise
1609 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1610 step, domName=''):
1611 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1612 network, dst, step, domName)
1614 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1615 dst, step, domName=''):
1616 return self.getDeviceController(deviceClass).recover_migrate(
1617 deviceConfig, network, dst, step, domName)
1620 ## private:
1622 def _constructDomain(self):
1623 """Construct the domain.
1625 @raise: VmError on error
1626 """
1628 log.debug('XendDomainInfo.constructDomain')
1630 self.shutdownStartTime = None
1632 hap = 0
1633 hvm = self.info.is_hvm()
1634 if hvm:
1635 hap = self.info.is_hap()
1636 info = xc.xeninfo()
1637 if 'hvm' not in info['xen_caps']:
1638 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1639 "supported by your CPU and enabled in your "
1640 "BIOS?")
1642 # Hack to pre-reserve some memory for initial domain creation.
1643 # There is an implicit memory overhead for any domain creation. This
1644 # overhead is greater for some types of domain than others. For
1645 # example, an x86 HVM domain will have a default shadow-pagetable
1646 # allocation of 1MB. We free up 2MB here to be on the safe side.
1647 balloon.free(2*1024) # 2MB should be plenty
1649 ssidref = 0
1650 if security.on():
1651 ssidref = security.calc_dom_ssidref_from_info(self.info)
1652 if security.has_authorization(ssidref) == False:
1653 raise VmError("VM is not authorized to run.")
1655 try:
1656 self.domid = xc.domain_create(
1657 domid = 0,
1658 ssidref = ssidref,
1659 handle = uuid.fromString(self.info['uuid']),
1660 flags = (int(hvm) << 0) | (int(hap) << 1),
1661 target = self.info.target())
1662 except Exception, e:
1663 # may get here if due to ACM the operation is not permitted
1664 if security.on():
1665 raise VmError('Domain in conflict set with running domain?')
1667 if self.domid < 0:
1668 raise VmError('Creating domain failed: name=%s' %
1669 self.info['name_label'])
1671 self.dompath = GetDomainPath(self.domid)
1673 self._recreateDom()
1675 # Set timer configration of domain
1676 timer_mode = self.info["platform"].get("timer_mode")
1677 if hvm and timer_mode is not None:
1678 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
1679 long(timer_mode))
1681 # Set maximum number of vcpus in domain
1682 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1684 # Test whether the devices can be assigned with VT-d
1685 pci_str = str(self.info["platform"].get("pci"))
1686 if hvm and pci_str:
1687 bdf = xc.test_assign_device(self.domid, pci_str)
1688 if bdf != 0:
1689 bus = (bdf >> 16) & 0xff
1690 devfn = (bdf >> 8) & 0xff
1691 dev = (devfn >> 3) & 0x1f
1692 func = devfn & 0x7
1693 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
1694 "not enabled, or the device is not exist, or it "
1695 "has already been assigned to other domain"
1696 % (bus, dev, func))
1698 # register the domain in the list
1699 from xen.xend import XendDomain
1700 XendDomain.instance().add_domain(self)
1702 def _introduceDomain(self):
1703 assert self.domid is not None
1704 assert self.store_mfn is not None
1705 assert self.store_port is not None
1707 try:
1708 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1709 except RuntimeError, exn:
1710 raise XendError(str(exn))
1712 def _setTarget(self, target):
1713 assert self.domid is not None
1715 try:
1716 SetTarget(self.domid, target)
1717 self.storeDom('target', target)
1718 except RuntimeError, exn:
1719 raise XendError(str(exn))
1722 def _initDomain(self):
1723 log.debug('XendDomainInfo.initDomain: %s %s',
1724 self.domid,
1725 self.info['vcpus_params']['weight'])
1727 self._configureBootloader()
1729 try:
1730 if self.info['platform'].get('localtime', 0):
1731 t = time.time()
1732 loc = time.localtime(t)
1733 utc = time.gmtime(t)
1734 timeoffset = int(time.mktime(loc) - time.mktime(utc))
1735 self.info['platform']['rtc_timeoffset'] = timeoffset
1737 self.image = image.create(self, self.info)
1739 xc.domain_setcpuweight(self.domid, \
1740 self.info['vcpus_params']['weight'])
1742 # repin domain vcpus if a restricted cpus list is provided
1743 # this is done prior to memory allocation to aide in memory
1744 # distribution for NUMA systems.
1745 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1746 for v in range(0, self.info['VCPUs_max']):
1747 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1749 # Use architecture- and image-specific calculations to determine
1750 # the various headrooms necessary, given the raw configured
1751 # values. maxmem, memory, and shadow are all in KiB.
1752 # but memory_static_max etc are all stored in bytes now.
1753 memory = self.image.getRequiredAvailableMemory(
1754 self.info['memory_dynamic_max'] / 1024)
1755 maxmem = self.image.getRequiredAvailableMemory(
1756 self.info['memory_static_max'] / 1024)
1757 shadow = self.image.getRequiredShadowMemory(
1758 self.info['shadow_memory'] * 1024,
1759 self.info['memory_static_max'] / 1024)
1761 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'],)
1762 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1763 # takes MiB and we must not round down and end up under-providing.
1764 shadow = ((shadow + 1023) / 1024) * 1024
1766 # set memory limit
1767 xc.domain_setmaxmem(self.domid, maxmem)
1769 # Make sure there's enough RAM available for the domain
1770 balloon.free(memory + shadow)
1772 # Set up the shadow memory
1773 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1774 self.info['shadow_memory'] = shadow_cur
1776 self._createChannels()
1778 channel_details = self.image.createImage()
1780 self.store_mfn = channel_details['store_mfn']
1781 if 'console_mfn' in channel_details:
1782 self.console_mfn = channel_details['console_mfn']
1783 if 'notes' in channel_details:
1784 self.info.set_notes(channel_details['notes'])
1785 if 'native_protocol' in channel_details:
1786 self.native_protocol = channel_details['native_protocol'];
1788 self._introduceDomain()
1789 if self.info.target():
1790 self._setTarget(self.info.target())
1792 self._createDevices()
1794 self.image.cleanupBootloading()
1796 self.info['start_time'] = time.time()
1798 self._stateSet(DOM_STATE_RUNNING)
1799 except VmError, exn:
1800 log.exception("XendDomainInfo.initDomain: exception occurred")
1801 if self.image:
1802 self.image.cleanupBootloading()
1803 raise exn
1804 except RuntimeError, exn:
1805 log.exception("XendDomainInfo.initDomain: exception occurred")
1806 if self.image:
1807 self.image.cleanupBootloading()
1808 raise VmError(str(exn))
1811 def cleanupDomain(self):
1812 """Cleanup domain resources; release devices. Idempotent. Nothrow
1813 guarantee."""
1815 self.refresh_shutdown_lock.acquire()
1816 try:
1817 self.unwatchShutdown()
1818 self._releaseDevices()
1819 bootloader_tidy(self)
1821 if self.image:
1822 self.image = None
1824 try:
1825 self._removeDom()
1826 except:
1827 log.exception("Removing domain path failed.")
1829 self._stateSet(DOM_STATE_HALTED)
1830 self.domid = None # Do not push into _stateSet()!
1831 finally:
1832 self.refresh_shutdown_lock.release()
1835 def unwatchShutdown(self):
1836 """Remove the watch on the domain's control/shutdown node, if any.
1837 Idempotent. Nothrow guarantee. Expects to be protected by the
1838 refresh_shutdown_lock."""
1840 try:
1841 try:
1842 if self.shutdownWatch:
1843 self.shutdownWatch.unwatch()
1844 finally:
1845 self.shutdownWatch = None
1846 except:
1847 log.exception("Unwatching control/shutdown failed.")
1849 def waitForShutdown(self):
1850 self.state_updated.acquire()
1851 try:
1852 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1853 self.state_updated.wait()
1854 finally:
1855 self.state_updated.release()
1858 # TODO: recategorise - called from XendCheckpoint
1861 def completeRestore(self, store_mfn, console_mfn):
1863 log.debug("XendDomainInfo.completeRestore")
1865 self.store_mfn = store_mfn
1866 self.console_mfn = console_mfn
1868 self._introduceDomain()
1869 self.image = image.create(self, self.info)
1870 if self.image:
1871 self.image.createDeviceModel(True)
1872 self._storeDomDetails()
1873 self._registerWatches()
1874 self.refreshShutdown()
1876 log.debug("XendDomainInfo.completeRestore done")
1879 def _endRestore(self):
1880 self.setResume(False)
1883 # VM Destroy
1886 def _prepare_phantom_paths(self):
1887 # get associated devices to destroy
1888 # build list of phantom devices to be removed after normal devices
1889 plist = []
1890 if self.domid is not None:
1891 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1892 try:
1893 for dev in t.list():
1894 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1895 % (self.dompath, dev))
1896 if backend_phantom_vbd is not None:
1897 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1898 % backend_phantom_vbd)
1899 plist.append(backend_phantom_vbd)
1900 plist.append(frontend_phantom_vbd)
1901 finally:
1902 t.abort()
1903 return plist
1905 def _cleanup_phantom_devs(self, plist):
1906 # remove phantom devices
1907 if not plist == []:
1908 time.sleep(2)
1909 for paths in plist:
1910 if paths.find('backend') != -1:
1911 from xen.xend.server import DevController
1912 # Modify online status /before/ updating state (latter is watched by
1913 # drivers, so this ordering avoids a race).
1914 xstransact.Write(paths, 'online', "0")
1915 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1916 # force
1917 xstransact.Remove(paths)
1919 def destroy(self):
1920 """Cleanup VM and destroy domain. Nothrow guarantee."""
1922 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1924 paths = self._prepare_phantom_paths()
1926 self._cleanupVm()
1927 if self.dompath is not None:
1928 self.destroyDomain()
1930 self._cleanup_phantom_devs(paths)
1932 if "transient" in self.info["other_config"] \
1933 and bool(self.info["other_config"]["transient"]):
1934 from xen.xend import XendDomain
1935 XendDomain.instance().domain_delete_by_dominfo(self)
1938 def destroyDomain(self):
1939 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1941 paths = self._prepare_phantom_paths()
1943 try:
1944 if self.domid is not None:
1945 xc.domain_destroy_hook(self.domid)
1946 xc.domain_destroy(self.domid)
1947 for state in DOM_STATES_OLD:
1948 self.info[state] = 0
1949 self._stateSet(DOM_STATE_HALTED)
1950 except:
1951 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1953 from xen.xend import XendDomain
1954 XendDomain.instance().remove_domain(self)
1956 self.cleanupDomain()
1957 self._cleanup_phantom_devs(paths)
1960 def resumeDomain(self):
1961 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1963 if self.domid is None:
1964 return
1965 try:
1966 # could also fetch a parsed note from xenstore
1967 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1968 if not fast:
1969 self._releaseDevices()
1970 self.testDeviceComplete()
1971 self.testvifsComplete()
1972 log.debug("XendDomainInfo.resumeDomain: devices released")
1974 self._resetChannels()
1976 self._removeDom('control/shutdown')
1977 self._removeDom('device-misc/vif/nextDeviceID')
1979 self._createChannels()
1980 self._introduceDomain()
1981 self._storeDomDetails()
1983 self._createDevices()
1984 log.debug("XendDomainInfo.resumeDomain: devices created")
1986 xc.domain_resume(self.domid, fast)
1987 ResumeDomain(self.domid)
1988 except:
1989 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1990 self.image.resumeDeviceModel()
1991 log.debug("XendDomainInfo.resumeDomain: completed")
1995 # Channels for xenstore and console
1998 def _createChannels(self):
1999 """Create the channels to the domain.
2000 """
2001 self.store_port = self._createChannel()
2002 self.console_port = self._createChannel()
2005 def _createChannel(self):
2006 """Create an event channel to the domain.
2007 """
2008 try:
2009 if self.domid != None:
2010 return xc.evtchn_alloc_unbound(domid = self.domid,
2011 remote_dom = 0)
2012 except:
2013 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2014 raise
2016 def _resetChannels(self):
2017 """Reset all event channels in the domain.
2018 """
2019 try:
2020 if self.domid != None:
2021 return xc.evtchn_reset(dom = self.domid)
2022 except:
2023 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2024 raise
2028 # Bootloader configuration
2031 def _configureBootloader(self):
2032 """Run the bootloader if we're configured to do so."""
2034 blexec = self.info['PV_bootloader']
2035 bootloader_args = self.info['PV_bootloader_args']
2036 kernel = self.info['PV_kernel']
2037 ramdisk = self.info['PV_ramdisk']
2038 args = self.info['PV_args']
2039 boot = self.info['HVM_boot_policy']
2041 if boot:
2042 # HVM booting.
2043 pass
2044 elif not blexec and kernel:
2045 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2046 # will be picked up by image.py.
2047 pass
2048 else:
2049 # Boot using bootloader
2050 if not blexec or blexec == 'pygrub':
2051 blexec = osdep.pygrub_path
2053 blcfg = None
2054 disks = [x for x in self.info['vbd_refs']
2055 if self.info['devices'][x][1]['bootable']]
2057 if not disks:
2058 msg = "Had a bootloader specified, but no disks are bootable"
2059 log.error(msg)
2060 raise VmError(msg)
2062 devinfo = self.info['devices'][disks[0]]
2063 devtype = devinfo[0]
2064 disk = devinfo[1]['uname']
2066 fn = blkdev_uname_to_file(disk)
2067 taptype = blkdev_uname_to_taptype(disk)
2068 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2069 if mounted:
2070 # This is a file, not a device. pygrub can cope with a
2071 # file if it's raw, but if it's QCOW or other such formats
2072 # used through blktap, then we need to mount it first.
2074 log.info("Mounting %s on %s." %
2075 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2077 vbd = {
2078 'mode': 'RO',
2079 'device': BOOTLOADER_LOOPBACK_DEVICE,
2082 from xen.xend import XendDomain
2083 dom0 = XendDomain.instance().privilegedDomain()
2084 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2085 fn = BOOTLOADER_LOOPBACK_DEVICE
2087 try:
2088 blcfg = bootloader(blexec, fn, self, False,
2089 bootloader_args, kernel, ramdisk, args)
2090 finally:
2091 if mounted:
2092 log.info("Unmounting %s from %s." %
2093 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2095 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2097 if blcfg is None:
2098 msg = "Had a bootloader specified, but can't find disk"
2099 log.error(msg)
2100 raise VmError(msg)
2102 self.info.update_with_image_sxp(blcfg, True)
2106 # VM Functions
2109 def _readVMDetails(self, params):
2110 """Read the specified parameters from the store.
2111 """
2112 try:
2113 return self._gatherVm(*params)
2114 except ValueError:
2115 # One of the int/float entries in params has a corresponding store
2116 # entry that is invalid. We recover, because older versions of
2117 # Xend may have put the entry there (memory/target, for example),
2118 # but this is in general a bad situation to have reached.
2119 log.exception(
2120 "Store corrupted at %s! Domain %d's configuration may be "
2121 "affected.", self.vmpath, self.domid)
2122 return []
2124 def _cleanupVm(self):
2125 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2127 self._unwatchVm()
2129 try:
2130 self._removeVm()
2131 except:
2132 log.exception("Removing VM path failed.")
2135 def checkLiveMigrateMemory(self):
2136 """ Make sure there's enough memory to migrate this domain """
2137 overhead_kb = 0
2138 if arch.type == "x86":
2139 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2140 # the minimum that Xen would allocate if no value were given.
2141 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2142 (self.info['memory_static_max'] / 1024 / 1024) * 4
2143 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2144 # The domain might already have some shadow memory
2145 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2146 if overhead_kb > 0:
2147 balloon.free(overhead_kb)
2149 def _unwatchVm(self):
2150 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2151 guarantee."""
2152 try:
2153 try:
2154 if self.vmWatch:
2155 self.vmWatch.unwatch()
2156 finally:
2157 self.vmWatch = None
2158 except:
2159 log.exception("Unwatching VM path failed.")
2161 def testDeviceComplete(self):
2162 """ For Block IO migration safety we must ensure that
2163 the device has shutdown correctly, i.e. all blocks are
2164 flushed to disk
2165 """
2166 start = time.time()
2167 while True:
2168 test = 0
2169 diff = time.time() - start
2170 for i in self.getDeviceController('vbd').deviceIDs():
2171 test = 1
2172 log.info("Dev %s still active, looping...", i)
2173 time.sleep(0.1)
2175 if test == 0:
2176 break
2177 if diff >= MIGRATE_TIMEOUT:
2178 log.info("Dev still active but hit max loop timeout")
2179 break
2181 def testvifsComplete(self):
2182 """ In case vifs are released and then created for the same
2183 domain, we need to wait the device shut down.
2184 """
2185 start = time.time()
2186 while True:
2187 test = 0
2188 diff = time.time() - start
2189 for i in self.getDeviceController('vif').deviceIDs():
2190 test = 1
2191 log.info("Dev %s still active, looping...", i)
2192 time.sleep(0.1)
2194 if test == 0:
2195 break
2196 if diff >= MIGRATE_TIMEOUT:
2197 log.info("Dev still active but hit max loop timeout")
2198 break
2200 def _storeVmDetails(self):
2201 to_store = {}
2203 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2204 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2205 if self._infoIsSet(info_key):
2206 to_store[key] = str(self.info[info_key])
2208 if self._infoIsSet("static_memory_min"):
2209 to_store["memory"] = str(self.info["static_memory_min"])
2210 if self._infoIsSet("static_memory_max"):
2211 to_store["maxmem"] = str(self.info["static_memory_max"])
2213 image_sxpr = self.info.image_sxpr()
2214 if image_sxpr:
2215 to_store['image'] = sxp.to_string(image_sxpr)
2217 if not self._readVm('xend/restart_count'):
2218 to_store['xend/restart_count'] = str(0)
2220 log.debug("Storing VM details: %s", scrub_password(to_store))
2222 self._writeVm(to_store)
2223 self._setVmPermissions()
2226 def _setVmPermissions(self):
2227 """Allow the guest domain to read its UUID. We don't allow it to
2228 access any other entry, for security."""
2229 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2230 { 'dom' : self.domid,
2231 'read' : True,
2232 'write' : False })
2235 # Utility functions
2238 def __getattr__(self, name):
2239 if name == "state":
2240 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2241 log.warn("".join(traceback.format_stack()))
2242 return self._stateGet()
2243 else:
2244 raise AttributeError()
2246 def __setattr__(self, name, value):
2247 if name == "state":
2248 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2249 log.warn("".join(traceback.format_stack()))
2250 self._stateSet(value)
2251 else:
2252 self.__dict__[name] = value
2254 def _stateSet(self, state):
2255 self.state_updated.acquire()
2256 try:
2257 # TODO Not sure this is correct...
2258 # _stateGet is live now. Why not fire event
2259 # even when it hasn't changed?
2260 if self._stateGet() != state:
2261 self.state_updated.notifyAll()
2262 import XendAPI
2263 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2264 'power_state')
2265 finally:
2266 self.state_updated.release()
2268 def _stateGet(self):
2269 # Lets try and reconsitute the state from xc
2270 # first lets try and get the domain info
2271 # from xc - this will tell us if the domain
2272 # exists
2273 info = dom_get(self.getDomid())
2274 if info is None or info['shutdown']:
2275 # We are either HALTED or SUSPENDED
2276 # check saved image exists
2277 from xen.xend import XendDomain
2278 managed_config_path = \
2279 XendDomain.instance()._managed_check_point_path( \
2280 self.get_uuid())
2281 if os.path.exists(managed_config_path):
2282 return XEN_API_VM_POWER_STATE_SUSPENDED
2283 else:
2284 return XEN_API_VM_POWER_STATE_HALTED
2285 elif info['crashed']:
2286 # Crashed
2287 return XEN_API_VM_POWER_STATE_CRASHED
2288 else:
2289 # We are either RUNNING or PAUSED
2290 if info['paused']:
2291 return XEN_API_VM_POWER_STATE_PAUSED
2292 else:
2293 return XEN_API_VM_POWER_STATE_RUNNING
2295 def _infoIsSet(self, name):
2296 return name in self.info and self.info[name] is not None
2298 def _checkName(self, name):
2299 """Check if a vm name is valid. Valid names contain alphabetic
2300 characters, digits, or characters in '_-.:/+'.
2301 The same name cannot be used for more than one vm at the same time.
2303 @param name: name
2304 @raise: VmError if invalid
2305 """
2306 from xen.xend import XendDomain
2308 if name is None or name == '':
2309 raise VmError('Missing VM Name')
2311 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2312 raise VmError('Invalid VM Name')
2314 dom = XendDomain.instance().domain_lookup_nr(name)
2315 if dom and dom.info['uuid'] != self.info['uuid']:
2316 raise VmError("VM name '%s' already exists%s" %
2317 (name,
2318 dom.domid is not None and
2319 (" as domain %s" % str(dom.domid)) or ""))
2322 def update(self, info = None, refresh = True, transaction = None):
2323 """Update with info from xc.domain_getinfo().
2324 """
2325 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2326 str(self.domid))
2328 if not info:
2329 info = dom_get(self.domid)
2330 if not info:
2331 return
2333 if info["maxmem_kb"] < 0:
2334 info["maxmem_kb"] = XendNode.instance() \
2335 .physinfo_dict()['total_memory'] * 1024
2337 #ssidref field not used any longer
2338 if 'ssidref' in info:
2339 info.pop('ssidref')
2341 # make sure state is reset for info
2342 # TODO: we should eventually get rid of old_dom_states
2344 self.info.update_config(info)
2345 self._update_consoles(transaction)
2347 if refresh:
2348 self.refreshShutdown(info)
2350 log.trace("XendDomainInfo.update done on domain %s: %s",
2351 str(self.domid), self.info)
2353 def sxpr(self, ignore_store = False, legacy_only = True):
2354 result = self.info.to_sxp(domain = self,
2355 ignore_devices = ignore_store,
2356 legacy_only = legacy_only)
2358 #if not ignore_store and self.dompath:
2359 # vnc_port = self.readDom('console/vnc-port')
2360 # if vnc_port is not None:
2361 # result.append(['device',
2362 # ['console', ['vnc-port', str(vnc_port)]]])
2364 return result
2366 # Xen API
2367 # ----------------------------------------------------------------
2369 def get_uuid(self):
2370 dom_uuid = self.info.get('uuid')
2371 if not dom_uuid: # if it doesn't exist, make one up
2372 dom_uuid = uuid.createString()
2373 self.info['uuid'] = dom_uuid
2374 return dom_uuid
2376 def get_memory_static_max(self):
2377 return self.info.get('memory_static_max', 0)
2378 def get_memory_static_min(self):
2379 return self.info.get('memory_static_min', 0)
2380 def get_memory_dynamic_max(self):
2381 return self.info.get('memory_dynamic_max', 0)
2382 def get_memory_dynamic_min(self):
2383 return self.info.get('memory_dynamic_min', 0)
2385 # only update memory-related config values if they maintain sanity
2386 def _safe_set_memory(self, key, newval):
2387 oldval = self.info.get(key, 0)
2388 try:
2389 self.info[key] = newval
2390 self.info._memory_sanity_check()
2391 except Exception, ex:
2392 self.info[key] = oldval
2393 raise
2395 def set_memory_static_max(self, val):
2396 self._safe_set_memory('memory_static_max', val)
2397 def set_memory_static_min(self, val):
2398 self._safe_set_memory('memory_static_min', val)
2399 def set_memory_dynamic_max(self, val):
2400 self._safe_set_memory('memory_dynamic_max', val)
2401 def set_memory_dynamic_min(self, val):
2402 self._safe_set_memory('memory_dynamic_min', val)
2404 def get_vcpus_params(self):
2405 if self.getDomid() is None:
2406 return self.info['vcpus_params']
2408 retval = xc.sched_credit_domain_get(self.getDomid())
2409 return retval
2410 def get_power_state(self):
2411 return XEN_API_VM_POWER_STATE[self._stateGet()]
2412 def get_platform(self):
2413 return self.info.get('platform', {})
2414 def get_pci_bus(self):
2415 return self.info.get('pci_bus', '')
2416 def get_tools_version(self):
2417 return self.info.get('tools_version', {})
2418 def get_metrics(self):
2419 return self.metrics.get_uuid();
2422 def get_security_label(self, xspol=None):
2423 import xen.util.xsm.xsm as security
2424 label = security.get_security_label(self, xspol)
2425 return label
2427 def set_security_label(self, seclab, old_seclab, xspol=None,
2428 xspol_old=None):
2429 """
2430 Set the security label of a domain from its old to
2431 a new value.
2432 @param seclab New security label formatted in the form
2433 <policy type>:<policy name>:<vm label>
2434 @param old_seclab The current security label that the
2435 VM must have.
2436 @param xspol An optional policy under which this
2437 update should be done. If not given,
2438 then the current active policy is used.
2439 @param xspol_old The old policy; only to be passed during
2440 the updating of a policy
2441 @return Returns return code, a string with errors from
2442 the hypervisor's operation, old label of the
2443 domain
2444 """
2445 rc = 0
2446 errors = ""
2447 old_label = ""
2448 new_ssidref = 0
2449 domid = self.getDomid()
2450 res_labels = None
2451 is_policy_update = (xspol_old != None)
2453 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2454 from xen.util import xsconstants
2456 state = self._stateGet()
2457 # Relabel only HALTED or RUNNING or PAUSED domains
2458 if domid != 0 and \
2459 state not in \
2460 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2461 DOM_STATE_SUSPENDED ]:
2462 log.warn("Relabeling domain not possible in state '%s'" %
2463 DOM_STATES[state])
2464 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2466 # Remove security label. Works only for halted domains
2467 if not seclab or seclab == "":
2468 if state not in [ DOM_STATE_HALTED ]:
2469 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2471 if self.info.has_key('security_label'):
2472 old_label = self.info['security_label']
2473 # Check label against expected one.
2474 if old_label != old_seclab:
2475 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2476 del self.info['security_label']
2477 xen.xend.XendDomain.instance().managed_config_save(self)
2478 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2480 tmp = seclab.split(":")
2481 if len(tmp) != 3:
2482 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2483 typ, policy, label = tmp
2485 poladmin = XSPolicyAdminInstance()
2486 if not xspol:
2487 xspol = poladmin.get_policy_by_name(policy)
2489 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2490 #if domain is running or paused try to relabel in hypervisor
2491 if not xspol:
2492 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2494 if typ != xspol.get_type_name() or \
2495 policy != xspol.get_name():
2496 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2498 if typ == xsconstants.ACM_POLICY_ID:
2499 new_ssidref = xspol.vmlabel_to_ssidref(label)
2500 if new_ssidref == xsconstants.INVALID_SSIDREF:
2501 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2503 # Check that all used resources are accessible under the
2504 # new label
2505 if not is_policy_update and \
2506 not security.resources_compatible_with_vmlabel(xspol,
2507 self, label):
2508 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2510 #Check label against expected one. Can only do this
2511 # if the policy hasn't changed underneath in the meantime
2512 if xspol_old == None:
2513 old_label = self.get_security_label()
2514 if old_label != old_seclab:
2515 log.info("old_label != old_seclab: %s != %s" %
2516 (old_label, old_seclab))
2517 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2519 # relabel domain in the hypervisor
2520 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2521 log.info("rc from relabeling in HV: %d" % rc)
2522 else:
2523 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2525 if rc == 0:
2526 # HALTED, RUNNING or PAUSED
2527 if domid == 0:
2528 if xspol:
2529 self.info['security_label'] = seclab
2530 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2531 else:
2532 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2533 else:
2534 if self.info.has_key('security_label'):
2535 old_label = self.info['security_label']
2536 # Check label against expected one, unless wildcard
2537 if old_label != old_seclab:
2538 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2540 self.info['security_label'] = seclab
2542 try:
2543 xen.xend.XendDomain.instance().managed_config_save(self)
2544 except:
2545 pass
2546 return (rc, errors, old_label, new_ssidref)
2548 def get_on_shutdown(self):
2549 after_shutdown = self.info.get('actions_after_shutdown')
2550 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2551 return XEN_API_ON_NORMAL_EXIT[-1]
2552 return after_shutdown
2554 def get_on_reboot(self):
2555 after_reboot = self.info.get('actions_after_reboot')
2556 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2557 return XEN_API_ON_NORMAL_EXIT[-1]
2558 return after_reboot
2560 def get_on_suspend(self):
2561 # TODO: not supported
2562 after_suspend = self.info.get('actions_after_suspend')
2563 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2564 return XEN_API_ON_NORMAL_EXIT[-1]
2565 return after_suspend
2567 def get_on_crash(self):
2568 after_crash = self.info.get('actions_after_crash')
2569 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2570 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2571 return after_crash
2573 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2574 """ Get's a device configuration either from XendConfig or
2575 from the DevController.
2577 @param dev_class: device class, either, 'vbd' or 'vif'
2578 @param dev_uuid: device UUID
2580 @rtype: dictionary
2581 """
2582 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2584 # shortcut if the domain isn't started because
2585 # the devcontrollers will have no better information
2586 # than XendConfig.
2587 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2588 if dev_config:
2589 return copy.deepcopy(dev_config)
2590 return None
2592 # instead of using dev_class, we use the dev_type
2593 # that is from XendConfig.
2594 controller = self.getDeviceController(dev_type)
2595 if not controller:
2596 return None
2598 all_configs = controller.getAllDeviceConfigurations()
2599 if not all_configs:
2600 return None
2602 updated_dev_config = copy.deepcopy(dev_config)
2603 for _devid, _devcfg in all_configs.items():
2604 if _devcfg.get('uuid') == dev_uuid:
2605 updated_dev_config.update(_devcfg)
2606 updated_dev_config['id'] = _devid
2607 return updated_dev_config
2609 return updated_dev_config
2611 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2612 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2613 if not config:
2614 return {}
2616 config['VM'] = self.get_uuid()
2618 if dev_class == 'vif':
2619 if not config.has_key('name'):
2620 config['name'] = config.get('vifname', '')
2621 if not config.has_key('MAC'):
2622 config['MAC'] = config.get('mac', '')
2623 if not config.has_key('type'):
2624 config['type'] = 'paravirtualised'
2625 if not config.has_key('device'):
2626 devid = config.get('id')
2627 if devid != None:
2628 config['device'] = 'eth%d' % devid
2629 else:
2630 config['device'] = ''
2632 if not config.has_key('network'):
2633 try:
2634 bridge = config.get('bridge', None)
2635 if bridge is None:
2636 from xen.util import Brctl
2637 if_to_br = dict([(i,b)
2638 for (b,ifs) in Brctl.get_state().items()
2639 for i in ifs])
2640 vifname = "vif%s.%s" % (self.getDomid(),
2641 config.get('id'))
2642 bridge = if_to_br.get(vifname, None)
2643 config['network'] = \
2644 XendNode.instance().bridge_to_network(
2645 config.get('bridge')).get_uuid()
2646 except Exception:
2647 log.exception('bridge_to_network')
2648 # Ignore this for now -- it may happen if the device
2649 # has been specified using the legacy methods, but at
2650 # some point we're going to have to figure out how to
2651 # handle that properly.
2653 config['MTU'] = 1500 # TODO
2655 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2656 xennode = XendNode.instance()
2657 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2658 config['io_read_kbs'] = rx_bps/1024
2659 config['io_write_kbs'] = tx_bps/1024
2660 rx, tx = xennode.get_vif_stat(self.domid, devid)
2661 config['io_total_read_kbs'] = rx/1024
2662 config['io_total_write_kbs'] = tx/1024
2663 else:
2664 config['io_read_kbs'] = 0.0
2665 config['io_write_kbs'] = 0.0
2666 config['io_total_read_kbs'] = 0.0
2667 config['io_total_write_kbs'] = 0.0
2669 config['security_label'] = config.get('security_label', '')
2671 if dev_class == 'vbd':
2673 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2674 controller = self.getDeviceController(dev_class)
2675 devid, _1, _2 = controller.getDeviceDetails(config)
2676 xennode = XendNode.instance()
2677 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2678 config['io_read_kbs'] = rd_blkps
2679 config['io_write_kbs'] = wr_blkps
2680 else:
2681 config['io_read_kbs'] = 0.0
2682 config['io_write_kbs'] = 0.0
2684 config['VDI'] = config.get('VDI', '')
2685 config['device'] = config.get('dev', '')
2686 if ':' in config['device']:
2687 vbd_name, vbd_type = config['device'].split(':', 1)
2688 config['device'] = vbd_name
2689 if vbd_type == 'cdrom':
2690 config['type'] = XEN_API_VBD_TYPE[0]
2691 else:
2692 config['type'] = XEN_API_VBD_TYPE[1]
2694 config['driver'] = 'paravirtualised' # TODO
2695 config['image'] = config.get('uname', '')
2697 if config.get('mode', 'r') == 'r':
2698 config['mode'] = 'RO'
2699 else:
2700 config['mode'] = 'RW'
2702 if dev_class == 'vtpm':
2703 if not config.has_key('type'):
2704 config['type'] = 'paravirtualised' # TODO
2705 if not config.has_key('backend'):
2706 config['backend'] = "00000000-0000-0000-0000-000000000000"
2708 return config
2710 def get_dev_property(self, dev_class, dev_uuid, field):
2711 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2712 try:
2713 return config[field]
2714 except KeyError:
2715 raise XendError('Invalid property for device: %s' % field)
2717 def set_dev_property(self, dev_class, dev_uuid, field, value):
2718 self.info['devices'][dev_uuid][1][field] = value
2720 def get_vcpus_util(self):
2721 vcpu_util = {}
2722 xennode = XendNode.instance()
2723 if 'VCPUs_max' in self.info and self.domid != None:
2724 for i in range(0, self.info['VCPUs_max']):
2725 util = xennode.get_vcpu_util(self.domid, i)
2726 vcpu_util[str(i)] = util
2728 return vcpu_util
2730 def get_consoles(self):
2731 return self.info.get('console_refs', [])
2733 def get_vifs(self):
2734 return self.info.get('vif_refs', [])
2736 def get_vbds(self):
2737 return self.info.get('vbd_refs', [])
2739 def get_vtpms(self):
2740 return self.info.get('vtpm_refs', [])
2742 def create_vbd(self, xenapi_vbd, vdi_image_path):
2743 """Create a VBD using a VDI from XendStorageRepository.
2745 @param xenapi_vbd: vbd struct from the Xen API
2746 @param vdi_image_path: VDI UUID
2747 @rtype: string
2748 @return: uuid of the device
2749 """
2750 xenapi_vbd['image'] = vdi_image_path
2751 if vdi_image_path.startswith('tap'):
2752 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2753 else:
2754 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2756 if not dev_uuid:
2757 raise XendError('Failed to create device')
2759 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2760 XEN_API_VM_POWER_STATE_PAUSED):
2761 _, config = self.info['devices'][dev_uuid]
2763 if vdi_image_path.startswith('tap'):
2764 dev_control = self.getDeviceController('tap')
2765 else:
2766 dev_control = self.getDeviceController('vbd')
2768 try:
2769 devid = dev_control.createDevice(config)
2770 dev_control.waitForDevice(devid)
2771 self.info.device_update(dev_uuid,
2772 cfg_xenapi = {'devid': devid})
2773 except Exception, exn:
2774 log.exception(exn)
2775 del self.info['devices'][dev_uuid]
2776 self.info['vbd_refs'].remove(dev_uuid)
2777 raise
2779 return dev_uuid
2781 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2782 """Create a VBD using a VDI from XendStorageRepository.
2784 @param xenapi_vbd: vbd struct from the Xen API
2785 @param vdi_image_path: VDI UUID
2786 @rtype: string
2787 @return: uuid of the device
2788 """
2789 xenapi_vbd['image'] = vdi_image_path
2790 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2791 if not dev_uuid:
2792 raise XendError('Failed to create device')
2794 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2795 _, config = self.info['devices'][dev_uuid]
2796 config['devid'] = self.getDeviceController('tap').createDevice(config)
2798 return config['devid']
2800 def create_vif(self, xenapi_vif):
2801 """Create VIF device from the passed struct in Xen API format.
2803 @param xenapi_vif: Xen API VIF Struct.
2804 @rtype: string
2805 @return: UUID
2806 """
2807 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2808 if not dev_uuid:
2809 raise XendError('Failed to create device')
2811 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2812 XEN_API_VM_POWER_STATE_PAUSED):
2814 _, config = self.info['devices'][dev_uuid]
2815 dev_control = self.getDeviceController('vif')
2817 try:
2818 devid = dev_control.createDevice(config)
2819 dev_control.waitForDevice(devid)
2820 self.info.device_update(dev_uuid,
2821 cfg_xenapi = {'devid': devid})
2822 except Exception, exn:
2823 log.exception(exn)
2824 del self.info['devices'][dev_uuid]
2825 self.info['vif_refs'].remove(dev_uuid)
2826 raise
2828 return dev_uuid
2830 def create_vtpm(self, xenapi_vtpm):
2831 """Create a VTPM device from the passed struct in Xen API format.
2833 @return: uuid of the device
2834 @rtype: string
2835 """
2837 if self._stateGet() not in (DOM_STATE_HALTED,):
2838 raise VmError("Can only add vTPM to a halted domain.")
2839 if self.get_vtpms() != []:
2840 raise VmError('Domain already has a vTPM.')
2841 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2842 if not dev_uuid:
2843 raise XendError('Failed to create device')
2845 return dev_uuid
2847 def create_console(self, xenapi_console):
2848 """ Create a console device from a Xen API struct.
2850 @return: uuid of device
2851 @rtype: string
2852 """
2853 if self._stateGet() not in (DOM_STATE_HALTED,):
2854 raise VmError("Can only add console to a halted domain.")
2856 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2857 if not dev_uuid:
2858 raise XendError('Failed to create device')
2860 return dev_uuid
2862 def set_console_other_config(self, console_uuid, other_config):
2863 self.info.console_update(console_uuid, 'other_config', other_config)
2865 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2866 if dev_uuid not in self.info['devices']:
2867 raise XendError('Device does not exist')
2869 try:
2870 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2871 XEN_API_VM_POWER_STATE_PAUSED):
2872 _, config = self.info['devices'][dev_uuid]
2873 devid = config.get('devid')
2874 if devid != None:
2875 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2876 else:
2877 raise XendError('Unable to get devid for device: %s:%s' %
2878 (dev_type, dev_uuid))
2879 finally:
2880 del self.info['devices'][dev_uuid]
2881 self.info['%s_refs' % dev_type].remove(dev_uuid)
2883 def destroy_vbd(self, dev_uuid):
2884 self.destroy_device_by_uuid('vbd', dev_uuid)
2886 def destroy_vif(self, dev_uuid):
2887 self.destroy_device_by_uuid('vif', dev_uuid)
2889 def destroy_vtpm(self, dev_uuid):
2890 self.destroy_device_by_uuid('vtpm', dev_uuid)
2892 def has_device(self, dev_class, dev_uuid):
2893 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2895 def __str__(self):
2896 return '<domain id=%s name=%s memory=%s state=%s>' % \
2897 (str(self.domid), self.info['name_label'],
2898 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2900 __repr__ = __str__