ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 13819:b32a44bfb10c

Return the stored vcpus_params if the domain is not running.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Feb 01 18:14:40 2007 +0000 (2007-02-01)
parents f73e71aa0a90
children 6524e02edbeb
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)
698 else:
699 console_uuid = serial_consoles[0].get('uuid')
700 self.info.console_update(console_uuid, 'location',
701 self.console_port)
704 # Update VNC port if it exists and write to xenstore
705 vnc_port = self.readDom('console/vnc-port')
706 if vnc_port is not None:
707 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
708 if dev_type == 'vfb':
709 old_location = dev_info.get('location')
710 listen_host = dev_info.get('vnclisten', 'localhost')
711 new_location = '%s:%s' % (listen_host, str(vnc_port))
712 if old_location == new_location:
713 break
715 dev_info['location'] = new_location
716 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
717 vfb_ctrl = self.getDeviceController('vfb')
718 vfb_ctrl.reconfigureDevice(0, dev_info)
719 break
721 #
722 # Function to update xenstore /vm/*
723 #
725 def _readVm(self, *args):
726 return xstransact.Read(self.vmpath, *args)
728 def _writeVm(self, *args):
729 return xstransact.Write(self.vmpath, *args)
731 def _removeVm(self, *args):
732 return xstransact.Remove(self.vmpath, *args)
734 def _gatherVm(self, *args):
735 return xstransact.Gather(self.vmpath, *args)
737 def storeVm(self, *args):
738 return xstransact.Store(self.vmpath, *args)
740 #
741 # Function to update xenstore /dom/*
742 #
744 def readDom(self, *args):
745 return xstransact.Read(self.dompath, *args)
747 def gatherDom(self, *args):
748 return xstransact.Gather(self.dompath, *args)
750 def _writeDom(self, *args):
751 return xstransact.Write(self.dompath, *args)
753 def _removeDom(self, *args):
754 return xstransact.Remove(self.dompath, *args)
756 def storeDom(self, *args):
757 return xstransact.Store(self.dompath, *args)
759 def _recreateDom(self):
760 complete(self.dompath, lambda t: self._recreateDomFunc(t))
762 def _recreateDomFunc(self, t):
763 t.remove()
764 t.mkdir()
765 t.set_permissions({'dom' : self.domid})
766 t.write('vm', self.vmpath)
768 def _storeDomDetails(self):
769 to_store = {
770 'domid': str(self.domid),
771 'vm': self.vmpath,
772 'name': self.info['name_label'],
773 'console/limit': str(xoptions.get_console_limit() * 1024),
774 'memory/target': str(self.info['memory_static_min'] * 1024)
775 }
777 def f(n, v):
778 if v is not None:
779 to_store[n] = str(v)
781 f('console/port', self.console_port)
782 f('console/ring-ref', self.console_mfn)
783 f('store/port', self.store_port)
784 f('store/ring-ref', self.store_mfn)
786 to_store.update(self._vcpuDomDetails())
788 log.debug("Storing domain details: %s", scrub_password(to_store))
790 self._writeDom(to_store)
792 def _vcpuDomDetails(self):
793 def availability(n):
794 if self.info['vcpu_avail'] & (1 << n):
795 return 'online'
796 else:
797 return 'offline'
799 result = {}
800 for v in range(0, self.info['vcpus_number']):
801 result["cpu/%d/availability" % v] = availability(v)
802 return result
804 #
805 # xenstore watches
806 #
808 def _registerWatches(self):
809 """Register a watch on this VM's entries in the store, and the
810 domain's control/shutdown node, so that when they are changed
811 externally, we keep up to date. This should only be called by {@link
812 #create}, {@link #recreate}, or {@link #restore}, once the domain's
813 details have been written, but before the new instance is returned."""
814 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
815 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
816 self._handleShutdownWatch)
818 def _storeChanged(self, _):
819 log.trace("XendDomainInfo.storeChanged");
821 changed = False
823 # Check whether values in the configuration have
824 # changed in Xenstore.
826 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
828 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
829 for k in cfg_vm])
831 # convert two lists into a python dictionary
832 vm_details = dict(zip(cfg_vm, vm_details))
834 for arg, val in vm_details.items():
835 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
836 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
837 if val != None and val != self.info[xapiarg]:
838 self.info[xapiarg] = val
839 changed= True
841 # Check whether image definition has been updated
842 image_sxp = self._readVm('image')
843 if image_sxp and image_sxp != self.info.image_sxpr():
844 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
845 changed = True
847 if changed:
848 # Update the domain section of the store, as this contains some
849 # parameters derived from the VM configuration.
850 self._storeDomDetails()
852 return 1
854 def _handleShutdownWatch(self, _):
855 log.debug('XendDomainInfo.handleShutdownWatch')
857 reason = self.readDom('control/shutdown')
859 if reason and reason != 'suspend':
860 sst = self.readDom('xend/shutdown_start_time')
861 now = time.time()
862 if sst:
863 self.shutdownStartTime = float(sst)
864 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
865 else:
866 self.shutdownStartTime = now
867 self.storeDom('xend/shutdown_start_time', now)
868 timeout = SHUTDOWN_TIMEOUT
870 log.trace(
871 "Scheduling refreshShutdown on domain %d in %ds.",
872 self.domid, timeout)
873 threading.Timer(timeout, self.refreshShutdown).start()
875 return True
878 #
879 # Public Attributes for the VM
880 #
883 def getDomid(self):
884 return self.domid
886 def setName(self, name):
887 self._checkName(name)
888 self.info['name_label'] = name
889 self.storeVm("name", name)
891 def getName(self):
892 return self.info['name_label']
894 def getDomainPath(self):
895 return self.dompath
897 def getShutdownReason(self):
898 return self.readDom('control/shutdown')
900 def getStorePort(self):
901 """For use only by image.py and XendCheckpoint.py."""
902 return self.store_port
904 def getConsolePort(self):
905 """For use only by image.py and XendCheckpoint.py"""
906 return self.console_port
908 def getFeatures(self):
909 """For use only by image.py."""
910 return self.info['features']
912 def getVCpuCount(self):
913 return self.info['vcpus_number']
915 def setVCpuCount(self, vcpus):
916 self.info['vcpu_avail'] = (1 << vcpus) - 1
917 if self.domid >= 0:
918 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
919 # update dom differently depending on whether we are adjusting
920 # vcpu number up or down, otherwise _vcpuDomDetails does not
921 # disable the vcpus
922 if self.info['vcpus_number'] > vcpus:
923 # decreasing
924 self._writeDom(self._vcpuDomDetails())
925 self.info['vcpus_number'] = vcpus
926 else:
927 # same or increasing
928 self.info['vcpus_number'] = vcpus
929 self._writeDom(self._vcpuDomDetails())
930 else:
931 self.info['vcpus_number'] = vcpus
932 xen.xend.XendDomain.instance().managed_config_save(self)
933 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
934 vcpus)
936 def getLabel(self):
937 return security.get_security_info(self.info, 'label')
939 def getMemoryTarget(self):
940 """Get this domain's target memory size, in KB."""
941 return self.info['memory_static_min'] * 1024
943 def getMemoryMaximum(self):
944 """Get this domain's maximum memory size, in KB."""
945 return self.info['memory_static_max'] * 1024
947 def getResume(self):
948 return str(self._resume)
950 def getCap(self):
951 return self.info.get('cpu_cap', 0)
953 def getWeight(self):
954 return self.info.get('cpu_weight', 256)
956 def setResume(self, state):
957 self._resume = state
959 def getRestartCount(self):
960 return self._readVm('xend/restart_count')
962 def refreshShutdown(self, xeninfo = None):
963 """ Checks the domain for whether a shutdown is required.
965 Called from XendDomainInfo and also image.py for HVM images.
966 """
968 # If set at the end of this method, a restart is required, with the
969 # given reason. This restart has to be done out of the scope of
970 # refresh_shutdown_lock.
971 restart_reason = None
973 self.refresh_shutdown_lock.acquire()
974 try:
975 if xeninfo is None:
976 xeninfo = dom_get(self.domid)
977 if xeninfo is None:
978 # The domain no longer exists. This will occur if we have
979 # scheduled a timer to check for shutdown timeouts and the
980 # shutdown succeeded. It will also occur if someone
981 # destroys a domain beneath us. We clean up the domain,
982 # just in case, but we can't clean up the VM, because that
983 # VM may have migrated to a different domain on this
984 # machine.
985 self.cleanupDomain()
986 self._stateSet(DOM_STATE_HALTED)
987 return
989 if xeninfo['dying']:
990 # Dying means that a domain has been destroyed, but has not
991 # yet been cleaned up by Xen. This state could persist
992 # indefinitely if, for example, another domain has some of its
993 # pages mapped. We might like to diagnose this problem in the
994 # future, but for now all we do is make sure that it's not us
995 # holding the pages, by calling cleanupDomain. We can't
996 # clean up the VM, as above.
997 self.cleanupDomain()
998 self._stateSet(DOM_STATE_SHUTDOWN)
999 return
1001 elif xeninfo['crashed']:
1002 if self.readDom('xend/shutdown_completed'):
1003 # We've seen this shutdown already, but we are preserving
1004 # the domain for debugging. Leave it alone.
1005 return
1007 log.warn('Domain has crashed: name=%s id=%d.',
1008 self.info['name_label'], self.domid)
1009 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1011 if xoptions.get_enable_dump():
1012 try:
1013 self.dumpCore()
1014 except XendError:
1015 # This error has been logged -- there's nothing more
1016 # we can do in this context.
1017 pass
1019 restart_reason = 'crash'
1020 self._stateSet(DOM_STATE_HALTED)
1022 elif xeninfo['shutdown']:
1023 self._stateSet(DOM_STATE_SHUTDOWN)
1024 if self.readDom('xend/shutdown_completed'):
1025 # We've seen this shutdown already, but we are preserving
1026 # the domain for debugging. Leave it alone.
1027 return
1029 else:
1030 reason = shutdown_reason(xeninfo['shutdown_reason'])
1032 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1033 self.info['name_label'], self.domid, reason)
1034 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1036 self._clearRestart()
1038 if reason == 'suspend':
1039 self._stateSet(DOM_STATE_SUSPENDED)
1040 # Don't destroy the domain. XendCheckpoint will do
1041 # this once it has finished. However, stop watching
1042 # the VM path now, otherwise we will end up with one
1043 # watch for the old domain, and one for the new.
1044 self._unwatchVm()
1045 elif reason in ('poweroff', 'reboot'):
1046 restart_reason = reason
1047 else:
1048 self.destroy()
1050 elif self.dompath is None:
1051 # We have yet to manage to call introduceDomain on this
1052 # domain. This can happen if a restore is in progress, or has
1053 # failed. Ignore this domain.
1054 pass
1055 else:
1056 # Domain is alive. If we are shutting it down, then check
1057 # the timeout on that, and destroy it if necessary.
1058 if xeninfo['paused']:
1059 self._stateSet(DOM_STATE_PAUSED)
1060 else:
1061 self._stateSet(DOM_STATE_RUNNING)
1063 if self.shutdownStartTime:
1064 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1065 self.shutdownStartTime)
1066 if timeout < 0:
1067 log.info(
1068 "Domain shutdown timeout expired: name=%s id=%s",
1069 self.info['name_label'], self.domid)
1070 self.destroy()
1071 finally:
1072 self.refresh_shutdown_lock.release()
1074 if restart_reason:
1075 threading.Thread(target = self._maybeRestart,
1076 args = (restart_reason,)).start()
1080 # Restart functions - handling whether we come back up on shutdown.
1083 def _clearRestart(self):
1084 self._removeDom("xend/shutdown_start_time")
1087 def _maybeRestart(self, reason):
1088 # Dispatch to the correct method based upon the configured on_{reason}
1089 # behaviour.
1090 actions = {"destroy" : self.destroy,
1091 "restart" : self._restart,
1092 "preserve" : self._preserve,
1093 "rename-restart" : self._renameRestart}
1095 action_conf = {
1096 'poweroff': 'actions_after_shutdown',
1097 'reboot': 'actions_after_reboot',
1098 'crash': 'actions_after_crash',
1101 action_target = self.info.get(action_conf.get(reason))
1102 func = actions.get(action_target, None)
1103 if func and callable(func):
1104 func()
1105 else:
1106 self.destroy() # default to destroy
1108 def _renameRestart(self):
1109 self._restart(True)
1111 def _restart(self, rename = False):
1112 """Restart the domain after it has exited.
1114 @param rename True if the old domain is to be renamed and preserved,
1115 False if it is to be destroyed.
1116 """
1117 from xen.xend import XendDomain
1119 if self._readVm(RESTART_IN_PROGRESS):
1120 log.error('Xend failed during restart of domain %s. '
1121 'Refusing to restart to avoid loops.',
1122 str(self.domid))
1123 self.destroy()
1124 return
1126 old_domid = self.domid
1127 self._writeVm(RESTART_IN_PROGRESS, 'True')
1129 now = time.time()
1130 rst = self._readVm('xend/previous_restart_time')
1131 if rst:
1132 rst = float(rst)
1133 timeout = now - rst
1134 if timeout < MINIMUM_RESTART_TIME:
1135 log.error(
1136 'VM %s restarting too fast (%f seconds since the last '
1137 'restart). Refusing to restart to avoid loops.',
1138 self.info['name_label'], timeout)
1139 self.destroy()
1140 return
1142 self._writeVm('xend/previous_restart_time', str(now))
1144 try:
1145 if rename:
1146 self._preserveForRestart()
1147 else:
1148 self._unwatchVm()
1149 self.destroyDomain()
1151 # new_dom's VM will be the same as this domain's VM, except where
1152 # the rename flag has instructed us to call preserveForRestart.
1153 # In that case, it is important that we remove the
1154 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1155 # once the new one is available.
1157 new_dom = None
1158 try:
1159 new_dom = XendDomain.instance().domain_create_from_dict(
1160 self.info)
1161 new_dom.unpause()
1162 rst_cnt = self._readVm('xend/restart_count')
1163 rst_cnt = int(rst_cnt) + 1
1164 self._writeVm('xend/restart_count', str(rst_cnt))
1165 new_dom._removeVm(RESTART_IN_PROGRESS)
1166 except:
1167 if new_dom:
1168 new_dom._removeVm(RESTART_IN_PROGRESS)
1169 new_dom.destroy()
1170 else:
1171 self._removeVm(RESTART_IN_PROGRESS)
1172 raise
1173 except:
1174 log.exception('Failed to restart domain %s.', str(old_domid))
1176 def _preserveForRestart(self):
1177 """Preserve a domain that has been shut down, by giving it a new UUID,
1178 cloning the VM details, and giving it a new name. This allows us to
1179 keep this domain for debugging, but restart a new one in its place
1180 preserving the restart semantics (name and UUID preserved).
1181 """
1183 new_uuid = uuid.createString()
1184 new_name = 'Domain-%s' % new_uuid
1185 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1186 self.info['name_label'], self.domid, self.info['uuid'],
1187 new_name, new_uuid)
1188 self._unwatchVm()
1189 self._releaseDevices()
1190 self.info['name_label'] = new_name
1191 self.info['uuid'] = new_uuid
1192 self.vmpath = XS_VMROOT + new_uuid
1193 self._storeVmDetails()
1194 self._preserve()
1197 def _preserve(self):
1198 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1199 self.domid)
1200 self._unwatchVm()
1201 self.storeDom('xend/shutdown_completed', 'True')
1202 self._stateSet(DOM_STATE_HALTED)
1205 # Debugging ..
1208 def dumpCore(self, corefile = None):
1209 """Create a core dump for this domain.
1211 @raise: XendError if core dumping failed.
1212 """
1214 try:
1215 if not corefile:
1216 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1217 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1218 self.info['name_label'], self.domid)
1220 if os.path.isdir(corefile):
1221 raise XendError("Cannot dump core in a directory: %s" %
1222 corefile)
1224 xc.domain_dumpcore(self.domid, corefile)
1225 except RuntimeError, ex:
1226 corefile_incomp = corefile+'-incomplete'
1227 os.rename(corefile, corefile_incomp)
1228 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1229 self.domid, self.info['name_label'])
1230 raise XendError("Failed to dump core: %s" % str(ex))
1233 # Device creation/deletion functions
1236 def _createDevice(self, deviceClass, devConfig):
1237 return self.getDeviceController(deviceClass).createDevice(devConfig)
1239 def _waitForDevice(self, deviceClass, devid):
1240 return self.getDeviceController(deviceClass).waitForDevice(devid)
1242 def _waitForDeviceUUID(self, dev_uuid):
1243 deviceClass, config = self.info['devices'].get(dev_uuid)
1244 self._waitForDevice(deviceClass, config['devid'])
1246 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1247 return self.getDeviceController(deviceClass).reconfigureDevice(
1248 devid, devconfig)
1250 def _createDevices(self):
1251 """Create the devices for a vm.
1253 @raise: VmError for invalid devices
1254 """
1255 ordered_refs = self.info.ordered_device_refs()
1256 for dev_uuid in ordered_refs:
1257 devclass, config = self.info['devices'][dev_uuid]
1258 if devclass in XendDevices.valid_devices():
1259 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1260 dev_uuid = config.get('uuid')
1261 devid = self._createDevice(devclass, config)
1263 # store devid in XendConfig for caching reasons
1264 if dev_uuid in self.info['devices']:
1265 self.info['devices'][dev_uuid][1]['devid'] = devid
1267 if self.image:
1268 self.image.createDeviceModel()
1270 def _releaseDevices(self, suspend = False):
1271 """Release all domain's devices. Nothrow guarantee."""
1272 if suspend and self.image:
1273 self.image.destroy(suspend)
1274 return
1276 while True:
1277 t = xstransact("%s/device" % self.dompath)
1278 for devclass in XendDevices.valid_devices():
1279 for dev in t.list(devclass):
1280 try:
1281 t.remove(dev)
1282 except:
1283 # Log and swallow any exceptions in removal --
1284 # there's nothing more we can do.
1285 log.exception(
1286 "Device release failed: %s; %s; %s",
1287 self.info['name_label'], devclass, dev)
1288 if t.commit():
1289 break
1291 def getDeviceController(self, name):
1292 """Get the device controller for this domain, and if it
1293 doesn't exist, create it.
1295 @param name: device class name
1296 @type name: string
1297 @rtype: subclass of DevController
1298 """
1299 if name not in self._deviceControllers:
1300 devController = XendDevices.make_controller(name, self)
1301 if not devController:
1302 raise XendError("Unknown device type: %s" % name)
1303 self._deviceControllers[name] = devController
1305 return self._deviceControllers[name]
1308 # Migration functions (public)
1311 def testMigrateDevices(self, network, dst):
1312 """ Notify all device about intention of migration
1313 @raise: XendError for a device that cannot be migrated
1314 """
1315 for (n, c) in self.info.all_devices_sxpr():
1316 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1317 if rc != 0:
1318 raise XendError("Device of type '%s' refuses migration." % n)
1320 def migrateDevices(self, network, dst, step, domName=''):
1321 """Notify the devices about migration
1322 """
1323 ctr = 0
1324 try:
1325 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1326 self.migrateDevice(dev_type, dev_conf, network, dst,
1327 step, domName)
1328 ctr = ctr + 1
1329 except:
1330 for dev_type, dev_conf in self.info.all_devices_sxpr():
1331 if ctr == 0:
1332 step = step - 1
1333 ctr = ctr - 1
1334 self._recoverMigrateDevice(dev_type, dev_conf, network,
1335 dst, step, domName)
1336 raise
1338 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1339 step, domName=''):
1340 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1341 network, dst, step, domName)
1343 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1344 dst, step, domName=''):
1345 return self.getDeviceController(deviceClass).recover_migrate(
1346 deviceConfig, network, dst, step, domName)
1349 ## private:
1351 def _constructDomain(self):
1352 """Construct the domain.
1354 @raise: VmError on error
1355 """
1357 log.debug('XendDomainInfo.constructDomain')
1359 self.shutdownStartTime = None
1361 image_cfg = self.info.get('image', {})
1362 hvm = image_cfg.has_key('hvm')
1364 if hvm:
1365 info = xc.xeninfo()
1366 if 'hvm' not in info['xen_caps']:
1367 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1368 "supported by your CPU and enabled in your "
1369 "BIOS?")
1371 self.domid = xc.domain_create(
1372 domid = 0,
1373 ssidref = security.get_security_info(self.info, 'ssidref'),
1374 handle = uuid.fromString(self.info['uuid']),
1375 hvm = int(hvm))
1377 if self.domid < 0:
1378 raise VmError('Creating domain failed: name=%s' %
1379 self.info['name_label'])
1381 self.dompath = GetDomainPath(self.domid)
1383 self._recreateDom()
1385 # Set maximum number of vcpus in domain
1386 xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
1388 # register the domain in the list
1389 from xen.xend import XendDomain
1390 XendDomain.instance().add_domain(self)
1392 def _introduceDomain(self):
1393 assert self.domid is not None
1394 assert self.store_mfn is not None
1395 assert self.store_port is not None
1397 try:
1398 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1399 except RuntimeError, exn:
1400 raise XendError(str(exn))
1403 def _initDomain(self):
1404 log.debug('XendDomainInfo.initDomain: %s %s',
1405 self.domid,
1406 self.info['cpu_weight'])
1408 self._configureBootloader()
1410 if not self._infoIsSet('image'):
1411 raise VmError('Missing image in configuration')
1413 try:
1414 self.image = image.create(self,
1415 self.info,
1416 self.info['image'],
1417 self.info['devices'])
1419 localtime = self.info.get('localtime', False)
1420 if localtime:
1421 xc.domain_set_time_offset(self.domid)
1423 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1425 # repin domain vcpus if a restricted cpus list is provided
1426 # this is done prior to memory allocation to aide in memory
1427 # distribution for NUMA systems.
1428 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1429 for v in range(0, self.info['vcpus_number']):
1430 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1432 # Use architecture- and image-specific calculations to determine
1433 # the various headrooms necessary, given the raw configured
1434 # values. maxmem, memory, and shadow are all in KiB.
1435 memory = self.image.getRequiredAvailableMemory(
1436 self.info['memory_static_min'] * 1024)
1437 maxmem = self.image.getRequiredAvailableMemory(
1438 self.info['memory_static_max'] * 1024)
1439 shadow = self.image.getRequiredShadowMemory(
1440 self.info['shadow_memory'] * 1024,
1441 self.info['memory_static_max'] * 1024)
1443 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'],)
1444 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1445 # takes MiB and we must not round down and end up under-providing.
1446 shadow = ((shadow + 1023) / 1024) * 1024
1448 # set memory limit
1449 xc.domain_setmaxmem(self.domid, maxmem)
1451 # Make sure there's enough RAM available for the domain
1452 balloon.free(memory + shadow)
1454 # Set up the shadow memory
1455 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1456 self.info['shadow_memory'] = shadow_cur
1458 self._createChannels()
1460 channel_details = self.image.createImage()
1462 self.store_mfn = channel_details['store_mfn']
1463 if 'console_mfn' in channel_details:
1464 self.console_mfn = channel_details['console_mfn']
1466 self._introduceDomain()
1468 self._createDevices()
1470 self.image.cleanupBootloading()
1472 self.info['start_time'] = time.time()
1474 self._stateSet(DOM_STATE_RUNNING)
1475 except RuntimeError, exn:
1476 log.exception("XendDomainInfo.initDomain: exception occurred")
1477 self.image.cleanupBootloading()
1478 raise VmError(str(exn))
1481 def cleanupDomain(self):
1482 """Cleanup domain resources; release devices. Idempotent. Nothrow
1483 guarantee."""
1485 self.refresh_shutdown_lock.acquire()
1486 try:
1487 self.unwatchShutdown()
1488 self._releaseDevices()
1489 bootloader_tidy(self)
1491 if self.image:
1492 try:
1493 self.image.destroy()
1494 except:
1495 log.exception(
1496 "XendDomainInfo.cleanup: image.destroy() failed.")
1497 self.image = None
1499 try:
1500 self._removeDom()
1501 except:
1502 log.exception("Removing domain path failed.")
1504 self._stateSet(DOM_STATE_HALTED)
1505 finally:
1506 self.refresh_shutdown_lock.release()
1509 def unwatchShutdown(self):
1510 """Remove the watch on the domain's control/shutdown node, if any.
1511 Idempotent. Nothrow guarantee. Expects to be protected by the
1512 refresh_shutdown_lock."""
1514 try:
1515 try:
1516 if self.shutdownWatch:
1517 self.shutdownWatch.unwatch()
1518 finally:
1519 self.shutdownWatch = None
1520 except:
1521 log.exception("Unwatching control/shutdown failed.")
1523 def waitForShutdown(self):
1524 self.state_updated.acquire()
1525 try:
1526 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1527 self.state_updated.wait()
1528 finally:
1529 self.state_updated.release()
1533 # TODO: recategorise - called from XendCheckpoint
1536 def completeRestore(self, store_mfn, console_mfn):
1538 log.debug("XendDomainInfo.completeRestore")
1540 self.store_mfn = store_mfn
1541 self.console_mfn = console_mfn
1543 self._introduceDomain()
1544 image_cfg = self.info.get('image', {})
1545 hvm = image_cfg.has_key('hvm')
1546 if hvm:
1547 self.image = image.create(self,
1548 self.info,
1549 self.info['image'],
1550 self.info['devices'])
1551 if self.image:
1552 self.image.createDeviceModel(True)
1553 self.image.register_shutdown_watch()
1554 self._storeDomDetails()
1555 self._registerWatches()
1556 self.refreshShutdown()
1558 log.debug("XendDomainInfo.completeRestore done")
1561 def _endRestore(self):
1562 self.setResume(False)
1565 # VM Destroy
1568 def destroy(self):
1569 """Cleanup VM and destroy domain. Nothrow guarantee."""
1571 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1573 self._cleanupVm()
1574 if self.dompath is not None:
1575 self.destroyDomain()
1578 def destroyDomain(self):
1579 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1581 try:
1582 if self.domid is not None:
1583 xc.domain_destroy(self.domid)
1584 self.domid = None
1585 for state in DOM_STATES_OLD:
1586 self.info[state] = 0
1587 except:
1588 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1590 from xen.xend import XendDomain
1591 XendDomain.instance().remove_domain(self)
1593 self.cleanupDomain()
1596 def resumeDomain(self):
1597 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1599 try:
1600 if self.domid is not None:
1601 xc.domain_resume(self.domid)
1602 ResumeDomain(self.domid)
1603 except:
1604 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1607 # Channels for xenstore and console
1610 def _createChannels(self):
1611 """Create the channels to the domain.
1612 """
1613 self.store_port = self._createChannel()
1614 self.console_port = self._createChannel()
1617 def _createChannel(self):
1618 """Create an event channel to the domain.
1619 """
1620 try:
1621 if self.domid != None:
1622 return xc.evtchn_alloc_unbound(domid = self.domid,
1623 remote_dom = 0)
1624 except:
1625 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1626 raise
1628 def _resetChannels(self):
1629 """Reset all event channels in the domain.
1630 """
1631 try:
1632 if self.domid != None:
1633 return xc.evtchn_reset(dom = self.domid)
1634 except:
1635 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1636 raise
1640 # Bootloader configuration
1643 def _configureBootloader(self):
1644 """Run the bootloader if we're configured to do so."""
1646 blexec = self.info['PV_bootloader']
1647 bootloader_args = self.info['PV_bootloader_args']
1648 kernel = self.info['PV_kernel']
1649 ramdisk = self.info['PV_ramdisk']
1650 args = self.info['PV_args']
1651 boot = self.info['HVM_boot_policy']
1653 if boot:
1654 # HVM booting.
1655 self.info['image']['type'] = 'hvm'
1656 if not 'devices' in self.info['image']:
1657 self.info['image']['devices'] = {}
1658 self.info['image']['devices']['boot'] = \
1659 self.info['HVM_boot_params'].get('order', 'dc')
1660 elif not blexec and kernel:
1661 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1662 # will be picked up by image.py.
1663 pass
1664 else:
1665 # Boot using bootloader
1666 if not blexec or blexec == 'pygrub':
1667 blexec = osdep.pygrub_path
1669 blcfg = None
1670 disks = [x for x in self.info['vbd_refs']
1671 if self.info['devices'][x][1]['bootable']]
1673 if not disks:
1674 msg = "Had a bootloader specified, but no disks are bootable"
1675 log.error(msg)
1676 raise VmError(msg)
1678 devinfo = self.info['devices'][disks[0]]
1679 devtype = devinfo[0]
1680 disk = devinfo[1]['uname']
1682 fn = blkdev_uname_to_file(disk)
1683 mounted = devtype == 'tap' and not os.stat(fn).st_rdev
1684 if mounted:
1685 # This is a file, not a device. pygrub can cope with a
1686 # file if it's raw, but if it's QCOW or other such formats
1687 # used through blktap, then we need to mount it first.
1689 log.info("Mounting %s on %s." %
1690 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1692 vbd = {
1693 'mode': 'RO',
1694 'device': BOOTLOADER_LOOPBACK_DEVICE,
1697 from xen.xend import XendDomain
1698 dom0 = XendDomain.instance().privilegedDomain()
1699 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
1700 fn = BOOTLOADER_LOOPBACK_DEVICE
1702 try:
1703 blcfg = bootloader(blexec, fn, self, False,
1704 bootloader_args, kernel, ramdisk, args)
1705 finally:
1706 if mounted:
1707 log.info("Unmounting %s from %s." %
1708 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1710 dom0.destroyDevice('tap', '/dev/xvdp')
1712 if blcfg is None:
1713 msg = "Had a bootloader specified, but can't find disk"
1714 log.error(msg)
1715 raise VmError(msg)
1717 self.info.update_with_image_sxp(blcfg, True)
1721 # VM Functions
1724 def _readVMDetails(self, params):
1725 """Read the specified parameters from the store.
1726 """
1727 try:
1728 return self._gatherVm(*params)
1729 except ValueError:
1730 # One of the int/float entries in params has a corresponding store
1731 # entry that is invalid. We recover, because older versions of
1732 # Xend may have put the entry there (memory/target, for example),
1733 # but this is in general a bad situation to have reached.
1734 log.exception(
1735 "Store corrupted at %s! Domain %d's configuration may be "
1736 "affected.", self.vmpath, self.domid)
1737 return []
1739 def _cleanupVm(self):
1740 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1742 self._unwatchVm()
1744 try:
1745 self._removeVm()
1746 except:
1747 log.exception("Removing VM path failed.")
1750 def checkLiveMigrateMemory(self):
1751 """ Make sure there's enough memory to migrate this domain """
1752 overhead_kb = 0
1753 if arch.type == "x86":
1754 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1755 # the minimum that Xen would allocate if no value were given.
1756 overhead_kb = self.info['vcpus_number'] * 1024 + \
1757 self.info['memory_static_max'] * 4
1758 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1759 # The domain might already have some shadow memory
1760 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1761 if overhead_kb > 0:
1762 balloon.free(overhead_kb)
1764 def _unwatchVm(self):
1765 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1766 guarantee."""
1767 try:
1768 try:
1769 if self.vmWatch:
1770 self.vmWatch.unwatch()
1771 finally:
1772 self.vmWatch = None
1773 except:
1774 log.exception("Unwatching VM path failed.")
1776 def testDeviceComplete(self):
1777 """ For Block IO migration safety we must ensure that
1778 the device has shutdown correctly, i.e. all blocks are
1779 flushed to disk
1780 """
1781 start = time.time()
1782 while True:
1783 test = 0
1784 diff = time.time() - start
1785 for i in self.getDeviceController('vbd').deviceIDs():
1786 test = 1
1787 log.info("Dev %s still active, looping...", i)
1788 time.sleep(0.1)
1790 if test == 0:
1791 break
1792 if diff >= MIGRATE_TIMEOUT:
1793 log.info("Dev still active but hit max loop timeout")
1794 break
1796 def testvifsComplete(self):
1797 """ In case vifs are released and then created for the same
1798 domain, we need to wait the device shut down.
1799 """
1800 start = time.time()
1801 while True:
1802 test = 0
1803 diff = time.time() - start
1804 for i in self.getDeviceController('vif').deviceIDs():
1805 test = 1
1806 log.info("Dev %s still active, looping...", i)
1807 time.sleep(0.1)
1809 if test == 0:
1810 break
1811 if diff >= MIGRATE_TIMEOUT:
1812 log.info("Dev still active but hit max loop timeout")
1813 break
1815 def _storeVmDetails(self):
1816 to_store = {}
1818 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1819 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1820 if self._infoIsSet(info_key):
1821 to_store[key] = str(self.info[info_key])
1823 if self.info.get('image'):
1824 image_sxpr = self.info.image_sxpr()
1825 if image_sxpr:
1826 to_store['image'] = sxp.to_string(image_sxpr)
1828 if self._infoIsSet('security'):
1829 secinfo = self.info['security']
1830 to_store['security'] = sxp.to_string(secinfo)
1831 for idx in range(0, len(secinfo)):
1832 if secinfo[idx][0] == 'access_control':
1833 to_store['security/access_control'] = sxp.to_string(
1834 [secinfo[idx][1], secinfo[idx][2]])
1835 for aidx in range(1, len(secinfo[idx])):
1836 if secinfo[idx][aidx][0] == 'label':
1837 to_store['security/access_control/label'] = \
1838 secinfo[idx][aidx][1]
1839 if secinfo[idx][aidx][0] == 'policy':
1840 to_store['security/access_control/policy'] = \
1841 secinfo[idx][aidx][1]
1842 if secinfo[idx][0] == 'ssidref':
1843 to_store['security/ssidref'] = str(secinfo[idx][1])
1846 if not self._readVm('xend/restart_count'):
1847 to_store['xend/restart_count'] = str(0)
1849 log.debug("Storing VM details: %s", scrub_password(to_store))
1851 self._writeVm(to_store)
1852 self._setVmPermissions()
1855 def _setVmPermissions(self):
1856 """Allow the guest domain to read its UUID. We don't allow it to
1857 access any other entry, for security."""
1858 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1859 { 'dom' : self.domid,
1860 'read' : True,
1861 'write' : False })
1864 # Utility functions
1867 def _stateSet(self, state):
1868 self.state_updated.acquire()
1869 try:
1870 if self.state != state:
1871 self.state = state
1872 self.state_updated.notifyAll()
1873 finally:
1874 self.state_updated.release()
1876 def _infoIsSet(self, name):
1877 return name in self.info and self.info[name] is not None
1879 def _checkName(self, name):
1880 """Check if a vm name is valid. Valid names contain alphabetic
1881 characters, digits, or characters in '_-.:/+'.
1882 The same name cannot be used for more than one vm at the same time.
1884 @param name: name
1885 @raise: VmError if invalid
1886 """
1887 from xen.xend import XendDomain
1889 if name is None or name == '':
1890 raise VmError('Missing VM Name')
1892 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
1893 raise VmError('Invalid VM Name')
1895 dom = XendDomain.instance().domain_lookup_nr(name)
1896 if dom and dom.info['uuid'] != self.info['uuid']:
1897 raise VmError("VM name '%s' already exists%s" %
1898 (name,
1899 dom.domid is not None and
1900 (" as domain %s" % str(dom.domid)) or ""))
1903 def update(self, info = None, refresh = True):
1904 """Update with info from xc.domain_getinfo().
1905 """
1906 log.trace("XendDomainInfo.update(%s) on domain %s", info,
1907 str(self.domid))
1909 if not info:
1910 info = dom_get(self.domid)
1911 if not info:
1912 return
1914 #manually update ssidref / security fields
1915 if security.on() and info.has_key('ssidref'):
1916 if (info['ssidref'] != 0) and self.info.has_key('security'):
1917 security_field = self.info['security']
1918 if not security_field:
1919 #create new security element
1920 self.info.update({'security':
1921 [['ssidref', str(info['ssidref'])]]})
1923 #ssidref field not used any longer
1924 if 'ssidref' in info:
1925 info.pop('ssidref')
1927 # make sure state is reset for info
1928 # TODO: we should eventually get rid of old_dom_states
1930 self.info.update_config(info)
1931 self._update_consoles()
1933 if refresh:
1934 self.refreshShutdown(info)
1936 log.trace("XendDomainInfo.update done on domain %s: %s",
1937 str(self.domid), self.info)
1939 def sxpr(self, ignore_store = False, legacy_only = True):
1940 result = self.info.to_sxp(domain = self,
1941 ignore_devices = ignore_store,
1942 legacy_only = legacy_only)
1944 #if not ignore_store and self.dompath:
1945 # vnc_port = self.readDom('console/vnc-port')
1946 # if vnc_port is not None:
1947 # result.append(['device',
1948 # ['console', ['vnc-port', str(vnc_port)]]])
1950 return result
1952 # Xen API
1953 # ----------------------------------------------------------------
1955 def get_uuid(self):
1956 dom_uuid = self.info.get('uuid')
1957 if not dom_uuid: # if it doesn't exist, make one up
1958 dom_uuid = uuid.createString()
1959 self.info['uuid'] = dom_uuid
1960 return dom_uuid
1962 def get_memory_static_max(self):
1963 return self.info.get('memory_static_max', 0)
1964 def get_memory_static_min(self):
1965 return self.info.get('memory_static_min', 0)
1966 def get_memory_dynamic_max(self):
1967 return self.info.get('memory_dynamic_max', 0)
1968 def get_memory_dynamic_min(self):
1969 return self.info.get('memory_dynamic_min', 0)
1971 def get_vcpus_policy(self):
1972 sched_id = xc.sched_id_get()
1973 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
1974 return 'sedf'
1975 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
1976 return 'credit'
1977 else:
1978 return 'unknown'
1979 def get_vcpus_params(self):
1980 if self.getDomid() is None:
1981 return self.info['vcpus_params']
1983 retval = xc.sched_credit_domain_get(self.getDomid())
1984 return retval
1985 def get_power_state(self):
1986 return XEN_API_VM_POWER_STATE[self.state]
1987 def get_platform_std_vga(self):
1988 return self.info.get('platform_std_vga', False)
1989 def get_platform_serial(self):
1990 return self.info.get('platform_serial', '')
1991 def get_platform_localtime(self):
1992 return self.info.get('platform_localtime', False)
1993 def get_platform_clock_offset(self):
1994 return self.info.get('platform_clock_offset', False)
1995 def get_platform_enable_audio(self):
1996 return self.info.get('platform_enable_audio', False)
1997 def get_platform_keymap(self):
1998 return self.info.get('platform_keymap', '')
1999 def get_pci_bus(self):
2000 return self.info.get('pci_bus', '')
2001 def get_tools_version(self):
2002 return self.info.get('tools_version', {})
2004 def get_on_shutdown(self):
2005 after_shutdown = self.info.get('action_after_shutdown')
2006 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2007 return XEN_API_ON_NORMAL_EXIT[-1]
2008 return after_shutdown
2010 def get_on_reboot(self):
2011 after_reboot = self.info.get('action_after_reboot')
2012 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2013 return XEN_API_ON_NORMAL_EXIT[-1]
2014 return after_reboot
2016 def get_on_suspend(self):
2017 # TODO: not supported
2018 after_suspend = self.info.get('action_after_suspend')
2019 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2020 return XEN_API_ON_NORMAL_EXIT[-1]
2021 return after_suspend
2023 def get_on_crash(self):
2024 after_crash = self.info.get('action_after_crash')
2025 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2026 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2027 return after_crash
2029 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2030 """ Get's a device configuration either from XendConfig or
2031 from the DevController.
2033 @param dev_class: device class, either, 'vbd' or 'vif'
2034 @param dev_uuid: device UUID
2036 @rtype: dictionary
2037 """
2038 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2040 # shortcut if the domain isn't started because
2041 # the devcontrollers will have no better information
2042 # than XendConfig.
2043 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2044 if dev_config:
2045 return copy.deepcopy(dev_config)
2046 return None
2048 # instead of using dev_class, we use the dev_type
2049 # that is from XendConfig.
2050 controller = self.getDeviceController(dev_type)
2051 if not controller:
2052 return None
2054 all_configs = controller.getAllDeviceConfigurations()
2055 if not all_configs:
2056 return None
2058 updated_dev_config = copy.deepcopy(dev_config)
2059 for _devid, _devcfg in all_configs.items():
2060 if _devcfg.get('uuid') == dev_uuid:
2061 updated_dev_config.update(_devcfg)
2062 updated_dev_config['id'] = _devid
2063 return updated_dev_config
2065 return updated_dev_config
2067 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2068 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2069 if not config:
2070 return {}
2072 config['VM'] = self.get_uuid()
2074 if dev_class == 'vif':
2075 if not config.has_key('name'):
2076 config['name'] = config.get('vifname', '')
2077 if not config.has_key('MAC'):
2078 config['MAC'] = config.get('mac', '')
2079 if not config.has_key('type'):
2080 config['type'] = 'paravirtualised'
2081 if not config.has_key('device'):
2082 devid = config.get('id')
2083 if devid != None:
2084 config['device'] = 'eth%d' % devid
2085 else:
2086 config['device'] = ''
2088 if not config.has_key('network'):
2089 try:
2090 config['network'] = \
2091 XendNode.instance().bridge_to_network(
2092 config.get('bridge')).uuid
2093 except Exception:
2094 log.exception('bridge_to_network')
2095 # Ignore this for now -- it may happen if the device
2096 # has been specified using the legacy methods, but at
2097 # some point we're going to have to figure out how to
2098 # handle that properly.
2100 config['MTU'] = 1500 # TODO
2102 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2103 xennode = XendNode.instance()
2104 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2105 config['io_read_kbs'] = rx_bps/1024
2106 config['io_write_kbs'] = tx_bps/1024
2107 else:
2108 config['io_read_kbs'] = 0.0
2109 config['io_write_kbs'] = 0.0
2111 if dev_class == 'vbd':
2113 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2114 controller = self.getDeviceController(dev_class)
2115 devid, _1, _2 = controller.getDeviceDetails(config)
2116 xennode = XendNode.instance()
2117 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2118 config['io_read_kbs'] = rd_blkps
2119 config['io_write_kbs'] = wr_blkps
2120 else:
2121 config['io_read_kbs'] = 0.0
2122 config['io_write_kbs'] = 0.0
2124 config['VDI'] = config.get('VDI', '')
2125 config['device'] = config.get('dev', '')
2126 if ':' in config['device']:
2127 vbd_name, vbd_type = config['device'].split(':', 1)
2128 config['device'] = vbd_name
2129 if vbd_type == 'cdrom':
2130 config['type'] = XEN_API_VBD_TYPE[0]
2131 else:
2132 config['type'] = XEN_API_VBD_TYPE[1]
2134 config['driver'] = 'paravirtualised' # TODO
2135 config['image'] = config.get('uname', '')
2137 if config.get('mode', 'r') == 'r':
2138 config['mode'] = 'RO'
2139 else:
2140 config['mode'] = 'RW'
2142 if dev_class == 'vtpm':
2143 if not config.has_key('type'):
2144 config['type'] = 'paravirtualised' # TODO
2145 if not config.has_key('backend'):
2146 config['backend'] = "00000000-0000-0000-0000-000000000000"
2148 return config
2150 def get_dev_property(self, dev_class, dev_uuid, field):
2151 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2152 try:
2153 return config[field]
2154 except KeyError:
2155 raise XendError('Invalid property for device: %s' % field)
2157 def set_dev_property(self, dev_class, dev_uuid, field, value):
2158 self.info['devices'][dev_uuid][1][field] = value
2160 def get_vcpus_util(self):
2161 vcpu_util = {}
2162 xennode = XendNode.instance()
2163 if 'vcpus_number' in self.info and self.domid != None:
2164 for i in range(0, self.info['vcpus_number']):
2165 util = xennode.get_vcpu_util(self.domid, i)
2166 vcpu_util[str(i)] = util
2168 return vcpu_util
2170 def get_consoles(self):
2171 return self.info.get('console_refs', [])
2173 def get_vifs(self):
2174 return self.info.get('vif_refs', [])
2176 def get_vbds(self):
2177 return self.info.get('vbd_refs', [])
2179 def get_vtpms(self):
2180 return self.info.get('vtpm_refs', [])
2182 def create_vbd(self, xenapi_vbd, vdi_image_path):
2183 """Create a VBD using a VDI from XendStorageRepository.
2185 @param xenapi_vbd: vbd struct from the Xen API
2186 @param vdi_image_path: VDI UUID
2187 @rtype: string
2188 @return: uuid of the device
2189 """
2190 xenapi_vbd['image'] = vdi_image_path
2191 log.debug('create_vbd: %s' % xenapi_vbd)
2192 dev_uuid = ''
2193 if vdi_image_path.startswith('tap'):
2194 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2195 else:
2196 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2198 if not dev_uuid:
2199 raise XendError('Failed to create device')
2201 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2202 _, config = self.info['devices'][dev_uuid]
2203 dev_control = None
2205 if vdi_image_path.startswith('tap'):
2206 dev_control = self.getDeviceController('tap')
2207 else:
2208 dev_control = self.getDeviceController('vbd')
2210 config['devid'] = dev_control.createDevice(config)
2212 return dev_uuid
2214 def create_vif(self, xenapi_vif):
2215 """Create VIF device from the passed struct in Xen API format.
2217 @param xenapi_vif: Xen API VIF Struct.
2218 @rtype: string
2219 @return: UUID
2220 """
2221 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2222 if not dev_uuid:
2223 raise XendError('Failed to create device')
2225 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2226 _, config = self.info['devices'][dev_uuid]
2227 config['devid'] = self.getDeviceController('vif').createDevice(config)
2229 return dev_uuid
2231 def create_vtpm(self, xenapi_vtpm):
2232 """Create a VTPM device from the passed struct in Xen API format.
2234 @return: uuid of the device
2235 @rtype: string
2236 """
2238 if self.state not in (DOM_STATE_HALTED,):
2239 raise VmError("Can only add vTPM to a halted domain.")
2240 if self.get_vtpms() != []:
2241 raise VmError('Domain already has a vTPM.')
2242 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2243 if not dev_uuid:
2244 raise XendError('Failed to create device')
2246 return dev_uuid
2248 def create_console(self, xenapi_console):
2249 """ Create a console device from a Xen API struct.
2251 @return: uuid of device
2252 @rtype: string
2253 """
2254 if self.state not in (DOM_STATE_HALTED,):
2255 raise VmError("Can only add console to a halted domain.")
2257 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2258 if not dev_uuid:
2259 raise XendError('Failed to create device')
2261 return dev_uuid
2263 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2264 if dev_uuid not in self.info['devices']:
2265 raise XendError('Device does not exist')
2267 try:
2268 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2269 _, config = self.info['devices'][dev_uuid]
2270 devid = config.get('devid')
2271 if devid != None:
2272 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2273 else:
2274 raise XendError('Unable to get devid for device: %s:%s' %
2275 (dev_type, dev_uuid))
2276 finally:
2277 del self.info['devices'][dev_uuid]
2278 self.info['%s_refs' % dev_type].remove(dev_uuid)
2280 def destroy_vbd(self, dev_uuid):
2281 self.destroy_device_by_uuid('vbd', dev_uuid)
2283 def destroy_vif(self, dev_uuid):
2284 self.destroy_device_by_uuid('vif', dev_uuid)
2286 def destroy_vtpm(self, dev_uuid):
2287 self.destroy_device_by_uuid('vtpm', dev_uuid)
2289 def has_device(self, dev_class, dev_uuid):
2290 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2292 def __str__(self):
2293 return '<domain id=%s name=%s memory=%s state=%s>' % \
2294 (str(self.domid), self.info['name_label'],
2295 str(self.info['memory_static_min']), DOM_STATES[self.state])
2297 __repr__ = __str__