direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 13684:5b973d94bc00

Fix bootloader handling.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Sun Jan 28 18:38:47 2007 +0000 (2007-01-28)
parents 1e88f0b736b5
children 7fdfa020d4ed
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, 2006 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 from types import StringTypes
35 import xen.lowlevel.xc
36 from xen.util import asserts
37 from xen.util.blkif import blkdev_uname_to_file
38 from xen.util import security
40 from xen.xend import balloon, sxp, uuid, image, arch, osdep
41 from xen.xend import XendOptions, XendNode, XendConfig
43 from xen.xend.XendConfig import scrub_password
44 from xen.xend.XendBootloader import bootloader, bootloader_tidy
45 from xen.xend.XendError import XendError, VmError
46 from xen.xend.XendDevices import XendDevices
47 from xen.xend.XendTask import XendTask
48 from xen.xend.xenstore.xstransact import xstransact, complete
49 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, ResumeDomain
50 from xen.xend.xenstore.xswatch import xswatch
51 from xen.xend.XendConstants import *
52 from xen.xend.XendAPIConstants import *
54 MIGRATE_TIMEOUT = 30.0
55 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
57 xc = xen.lowlevel.xc.xc()
58 xoptions = XendOptions.instance()
60 log = logging.getLogger("xend.XendDomainInfo")
61 #log.setLevel(logging.TRACE)
64 def create(config):
65 """Creates and start a VM using the supplied configuration.
67 @param config: A configuration object involving lists of tuples.
68 @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
70 @rtype: XendDomainInfo
71 @return: An up and running XendDomainInfo instance
72 @raise VmError: Invalid configuration or failure to start.
73 """
75 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
76 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config))
77 try:
78 vm.start()
79 except:
80 log.exception('Domain construction failed')
81 vm.destroy()
82 raise
84 return vm
86 def create_from_dict(config_dict):
87 """Creates and start a VM using the supplied configuration.
89 @param config_dict: An configuration dictionary.
91 @rtype: XendDomainInfo
92 @return: An up and running XendDomainInfo instance
93 @raise VmError: Invalid configuration or failure to start.
94 """
96 log.debug("XendDomainInfo.create_from_dict(%s)",
97 scrub_password(config_dict))
98 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
99 try:
100 vm.start()
101 except:
102 log.exception('Domain construction failed')
103 vm.destroy()
104 raise
105 return vm
107 def recreate(info, priv):
108 """Create the VM object for an existing domain. The domain must not
109 be dying, as the paths in the store should already have been removed,
110 and asking us to recreate them causes problems.
112 @param xeninfo: Parsed configuration
113 @type xeninfo: Dictionary
114 @param priv: Is a privileged domain (Dom 0)
115 @type priv: bool
117 @rtype: XendDomainInfo
118 @return: A up and running XendDomainInfo instance
119 @raise VmError: Invalid configuration.
120 @raise XendError: Errors with configuration.
121 """
123 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
125 assert not info['dying']
127 xeninfo = XendConfig.XendConfig(dominfo = info)
128 domid = xeninfo['domid']
129 uuid1 = uuid.fromString(xeninfo['uuid'])
130 needs_reinitialising = False
132 dompath = GetDomainPath(domid)
133 if not dompath:
134 raise XendError('No domain path in store for existing '
135 'domain %d' % domid)
137 log.info("Recreating domain %d, UUID %s. at %s" %
138 (domid, xeninfo['uuid'], dompath))
140 # need to verify the path and uuid if not Domain-0
141 # if the required uuid and vm aren't set, then that means
142 # we need to recreate the dom with our own values
143 #
144 # NOTE: this is probably not desirable, really we should just
145 # abort or ignore, but there may be cases where xenstore's
146 # entry disappears (eg. xenstore-rm /)
147 #
148 try:
149 vmpath = xstransact.Read(dompath, "vm")
150 if not vmpath:
151 log.warn('/local/domain/%d/vm is missing. recreate is '
152 'confused, trying our best to recover' % domid)
153 needs_reinitialising = True
154 raise XendError('reinit')
156 uuid2_str = xstransact.Read(vmpath, "uuid")
157 if not uuid2_str:
158 log.warn('%s/uuid/ is missing. recreate is confused, '
159 'trying our best to recover' % vmpath)
160 needs_reinitialising = True
161 raise XendError('reinit')
163 uuid2 = uuid.fromString(uuid2_str)
164 if uuid1 != uuid2:
165 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
166 'Trying out best to recover' % domid)
167 needs_reinitialising = True
168 except XendError:
169 pass # our best shot at 'goto' in python :)
171 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv)
173 if needs_reinitialising:
174 vm._recreateDom()
175 vm._removeVm()
176 vm._storeVmDetails()
177 vm._storeDomDetails()
179 if vm.info['image']: # Only dom0 should be without an image entry when
180 # recreating, but we cope with missing ones
181 # elsewhere just in case.
182 vm.image = image.create(vm,
183 vm.info,
184 vm.info['image'],
185 vm.info['devices'])
186 vm.image.recreate()
188 vm._registerWatches()
189 vm.refreshShutdown(xeninfo)
191 # register the domain in the list
192 from xen.xend import XendDomain
193 XendDomain.instance().add_domain(vm)
195 return vm
198 def restore(config):
199 """Create a domain and a VM object to do a restore.
201 @param config: Domain SXP configuration
202 @type config: list of lists. (see C{create})
204 @rtype: XendDomainInfo
205 @return: A up and running XendDomainInfo instance
206 @raise VmError: Invalid configuration or failure to start.
207 @raise XendError: Errors with configuration.
208 """
210 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
211 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
212 resume = True)
213 try:
214 vm.resume()
215 return vm
216 except:
217 vm.destroy()
218 raise
220 def createDormant(domconfig):
221 """Create a dormant/inactive XenDomainInfo without creating VM.
222 This is for creating instances of persistent domains that are not
223 yet start.
225 @param domconfig: Parsed configuration
226 @type domconfig: XendConfig object
228 @rtype: XendDomainInfo
229 @return: A up and running XendDomainInfo instance
230 @raise XendError: Errors with configuration.
231 """
233 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
235 # domid does not make sense for non-running domains.
236 domconfig.pop('domid', None)
237 vm = XendDomainInfo(domconfig)
238 return vm
240 def domain_by_name(name):
241 """Get domain by name
243 @params name: Name of the domain
244 @type name: string
245 @return: XendDomainInfo or None
246 """
247 from xen.xend import XendDomain
248 return XendDomain.instance().domain_lookup_by_name_nr(name)
251 def shutdown_reason(code):
252 """Get a shutdown reason from a code.
254 @param code: shutdown code
255 @type code: int
256 @return: shutdown reason
257 @rtype: string
258 """
259 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
261 def dom_get(dom):
262 """Get info from xen for an existing domain.
264 @param dom: domain id
265 @type dom: int
266 @return: info or None
267 @rtype: dictionary
268 """
269 try:
270 domlist = xc.domain_getinfo(dom, 1)
271 if domlist and dom == domlist[0]['domid']:
272 return domlist[0]
273 except Exception, err:
274 # ignore missing domain
275 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
276 return None
279 class XendDomainInfo:
280 """An object represents a domain.
282 @TODO: try to unify dom and domid, they mean the same thing, but
283 xc refers to it as dom, and everywhere else, including
284 xenstore it is domid. The best way is to change xc's
285 python interface.
287 @ivar info: Parsed configuration
288 @type info: dictionary
289 @ivar domid: Domain ID (if VM has started)
290 @type domid: int or None
291 @ivar vmpath: XenStore path to this VM.
292 @type vmpath: string
293 @ivar dompath: XenStore path to this Domain.
294 @type dompath: string
295 @ivar image: Reference to the VM Image.
296 @type image: xen.xend.image.ImageHandler
297 @ivar store_port: event channel to xenstored
298 @type store_port: int
299 @ivar console_port: event channel to xenconsoled
300 @type console_port: int
301 @ivar store_mfn: xenstored mfn
302 @type store_mfn: int
303 @ivar console_mfn: xenconsoled mfn
304 @type console_mfn: int
305 @ivar vmWatch: reference to a watch on the xenstored vmpath
306 @type vmWatch: xen.xend.xenstore.xswatch
307 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
308 @type shutdownWatch: xen.xend.xenstore.xswatch
309 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
310 @type shutdownStartTime: float or None
311 @ivar state: Domain state
312 @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
313 @ivar state_updated: lock for self.state
314 @type state_updated: threading.Condition
315 @ivar refresh_shutdown_lock: lock for polling shutdown state
316 @type refresh_shutdown_lock: threading.Condition
317 @ivar _deviceControllers: device controller cache for this domain
318 @type _deviceControllers: dict 'string' to DevControllers
319 """
321 def __init__(self, info, domid = None, dompath = None, augment = False,
322 priv = False, resume = False):
323 """Constructor for a domain
325 @param info: parsed configuration
326 @type info: dictionary
327 @keyword domid: Set initial domain id (if any)
328 @type domid: int
329 @keyword dompath: Set initial dompath (if any)
330 @type dompath: string
331 @keyword augment: Augment given info with xenstored VM info
332 @type augment: bool
333 @keyword priv: Is a privileged domain (Dom 0)
334 @type priv: bool
335 @keyword resume: Is this domain being resumed?
336 @type resume: bool
337 """
339 self.info = info
340 if domid == None:
341 self.domid = self.info.get('domid')
342 else:
343 self.domid = domid
345 #REMOVE: uuid is now generated in XendConfig
346 #if not self._infoIsSet('uuid'):
347 # self.info['uuid'] = uuid.toString(uuid.create())
349 self.vmpath = XS_VMROOT + self.info['uuid']
350 self.dompath = dompath
352 self.image = None
353 self.store_port = None
354 self.store_mfn = None
355 self.console_port = None
356 self.console_mfn = None
358 self.vmWatch = None
359 self.shutdownWatch = None
360 self.shutdownStartTime = None
361 self._resume = resume
363 self.state = DOM_STATE_HALTED
364 self.state_updated = threading.Condition()
365 self.refresh_shutdown_lock = threading.Condition()
367 self._deviceControllers = {}
369 for state in DOM_STATES_OLD:
370 self.info[state] = 0
372 if augment:
373 self._augmentInfo(priv)
375 self._checkName(self.info['name_label'])
378 #
379 # Public functions available through XMLRPC
380 #
383 def start(self, is_managed = False):
384 """Attempts to start the VM by do the appropriate
385 initialisation if it not started.
386 """
387 from xen.xend import XendDomain
389 if self.state == DOM_STATE_HALTED:
390 try:
391 XendTask.log_progress(0, 30, self._constructDomain)
392 XendTask.log_progress(31, 60, self._initDomain)
394 XendTask.log_progress(61, 70, self._storeVmDetails)
395 XendTask.log_progress(71, 80, self._storeDomDetails)
396 XendTask.log_progress(81, 90, self._registerWatches)
397 XendTask.log_progress(91, 100, self.refreshShutdown)
399 # save running configuration if XendDomains believe domain is
400 # persistent
401 if is_managed:
402 xendomains = XendDomain.instance()
403 xendomains.managed_config_save(self)
404 except:
405 log.exception('VM start failed')
406 self.destroy()
407 raise
408 else:
409 raise XendError('VM already running')
411 def resume(self):
412 """Resumes a domain that has come back from suspension."""
413 if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED):
414 try:
415 self._constructDomain()
416 self._storeVmDetails()
417 self._createDevices()
418 self._createChannels()
419 self._storeDomDetails()
420 self._endRestore()
421 except:
422 log.exception('VM resume failed')
423 raise
424 else:
425 raise XendError('VM already running')
427 def shutdown(self, reason):
428 """Shutdown a domain by signalling this via xenstored."""
429 log.debug('XendDomainInfo.shutdown(%s)', reason)
430 if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
431 raise XendError('Domain cannot be shutdown')
433 if self.domid == 0:
434 raise XendError('Domain 0 cannot be shutdown')
436 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
437 raise XendError('Invalid reason: %s' % reason)
438 self._removeVm('xend/previous_restart_time')
439 self.storeDom("control/shutdown", reason)
441 ## shutdown hypercall for hvm domain desides xenstore write
442 image_cfg = self.info.get('image', {})
443 hvm = image_cfg.has_key('hvm')
444 if hvm:
445 for code in DOMAIN_SHUTDOWN_REASONS.keys():
446 if DOMAIN_SHUTDOWN_REASONS[code] == reason:
447 break
448 xc.domain_shutdown(self.domid, code)
451 def pause(self):
452 """Pause domain
454 @raise XendError: Failed pausing a domain
455 """
456 try:
457 xc.domain_pause(self.domid)
458 self._stateSet(DOM_STATE_PAUSED)
459 except Exception, ex:
460 log.exception(ex)
461 raise XendError("Domain unable to be paused: %s" % str(ex))
463 def unpause(self):
464 """Unpause domain
466 @raise XendError: Failed unpausing a domain
467 """
468 try:
469 xc.domain_unpause(self.domid)
470 self._stateSet(DOM_STATE_RUNNING)
471 except Exception, ex:
472 log.exception(ex)
473 raise XendError("Domain unable to be unpaused: %s" % str(ex))
475 def send_sysrq(self, key):
476 """ Send a Sysrq equivalent key via xenstored."""
477 asserts.isCharConvertible(key)
478 self.storeDom("control/sysrq", '%c' % key)
480 def device_create(self, dev_config):
481 """Create a new device.
483 @param dev_config: device configuration
484 @type dev_config: SXP object (parsed config)
485 """
486 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
487 dev_type = sxp.name(dev_config)
488 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
489 dev_config_dict = self.info['devices'][dev_uuid][1]
490 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
491 dev_config_dict['devid'] = devid = \
492 self._createDevice(dev_type, dev_config_dict)
493 self._waitForDevice(dev_type, devid)
494 return self.getDeviceController(dev_type).sxpr(devid)
496 def device_configure(self, dev_sxp, devid = None):
497 """Configure an existing device.
499 @param dev_config: device configuration
500 @type dev_config: SXP object (parsed config)
501 @param devid: device id
502 @type devid: int
503 @return: Returns True if successfully updated device
504 @rtype: boolean
505 """
507 # convert device sxp to a dict
508 dev_class = sxp.name(dev_sxp)
509 dev_config = {}
510 for opt_val in dev_sxp[1:]:
511 try:
512 dev_config[opt_val[0]] = opt_val[1]
513 except IndexError:
514 pass
516 # use DevController.reconfigureDevice to change device config
517 dev_control = self.getDeviceController(dev_class)
518 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
520 # update XendConfig with new device info
521 if dev_uuid:
522 self.info.device_update(dev_uuid, dev_sxp)
524 return True
526 def waitForDevices(self):
527 """Wait for this domain's configured devices to connect.
529 @raise VmError: if any device fails to initialise.
530 """
531 for devclass in XendDevices.valid_devices():
532 self.getDeviceController(devclass).waitForDevices()
534 def destroyDevice(self, deviceClass, devid, force = False):
535 try:
536 devid = int(devid)
537 except ValueError:
538 # devid is not a number, let's search for it in xenstore.
539 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
540 for entry in xstransact.List(devicePath):
541 backend = xstransact.Read('%s/%s' % (devicePath, entry),
542 "backend")
543 devName = xstransact.Read(backend, "dev")
544 if devName == devid:
545 # We found the integer matching our devid, use it instead
546 devid = entry
547 break
549 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
551 def getDeviceSxprs(self, deviceClass):
552 if self.state == DOM_STATE_RUNNING:
553 return self.getDeviceController(deviceClass).sxprs()
554 else:
555 sxprs = []
556 dev_num = 0
557 for dev_type, dev_info in self.info.all_devices_sxpr():
558 if dev_type == deviceClass:
559 sxprs.append([dev_num, dev_info])
560 dev_num += 1
561 return sxprs
564 def setMemoryTarget(self, target):
565 """Set the memory target of this domain.
566 @param target: In MiB.
567 """
568 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
569 self.info['name_label'], self.domid, target)
571 if target <= 0:
572 raise XendError('Invalid memory size')
574 self.info['memory_static_min'] = target
575 if self.domid >= 0:
576 self.storeVm("memory", target)
577 self.storeDom("memory/target", target << 10)
578 else:
579 self.info['memory_dynamic_min'] = target
580 xen.xend.XendDomain.instance().managed_config_save(self)
582 def setMemoryMaximum(self, limit):
583 """Set the maximum memory limit of this domain
584 @param limit: In MiB.
585 """
586 log.debug("Setting memory maximum of domain %s (%d) to %d MiB.",
587 self.info['name_label'], self.domid, limit)
589 if limit <= 0:
590 raise XendError('Invalid memory size')
592 self.info['memory_static_max'] = limit
593 if self.domid >= 0:
594 maxmem = int(limit) * 1024
595 try:
596 return xc.domain_setmaxmem(self.domid, maxmem)
597 except Exception, ex:
598 raise XendError(str(ex))
599 else:
600 self.info['memory_dynamic_max'] = limit
601 xen.xend.XendDomain.instance().managed_config_save(self)
604 def getVCPUInfo(self):
605 try:
606 # We include the domain name and ID, to help xm.
607 sxpr = ['domain',
608 ['domid', self.domid],
609 ['name', self.info['name_label']],
610 ['vcpu_count', self.info['vcpus_number']]]
612 for i in range(0, self.info['vcpus_number']):
613 info = xc.vcpu_getinfo(self.domid, i)
615 sxpr.append(['vcpu',
616 ['number', i],
617 ['online', info['online']],
618 ['blocked', info['blocked']],
619 ['running', info['running']],
620 ['cpu_time', info['cpu_time'] / 1e9],
621 ['cpu', info['cpu']],
622 ['cpumap', info['cpumap']]])
624 return sxpr
626 except RuntimeError, exn:
627 raise XendError(str(exn))
629 #
630 # internal functions ... TODO: re-categorised
631 #
633 def _augmentInfo(self, priv):
634 """Augment self.info, as given to us through L{recreate}, with
635 values taken from the store. This recovers those values known
636 to xend but not to the hypervisor.
637 """
638 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
639 if priv:
640 augment_entries.remove('memory')
641 augment_entries.remove('maxmem')
642 augment_entries.remove('vcpus')
643 augment_entries.remove('vcpu_avail')
645 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
646 for k in augment_entries])
648 # make returned lists into a dictionary
649 vm_config = dict(zip(augment_entries, vm_config))
651 for arg in augment_entries:
652 xapicfg = arg
653 val = vm_config[arg]
654 if val != None:
655 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
656 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
657 self.info[xapiarg] = val
658 else:
659 self.info[arg] = val
661 # For dom0, we ignore any stored value for the vcpus fields, and
662 # read the current value from Xen instead. This allows boot-time
663 # settings to take precedence over any entries in the store.
664 if priv:
665 xeninfo = dom_get(self.domid)
666 self.info['vcpus_number'] = xeninfo['online_vcpus']
667 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
669 # read image value
670 image_sxp = self._readVm('image')
671 if image_sxp:
672 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
674 # read devices
675 devices = []
676 for devclass in XendDevices.valid_devices():
677 devconfig = self.getDeviceController(devclass).configurations()
678 if devconfig:
679 devices.extend(devconfig)
681 if not self.info['devices'] and devices is not None:
682 for device in devices:
683 self.info.device_add(device[0], cfg_sxp = device)
685 self._update_consoles()
687 def _update_consoles(self):
688 if self.domid == None or self.domid == 0:
689 return
691 # Update VT100 port if it exists
692 self.console_port = self.readDom('console/port')
693 if self.console_port is not None:
694 serial_consoles = self.info.console_get_all('vt100')
695 if not serial_consoles:
696 cfg = self.info.console_add('vt100', self.console_port)
697 self._createDevice('console', cfg)
699 # Update VNC port if it exists
700 vnc_port = self.readDom('console/vnc-port')
701 if vnc_port is not None:
702 vnc_consoles = self.info.console_get_all('rfb')
703 if not vnc_consoles:
704 cfg = self.info.console_add('rfb', 'localhost:%s' %
705 str(vnc_port))
706 self._createDevice('console', cfg)
708 #
709 # Function to update xenstore /vm/*
710 #
712 def _readVm(self, *args):
713 return xstransact.Read(self.vmpath, *args)
715 def _writeVm(self, *args):
716 return xstransact.Write(self.vmpath, *args)
718 def _removeVm(self, *args):
719 return xstransact.Remove(self.vmpath, *args)
721 def _gatherVm(self, *args):
722 return xstransact.Gather(self.vmpath, *args)
724 def storeVm(self, *args):
725 return xstransact.Store(self.vmpath, *args)
727 #
728 # Function to update xenstore /dom/*
729 #
731 def readDom(self, *args):
732 return xstransact.Read(self.dompath, *args)
734 def gatherDom(self, *args):
735 return xstransact.Gather(self.dompath, *args)
737 def _writeDom(self, *args):
738 return xstransact.Write(self.dompath, *args)
740 def _removeDom(self, *args):
741 return xstransact.Remove(self.dompath, *args)
743 def storeDom(self, *args):
744 return xstransact.Store(self.dompath, *args)
746 def _recreateDom(self):
747 complete(self.dompath, lambda t: self._recreateDomFunc(t))
749 def _recreateDomFunc(self, t):
750 t.remove()
751 t.mkdir()
752 t.set_permissions({'dom' : self.domid})
753 t.write('vm', self.vmpath)
755 def _storeDomDetails(self):
756 to_store = {
757 'domid': str(self.domid),
758 'vm': self.vmpath,
759 'name': self.info['name_label'],
760 'console/limit': str(xoptions.get_console_limit() * 1024),
761 'memory/target': str(self.info['memory_static_min'] * 1024)
762 }
764 def f(n, v):
765 if v is not None:
766 to_store[n] = str(v)
768 f('console/port', self.console_port)
769 f('console/ring-ref', self.console_mfn)
770 f('store/port', self.store_port)
771 f('store/ring-ref', self.store_mfn)
773 to_store.update(self._vcpuDomDetails())
775 log.debug("Storing domain details: %s", scrub_password(to_store))
777 self._writeDom(to_store)
779 def _vcpuDomDetails(self):
780 def availability(n):
781 if self.info['vcpu_avail'] & (1 << n):
782 return 'online'
783 else:
784 return 'offline'
786 result = {}
787 for v in range(0, self.info['vcpus_number']):
788 result["cpu/%d/availability" % v] = availability(v)
789 return result
791 #
792 # xenstore watches
793 #
795 def _registerWatches(self):
796 """Register a watch on this VM's entries in the store, and the
797 domain's control/shutdown node, so that when they are changed
798 externally, we keep up to date. This should only be called by {@link
799 #create}, {@link #recreate}, or {@link #restore}, once the domain's
800 details have been written, but before the new instance is returned."""
801 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
802 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
803 self._handleShutdownWatch)
805 def _storeChanged(self, _):
806 log.trace("XendDomainInfo.storeChanged");
808 changed = False
810 # Check whether values in the configuration have
811 # changed in Xenstore.
813 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
815 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
816 for k in cfg_vm])
818 # convert two lists into a python dictionary
819 vm_details = dict(zip(cfg_vm, vm_details))
821 for arg, val in vm_details.items():
822 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
823 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
824 if val != None and val != self.info[xapiarg]:
825 self.info[xapiarg] = val
826 changed= True
828 # Check whether image definition has been updated
829 image_sxp = self._readVm('image')
830 if image_sxp and image_sxp != self.info.image_sxpr():
831 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
832 changed = True
834 if changed:
835 # Update the domain section of the store, as this contains some
836 # parameters derived from the VM configuration.
837 self._storeDomDetails()
839 return 1
841 def _handleShutdownWatch(self, _):
842 log.debug('XendDomainInfo.handleShutdownWatch')
844 reason = self.readDom('control/shutdown')
846 if reason and reason != 'suspend':
847 sst = self.readDom('xend/shutdown_start_time')
848 now = time.time()
849 if sst:
850 self.shutdownStartTime = float(sst)
851 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
852 else:
853 self.shutdownStartTime = now
854 self.storeDom('xend/shutdown_start_time', now)
855 timeout = SHUTDOWN_TIMEOUT
857 log.trace(
858 "Scheduling refreshShutdown on domain %d in %ds.",
859 self.domid, timeout)
860 threading.Timer(timeout, self.refreshShutdown).start()
862 return True
865 #
866 # Public Attributes for the VM
867 #
870 def getDomid(self):
871 return self.domid
873 def setName(self, name):
874 self._checkName(name)
875 self.info['name_label'] = name
876 self.storeVm("name", name)
878 def getName(self):
879 return self.info['name_label']
881 def getDomainPath(self):
882 return self.dompath
884 def getShutdownReason(self):
885 return self.readDom('control/shutdown')
887 def getStorePort(self):
888 """For use only by image.py and XendCheckpoint.py."""
889 return self.store_port
891 def getConsolePort(self):
892 """For use only by image.py and XendCheckpoint.py"""
893 return self.console_port
895 def getFeatures(self):
896 """For use only by image.py."""
897 return self.info['features']
899 def getVCpuCount(self):
900 return self.info['vcpus_number']
902 def setVCpuCount(self, vcpus):
903 self.info['vcpu_avail'] = (1 << vcpus) - 1
904 if self.domid >= 0:
905 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
906 # update dom differently depending on whether we are adjusting
907 # vcpu number up or down, otherwise _vcpuDomDetails does not
908 # disable the vcpus
909 if self.info['vcpus_number'] > vcpus:
910 # decreasing
911 self._writeDom(self._vcpuDomDetails())
912 self.info['vcpus_number'] = vcpus
913 else:
914 # same or increasing
915 self.info['vcpus_number'] = vcpus
916 self._writeDom(self._vcpuDomDetails())
917 else:
918 self.info['vcpus_number'] = vcpus
919 xen.xend.XendDomain.instance().managed_config_save(self)
920 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
921 vcpus)
923 def getLabel(self):
924 return security.get_security_info(self.info, 'label')
926 def getMemoryTarget(self):
927 """Get this domain's target memory size, in KB."""
928 return self.info['memory_static_min'] * 1024
930 def getMemoryMaximum(self):
931 """Get this domain's maximum memory size, in KB."""
932 return self.info['memory_static_max'] * 1024
934 def getResume(self):
935 return str(self._resume)
937 def getCap(self):
938 return self.info.get('cpu_cap', 0)
940 def getWeight(self):
941 return self.info.get('cpu_weight', 256)
943 def setResume(self, state):
944 self._resume = state
946 def getRestartCount(self):
947 return self._readVm('xend/restart_count')
949 def refreshShutdown(self, xeninfo = None):
950 """ Checks the domain for whether a shutdown is required.
952 Called from XendDomainInfo and also image.py for HVM images.
953 """
955 # If set at the end of this method, a restart is required, with the
956 # given reason. This restart has to be done out of the scope of
957 # refresh_shutdown_lock.
958 restart_reason = None
960 self.refresh_shutdown_lock.acquire()
961 try:
962 if xeninfo is None:
963 xeninfo = dom_get(self.domid)
964 if xeninfo is None:
965 # The domain no longer exists. This will occur if we have
966 # scheduled a timer to check for shutdown timeouts and the
967 # shutdown succeeded. It will also occur if someone
968 # destroys a domain beneath us. We clean up the domain,
969 # just in case, but we can't clean up the VM, because that
970 # VM may have migrated to a different domain on this
971 # machine.
972 self.cleanupDomain()
973 self._stateSet(DOM_STATE_HALTED)
974 return
976 if xeninfo['dying']:
977 # Dying means that a domain has been destroyed, but has not
978 # yet been cleaned up by Xen. This state could persist
979 # indefinitely if, for example, another domain has some of its
980 # pages mapped. We might like to diagnose this problem in the
981 # future, but for now all we do is make sure that it's not us
982 # holding the pages, by calling cleanupDomain. We can't
983 # clean up the VM, as above.
984 self.cleanupDomain()
985 self._stateSet(DOM_STATE_SHUTDOWN)
986 return
988 elif xeninfo['crashed']:
989 if self.readDom('xend/shutdown_completed'):
990 # We've seen this shutdown already, but we are preserving
991 # the domain for debugging. Leave it alone.
992 return
994 log.warn('Domain has crashed: name=%s id=%d.',
995 self.info['name_label'], self.domid)
996 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
998 if xoptions.get_enable_dump():
999 try:
1000 self.dumpCore()
1001 except XendError:
1002 # This error has been logged -- there's nothing more
1003 # we can do in this context.
1004 pass
1006 restart_reason = 'crash'
1007 self._stateSet(DOM_STATE_HALTED)
1009 elif xeninfo['shutdown']:
1010 self._stateSet(DOM_STATE_SHUTDOWN)
1011 if self.readDom('xend/shutdown_completed'):
1012 # We've seen this shutdown already, but we are preserving
1013 # the domain for debugging. Leave it alone.
1014 return
1016 else:
1017 reason = shutdown_reason(xeninfo['shutdown_reason'])
1019 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1020 self.info['name_label'], self.domid, reason)
1021 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1023 self._clearRestart()
1025 if reason == 'suspend':
1026 self._stateSet(DOM_STATE_SUSPENDED)
1027 # Don't destroy the domain. XendCheckpoint will do
1028 # this once it has finished. However, stop watching
1029 # the VM path now, otherwise we will end up with one
1030 # watch for the old domain, and one for the new.
1031 self._unwatchVm()
1032 elif reason in ('poweroff', 'reboot'):
1033 restart_reason = reason
1034 else:
1035 self.destroy()
1037 elif self.dompath is None:
1038 # We have yet to manage to call introduceDomain on this
1039 # domain. This can happen if a restore is in progress, or has
1040 # failed. Ignore this domain.
1041 pass
1042 else:
1043 # Domain is alive. If we are shutting it down, then check
1044 # the timeout on that, and destroy it if necessary.
1045 if xeninfo['paused']:
1046 self._stateSet(DOM_STATE_PAUSED)
1047 else:
1048 self._stateSet(DOM_STATE_RUNNING)
1050 if self.shutdownStartTime:
1051 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1052 self.shutdownStartTime)
1053 if timeout < 0:
1054 log.info(
1055 "Domain shutdown timeout expired: name=%s id=%s",
1056 self.info['name_label'], self.domid)
1057 self.destroy()
1058 finally:
1059 self.refresh_shutdown_lock.release()
1061 if restart_reason:
1062 threading.Thread(target = self._maybeRestart,
1063 args = (restart_reason,)).start()
1067 # Restart functions - handling whether we come back up on shutdown.
1070 def _clearRestart(self):
1071 self._removeDom("xend/shutdown_start_time")
1074 def _maybeRestart(self, reason):
1075 # Dispatch to the correct method based upon the configured on_{reason}
1076 # behaviour.
1077 actions = {"destroy" : self.destroy,
1078 "restart" : self._restart,
1079 "preserve" : self._preserve,
1080 "rename-restart" : self._renameRestart}
1082 action_conf = {
1083 'poweroff': 'actions_after_shutdown',
1084 'reboot': 'actions_after_reboot',
1085 'crash': 'actions_after_crash',
1088 action_target = self.info.get(action_conf.get(reason))
1089 func = actions.get(action_target, None)
1090 if func and callable(func):
1091 func()
1092 else:
1093 self.destroy() # default to destroy
1095 def _renameRestart(self):
1096 self._restart(True)
1098 def _restart(self, rename = False):
1099 """Restart the domain after it has exited.
1101 @param rename True if the old domain is to be renamed and preserved,
1102 False if it is to be destroyed.
1103 """
1104 from xen.xend import XendDomain
1106 if self._readVm(RESTART_IN_PROGRESS):
1107 log.error('Xend failed during restart of domain %s. '
1108 'Refusing to restart to avoid loops.',
1109 str(self.domid))
1110 self.destroy()
1111 return
1113 old_domid = self.domid
1114 self._writeVm(RESTART_IN_PROGRESS, 'True')
1116 now = time.time()
1117 rst = self._readVm('xend/previous_restart_time')
1118 if rst:
1119 rst = float(rst)
1120 timeout = now - rst
1121 if timeout < MINIMUM_RESTART_TIME:
1122 log.error(
1123 'VM %s restarting too fast (%f seconds since the last '
1124 'restart). Refusing to restart to avoid loops.',
1125 self.info['name_label'], timeout)
1126 self.destroy()
1127 return
1129 self._writeVm('xend/previous_restart_time', str(now))
1131 try:
1132 if rename:
1133 self._preserveForRestart()
1134 else:
1135 self._unwatchVm()
1136 self.destroyDomain()
1138 # new_dom's VM will be the same as this domain's VM, except where
1139 # the rename flag has instructed us to call preserveForRestart.
1140 # In that case, it is important that we remove the
1141 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1142 # once the new one is available.
1144 new_dom = None
1145 try:
1146 new_dom = XendDomain.instance().domain_create_from_dict(
1147 self.info)
1148 new_dom.unpause()
1149 rst_cnt = self._readVm('xend/restart_count')
1150 rst_cnt = int(rst_cnt) + 1
1151 self._writeVm('xend/restart_count', str(rst_cnt))
1152 new_dom._removeVm(RESTART_IN_PROGRESS)
1153 except:
1154 if new_dom:
1155 new_dom._removeVm(RESTART_IN_PROGRESS)
1156 new_dom.destroy()
1157 else:
1158 self._removeVm(RESTART_IN_PROGRESS)
1159 raise
1160 except:
1161 log.exception('Failed to restart domain %s.', str(old_domid))
1163 def _preserveForRestart(self):
1164 """Preserve a domain that has been shut down, by giving it a new UUID,
1165 cloning the VM details, and giving it a new name. This allows us to
1166 keep this domain for debugging, but restart a new one in its place
1167 preserving the restart semantics (name and UUID preserved).
1168 """
1170 new_uuid = uuid.createString()
1171 new_name = 'Domain-%s' % new_uuid
1172 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1173 self.info['name_label'], self.domid, self.info['uuid'],
1174 new_name, new_uuid)
1175 self._unwatchVm()
1176 self._releaseDevices()
1177 self.info['name_label'] = new_name
1178 self.info['uuid'] = new_uuid
1179 self.vmpath = XS_VMROOT + new_uuid
1180 self._storeVmDetails()
1181 self._preserve()
1184 def _preserve(self):
1185 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1186 self.domid)
1187 self._unwatchVm()
1188 self.storeDom('xend/shutdown_completed', 'True')
1189 self._stateSet(DOM_STATE_HALTED)
1192 # Debugging ..
1195 def dumpCore(self, corefile = None):
1196 """Create a core dump for this domain.
1198 @raise: XendError if core dumping failed.
1199 """
1201 try:
1202 if not corefile:
1203 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1204 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1205 self.info['name_label'], self.domid)
1207 if os.path.isdir(corefile):
1208 raise XendError("Cannot dump core in a directory: %s" %
1209 corefile)
1211 xc.domain_dumpcore(self.domid, corefile)
1212 except RuntimeError, ex:
1213 corefile_incomp = corefile+'-incomplete'
1214 os.rename(corefile, corefile_incomp)
1215 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1216 self.domid, self.info['name_label'])
1217 raise XendError("Failed to dump core: %s" % str(ex))
1220 # Device creation/deletion functions
1223 def _createDevice(self, deviceClass, devConfig):
1224 return self.getDeviceController(deviceClass).createDevice(devConfig)
1226 def _waitForDevice(self, deviceClass, devid):
1227 return self.getDeviceController(deviceClass).waitForDevice(devid)
1229 def _waitForDeviceUUID(self, dev_uuid):
1230 deviceClass, config = self.info['devices'].get(dev_uuid)
1231 self._waitForDevice(deviceClass, config['devid'])
1233 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1234 return self.getDeviceController(deviceClass).reconfigureDevice(
1235 devid, devconfig)
1237 def _createDevices(self):
1238 """Create the devices for a vm.
1240 @raise: VmError for invalid devices
1241 """
1242 ordered_refs = self.info.ordered_device_refs()
1243 for dev_uuid in ordered_refs:
1244 devclass, config = self.info['devices'][dev_uuid]
1245 if devclass in XendDevices.valid_devices():
1246 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1247 dev_uuid = config.get('uuid')
1248 devid = self._createDevice(devclass, config)
1250 # store devid in XendConfig for caching reasons
1251 if dev_uuid in self.info['devices']:
1252 self.info['devices'][dev_uuid][1]['devid'] = devid
1254 if self.image:
1255 self.image.createDeviceModel()
1257 def _releaseDevices(self, suspend = False):
1258 """Release all domain's devices. Nothrow guarantee."""
1259 if suspend and self.image:
1260 self.image.destroy(suspend)
1261 return
1263 while True:
1264 t = xstransact("%s/device" % self.dompath)
1265 for devclass in XendDevices.valid_devices():
1266 for dev in t.list(devclass):
1267 try:
1268 t.remove(dev)
1269 except:
1270 # Log and swallow any exceptions in removal --
1271 # there's nothing more we can do.
1272 log.exception(
1273 "Device release failed: %s; %s; %s",
1274 self.info['name_label'], devclass, dev)
1275 if t.commit():
1276 break
1278 def getDeviceController(self, name):
1279 """Get the device controller for this domain, and if it
1280 doesn't exist, create it.
1282 @param name: device class name
1283 @type name: string
1284 @rtype: subclass of DevController
1285 """
1286 if name not in self._deviceControllers:
1287 devController = XendDevices.make_controller(name, self)
1288 if not devController:
1289 raise XendError("Unknown device type: %s" % name)
1290 self._deviceControllers[name] = devController
1292 return self._deviceControllers[name]
1295 # Migration functions (public)
1298 def testMigrateDevices(self, network, dst):
1299 """ Notify all device about intention of migration
1300 @raise: XendError for a device that cannot be migrated
1301 """
1302 for (n, c) in self.info.all_devices_sxpr():
1303 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1304 if rc != 0:
1305 raise XendError("Device of type '%s' refuses migration." % n)
1307 def migrateDevices(self, network, dst, step, domName=''):
1308 """Notify the devices about migration
1309 """
1310 ctr = 0
1311 try:
1312 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1313 self.migrateDevice(dev_type, dev_conf, network, dst,
1314 step, domName)
1315 ctr = ctr + 1
1316 except:
1317 for dev_type, dev_conf in self.info.all_devices_sxpr():
1318 if ctr == 0:
1319 step = step - 1
1320 ctr = ctr - 1
1321 self._recoverMigrateDevice(dev_type, dev_conf, network,
1322 dst, step, domName)
1323 raise
1325 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1326 step, domName=''):
1327 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1328 network, dst, step, domName)
1330 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1331 dst, step, domName=''):
1332 return self.getDeviceController(deviceClass).recover_migrate(
1333 deviceConfig, network, dst, step, domName)
1336 ## private:
1338 def _constructDomain(self):
1339 """Construct the domain.
1341 @raise: VmError on error
1342 """
1344 log.debug('XendDomainInfo.constructDomain')
1346 self.shutdownStartTime = None
1348 image_cfg = self.info.get('image', {})
1349 hvm = image_cfg.has_key('hvm')
1351 if hvm:
1352 info = xc.xeninfo()
1353 if 'hvm' not in info['xen_caps']:
1354 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1355 "supported by your CPU and enabled in your "
1356 "BIOS?")
1358 self.domid = xc.domain_create(
1359 domid = 0,
1360 ssidref = security.get_security_info(self.info, 'ssidref'),
1361 handle = uuid.fromString(self.info['uuid']),
1362 hvm = int(hvm))
1364 if self.domid < 0:
1365 raise VmError('Creating domain failed: name=%s' %
1366 self.info['name_label'])
1368 self.dompath = GetDomainPath(self.domid)
1370 self._recreateDom()
1372 # Set maximum number of vcpus in domain
1373 xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
1375 # register the domain in the list
1376 from xen.xend import XendDomain
1377 XendDomain.instance().add_domain(self)
1379 def _introduceDomain(self):
1380 assert self.domid is not None
1381 assert self.store_mfn is not None
1382 assert self.store_port is not None
1384 try:
1385 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1386 except RuntimeError, exn:
1387 raise XendError(str(exn))
1390 def _initDomain(self):
1391 log.debug('XendDomainInfo.initDomain: %s %s',
1392 self.domid,
1393 self.info['cpu_weight'])
1395 self._configureBootloader()
1397 if not self._infoIsSet('image'):
1398 raise VmError('Missing image in configuration')
1400 try:
1401 self.image = image.create(self,
1402 self.info,
1403 self.info['image'],
1404 self.info['devices'])
1406 localtime = self.info.get('localtime', False)
1407 if localtime:
1408 xc.domain_set_time_offset(self.domid)
1410 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1412 # repin domain vcpus if a restricted cpus list is provided
1413 # this is done prior to memory allocation to aide in memory
1414 # distribution for NUMA systems.
1415 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1416 for v in range(0, self.info['vcpus_number']):
1417 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1419 # Use architecture- and image-specific calculations to determine
1420 # the various headrooms necessary, given the raw configured
1421 # values. maxmem, memory, and shadow are all in KiB.
1422 memory = self.image.getRequiredAvailableMemory(
1423 self.info['memory_static_min'] * 1024)
1424 maxmem = self.image.getRequiredAvailableMemory(
1425 self.info['memory_static_max'] * 1024)
1426 shadow = self.image.getRequiredShadowMemory(
1427 self.info['shadow_memory'] * 1024,
1428 self.info['memory_static_max'] * 1024)
1430 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'],)
1431 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1432 # takes MiB and we must not round down and end up under-providing.
1433 shadow = ((shadow + 1023) / 1024) * 1024
1435 # set memory limit
1436 xc.domain_setmaxmem(self.domid, maxmem)
1438 # Make sure there's enough RAM available for the domain
1439 balloon.free(memory + shadow)
1441 # Set up the shadow memory
1442 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1443 self.info['shadow_memory'] = shadow_cur
1445 self._createChannels()
1447 channel_details = self.image.createImage()
1449 self.store_mfn = channel_details['store_mfn']
1450 if 'console_mfn' in channel_details:
1451 self.console_mfn = channel_details['console_mfn']
1453 self._introduceDomain()
1455 self._createDevices()
1457 self.image.cleanupBootloading()
1459 self.info['start_time'] = time.time()
1461 self._stateSet(DOM_STATE_RUNNING)
1462 except RuntimeError, exn:
1463 log.exception("XendDomainInfo.initDomain: exception occurred")
1464 self.image.cleanupBootloading()
1465 raise VmError(str(exn))
1468 def cleanupDomain(self):
1469 """Cleanup domain resources; release devices. Idempotent. Nothrow
1470 guarantee."""
1472 self.refresh_shutdown_lock.acquire()
1473 try:
1474 self.unwatchShutdown()
1475 self._releaseDevices()
1476 bootloader_tidy(self)
1478 if self.image:
1479 try:
1480 self.image.destroy()
1481 except:
1482 log.exception(
1483 "XendDomainInfo.cleanup: image.destroy() failed.")
1484 self.image = None
1486 try:
1487 self._removeDom()
1488 except:
1489 log.exception("Removing domain path failed.")
1491 self._stateSet(DOM_STATE_HALTED)
1492 finally:
1493 self.refresh_shutdown_lock.release()
1496 def unwatchShutdown(self):
1497 """Remove the watch on the domain's control/shutdown node, if any.
1498 Idempotent. Nothrow guarantee. Expects to be protected by the
1499 refresh_shutdown_lock."""
1501 try:
1502 try:
1503 if self.shutdownWatch:
1504 self.shutdownWatch.unwatch()
1505 finally:
1506 self.shutdownWatch = None
1507 except:
1508 log.exception("Unwatching control/shutdown failed.")
1510 def waitForShutdown(self):
1511 self.state_updated.acquire()
1512 try:
1513 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1514 self.state_updated.wait()
1515 finally:
1516 self.state_updated.release()
1520 # TODO: recategorise - called from XendCheckpoint
1523 def completeRestore(self, store_mfn, console_mfn):
1525 log.debug("XendDomainInfo.completeRestore")
1527 self.store_mfn = store_mfn
1528 self.console_mfn = console_mfn
1530 self._introduceDomain()
1531 image_cfg = self.info.get('image', {})
1532 hvm = image_cfg.has_key('hvm')
1533 if hvm:
1534 self.image = image.create(self,
1535 self.info,
1536 self.info['image'],
1537 self.info['devices'])
1538 if self.image:
1539 self.image.createDeviceModel(True)
1540 self.image.register_shutdown_watch()
1541 self._storeDomDetails()
1542 self._registerWatches()
1543 self.refreshShutdown()
1545 log.debug("XendDomainInfo.completeRestore done")
1548 def _endRestore(self):
1549 self.setResume(False)
1552 # VM Destroy
1555 def destroy(self):
1556 """Cleanup VM and destroy domain. Nothrow guarantee."""
1558 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1560 self._cleanupVm()
1561 if self.dompath is not None:
1562 self.destroyDomain()
1565 def destroyDomain(self):
1566 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1568 try:
1569 if self.domid is not None:
1570 xc.domain_destroy(self.domid)
1571 self.domid = None
1572 for state in DOM_STATES_OLD:
1573 self.info[state] = 0
1574 except:
1575 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1577 from xen.xend import XendDomain
1578 XendDomain.instance().remove_domain(self)
1580 self.cleanupDomain()
1583 def resumeDomain(self):
1584 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1586 try:
1587 if self.domid is not None:
1588 xc.domain_resume(self.domid)
1589 ResumeDomain(self.domid)
1590 except:
1591 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1594 # Channels for xenstore and console
1597 def _createChannels(self):
1598 """Create the channels to the domain.
1599 """
1600 self.store_port = self._createChannel()
1601 self.console_port = self._createChannel()
1604 def _createChannel(self):
1605 """Create an event channel to the domain.
1606 """
1607 try:
1608 if self.domid != None:
1609 return xc.evtchn_alloc_unbound(domid = self.domid,
1610 remote_dom = 0)
1611 except:
1612 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1613 raise
1615 def _resetChannels(self):
1616 """Reset all event channels in the domain.
1617 """
1618 try:
1619 if self.domid != None:
1620 return xc.evtchn_reset(dom = self.domid)
1621 except:
1622 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1623 raise
1627 # Bootloader configuration
1630 def _configureBootloader(self):
1631 """Run the bootloader if we're configured to do so."""
1633 blexec = self.info['PV_bootloader']
1634 bootloader_args = self.info['PV_bootloader_args']
1635 kernel = self.info['PV_kernel']
1636 ramdisk = self.info['PV_ramdisk']
1637 args = self.info['PV_args']
1638 boot = self.info['HVM_boot']
1640 if boot:
1641 # HVM booting.
1642 self.info['image']['type'] = 'hvm'
1643 if not 'devices' in self.info['image']:
1644 self.info['image']['devices'] = {}
1645 self.info['image']['devices']['boot'] = boot
1646 elif not blexec and kernel:
1647 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1648 # will be picked up by image.py.
1649 pass
1650 else:
1651 # Boot using bootloader
1652 if not blexec or blexec == 'pygrub':
1653 blexec = osdep.pygrub_path
1655 blcfg = None
1656 disks = [x for x in self.info['vbd_refs']
1657 if self.info['devices'][x][1]['bootable']]
1659 if not disks:
1660 msg = "Had a bootloader specified, but no disks are bootable"
1661 log.error(msg)
1662 raise VmError(msg)
1664 devinfo = self.info['devices'][disks[0]]
1665 devtype = devinfo[0]
1666 disk = devinfo[1]['uname']
1668 fn = blkdev_uname_to_file(disk)
1669 mounted = devtype == 'tap' and not os.stat(fn).st_rdev
1670 if mounted:
1671 # This is a file, not a device. pygrub can cope with a
1672 # file if it's raw, but if it's QCOW or other such formats
1673 # used through blktap, then we need to mount it first.
1675 log.info("Mounting %s on %s." %
1676 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1678 vbd = {
1679 'mode': 'RO',
1680 'device': BOOTLOADER_LOOPBACK_DEVICE,
1683 from xen.xend import XendDomain
1684 dom0 = XendDomain.instance().privilegedDomain()
1685 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
1686 fn = BOOTLOADER_LOOPBACK_DEVICE
1688 try:
1689 blcfg = bootloader(blexec, fn, self, False,
1690 bootloader_args, kernel, ramdisk, args)
1691 finally:
1692 if mounted:
1693 log.info("Unmounting %s from %s." %
1694 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1696 dom0.destroyDevice('tap', '/dev/xvdp')
1698 if blcfg is None:
1699 msg = "Had a bootloader specified, but can't find disk"
1700 log.error(msg)
1701 raise VmError(msg)
1703 self.info.update_with_image_sxp(blcfg, True)
1707 # VM Functions
1710 def _readVMDetails(self, params):
1711 """Read the specified parameters from the store.
1712 """
1713 try:
1714 return self._gatherVm(*params)
1715 except ValueError:
1716 # One of the int/float entries in params has a corresponding store
1717 # entry that is invalid. We recover, because older versions of
1718 # Xend may have put the entry there (memory/target, for example),
1719 # but this is in general a bad situation to have reached.
1720 log.exception(
1721 "Store corrupted at %s! Domain %d's configuration may be "
1722 "affected.", self.vmpath, self.domid)
1723 return []
1725 def _cleanupVm(self):
1726 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1728 self._unwatchVm()
1730 try:
1731 self._removeVm()
1732 except:
1733 log.exception("Removing VM path failed.")
1736 def checkLiveMigrateMemory(self):
1737 """ Make sure there's enough memory to migrate this domain """
1738 overhead_kb = 0
1739 if arch.type == "x86":
1740 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1741 # the minimum that Xen would allocate if no value were given.
1742 overhead_kb = self.info['vcpus_number'] * 1024 + \
1743 self.info['memory_static_max'] * 4
1744 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1745 # The domain might already have some shadow memory
1746 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1747 if overhead_kb > 0:
1748 balloon.free(overhead_kb)
1750 def _unwatchVm(self):
1751 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1752 guarantee."""
1753 try:
1754 try:
1755 if self.vmWatch:
1756 self.vmWatch.unwatch()
1757 finally:
1758 self.vmWatch = None
1759 except:
1760 log.exception("Unwatching VM path failed.")
1762 def testDeviceComplete(self):
1763 """ For Block IO migration safety we must ensure that
1764 the device has shutdown correctly, i.e. all blocks are
1765 flushed to disk
1766 """
1767 start = time.time()
1768 while True:
1769 test = 0
1770 diff = time.time() - start
1771 for i in self.getDeviceController('vbd').deviceIDs():
1772 test = 1
1773 log.info("Dev %s still active, looping...", i)
1774 time.sleep(0.1)
1776 if test == 0:
1777 break
1778 if diff >= MIGRATE_TIMEOUT:
1779 log.info("Dev still active but hit max loop timeout")
1780 break
1782 def testvifsComplete(self):
1783 """ In case vifs are released and then created for the same
1784 domain, we need to wait the device shut down.
1785 """
1786 start = time.time()
1787 while True:
1788 test = 0
1789 diff = time.time() - start
1790 for i in self.getDeviceController('vif').deviceIDs():
1791 test = 1
1792 log.info("Dev %s still active, looping...", i)
1793 time.sleep(0.1)
1795 if test == 0:
1796 break
1797 if diff >= MIGRATE_TIMEOUT:
1798 log.info("Dev still active but hit max loop timeout")
1799 break
1801 def _storeVmDetails(self):
1802 to_store = {}
1804 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1805 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1806 if self._infoIsSet(info_key):
1807 to_store[key] = str(self.info[info_key])
1809 if self.info.get('image'):
1810 image_sxpr = self.info.image_sxpr()
1811 if image_sxpr:
1812 to_store['image'] = sxp.to_string(image_sxpr)
1814 if self._infoIsSet('security'):
1815 secinfo = self.info['security']
1816 to_store['security'] = sxp.to_string(secinfo)
1817 for idx in range(0, len(secinfo)):
1818 if secinfo[idx][0] == 'access_control':
1819 to_store['security/access_control'] = sxp.to_string(
1820 [secinfo[idx][1], secinfo[idx][2]])
1821 for aidx in range(1, len(secinfo[idx])):
1822 if secinfo[idx][aidx][0] == 'label':
1823 to_store['security/access_control/label'] = \
1824 secinfo[idx][aidx][1]
1825 if secinfo[idx][aidx][0] == 'policy':
1826 to_store['security/access_control/policy'] = \
1827 secinfo[idx][aidx][1]
1828 if secinfo[idx][0] == 'ssidref':
1829 to_store['security/ssidref'] = str(secinfo[idx][1])
1832 if not self._readVm('xend/restart_count'):
1833 to_store['xend/restart_count'] = str(0)
1835 log.debug("Storing VM details: %s", scrub_password(to_store))
1837 self._writeVm(to_store)
1838 self._setVmPermissions()
1841 def _setVmPermissions(self):
1842 """Allow the guest domain to read its UUID. We don't allow it to
1843 access any other entry, for security."""
1844 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1845 { 'dom' : self.domid,
1846 'read' : True,
1847 'write' : False })
1850 # Utility functions
1853 def _stateSet(self, state):
1854 self.state_updated.acquire()
1855 try:
1856 if self.state != state:
1857 self.state = state
1858 self.state_updated.notifyAll()
1859 finally:
1860 self.state_updated.release()
1862 def _infoIsSet(self, name):
1863 return name in self.info and self.info[name] is not None
1865 def _checkName(self, name):
1866 """Check if a vm name is valid. Valid names contain alphabetic
1867 characters, digits, or characters in '_-.:/+'.
1868 The same name cannot be used for more than one vm at the same time.
1870 @param name: name
1871 @raise: VmError if invalid
1872 """
1873 from xen.xend import XendDomain
1875 if name is None or name == '':
1876 raise VmError('Missing VM Name')
1878 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
1879 raise VmError('Invalid VM Name')
1881 dom = XendDomain.instance().domain_lookup_nr(name)
1882 if dom and dom.info['uuid'] != self.info['uuid']:
1883 raise VmError("VM name '%s' already exists%s" %
1884 (name,
1885 dom.domid is not None and
1886 (" as domain %s" % str(dom.domid)) or ""))
1889 def update(self, info = None, refresh = True):
1890 """Update with info from xc.domain_getinfo().
1891 """
1892 log.trace("XendDomainInfo.update(%s) on domain %s", info,
1893 str(self.domid))
1895 if not info:
1896 info = dom_get(self.domid)
1897 if not info:
1898 return
1900 #manually update ssidref / security fields
1901 if security.on() and info.has_key('ssidref'):
1902 if (info['ssidref'] != 0) and self.info.has_key('security'):
1903 security_field = self.info['security']
1904 if not security_field:
1905 #create new security element
1906 self.info.update({'security':
1907 [['ssidref', str(info['ssidref'])]]})
1909 #ssidref field not used any longer
1910 if 'ssidref' in info:
1911 info.pop('ssidref')
1913 # make sure state is reset for info
1914 # TODO: we should eventually get rid of old_dom_states
1916 self.info.update_config(info)
1917 self._update_consoles()
1919 if refresh:
1920 self.refreshShutdown(info)
1922 log.trace("XendDomainInfo.update done on domain %s: %s",
1923 str(self.domid), self.info)
1925 def sxpr(self, ignore_store = False, legacy_only = True):
1926 result = self.info.to_sxp(domain = self,
1927 ignore_devices = ignore_store,
1928 legacy_only = legacy_only)
1930 #if not ignore_store and self.dompath:
1931 # vnc_port = self.readDom('console/vnc-port')
1932 # if vnc_port is not None:
1933 # result.append(['device',
1934 # ['console', ['vnc-port', str(vnc_port)]]])
1936 return result
1938 # Xen API
1939 # ----------------------------------------------------------------
1941 def get_uuid(self):
1942 dom_uuid = self.info.get('uuid')
1943 if not dom_uuid: # if it doesn't exist, make one up
1944 dom_uuid = uuid.createString()
1945 self.info['uuid'] = dom_uuid
1946 return dom_uuid
1948 def get_memory_static_max(self):
1949 return self.info.get('memory_static_max', 0)
1950 def get_memory_static_min(self):
1951 return self.info.get('memory_static_min', 0)
1952 def get_memory_dynamic_max(self):
1953 return self.info.get('memory_dynamic_max', 0)
1954 def get_memory_dynamic_min(self):
1955 return self.info.get('memory_dynamic_min', 0)
1957 def get_vcpus_policy(self):
1958 sched_id = xc.sched_id_get()
1959 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
1960 return 'sedf'
1961 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
1962 return 'credit'
1963 else:
1964 return 'unknown'
1965 def get_vcpus_params(self):
1966 return '' # TODO
1967 def get_power_state(self):
1968 return XEN_API_VM_POWER_STATE[self.state]
1969 def get_platform_std_vga(self):
1970 return self.info.get('platform_std_vga', False)
1971 def get_platform_serial(self):
1972 return self.info.get('platform_serial', '')
1973 def get_platform_localtime(self):
1974 return self.info.get('platform_localtime', False)
1975 def get_platform_clock_offset(self):
1976 return self.info.get('platform_clock_offset', False)
1977 def get_platform_enable_audio(self):
1978 return self.info.get('platform_enable_audio', False)
1979 def get_platform_keymap(self):
1980 return self.info.get('platform_keymap', '')
1981 def get_pci_bus(self):
1982 return self.info.get('pci_bus', '')
1983 def get_tools_version(self):
1984 return self.info.get('tools_version', {})
1986 def get_on_shutdown(self):
1987 after_shutdown = self.info.get('action_after_shutdown')
1988 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
1989 return XEN_API_ON_NORMAL_EXIT[-1]
1990 return after_shutdown
1992 def get_on_reboot(self):
1993 after_reboot = self.info.get('action_after_reboot')
1994 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
1995 return XEN_API_ON_NORMAL_EXIT[-1]
1996 return after_reboot
1998 def get_on_suspend(self):
1999 # TODO: not supported
2000 after_suspend = self.info.get('action_after_suspend')
2001 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2002 return XEN_API_ON_NORMAL_EXIT[-1]
2003 return after_suspend
2005 def get_on_crash(self):
2006 after_crash = self.info.get('action_after_crash')
2007 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2008 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2009 return after_crash
2011 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2012 """ Get's a device configuration either from XendConfig or
2013 from the DevController.
2015 @param dev_class: device class, either, 'vbd' or 'vif'
2016 @param dev_uuid: device UUID
2018 @rtype: dictionary
2019 """
2020 dev_type_config = self.info['devices'].get(dev_uuid)
2022 # shortcut if the domain isn't started because
2023 # the devcontrollers will have no better information
2024 # than XendConfig.
2025 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2026 if dev_type_config:
2027 return copy.deepcopy(dev_type_config[1])
2028 return None
2030 # instead of using dev_class, we use the dev_type
2031 # that is from XendConfig.
2032 # This will accomdate 'tap' as well as 'vbd'
2033 dev_type = dev_type_config[0]
2035 controller = self.getDeviceController(dev_type)
2036 if not controller:
2037 return None
2039 all_configs = controller.getAllDeviceConfigurations()
2040 if not all_configs:
2041 return None
2043 dev_config = copy.deepcopy(dev_type_config[1])
2044 for _devid, _devcfg in all_configs.items():
2045 if _devcfg.get('uuid') == dev_uuid:
2046 dev_config.update(_devcfg)
2047 dev_config['id'] = _devid
2048 return dev_config
2050 return dev_config
2052 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2053 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2054 if not config:
2055 return {}
2057 config['VM'] = self.get_uuid()
2059 if dev_class == 'vif':
2060 if not config.has_key('name'):
2061 config['name'] = config.get('vifname', '')
2062 if not config.has_key('MAC'):
2063 config['MAC'] = config.get('mac', '')
2064 if not config.has_key('type'):
2065 config['type'] = 'paravirtualised'
2066 if not config.has_key('device'):
2067 devid = config.get('id')
2068 if devid != None:
2069 config['device'] = 'eth%d' % devid
2070 else:
2071 config['device'] = ''
2073 if not config.has_key('network'):
2074 try:
2075 config['network'] = \
2076 XendNode.instance().bridge_to_network(
2077 config.get('bridge')).uuid
2078 except Exception:
2079 log.exception('bridge_to_network')
2080 # Ignore this for now -- it may happen if the device
2081 # has been specified using the legacy methods, but at
2082 # some point we're going to have to figure out how to
2083 # handle that properly.
2085 config['MTU'] = 1500 # TODO
2087 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2088 xennode = XendNode.instance()
2089 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2090 config['io_read_kbs'] = rx_bps/1024
2091 config['io_write_kbs'] = tx_bps/1024
2092 else:
2093 config['io_read_kbs'] = 0.0
2094 config['io_write_kbs'] = 0.0
2096 if dev_class == 'vbd':
2098 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2099 controller = self.getDeviceController(dev_class)
2100 devid, _1, _2 = controller.getDeviceDetails(config)
2101 xennode = XendNode.instance()
2102 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2103 config['io_read_kbs'] = rd_blkps
2104 config['io_write_kbs'] = wr_blkps
2105 else:
2106 config['io_read_kbs'] = 0.0
2107 config['io_write_kbs'] = 0.0
2109 config['VDI'] = config.get('VDI', '')
2110 config['device'] = config.get('dev', '')
2111 if ':' in config['device']:
2112 vbd_name, vbd_type = config['device'].split(':', 1)
2113 config['device'] = vbd_name
2114 if vbd_type == 'cdrom':
2115 config['type'] = XEN_API_VBD_TYPE[0]
2116 else:
2117 config['type'] = XEN_API_VBD_TYPE[1]
2119 config['driver'] = 'paravirtualised' # TODO
2120 config['image'] = config.get('uname', '')
2122 if config.get('mode', 'r') == 'r':
2123 config['mode'] = 'RO'
2124 else:
2125 config['mode'] = 'RW'
2127 if dev_class == 'vtpm':
2128 config['driver'] = 'paravirtualised' # TODO
2130 return config
2132 def get_dev_property(self, dev_class, dev_uuid, field):
2133 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2134 try:
2135 return config[field]
2136 except KeyError:
2137 raise XendError('Invalid property for device: %s' % field)
2139 def set_dev_property(self, dev_class, dev_uuid, field, value):
2140 self.info['devices'][dev_uuid][1][field] = value
2142 def get_vcpus_util(self):
2143 vcpu_util = {}
2144 xennode = XendNode.instance()
2145 if 'vcpus_number' in self.info and self.domid != None:
2146 for i in range(0, self.info['vcpus_number']):
2147 util = xennode.get_vcpu_util(self.domid, i)
2148 vcpu_util[str(i)] = util
2150 return vcpu_util
2152 def get_consoles(self):
2153 return self.info.get('console_refs', [])
2155 def get_vifs(self):
2156 return self.info.get('vif_refs', [])
2158 def get_vbds(self):
2159 return self.info.get('vbd_refs', [])
2161 def get_vtpms(self):
2162 return self.info.get('vtpm_refs', [])
2164 def create_vbd(self, xenapi_vbd, vdi_image_path):
2165 """Create a VBD using a VDI from XendStorageRepository.
2167 @param xenapi_vbd: vbd struct from the Xen API
2168 @param vdi_image_path: VDI UUID
2169 @rtype: string
2170 @return: uuid of the device
2171 """
2172 xenapi_vbd['image'] = vdi_image_path
2173 log.debug('create_vbd: %s' % xenapi_vbd)
2174 dev_uuid = ''
2175 if vdi_image_path.startswith('tap'):
2176 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2177 else:
2178 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2180 if not dev_uuid:
2181 raise XendError('Failed to create device')
2183 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2184 _, config = self.info['devices'][dev_uuid]
2185 dev_control = None
2187 if vdi_image_path.startswith('tap'):
2188 dev_control = self.getDeviceController('tap')
2189 else:
2190 dev_control = self.getDeviceController('vbd')
2192 config['devid'] = dev_control.createDevice(config)
2194 return dev_uuid
2196 def create_vif(self, xenapi_vif):
2197 """Create VIF device from the passed struct in Xen API format.
2199 @param xenapi_vif: Xen API VIF Struct.
2200 @rtype: string
2201 @return: UUID
2202 """
2203 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2204 if not dev_uuid:
2205 raise XendError('Failed to create device')
2207 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2208 _, config = self.info['devices'][dev_uuid]
2209 config['devid'] = self.getDeviceController('vif').createDevice(config)
2211 return dev_uuid
2213 def create_vtpm(self, xenapi_vtpm):
2214 """Create a VTPM device from the passed struct in Xen API format.
2216 @return: uuid of the device
2217 @rtype: string
2218 """
2220 if self.state not in (DOM_STATE_HALTED,):
2221 raise VmError("Can only add vTPM to a halted domain.")
2222 if self.get_vtpms() != []:
2223 raise VmError('Domain already has a vTPM.')
2224 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2225 if not dev_uuid:
2226 raise XendError('Failed to create device')
2228 return dev_uuid
2230 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2231 if dev_uuid not in self.info['devices']:
2232 raise XendError('Device does not exist')
2234 try:
2235 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2236 _, config = self.info['devices'][dev_uuid]
2237 devid = config.get('devid')
2238 if devid != None:
2239 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2240 else:
2241 raise XendError('Unable to get devid for device: %s:%s' %
2242 (dev_type, dev_uuid))
2243 finally:
2244 del self.info['devices'][dev_uuid]
2245 self.info['%s_refs' % dev_type].remove(dev_uuid)
2247 def destroy_vbd(self, dev_uuid):
2248 self.destroy_device_by_uuid('vbd', dev_uuid)
2250 def destroy_vif(self, dev_uuid):
2251 self.destroy_device_by_uuid('vif', dev_uuid)
2253 def destroy_vtpm(self, dev_uuid):
2254 self.destroy_device_by_uuid('vtpm', dev_uuid)
2256 def has_device(self, dev_class, dev_uuid):
2257 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2259 def __str__(self):
2260 return '<domain id=%s name=%s memory=%s state=%s>' % \
2261 (str(self.domid), self.info['name_label'],
2262 str(self.info['memory_static_min']), DOM_STATES[self.state])
2264 __repr__ = __str__