debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 17015:def2adbce510

xend: Restore values of /vm/uuid/xend/* to recreated domains.

When guest domains are restarted, previous values of /vm/uuid/xend/*
in xenstore are lost. (e.g. previous_restart_time,
last_shutdown_reason). This patch restores them to restarting domains.
And we should update /vm/uuid/xend/restart_count of restarting
domains, not previous domains.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Feb 05 10:39:35 2008 +0000 (2008-02-05)
parents c4a06902febf
children 58e5e9ae0f8d
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 _listRecursiveVm(self, *args):
883 return xstransact.ListRecursive(self.vmpath, *args)
885 def storeVm(self, *args):
886 return xstransact.Store(self.vmpath, *args)
888 def permissionsVm(self, *args):
889 return xstransact.SetPermissions(self.vmpath, *args)
892 def _readVmTxn(self, transaction, *args):
893 paths = map(lambda x: self.vmpath + "/" + x, args)
894 return transaction.read(*paths)
896 def _writeVmTxn(self, transaction, *args):
897 paths = map(lambda x: self.vmpath + "/" + x, args)
898 return transaction.write(*paths)
900 def _removeVmTxn(self, transaction, *args):
901 paths = map(lambda x: self.vmpath + "/" + x, args)
902 return transaction.remove(*paths)
904 def _gatherVmTxn(self, transaction, *args):
905 paths = map(lambda x: self.vmpath + "/" + x, args)
906 return transaction.gather(paths)
908 def storeVmTxn(self, transaction, *args):
909 paths = map(lambda x: self.vmpath + "/" + x, args)
910 return transaction.store(*paths)
912 def permissionsVmTxn(self, transaction, *args):
913 paths = map(lambda x: self.vmpath + "/" + x, args)
914 return transaction.set_permissions(*paths)
916 #
917 # Function to update xenstore /dom/*
918 #
920 def readDom(self, *args):
921 return xstransact.Read(self.dompath, *args)
923 def gatherDom(self, *args):
924 return xstransact.Gather(self.dompath, *args)
926 def _writeDom(self, *args):
927 return xstransact.Write(self.dompath, *args)
929 def _removeDom(self, *args):
930 return xstransact.Remove(self.dompath, *args)
932 def storeDom(self, *args):
933 return xstransact.Store(self.dompath, *args)
936 def readDomTxn(self, transaction, *args):
937 paths = map(lambda x: self.dompath + "/" + x, args)
938 return transaction.read(*paths)
940 def gatherDomTxn(self, transaction, *args):
941 paths = map(lambda x: self.dompath + "/" + x, args)
942 return transaction.gather(*paths)
944 def _writeDomTxn(self, transaction, *args):
945 paths = map(lambda x: self.dompath + "/" + x, args)
946 return transaction.write(*paths)
948 def _removeDomTxn(self, transaction, *args):
949 paths = map(lambda x: self.dompath + "/" + x, args)
950 return transaction.remove(*paths)
952 def storeDomTxn(self, transaction, *args):
953 paths = map(lambda x: self.dompath + "/" + x, args)
954 return transaction.store(*paths)
957 def _recreateDom(self):
958 complete(self.dompath, lambda t: self._recreateDomFunc(t))
960 def _recreateDomFunc(self, t):
961 t.remove()
962 t.mkdir()
963 t.set_permissions({'dom' : self.domid})
964 t.write('vm', self.vmpath)
966 def _storeDomDetails(self):
967 to_store = {
968 'domid': str(self.domid),
969 'vm': self.vmpath,
970 'name': self.info['name_label'],
971 'console/limit': str(xoptions.get_console_limit() * 1024),
972 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
973 }
975 def f(n, v):
976 if v is not None:
977 if type(v) == bool:
978 to_store[n] = v and "1" or "0"
979 else:
980 to_store[n] = str(v)
982 # Figure out if we need to tell xenconsoled to ignore this guest's
983 # console - device model will handle console if it is running
984 constype = "ioemu"
985 if 'device_model' not in self.info['platform']:
986 constype = "xenconsoled"
988 f('console/port', self.console_port)
989 f('console/ring-ref', self.console_mfn)
990 f('console/type', constype)
991 f('store/port', self.store_port)
992 f('store/ring-ref', self.store_mfn)
994 if arch.type == "x86":
995 f('control/platform-feature-multiprocessor-suspend', True)
997 # elfnotes
998 for n, v in self.info.get_notes().iteritems():
999 n = n.lower().replace('_', '-')
1000 if n == 'features':
1001 for v in v.split('|'):
1002 v = v.replace('_', '-')
1003 if v.startswith('!'):
1004 f('image/%s/%s' % (n, v[1:]), False)
1005 else:
1006 f('image/%s/%s' % (n, v), True)
1007 else:
1008 f('image/%s' % n, v)
1010 if self.info.has_key('security_label'):
1011 f('security_label', self.info['security_label'])
1013 to_store.update(self._vcpuDomDetails())
1015 log.debug("Storing domain details: %s", scrub_password(to_store))
1017 self._writeDom(to_store)
1019 def _vcpuDomDetails(self):
1020 def availability(n):
1021 if self.info['vcpu_avail'] & (1 << n):
1022 return 'online'
1023 else:
1024 return 'offline'
1026 result = {}
1027 for v in range(0, self.info['VCPUs_max']):
1028 result["cpu/%d/availability" % v] = availability(v)
1029 return result
1032 # xenstore watches
1035 def _registerWatches(self):
1036 """Register a watch on this VM's entries in the store, and the
1037 domain's control/shutdown node, so that when they are changed
1038 externally, we keep up to date. This should only be called by {@link
1039 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1040 details have been written, but before the new instance is returned."""
1041 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1042 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1043 self._handleShutdownWatch)
1045 def _storeChanged(self, _):
1046 log.trace("XendDomainInfo.storeChanged");
1048 changed = False
1050 # Check whether values in the configuration have
1051 # changed in Xenstore.
1053 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1054 'rtc/timeoffset']
1056 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1057 for k in cfg_vm])
1059 # convert two lists into a python dictionary
1060 vm_details = dict(zip(cfg_vm, vm_details))
1062 if vm_details['rtc/timeoffset'] == None:
1063 vm_details['rtc/timeoffset'] = "0"
1065 for arg, val in vm_details.items():
1066 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1067 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1068 if val != None and val != self.info[xapiarg]:
1069 self.info[xapiarg] = val
1070 changed = True
1071 elif arg == "memory":
1072 if val != None and val != self.info["static_memory_min"]:
1073 self.info["static_memory_min"] = val
1074 changed = True
1075 elif arg == "maxmem":
1076 if val != None and val != self.info["static_memory_max"]:
1077 self.info["static_memory_max"] = val
1078 changed = True
1080 # Check whether image definition has been updated
1081 image_sxp = self._readVm('image')
1082 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1083 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1084 changed = True
1086 # Check if the rtc offset has changes
1087 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1088 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1089 changed = True
1091 if changed:
1092 # Update the domain section of the store, as this contains some
1093 # parameters derived from the VM configuration.
1094 self._storeDomDetails()
1096 return 1
1098 def _handleShutdownWatch(self, _):
1099 log.debug('XendDomainInfo.handleShutdownWatch')
1101 reason = self.readDom('control/shutdown')
1103 if reason and reason != 'suspend':
1104 sst = self.readDom('xend/shutdown_start_time')
1105 now = time.time()
1106 if sst:
1107 self.shutdownStartTime = float(sst)
1108 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1109 else:
1110 self.shutdownStartTime = now
1111 self.storeDom('xend/shutdown_start_time', now)
1112 timeout = SHUTDOWN_TIMEOUT
1114 log.trace(
1115 "Scheduling refreshShutdown on domain %d in %ds.",
1116 self.domid, timeout)
1117 threading.Timer(timeout, self.refreshShutdown).start()
1119 return True
1123 # Public Attributes for the VM
1127 def getDomid(self):
1128 return self.domid
1130 def setName(self, name, to_store = True):
1131 self._checkName(name)
1132 self.info['name_label'] = name
1133 if to_store:
1134 self.storeVm("name", name)
1136 def getName(self):
1137 return self.info['name_label']
1139 def getDomainPath(self):
1140 return self.dompath
1142 def getShutdownReason(self):
1143 return self.readDom('control/shutdown')
1145 def getStorePort(self):
1146 """For use only by image.py and XendCheckpoint.py."""
1147 return self.store_port
1149 def getConsolePort(self):
1150 """For use only by image.py and XendCheckpoint.py"""
1151 return self.console_port
1153 def getFeatures(self):
1154 """For use only by image.py."""
1155 return self.info['features']
1157 def getVCpuCount(self):
1158 return self.info['VCPUs_max']
1160 def setVCpuCount(self, vcpus):
1161 if vcpus <= 0:
1162 raise XendError('Invalid VCPUs')
1164 self.info['vcpu_avail'] = (1 << vcpus) - 1
1165 if self.domid >= 0:
1166 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1167 # update dom differently depending on whether we are adjusting
1168 # vcpu number up or down, otherwise _vcpuDomDetails does not
1169 # disable the vcpus
1170 if self.info['VCPUs_max'] > vcpus:
1171 # decreasing
1172 self._writeDom(self._vcpuDomDetails())
1173 self.info['VCPUs_live'] = vcpus
1174 else:
1175 # same or increasing
1176 self.info['VCPUs_live'] = vcpus
1177 self._writeDom(self._vcpuDomDetails())
1178 else:
1179 self.info['VCPUs_max'] = vcpus
1180 xen.xend.XendDomain.instance().managed_config_save(self)
1181 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1182 vcpus)
1184 def getMemoryTarget(self):
1185 """Get this domain's target memory size, in KB."""
1186 return self.info['memory_dynamic_max'] / 1024
1188 def getMemoryMaximum(self):
1189 """Get this domain's maximum memory size, in KB."""
1190 # remember, info now stores memory in bytes
1191 return self.info['memory_static_max'] / 1024
1193 def getResume(self):
1194 return str(self._resume)
1196 def setResume(self, isresume):
1197 self._resume = isresume
1199 def getCap(self):
1200 return self.info['vcpus_params']['cap']
1202 def setCap(self, cpu_cap):
1203 self.info['vcpus_params']['cap'] = cpu_cap
1205 def getWeight(self):
1206 return self.info['vcpus_params']['weight']
1208 def setWeight(self, cpu_weight):
1209 self.info['vcpus_params']['weight'] = cpu_weight
1211 def setResume(self, state):
1212 self._resume = state
1214 def getRestartCount(self):
1215 return self._readVm('xend/restart_count')
1217 def refreshShutdown(self, xeninfo = None):
1218 """ Checks the domain for whether a shutdown is required.
1220 Called from XendDomainInfo and also image.py for HVM images.
1221 """
1223 # If set at the end of this method, a restart is required, with the
1224 # given reason. This restart has to be done out of the scope of
1225 # refresh_shutdown_lock.
1226 restart_reason = None
1228 self.refresh_shutdown_lock.acquire()
1229 try:
1230 if xeninfo is None:
1231 xeninfo = dom_get(self.domid)
1232 if xeninfo is None:
1233 # The domain no longer exists. This will occur if we have
1234 # scheduled a timer to check for shutdown timeouts and the
1235 # shutdown succeeded. It will also occur if someone
1236 # destroys a domain beneath us. We clean up the domain,
1237 # just in case, but we can't clean up the VM, because that
1238 # VM may have migrated to a different domain on this
1239 # machine.
1240 self.cleanupDomain()
1241 self._stateSet(DOM_STATE_HALTED)
1242 return
1244 if xeninfo['dying']:
1245 # Dying means that a domain has been destroyed, but has not
1246 # yet been cleaned up by Xen. This state could persist
1247 # indefinitely if, for example, another domain has some of its
1248 # pages mapped. We might like to diagnose this problem in the
1249 # future, but for now all we do is make sure that it's not us
1250 # holding the pages, by calling cleanupDomain. We can't
1251 # clean up the VM, as above.
1252 self.cleanupDomain()
1253 self._stateSet(DOM_STATE_SHUTDOWN)
1254 return
1256 elif xeninfo['crashed']:
1257 if self.readDom('xend/shutdown_completed'):
1258 # We've seen this shutdown already, but we are preserving
1259 # the domain for debugging. Leave it alone.
1260 return
1262 log.warn('Domain has crashed: name=%s id=%d.',
1263 self.info['name_label'], self.domid)
1264 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1266 if xoptions.get_enable_dump():
1267 try:
1268 self.dumpCore()
1269 except XendError:
1270 # This error has been logged -- there's nothing more
1271 # we can do in this context.
1272 pass
1274 restart_reason = 'crash'
1275 self._stateSet(DOM_STATE_HALTED)
1277 elif xeninfo['shutdown']:
1278 self._stateSet(DOM_STATE_SHUTDOWN)
1279 if self.readDom('xend/shutdown_completed'):
1280 # We've seen this shutdown already, but we are preserving
1281 # the domain for debugging. Leave it alone.
1282 return
1284 else:
1285 reason = shutdown_reason(xeninfo['shutdown_reason'])
1287 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1288 self.info['name_label'], self.domid, reason)
1289 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1291 self._clearRestart()
1293 if reason == 'suspend':
1294 self._stateSet(DOM_STATE_SUSPENDED)
1295 # Don't destroy the domain. XendCheckpoint will do
1296 # this once it has finished. However, stop watching
1297 # the VM path now, otherwise we will end up with one
1298 # watch for the old domain, and one for the new.
1299 self._unwatchVm()
1300 elif reason in ('poweroff', 'reboot'):
1301 restart_reason = reason
1302 else:
1303 self.destroy()
1305 elif self.dompath is None:
1306 # We have yet to manage to call introduceDomain on this
1307 # domain. This can happen if a restore is in progress, or has
1308 # failed. Ignore this domain.
1309 pass
1310 else:
1311 # Domain is alive. If we are shutting it down, log a message
1312 # if it seems unresponsive.
1313 if xeninfo['paused']:
1314 self._stateSet(DOM_STATE_PAUSED)
1315 else:
1316 self._stateSet(DOM_STATE_RUNNING)
1318 if self.shutdownStartTime:
1319 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1320 self.shutdownStartTime)
1321 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1322 log.info(
1323 "Domain shutdown timeout expired: name=%s id=%s",
1324 self.info['name_label'], self.domid)
1325 self.storeDom('xend/unresponsive', 'True')
1326 finally:
1327 self.refresh_shutdown_lock.release()
1329 if restart_reason:
1330 threading.Thread(target = self._maybeRestart,
1331 args = (restart_reason,)).start()
1335 # Restart functions - handling whether we come back up on shutdown.
1338 def _clearRestart(self):
1339 self._removeDom("xend/shutdown_start_time")
1342 def _maybeRestart(self, reason):
1343 # Dispatch to the correct method based upon the configured on_{reason}
1344 # behaviour.
1345 actions = {"destroy" : self.destroy,
1346 "restart" : self._restart,
1347 "preserve" : self._preserve,
1348 "rename-restart" : self._renameRestart}
1350 action_conf = {
1351 'poweroff': 'actions_after_shutdown',
1352 'reboot': 'actions_after_reboot',
1353 'crash': 'actions_after_crash',
1356 action_target = self.info.get(action_conf.get(reason))
1357 func = actions.get(action_target, None)
1358 if func and callable(func):
1359 func()
1360 else:
1361 self.destroy() # default to destroy
1363 def _renameRestart(self):
1364 self._restart(True)
1366 def _restart(self, rename = False):
1367 """Restart the domain after it has exited.
1369 @param rename True if the old domain is to be renamed and preserved,
1370 False if it is to be destroyed.
1371 """
1372 from xen.xend import XendDomain
1374 if self._readVm(RESTART_IN_PROGRESS):
1375 log.error('Xend failed during restart of domain %s. '
1376 'Refusing to restart to avoid loops.',
1377 str(self.domid))
1378 self.destroy()
1379 return
1381 old_domid = self.domid
1382 self._writeVm(RESTART_IN_PROGRESS, 'True')
1384 now = time.time()
1385 rst = self._readVm('xend/previous_restart_time')
1386 if rst:
1387 rst = float(rst)
1388 timeout = now - rst
1389 if timeout < MINIMUM_RESTART_TIME:
1390 log.error(
1391 'VM %s restarting too fast (%f seconds since the last '
1392 'restart). Refusing to restart to avoid loops.',
1393 self.info['name_label'], timeout)
1394 self.destroy()
1395 return
1397 self._writeVm('xend/previous_restart_time', str(now))
1399 prev_vm_xend = self._listRecursiveVm('xend')
1400 new_dom_info = self.info
1401 try:
1402 if rename:
1403 new_dom_info = self._preserveForRestart()
1404 else:
1405 self._unwatchVm()
1406 self.destroy()
1408 # new_dom's VM will be the same as this domain's VM, except where
1409 # the rename flag has instructed us to call preserveForRestart.
1410 # In that case, it is important that we remove the
1411 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1412 # once the new one is available.
1414 new_dom = None
1415 try:
1416 new_dom = XendDomain.instance().domain_create_from_dict(
1417 new_dom_info)
1418 for x in prev_vm_xend[0][1]:
1419 new_dom._writeVm('xend/%s' % x[0], x[1])
1420 new_dom.waitForDevices()
1421 new_dom.unpause()
1422 rst_cnt = new_dom._readVm('xend/restart_count')
1423 rst_cnt = int(rst_cnt) + 1
1424 new_dom._writeVm('xend/restart_count', str(rst_cnt))
1425 new_dom._removeVm(RESTART_IN_PROGRESS)
1426 except:
1427 if new_dom:
1428 new_dom._removeVm(RESTART_IN_PROGRESS)
1429 new_dom.destroy()
1430 else:
1431 self._removeVm(RESTART_IN_PROGRESS)
1432 raise
1433 except:
1434 log.exception('Failed to restart domain %s.', str(old_domid))
1436 def _preserveForRestart(self):
1437 """Preserve a domain that has been shut down, by giving it a new UUID,
1438 cloning the VM details, and giving it a new name. This allows us to
1439 keep this domain for debugging, but restart a new one in its place
1440 preserving the restart semantics (name and UUID preserved).
1441 """
1443 new_uuid = uuid.createString()
1444 new_name = 'Domain-%s' % new_uuid
1445 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1446 self.info['name_label'], self.domid, self.info['uuid'],
1447 new_name, new_uuid)
1448 self._unwatchVm()
1449 self._releaseDevices()
1450 # Remove existing vm node in xenstore
1451 self._removeVm()
1452 new_dom_info = self.info.copy()
1453 new_dom_info['name_label'] = self.info['name_label']
1454 new_dom_info['uuid'] = self.info['uuid']
1455 self.info['name_label'] = new_name
1456 self.info['uuid'] = new_uuid
1457 self.vmpath = XS_VMROOT + new_uuid
1458 # Write out new vm node to xenstore
1459 self._storeVmDetails()
1460 self._preserve()
1461 return new_dom_info
1464 def _preserve(self):
1465 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1466 self.domid)
1467 self._unwatchVm()
1468 self.storeDom('xend/shutdown_completed', 'True')
1469 self._stateSet(DOM_STATE_HALTED)
1472 # Debugging ..
1475 def dumpCore(self, corefile = None):
1476 """Create a core dump for this domain.
1478 @raise: XendError if core dumping failed.
1479 """
1481 try:
1482 if not corefile:
1483 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1484 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1485 self.info['name_label'], self.domid)
1487 if os.path.isdir(corefile):
1488 raise XendError("Cannot dump core in a directory: %s" %
1489 corefile)
1491 xc.domain_dumpcore(self.domid, corefile)
1492 except RuntimeError, ex:
1493 corefile_incomp = corefile+'-incomplete'
1494 os.rename(corefile, corefile_incomp)
1495 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1496 self.domid, self.info['name_label'])
1497 raise XendError("Failed to dump core: %s" % str(ex))
1500 # Device creation/deletion functions
1503 def _createDevice(self, deviceClass, devConfig):
1504 return self.getDeviceController(deviceClass).createDevice(devConfig)
1506 def _waitForDevice(self, deviceClass, devid):
1507 return self.getDeviceController(deviceClass).waitForDevice(devid)
1509 def _waitForDeviceUUID(self, dev_uuid):
1510 deviceClass, config = self.info['devices'].get(dev_uuid)
1511 self._waitForDevice(deviceClass, config['devid'])
1513 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1514 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1515 devid, backpath)
1517 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1518 return self.getDeviceController(deviceClass).reconfigureDevice(
1519 devid, devconfig)
1521 def _createDevices(self):
1522 """Create the devices for a vm.
1524 @raise: VmError for invalid devices
1525 """
1526 ordered_refs = self.info.ordered_device_refs()
1527 for dev_uuid in ordered_refs:
1528 devclass, config = self.info['devices'][dev_uuid]
1529 if devclass in XendDevices.valid_devices():
1530 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1531 dev_uuid = config.get('uuid')
1532 devid = self._createDevice(devclass, config)
1534 # store devid in XendConfig for caching reasons
1535 if dev_uuid in self.info['devices']:
1536 self.info['devices'][dev_uuid][1]['devid'] = devid
1538 if self.image:
1539 self.image.createDeviceModel()
1541 def _releaseDevices(self, suspend = False):
1542 """Release all domain's devices. Nothrow guarantee."""
1543 if self.image:
1544 try:
1545 log.debug("Destroying device model")
1546 self.image.destroyDeviceModel()
1547 except Exception, e:
1548 log.exception("Device model destroy failed %s" % str(e))
1549 else:
1550 log.debug("No device model")
1552 log.debug("Releasing devices")
1553 t = xstransact("%s/device" % self.dompath)
1554 try:
1555 for devclass in XendDevices.valid_devices():
1556 for dev in t.list(devclass):
1557 try:
1558 log.debug("Removing %s", dev);
1559 self.destroyDevice(devclass, dev, False);
1560 except:
1561 # Log and swallow any exceptions in removal --
1562 # there's nothing more we can do.
1563 log.exception("Device release failed: %s; %s; %s",
1564 self.info['name_label'], devclass, dev)
1565 finally:
1566 t.abort()
1568 def getDeviceController(self, name):
1569 """Get the device controller for this domain, and if it
1570 doesn't exist, create it.
1572 @param name: device class name
1573 @type name: string
1574 @rtype: subclass of DevController
1575 """
1576 if name not in self._deviceControllers:
1577 devController = XendDevices.make_controller(name, self)
1578 if not devController:
1579 raise XendError("Unknown device type: %s" % name)
1580 self._deviceControllers[name] = devController
1582 return self._deviceControllers[name]
1585 # Migration functions (public)
1588 def testMigrateDevices(self, network, dst):
1589 """ Notify all device about intention of migration
1590 @raise: XendError for a device that cannot be migrated
1591 """
1592 for (n, c) in self.info.all_devices_sxpr():
1593 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1594 if rc != 0:
1595 raise XendError("Device of type '%s' refuses migration." % n)
1597 def migrateDevices(self, network, dst, step, domName=''):
1598 """Notify the devices about migration
1599 """
1600 ctr = 0
1601 try:
1602 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1603 self.migrateDevice(dev_type, dev_conf, network, dst,
1604 step, domName)
1605 ctr = ctr + 1
1606 except:
1607 for dev_type, dev_conf in self.info.all_devices_sxpr():
1608 if ctr == 0:
1609 step = step - 1
1610 ctr = ctr - 1
1611 self._recoverMigrateDevice(dev_type, dev_conf, network,
1612 dst, step, domName)
1613 raise
1615 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1616 step, domName=''):
1617 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1618 network, dst, step, domName)
1620 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1621 dst, step, domName=''):
1622 return self.getDeviceController(deviceClass).recover_migrate(
1623 deviceConfig, network, dst, step, domName)
1626 ## private:
1628 def _constructDomain(self):
1629 """Construct the domain.
1631 @raise: VmError on error
1632 """
1634 log.debug('XendDomainInfo.constructDomain')
1636 self.shutdownStartTime = None
1638 hap = 0
1639 hvm = self.info.is_hvm()
1640 if hvm:
1641 hap = self.info.is_hap()
1642 info = xc.xeninfo()
1643 if 'hvm' not in info['xen_caps']:
1644 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1645 "supported by your CPU and enabled in your "
1646 "BIOS?")
1648 # Hack to pre-reserve some memory for initial domain creation.
1649 # There is an implicit memory overhead for any domain creation. This
1650 # overhead is greater for some types of domain than others. For
1651 # example, an x86 HVM domain will have a default shadow-pagetable
1652 # allocation of 1MB. We free up 2MB here to be on the safe side.
1653 balloon.free(2*1024) # 2MB should be plenty
1655 ssidref = 0
1656 if security.on():
1657 ssidref = security.calc_dom_ssidref_from_info(self.info)
1658 if security.has_authorization(ssidref) == False:
1659 raise VmError("VM is not authorized to run.")
1661 try:
1662 self.domid = xc.domain_create(
1663 domid = 0,
1664 ssidref = ssidref,
1665 handle = uuid.fromString(self.info['uuid']),
1666 flags = (int(hvm) << 0) | (int(hap) << 1),
1667 target = self.info.target())
1668 except Exception, e:
1669 # may get here if due to ACM the operation is not permitted
1670 if security.on():
1671 raise VmError('Domain in conflict set with running domain?')
1673 if self.domid < 0:
1674 raise VmError('Creating domain failed: name=%s' %
1675 self.info['name_label'])
1677 self.dompath = GetDomainPath(self.domid)
1679 self._recreateDom()
1681 # Set timer configration of domain
1682 timer_mode = self.info["platform"].get("timer_mode")
1683 if hvm and timer_mode is not None:
1684 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
1685 long(timer_mode))
1687 # Set maximum number of vcpus in domain
1688 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1690 # Test whether the devices can be assigned with VT-d
1691 pci_str = str(self.info["platform"].get("pci"))
1692 if hvm and pci_str:
1693 bdf = xc.test_assign_device(self.domid, pci_str)
1694 if bdf != 0:
1695 bus = (bdf >> 16) & 0xff
1696 devfn = (bdf >> 8) & 0xff
1697 dev = (devfn >> 3) & 0x1f
1698 func = devfn & 0x7
1699 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
1700 "not enabled, or the device is not exist, or it "
1701 "has already been assigned to other domain"
1702 % (bus, dev, func))
1704 # register the domain in the list
1705 from xen.xend import XendDomain
1706 XendDomain.instance().add_domain(self)
1708 def _introduceDomain(self):
1709 assert self.domid is not None
1710 assert self.store_mfn is not None
1711 assert self.store_port is not None
1713 try:
1714 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1715 except RuntimeError, exn:
1716 raise XendError(str(exn))
1718 def _setTarget(self, target):
1719 assert self.domid is not None
1721 try:
1722 SetTarget(self.domid, target)
1723 self.storeDom('target', target)
1724 except RuntimeError, exn:
1725 raise XendError(str(exn))
1728 def _initDomain(self):
1729 log.debug('XendDomainInfo.initDomain: %s %s',
1730 self.domid,
1731 self.info['vcpus_params']['weight'])
1733 self._configureBootloader()
1735 try:
1736 if self.info['platform'].get('localtime', 0):
1737 t = time.time()
1738 loc = time.localtime(t)
1739 utc = time.gmtime(t)
1740 timeoffset = int(time.mktime(loc) - time.mktime(utc))
1741 self.info['platform']['rtc_timeoffset'] = timeoffset
1743 self.image = image.create(self, self.info)
1745 xc.domain_setcpuweight(self.domid, \
1746 self.info['vcpus_params']['weight'])
1748 # repin domain vcpus if a restricted cpus list is provided
1749 # this is done prior to memory allocation to aide in memory
1750 # distribution for NUMA systems.
1751 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1752 for v in range(0, self.info['VCPUs_max']):
1753 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1755 # Use architecture- and image-specific calculations to determine
1756 # the various headrooms necessary, given the raw configured
1757 # values. maxmem, memory, and shadow are all in KiB.
1758 # but memory_static_max etc are all stored in bytes now.
1759 memory = self.image.getRequiredAvailableMemory(
1760 self.info['memory_dynamic_max'] / 1024)
1761 maxmem = self.image.getRequiredAvailableMemory(
1762 self.info['memory_static_max'] / 1024)
1763 shadow = self.image.getRequiredShadowMemory(
1764 self.info['shadow_memory'] * 1024,
1765 self.info['memory_static_max'] / 1024)
1767 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'],)
1768 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1769 # takes MiB and we must not round down and end up under-providing.
1770 shadow = ((shadow + 1023) / 1024) * 1024
1772 # set memory limit
1773 xc.domain_setmaxmem(self.domid, maxmem)
1775 # Make sure there's enough RAM available for the domain
1776 balloon.free(memory + shadow)
1778 # Set up the shadow memory
1779 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1780 self.info['shadow_memory'] = shadow_cur
1782 self._createChannels()
1784 channel_details = self.image.createImage()
1786 self.store_mfn = channel_details['store_mfn']
1787 if 'console_mfn' in channel_details:
1788 self.console_mfn = channel_details['console_mfn']
1789 if 'notes' in channel_details:
1790 self.info.set_notes(channel_details['notes'])
1791 if 'native_protocol' in channel_details:
1792 self.native_protocol = channel_details['native_protocol'];
1794 self._introduceDomain()
1795 if self.info.target():
1796 self._setTarget(self.info.target())
1798 self._createDevices()
1800 self.image.cleanupBootloading()
1802 self.info['start_time'] = time.time()
1804 self._stateSet(DOM_STATE_RUNNING)
1805 except VmError, exn:
1806 log.exception("XendDomainInfo.initDomain: exception occurred")
1807 if self.image:
1808 self.image.cleanupBootloading()
1809 raise exn
1810 except RuntimeError, exn:
1811 log.exception("XendDomainInfo.initDomain: exception occurred")
1812 if self.image:
1813 self.image.cleanupBootloading()
1814 raise VmError(str(exn))
1817 def cleanupDomain(self):
1818 """Cleanup domain resources; release devices. Idempotent. Nothrow
1819 guarantee."""
1821 self.refresh_shutdown_lock.acquire()
1822 try:
1823 self.unwatchShutdown()
1824 self._releaseDevices()
1825 bootloader_tidy(self)
1827 if self.image:
1828 self.image = None
1830 try:
1831 self._removeDom()
1832 except:
1833 log.exception("Removing domain path failed.")
1835 self._stateSet(DOM_STATE_HALTED)
1836 self.domid = None # Do not push into _stateSet()!
1837 finally:
1838 self.refresh_shutdown_lock.release()
1841 def unwatchShutdown(self):
1842 """Remove the watch on the domain's control/shutdown node, if any.
1843 Idempotent. Nothrow guarantee. Expects to be protected by the
1844 refresh_shutdown_lock."""
1846 try:
1847 try:
1848 if self.shutdownWatch:
1849 self.shutdownWatch.unwatch()
1850 finally:
1851 self.shutdownWatch = None
1852 except:
1853 log.exception("Unwatching control/shutdown failed.")
1855 def waitForShutdown(self):
1856 self.state_updated.acquire()
1857 try:
1858 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1859 self.state_updated.wait()
1860 finally:
1861 self.state_updated.release()
1864 # TODO: recategorise - called from XendCheckpoint
1867 def completeRestore(self, store_mfn, console_mfn):
1869 log.debug("XendDomainInfo.completeRestore")
1871 self.store_mfn = store_mfn
1872 self.console_mfn = console_mfn
1874 self._introduceDomain()
1875 self.image = image.create(self, self.info)
1876 if self.image:
1877 self.image.createDeviceModel(True)
1878 self._storeDomDetails()
1879 self._registerWatches()
1880 self.refreshShutdown()
1882 log.debug("XendDomainInfo.completeRestore done")
1885 def _endRestore(self):
1886 self.setResume(False)
1889 # VM Destroy
1892 def _prepare_phantom_paths(self):
1893 # get associated devices to destroy
1894 # build list of phantom devices to be removed after normal devices
1895 plist = []
1896 if self.domid is not None:
1897 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1898 try:
1899 for dev in t.list():
1900 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1901 % (self.dompath, dev))
1902 if backend_phantom_vbd is not None:
1903 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1904 % backend_phantom_vbd)
1905 plist.append(backend_phantom_vbd)
1906 plist.append(frontend_phantom_vbd)
1907 finally:
1908 t.abort()
1909 return plist
1911 def _cleanup_phantom_devs(self, plist):
1912 # remove phantom devices
1913 if not plist == []:
1914 time.sleep(2)
1915 for paths in plist:
1916 if paths.find('backend') != -1:
1917 from xen.xend.server import DevController
1918 # Modify online status /before/ updating state (latter is watched by
1919 # drivers, so this ordering avoids a race).
1920 xstransact.Write(paths, 'online', "0")
1921 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1922 # force
1923 xstransact.Remove(paths)
1925 def destroy(self):
1926 """Cleanup VM and destroy domain. Nothrow guarantee."""
1928 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1930 paths = self._prepare_phantom_paths()
1932 self._cleanupVm()
1933 if self.dompath is not None:
1934 self.destroyDomain()
1936 self._cleanup_phantom_devs(paths)
1938 if "transient" in self.info["other_config"] \
1939 and bool(self.info["other_config"]["transient"]):
1940 from xen.xend import XendDomain
1941 XendDomain.instance().domain_delete_by_dominfo(self)
1944 def destroyDomain(self):
1945 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1947 paths = self._prepare_phantom_paths()
1949 try:
1950 if self.domid is not None:
1951 xc.domain_destroy_hook(self.domid)
1952 xc.domain_destroy(self.domid)
1953 for state in DOM_STATES_OLD:
1954 self.info[state] = 0
1955 self._stateSet(DOM_STATE_HALTED)
1956 except:
1957 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1959 from xen.xend import XendDomain
1960 XendDomain.instance().remove_domain(self)
1962 self.cleanupDomain()
1963 self._cleanup_phantom_devs(paths)
1966 def resumeDomain(self):
1967 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1969 if self.domid is None:
1970 return
1971 try:
1972 # could also fetch a parsed note from xenstore
1973 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1974 if not fast:
1975 self._releaseDevices()
1976 self.testDeviceComplete()
1977 self.testvifsComplete()
1978 log.debug("XendDomainInfo.resumeDomain: devices released")
1980 self._resetChannels()
1982 self._removeDom('control/shutdown')
1983 self._removeDom('device-misc/vif/nextDeviceID')
1985 self._createChannels()
1986 self._introduceDomain()
1987 self._storeDomDetails()
1989 self._createDevices()
1990 log.debug("XendDomainInfo.resumeDomain: devices created")
1992 xc.domain_resume(self.domid, fast)
1993 ResumeDomain(self.domid)
1994 except:
1995 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1996 self.image.resumeDeviceModel()
1997 log.debug("XendDomainInfo.resumeDomain: completed")
2001 # Channels for xenstore and console
2004 def _createChannels(self):
2005 """Create the channels to the domain.
2006 """
2007 self.store_port = self._createChannel()
2008 self.console_port = self._createChannel()
2011 def _createChannel(self):
2012 """Create an event channel to the domain.
2013 """
2014 try:
2015 if self.domid != None:
2016 return xc.evtchn_alloc_unbound(domid = self.domid,
2017 remote_dom = 0)
2018 except:
2019 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2020 raise
2022 def _resetChannels(self):
2023 """Reset all event channels in the domain.
2024 """
2025 try:
2026 if self.domid != None:
2027 return xc.evtchn_reset(dom = self.domid)
2028 except:
2029 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2030 raise
2034 # Bootloader configuration
2037 def _configureBootloader(self):
2038 """Run the bootloader if we're configured to do so."""
2040 blexec = self.info['PV_bootloader']
2041 bootloader_args = self.info['PV_bootloader_args']
2042 kernel = self.info['PV_kernel']
2043 ramdisk = self.info['PV_ramdisk']
2044 args = self.info['PV_args']
2045 boot = self.info['HVM_boot_policy']
2047 if boot:
2048 # HVM booting.
2049 pass
2050 elif not blexec and kernel:
2051 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2052 # will be picked up by image.py.
2053 pass
2054 else:
2055 # Boot using bootloader
2056 if not blexec or blexec == 'pygrub':
2057 blexec = osdep.pygrub_path
2059 blcfg = None
2060 disks = [x for x in self.info['vbd_refs']
2061 if self.info['devices'][x][1]['bootable']]
2063 if not disks:
2064 msg = "Had a bootloader specified, but no disks are bootable"
2065 log.error(msg)
2066 raise VmError(msg)
2068 devinfo = self.info['devices'][disks[0]]
2069 devtype = devinfo[0]
2070 disk = devinfo[1]['uname']
2072 fn = blkdev_uname_to_file(disk)
2073 taptype = blkdev_uname_to_taptype(disk)
2074 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2075 if mounted:
2076 # This is a file, not a device. pygrub can cope with a
2077 # file if it's raw, but if it's QCOW or other such formats
2078 # used through blktap, then we need to mount it first.
2080 log.info("Mounting %s on %s." %
2081 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2083 vbd = {
2084 'mode': 'RO',
2085 'device': BOOTLOADER_LOOPBACK_DEVICE,
2088 from xen.xend import XendDomain
2089 dom0 = XendDomain.instance().privilegedDomain()
2090 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2091 fn = BOOTLOADER_LOOPBACK_DEVICE
2093 try:
2094 blcfg = bootloader(blexec, fn, self, False,
2095 bootloader_args, kernel, ramdisk, args)
2096 finally:
2097 if mounted:
2098 log.info("Unmounting %s from %s." %
2099 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2101 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2103 if blcfg is None:
2104 msg = "Had a bootloader specified, but can't find disk"
2105 log.error(msg)
2106 raise VmError(msg)
2108 self.info.update_with_image_sxp(blcfg, True)
2112 # VM Functions
2115 def _readVMDetails(self, params):
2116 """Read the specified parameters from the store.
2117 """
2118 try:
2119 return self._gatherVm(*params)
2120 except ValueError:
2121 # One of the int/float entries in params has a corresponding store
2122 # entry that is invalid. We recover, because older versions of
2123 # Xend may have put the entry there (memory/target, for example),
2124 # but this is in general a bad situation to have reached.
2125 log.exception(
2126 "Store corrupted at %s! Domain %d's configuration may be "
2127 "affected.", self.vmpath, self.domid)
2128 return []
2130 def _cleanupVm(self):
2131 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2133 self._unwatchVm()
2135 try:
2136 self._removeVm()
2137 except:
2138 log.exception("Removing VM path failed.")
2141 def checkLiveMigrateMemory(self):
2142 """ Make sure there's enough memory to migrate this domain """
2143 overhead_kb = 0
2144 if arch.type == "x86":
2145 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2146 # the minimum that Xen would allocate if no value were given.
2147 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2148 (self.info['memory_static_max'] / 1024 / 1024) * 4
2149 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2150 # The domain might already have some shadow memory
2151 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2152 if overhead_kb > 0:
2153 balloon.free(overhead_kb)
2155 def _unwatchVm(self):
2156 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2157 guarantee."""
2158 try:
2159 try:
2160 if self.vmWatch:
2161 self.vmWatch.unwatch()
2162 finally:
2163 self.vmWatch = None
2164 except:
2165 log.exception("Unwatching VM path failed.")
2167 def testDeviceComplete(self):
2168 """ For Block IO migration safety we must ensure that
2169 the device has shutdown correctly, i.e. all blocks are
2170 flushed to disk
2171 """
2172 start = time.time()
2173 while True:
2174 test = 0
2175 diff = time.time() - start
2176 for i in self.getDeviceController('vbd').deviceIDs():
2177 test = 1
2178 log.info("Dev %s still active, looping...", i)
2179 time.sleep(0.1)
2181 if test == 0:
2182 break
2183 if diff >= MIGRATE_TIMEOUT:
2184 log.info("Dev still active but hit max loop timeout")
2185 break
2187 def testvifsComplete(self):
2188 """ In case vifs are released and then created for the same
2189 domain, we need to wait the device shut down.
2190 """
2191 start = time.time()
2192 while True:
2193 test = 0
2194 diff = time.time() - start
2195 for i in self.getDeviceController('vif').deviceIDs():
2196 test = 1
2197 log.info("Dev %s still active, looping...", i)
2198 time.sleep(0.1)
2200 if test == 0:
2201 break
2202 if diff >= MIGRATE_TIMEOUT:
2203 log.info("Dev still active but hit max loop timeout")
2204 break
2206 def _storeVmDetails(self):
2207 to_store = {}
2209 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2210 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2211 if self._infoIsSet(info_key):
2212 to_store[key] = str(self.info[info_key])
2214 if self._infoIsSet("static_memory_min"):
2215 to_store["memory"] = str(self.info["static_memory_min"])
2216 if self._infoIsSet("static_memory_max"):
2217 to_store["maxmem"] = str(self.info["static_memory_max"])
2219 image_sxpr = self.info.image_sxpr()
2220 if image_sxpr:
2221 to_store['image'] = sxp.to_string(image_sxpr)
2223 if not self._readVm('xend/restart_count'):
2224 to_store['xend/restart_count'] = str(0)
2226 log.debug("Storing VM details: %s", scrub_password(to_store))
2228 self._writeVm(to_store)
2229 self._setVmPermissions()
2232 def _setVmPermissions(self):
2233 """Allow the guest domain to read its UUID. We don't allow it to
2234 access any other entry, for security."""
2235 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2236 { 'dom' : self.domid,
2237 'read' : True,
2238 'write' : False })
2241 # Utility functions
2244 def __getattr__(self, name):
2245 if name == "state":
2246 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2247 log.warn("".join(traceback.format_stack()))
2248 return self._stateGet()
2249 else:
2250 raise AttributeError()
2252 def __setattr__(self, name, value):
2253 if name == "state":
2254 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2255 log.warn("".join(traceback.format_stack()))
2256 self._stateSet(value)
2257 else:
2258 self.__dict__[name] = value
2260 def _stateSet(self, state):
2261 self.state_updated.acquire()
2262 try:
2263 # TODO Not sure this is correct...
2264 # _stateGet is live now. Why not fire event
2265 # even when it hasn't changed?
2266 if self._stateGet() != state:
2267 self.state_updated.notifyAll()
2268 import XendAPI
2269 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2270 'power_state')
2271 finally:
2272 self.state_updated.release()
2274 def _stateGet(self):
2275 # Lets try and reconsitute the state from xc
2276 # first lets try and get the domain info
2277 # from xc - this will tell us if the domain
2278 # exists
2279 info = dom_get(self.getDomid())
2280 if info is None or info['shutdown']:
2281 # We are either HALTED or SUSPENDED
2282 # check saved image exists
2283 from xen.xend import XendDomain
2284 managed_config_path = \
2285 XendDomain.instance()._managed_check_point_path( \
2286 self.get_uuid())
2287 if os.path.exists(managed_config_path):
2288 return XEN_API_VM_POWER_STATE_SUSPENDED
2289 else:
2290 return XEN_API_VM_POWER_STATE_HALTED
2291 elif info['crashed']:
2292 # Crashed
2293 return XEN_API_VM_POWER_STATE_CRASHED
2294 else:
2295 # We are either RUNNING or PAUSED
2296 if info['paused']:
2297 return XEN_API_VM_POWER_STATE_PAUSED
2298 else:
2299 return XEN_API_VM_POWER_STATE_RUNNING
2301 def _infoIsSet(self, name):
2302 return name in self.info and self.info[name] is not None
2304 def _checkName(self, name):
2305 """Check if a vm name is valid. Valid names contain alphabetic
2306 characters, digits, or characters in '_-.:/+'.
2307 The same name cannot be used for more than one vm at the same time.
2309 @param name: name
2310 @raise: VmError if invalid
2311 """
2312 from xen.xend import XendDomain
2314 if name is None or name == '':
2315 raise VmError('Missing VM Name')
2317 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2318 raise VmError('Invalid VM Name')
2320 dom = XendDomain.instance().domain_lookup_nr(name)
2321 if dom and dom.info['uuid'] != self.info['uuid']:
2322 raise VmError("VM name '%s' already exists%s" %
2323 (name,
2324 dom.domid is not None and
2325 (" as domain %s" % str(dom.domid)) or ""))
2328 def update(self, info = None, refresh = True, transaction = None):
2329 """Update with info from xc.domain_getinfo().
2330 """
2331 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2332 str(self.domid))
2334 if not info:
2335 info = dom_get(self.domid)
2336 if not info:
2337 return
2339 if info["maxmem_kb"] < 0:
2340 info["maxmem_kb"] = XendNode.instance() \
2341 .physinfo_dict()['total_memory'] * 1024
2343 #ssidref field not used any longer
2344 if 'ssidref' in info:
2345 info.pop('ssidref')
2347 # make sure state is reset for info
2348 # TODO: we should eventually get rid of old_dom_states
2350 self.info.update_config(info)
2351 self._update_consoles(transaction)
2353 if refresh:
2354 self.refreshShutdown(info)
2356 log.trace("XendDomainInfo.update done on domain %s: %s",
2357 str(self.domid), self.info)
2359 def sxpr(self, ignore_store = False, legacy_only = True):
2360 result = self.info.to_sxp(domain = self,
2361 ignore_devices = ignore_store,
2362 legacy_only = legacy_only)
2364 #if not ignore_store and self.dompath:
2365 # vnc_port = self.readDom('console/vnc-port')
2366 # if vnc_port is not None:
2367 # result.append(['device',
2368 # ['console', ['vnc-port', str(vnc_port)]]])
2370 return result
2372 # Xen API
2373 # ----------------------------------------------------------------
2375 def get_uuid(self):
2376 dom_uuid = self.info.get('uuid')
2377 if not dom_uuid: # if it doesn't exist, make one up
2378 dom_uuid = uuid.createString()
2379 self.info['uuid'] = dom_uuid
2380 return dom_uuid
2382 def get_memory_static_max(self):
2383 return self.info.get('memory_static_max', 0)
2384 def get_memory_static_min(self):
2385 return self.info.get('memory_static_min', 0)
2386 def get_memory_dynamic_max(self):
2387 return self.info.get('memory_dynamic_max', 0)
2388 def get_memory_dynamic_min(self):
2389 return self.info.get('memory_dynamic_min', 0)
2391 # only update memory-related config values if they maintain sanity
2392 def _safe_set_memory(self, key, newval):
2393 oldval = self.info.get(key, 0)
2394 try:
2395 self.info[key] = newval
2396 self.info._memory_sanity_check()
2397 except Exception, ex:
2398 self.info[key] = oldval
2399 raise
2401 def set_memory_static_max(self, val):
2402 self._safe_set_memory('memory_static_max', val)
2403 def set_memory_static_min(self, val):
2404 self._safe_set_memory('memory_static_min', val)
2405 def set_memory_dynamic_max(self, val):
2406 self._safe_set_memory('memory_dynamic_max', val)
2407 def set_memory_dynamic_min(self, val):
2408 self._safe_set_memory('memory_dynamic_min', val)
2410 def get_vcpus_params(self):
2411 if self.getDomid() is None:
2412 return self.info['vcpus_params']
2414 retval = xc.sched_credit_domain_get(self.getDomid())
2415 return retval
2416 def get_power_state(self):
2417 return XEN_API_VM_POWER_STATE[self._stateGet()]
2418 def get_platform(self):
2419 return self.info.get('platform', {})
2420 def get_pci_bus(self):
2421 return self.info.get('pci_bus', '')
2422 def get_tools_version(self):
2423 return self.info.get('tools_version', {})
2424 def get_metrics(self):
2425 return self.metrics.get_uuid();
2428 def get_security_label(self, xspol=None):
2429 import xen.util.xsm.xsm as security
2430 label = security.get_security_label(self, xspol)
2431 return label
2433 def set_security_label(self, seclab, old_seclab, xspol=None,
2434 xspol_old=None):
2435 """
2436 Set the security label of a domain from its old to
2437 a new value.
2438 @param seclab New security label formatted in the form
2439 <policy type>:<policy name>:<vm label>
2440 @param old_seclab The current security label that the
2441 VM must have.
2442 @param xspol An optional policy under which this
2443 update should be done. If not given,
2444 then the current active policy is used.
2445 @param xspol_old The old policy; only to be passed during
2446 the updating of a policy
2447 @return Returns return code, a string with errors from
2448 the hypervisor's operation, old label of the
2449 domain
2450 """
2451 rc = 0
2452 errors = ""
2453 old_label = ""
2454 new_ssidref = 0
2455 domid = self.getDomid()
2456 res_labels = None
2457 is_policy_update = (xspol_old != None)
2459 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2460 from xen.util import xsconstants
2462 state = self._stateGet()
2463 # Relabel only HALTED or RUNNING or PAUSED domains
2464 if domid != 0 and \
2465 state not in \
2466 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2467 DOM_STATE_SUSPENDED ]:
2468 log.warn("Relabeling domain not possible in state '%s'" %
2469 DOM_STATES[state])
2470 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2472 # Remove security label. Works only for halted domains
2473 if not seclab or seclab == "":
2474 if state not in [ DOM_STATE_HALTED ]:
2475 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2477 if self.info.has_key('security_label'):
2478 old_label = self.info['security_label']
2479 # Check label against expected one.
2480 if old_label != old_seclab:
2481 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2482 del self.info['security_label']
2483 xen.xend.XendDomain.instance().managed_config_save(self)
2484 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2486 tmp = seclab.split(":")
2487 if len(tmp) != 3:
2488 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2489 typ, policy, label = tmp
2491 poladmin = XSPolicyAdminInstance()
2492 if not xspol:
2493 xspol = poladmin.get_policy_by_name(policy)
2495 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2496 #if domain is running or paused try to relabel in hypervisor
2497 if not xspol:
2498 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2500 if typ != xspol.get_type_name() or \
2501 policy != xspol.get_name():
2502 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2504 if typ == xsconstants.ACM_POLICY_ID:
2505 new_ssidref = xspol.vmlabel_to_ssidref(label)
2506 if new_ssidref == xsconstants.INVALID_SSIDREF:
2507 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2509 # Check that all used resources are accessible under the
2510 # new label
2511 if not is_policy_update and \
2512 not security.resources_compatible_with_vmlabel(xspol,
2513 self, label):
2514 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2516 #Check label against expected one. Can only do this
2517 # if the policy hasn't changed underneath in the meantime
2518 if xspol_old == None:
2519 old_label = self.get_security_label()
2520 if old_label != old_seclab:
2521 log.info("old_label != old_seclab: %s != %s" %
2522 (old_label, old_seclab))
2523 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2525 # relabel domain in the hypervisor
2526 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2527 log.info("rc from relabeling in HV: %d" % rc)
2528 else:
2529 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2531 if rc == 0:
2532 # HALTED, RUNNING or PAUSED
2533 if domid == 0:
2534 if xspol:
2535 self.info['security_label'] = seclab
2536 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2537 else:
2538 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2539 else:
2540 if self.info.has_key('security_label'):
2541 old_label = self.info['security_label']
2542 # Check label against expected one, unless wildcard
2543 if old_label != old_seclab:
2544 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2546 self.info['security_label'] = seclab
2548 try:
2549 xen.xend.XendDomain.instance().managed_config_save(self)
2550 except:
2551 pass
2552 return (rc, errors, old_label, new_ssidref)
2554 def get_on_shutdown(self):
2555 after_shutdown = self.info.get('actions_after_shutdown')
2556 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2557 return XEN_API_ON_NORMAL_EXIT[-1]
2558 return after_shutdown
2560 def get_on_reboot(self):
2561 after_reboot = self.info.get('actions_after_reboot')
2562 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2563 return XEN_API_ON_NORMAL_EXIT[-1]
2564 return after_reboot
2566 def get_on_suspend(self):
2567 # TODO: not supported
2568 after_suspend = self.info.get('actions_after_suspend')
2569 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2570 return XEN_API_ON_NORMAL_EXIT[-1]
2571 return after_suspend
2573 def get_on_crash(self):
2574 after_crash = self.info.get('actions_after_crash')
2575 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2576 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2577 return after_crash
2579 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2580 """ Get's a device configuration either from XendConfig or
2581 from the DevController.
2583 @param dev_class: device class, either, 'vbd' or 'vif'
2584 @param dev_uuid: device UUID
2586 @rtype: dictionary
2587 """
2588 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2590 # shortcut if the domain isn't started because
2591 # the devcontrollers will have no better information
2592 # than XendConfig.
2593 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2594 if dev_config:
2595 return copy.deepcopy(dev_config)
2596 return None
2598 # instead of using dev_class, we use the dev_type
2599 # that is from XendConfig.
2600 controller = self.getDeviceController(dev_type)
2601 if not controller:
2602 return None
2604 all_configs = controller.getAllDeviceConfigurations()
2605 if not all_configs:
2606 return None
2608 updated_dev_config = copy.deepcopy(dev_config)
2609 for _devid, _devcfg in all_configs.items():
2610 if _devcfg.get('uuid') == dev_uuid:
2611 updated_dev_config.update(_devcfg)
2612 updated_dev_config['id'] = _devid
2613 return updated_dev_config
2615 return updated_dev_config
2617 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2618 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2619 if not config:
2620 return {}
2622 config['VM'] = self.get_uuid()
2624 if dev_class == 'vif':
2625 if not config.has_key('name'):
2626 config['name'] = config.get('vifname', '')
2627 if not config.has_key('MAC'):
2628 config['MAC'] = config.get('mac', '')
2629 if not config.has_key('type'):
2630 config['type'] = 'paravirtualised'
2631 if not config.has_key('device'):
2632 devid = config.get('id')
2633 if devid != None:
2634 config['device'] = 'eth%d' % devid
2635 else:
2636 config['device'] = ''
2638 if not config.has_key('network'):
2639 try:
2640 bridge = config.get('bridge', None)
2641 if bridge is None:
2642 from xen.util import Brctl
2643 if_to_br = dict([(i,b)
2644 for (b,ifs) in Brctl.get_state().items()
2645 for i in ifs])
2646 vifname = "vif%s.%s" % (self.getDomid(),
2647 config.get('id'))
2648 bridge = if_to_br.get(vifname, None)
2649 config['network'] = \
2650 XendNode.instance().bridge_to_network(
2651 config.get('bridge')).get_uuid()
2652 except Exception:
2653 log.exception('bridge_to_network')
2654 # Ignore this for now -- it may happen if the device
2655 # has been specified using the legacy methods, but at
2656 # some point we're going to have to figure out how to
2657 # handle that properly.
2659 config['MTU'] = 1500 # TODO
2661 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2662 xennode = XendNode.instance()
2663 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2664 config['io_read_kbs'] = rx_bps/1024
2665 config['io_write_kbs'] = tx_bps/1024
2666 rx, tx = xennode.get_vif_stat(self.domid, devid)
2667 config['io_total_read_kbs'] = rx/1024
2668 config['io_total_write_kbs'] = tx/1024
2669 else:
2670 config['io_read_kbs'] = 0.0
2671 config['io_write_kbs'] = 0.0
2672 config['io_total_read_kbs'] = 0.0
2673 config['io_total_write_kbs'] = 0.0
2675 config['security_label'] = config.get('security_label', '')
2677 if dev_class == 'vbd':
2679 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2680 controller = self.getDeviceController(dev_class)
2681 devid, _1, _2 = controller.getDeviceDetails(config)
2682 xennode = XendNode.instance()
2683 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2684 config['io_read_kbs'] = rd_blkps
2685 config['io_write_kbs'] = wr_blkps
2686 else:
2687 config['io_read_kbs'] = 0.0
2688 config['io_write_kbs'] = 0.0
2690 config['VDI'] = config.get('VDI', '')
2691 config['device'] = config.get('dev', '')
2692 if ':' in config['device']:
2693 vbd_name, vbd_type = config['device'].split(':', 1)
2694 config['device'] = vbd_name
2695 if vbd_type == 'cdrom':
2696 config['type'] = XEN_API_VBD_TYPE[0]
2697 else:
2698 config['type'] = XEN_API_VBD_TYPE[1]
2700 config['driver'] = 'paravirtualised' # TODO
2701 config['image'] = config.get('uname', '')
2703 if config.get('mode', 'r') == 'r':
2704 config['mode'] = 'RO'
2705 else:
2706 config['mode'] = 'RW'
2708 if dev_class == 'vtpm':
2709 if not config.has_key('type'):
2710 config['type'] = 'paravirtualised' # TODO
2711 if not config.has_key('backend'):
2712 config['backend'] = "00000000-0000-0000-0000-000000000000"
2714 return config
2716 def get_dev_property(self, dev_class, dev_uuid, field):
2717 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2718 try:
2719 return config[field]
2720 except KeyError:
2721 raise XendError('Invalid property for device: %s' % field)
2723 def set_dev_property(self, dev_class, dev_uuid, field, value):
2724 self.info['devices'][dev_uuid][1][field] = value
2726 def get_vcpus_util(self):
2727 vcpu_util = {}
2728 xennode = XendNode.instance()
2729 if 'VCPUs_max' in self.info and self.domid != None:
2730 for i in range(0, self.info['VCPUs_max']):
2731 util = xennode.get_vcpu_util(self.domid, i)
2732 vcpu_util[str(i)] = util
2734 return vcpu_util
2736 def get_consoles(self):
2737 return self.info.get('console_refs', [])
2739 def get_vifs(self):
2740 return self.info.get('vif_refs', [])
2742 def get_vbds(self):
2743 return self.info.get('vbd_refs', [])
2745 def get_vtpms(self):
2746 return self.info.get('vtpm_refs', [])
2748 def create_vbd(self, xenapi_vbd, vdi_image_path):
2749 """Create a VBD using a VDI from XendStorageRepository.
2751 @param xenapi_vbd: vbd struct from the Xen API
2752 @param vdi_image_path: VDI UUID
2753 @rtype: string
2754 @return: uuid of the device
2755 """
2756 xenapi_vbd['image'] = vdi_image_path
2757 if vdi_image_path.startswith('tap'):
2758 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2759 else:
2760 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2762 if not dev_uuid:
2763 raise XendError('Failed to create device')
2765 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2766 XEN_API_VM_POWER_STATE_PAUSED):
2767 _, config = self.info['devices'][dev_uuid]
2769 if vdi_image_path.startswith('tap'):
2770 dev_control = self.getDeviceController('tap')
2771 else:
2772 dev_control = self.getDeviceController('vbd')
2774 try:
2775 devid = dev_control.createDevice(config)
2776 dev_control.waitForDevice(devid)
2777 self.info.device_update(dev_uuid,
2778 cfg_xenapi = {'devid': devid})
2779 except Exception, exn:
2780 log.exception(exn)
2781 del self.info['devices'][dev_uuid]
2782 self.info['vbd_refs'].remove(dev_uuid)
2783 raise
2785 return dev_uuid
2787 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2788 """Create a VBD using a VDI from XendStorageRepository.
2790 @param xenapi_vbd: vbd struct from the Xen API
2791 @param vdi_image_path: VDI UUID
2792 @rtype: string
2793 @return: uuid of the device
2794 """
2795 xenapi_vbd['image'] = vdi_image_path
2796 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2797 if not dev_uuid:
2798 raise XendError('Failed to create device')
2800 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2801 _, config = self.info['devices'][dev_uuid]
2802 config['devid'] = self.getDeviceController('tap').createDevice(config)
2804 return config['devid']
2806 def create_vif(self, xenapi_vif):
2807 """Create VIF device from the passed struct in Xen API format.
2809 @param xenapi_vif: Xen API VIF Struct.
2810 @rtype: string
2811 @return: UUID
2812 """
2813 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2814 if not dev_uuid:
2815 raise XendError('Failed to create device')
2817 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2818 XEN_API_VM_POWER_STATE_PAUSED):
2820 _, config = self.info['devices'][dev_uuid]
2821 dev_control = self.getDeviceController('vif')
2823 try:
2824 devid = dev_control.createDevice(config)
2825 dev_control.waitForDevice(devid)
2826 self.info.device_update(dev_uuid,
2827 cfg_xenapi = {'devid': devid})
2828 except Exception, exn:
2829 log.exception(exn)
2830 del self.info['devices'][dev_uuid]
2831 self.info['vif_refs'].remove(dev_uuid)
2832 raise
2834 return dev_uuid
2836 def create_vtpm(self, xenapi_vtpm):
2837 """Create a VTPM device from the passed struct in Xen API format.
2839 @return: uuid of the device
2840 @rtype: string
2841 """
2843 if self._stateGet() not in (DOM_STATE_HALTED,):
2844 raise VmError("Can only add vTPM to a halted domain.")
2845 if self.get_vtpms() != []:
2846 raise VmError('Domain already has a vTPM.')
2847 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2848 if not dev_uuid:
2849 raise XendError('Failed to create device')
2851 return dev_uuid
2853 def create_console(self, xenapi_console):
2854 """ Create a console device from a Xen API struct.
2856 @return: uuid of device
2857 @rtype: string
2858 """
2859 if self._stateGet() not in (DOM_STATE_HALTED,):
2860 raise VmError("Can only add console to a halted domain.")
2862 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2863 if not dev_uuid:
2864 raise XendError('Failed to create device')
2866 return dev_uuid
2868 def set_console_other_config(self, console_uuid, other_config):
2869 self.info.console_update(console_uuid, 'other_config', other_config)
2871 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2872 if dev_uuid not in self.info['devices']:
2873 raise XendError('Device does not exist')
2875 try:
2876 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2877 XEN_API_VM_POWER_STATE_PAUSED):
2878 _, config = self.info['devices'][dev_uuid]
2879 devid = config.get('devid')
2880 if devid != None:
2881 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2882 else:
2883 raise XendError('Unable to get devid for device: %s:%s' %
2884 (dev_type, dev_uuid))
2885 finally:
2886 del self.info['devices'][dev_uuid]
2887 self.info['%s_refs' % dev_type].remove(dev_uuid)
2889 def destroy_vbd(self, dev_uuid):
2890 self.destroy_device_by_uuid('vbd', dev_uuid)
2892 def destroy_vif(self, dev_uuid):
2893 self.destroy_device_by_uuid('vif', dev_uuid)
2895 def destroy_vtpm(self, dev_uuid):
2896 self.destroy_device_by_uuid('vtpm', dev_uuid)
2898 def has_device(self, dev_class, dev_uuid):
2899 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2901 def __str__(self):
2902 return '<domain id=%s name=%s memory=%s state=%s>' % \
2903 (str(self.domid), self.info['name_label'],
2904 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2906 __repr__ = __str__