direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 14342:e5d7b878118f

Added an VM_HVM_REQUIRED error code, and use it on VM.start.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Sat Mar 10 23:22:00 2007 +0000 (2007-03-10)
parents 0affe03ee985
children 8a01644306ee
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005-2007 XenSource Ltd
17 #============================================================================
19 """Representation of a single domain.
20 Includes support for domain construction, using
21 open-ended configurations.
23 Author: Mike Wray <mike.wray@hp.com>
25 """
27 import logging
28 import time
29 import threading
30 import re
31 import copy
32 import os
33 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 vm.image = image.create(vm, vm.info)
180 vm.image.recreate()
182 vm._registerWatches()
183 vm.refreshShutdown(xeninfo)
185 # register the domain in the list
186 from xen.xend import XendDomain
187 XendDomain.instance().add_domain(vm)
189 return vm
192 def restore(config):
193 """Create a domain and a VM object to do a restore.
195 @param config: Domain SXP configuration
196 @type config: list of lists. (see C{create})
198 @rtype: XendDomainInfo
199 @return: A up and running XendDomainInfo instance
200 @raise VmError: Invalid configuration or failure to start.
201 @raise XendError: Errors with configuration.
202 """
204 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
205 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
206 resume = True)
207 try:
208 vm.resume()
209 return vm
210 except:
211 vm.destroy()
212 raise
214 def createDormant(domconfig):
215 """Create a dormant/inactive XenDomainInfo without creating VM.
216 This is for creating instances of persistent domains that are not
217 yet start.
219 @param domconfig: Parsed configuration
220 @type domconfig: XendConfig object
222 @rtype: XendDomainInfo
223 @return: A up and running XendDomainInfo instance
224 @raise XendError: Errors with configuration.
225 """
227 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
229 # domid does not make sense for non-running domains.
230 domconfig.pop('domid', None)
231 vm = XendDomainInfo(domconfig)
232 return vm
234 def domain_by_name(name):
235 """Get domain by name
237 @params name: Name of the domain
238 @type name: string
239 @return: XendDomainInfo or None
240 """
241 from xen.xend import XendDomain
242 return XendDomain.instance().domain_lookup_by_name_nr(name)
245 def shutdown_reason(code):
246 """Get a shutdown reason from a code.
248 @param code: shutdown code
249 @type code: int
250 @return: shutdown reason
251 @rtype: string
252 """
253 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
255 def dom_get(dom):
256 """Get info from xen for an existing domain.
258 @param dom: domain id
259 @type dom: int
260 @return: info or None
261 @rtype: dictionary
262 """
263 try:
264 domlist = xc.domain_getinfo(dom, 1)
265 if domlist and dom == domlist[0]['domid']:
266 return domlist[0]
267 except Exception, err:
268 # ignore missing domain
269 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
270 return None
273 class XendDomainInfo:
274 """An object represents a domain.
276 @TODO: try to unify dom and domid, they mean the same thing, but
277 xc refers to it as dom, and everywhere else, including
278 xenstore it is domid. The best way is to change xc's
279 python interface.
281 @ivar info: Parsed configuration
282 @type info: dictionary
283 @ivar domid: Domain ID (if VM has started)
284 @type domid: int or None
285 @ivar vmpath: XenStore path to this VM.
286 @type vmpath: string
287 @ivar dompath: XenStore path to this Domain.
288 @type dompath: string
289 @ivar image: Reference to the VM Image.
290 @type image: xen.xend.image.ImageHandler
291 @ivar store_port: event channel to xenstored
292 @type store_port: int
293 @ivar console_port: event channel to xenconsoled
294 @type console_port: int
295 @ivar store_mfn: xenstored mfn
296 @type store_mfn: int
297 @ivar console_mfn: xenconsoled mfn
298 @type console_mfn: int
299 @ivar notes: OS image notes
300 @type notes: dictionary
301 @ivar vmWatch: reference to a watch on the xenstored vmpath
302 @type vmWatch: xen.xend.xenstore.xswatch
303 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
304 @type shutdownWatch: xen.xend.xenstore.xswatch
305 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
306 @type shutdownStartTime: float or None
307 @ivar state: Domain state
308 @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
309 @ivar state_updated: lock for self.state
310 @type state_updated: threading.Condition
311 @ivar refresh_shutdown_lock: lock for polling shutdown state
312 @type refresh_shutdown_lock: threading.Condition
313 @ivar _deviceControllers: device controller cache for this domain
314 @type _deviceControllers: dict 'string' to DevControllers
315 """
317 def __init__(self, info, domid = None, dompath = None, augment = False,
318 priv = False, resume = False):
319 """Constructor for a domain
321 @param info: parsed configuration
322 @type info: dictionary
323 @keyword domid: Set initial domain id (if any)
324 @type domid: int
325 @keyword dompath: Set initial dompath (if any)
326 @type dompath: string
327 @keyword augment: Augment given info with xenstored VM info
328 @type augment: bool
329 @keyword priv: Is a privileged domain (Dom 0)
330 @type priv: bool
331 @keyword resume: Is this domain being resumed?
332 @type resume: bool
333 """
335 self.info = info
336 if domid == None:
337 self.domid = self.info.get('domid')
338 else:
339 self.domid = domid
341 #REMOVE: uuid is now generated in XendConfig
342 #if not self._infoIsSet('uuid'):
343 # self.info['uuid'] = uuid.toString(uuid.create())
345 self.vmpath = XS_VMROOT + self.info['uuid']
346 self.dompath = dompath
348 self.image = None
349 self.store_port = None
350 self.store_mfn = None
351 self.console_port = None
352 self.console_mfn = None
354 self.vmWatch = None
355 self.shutdownWatch = None
356 self.shutdownStartTime = None
357 self._resume = resume
359 self.state = DOM_STATE_HALTED
360 self.state_updated = threading.Condition()
361 self.refresh_shutdown_lock = threading.Condition()
363 self._deviceControllers = {}
365 for state in DOM_STATES_OLD:
366 self.info[state] = 0
368 if augment:
369 self._augmentInfo(priv)
371 self._checkName(self.info['name_label'])
374 #
375 # Public functions available through XMLRPC
376 #
379 def start(self, is_managed = False):
380 """Attempts to start the VM by do the appropriate
381 initialisation if it not started.
382 """
383 from xen.xend import XendDomain
385 if self.state == DOM_STATE_HALTED:
386 try:
387 XendTask.log_progress(0, 30, self._constructDomain)
388 XendTask.log_progress(31, 60, self._initDomain)
390 XendTask.log_progress(61, 70, self._storeVmDetails)
391 XendTask.log_progress(71, 80, self._storeDomDetails)
392 XendTask.log_progress(81, 90, self._registerWatches)
393 XendTask.log_progress(91, 100, self.refreshShutdown)
395 xendomains = XendDomain.instance()
396 xennode = XendNode.instance()
398 # save running configuration if XendDomains believe domain is
399 # persistent
400 if is_managed:
401 xendomains.managed_config_save(self)
403 if xennode.xenschedinfo() == 'credit':
404 xendomains.domain_sched_credit_set(self.getDomid(),
405 self.getWeight(),
406 self.getCap())
407 except:
408 log.exception('VM start failed')
409 self.destroy()
410 raise
411 else:
412 raise XendError('VM already running')
414 def resume(self):
415 """Resumes a domain that has come back from suspension."""
416 if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED):
417 try:
418 self._constructDomain()
419 self._storeVmDetails()
420 self._createDevices()
421 self._createChannels()
422 self._storeDomDetails()
423 self._endRestore()
424 except:
425 log.exception('VM resume failed')
426 raise
427 else:
428 raise XendError('VM already running')
430 def shutdown(self, reason):
431 """Shutdown a domain by signalling this via xenstored."""
432 log.debug('XendDomainInfo.shutdown(%s)', reason)
433 if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
434 raise XendError('Domain cannot be shutdown')
436 if self.domid == 0:
437 raise XendError('Domain 0 cannot be shutdown')
439 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
440 raise XendError('Invalid reason: %s' % reason)
441 self._removeVm('xend/previous_restart_time')
442 self.storeDom("control/shutdown", reason)
444 ## shutdown hypercall for hvm domain desides xenstore write
445 if self.info.is_hvm():
446 for code in DOMAIN_SHUTDOWN_REASONS.keys():
447 if DOMAIN_SHUTDOWN_REASONS[code] == reason:
448 break
449 xc.domain_shutdown(self.domid, code)
452 def pause(self):
453 """Pause domain
455 @raise XendError: Failed pausing a domain
456 """
457 try:
458 xc.domain_pause(self.domid)
459 self._stateSet(DOM_STATE_PAUSED)
460 except Exception, ex:
461 log.exception(ex)
462 raise XendError("Domain unable to be paused: %s" % str(ex))
464 def unpause(self):
465 """Unpause domain
467 @raise XendError: Failed unpausing a domain
468 """
469 try:
470 xc.domain_unpause(self.domid)
471 self._stateSet(DOM_STATE_RUNNING)
472 except Exception, ex:
473 log.exception(ex)
474 raise XendError("Domain unable to be unpaused: %s" % str(ex))
476 def send_sysrq(self, key):
477 """ Send a Sysrq equivalent key via xenstored."""
478 asserts.isCharConvertible(key)
479 self.storeDom("control/sysrq", '%c' % key)
481 def device_create(self, dev_config):
482 """Create a new device.
484 @param dev_config: device configuration
485 @type dev_config: SXP object (parsed config)
486 """
487 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
488 dev_type = sxp.name(dev_config)
489 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
490 dev_config_dict = self.info['devices'][dev_uuid][1]
491 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
492 dev_config_dict['devid'] = devid = \
493 self._createDevice(dev_type, dev_config_dict)
494 self._waitForDevice(dev_type, devid)
495 return self.getDeviceController(dev_type).sxpr(devid)
497 def device_configure(self, dev_sxp, devid = None):
498 """Configure an existing device.
500 @param dev_config: device configuration
501 @type dev_config: SXP object (parsed config)
502 @param devid: device id
503 @type devid: int
504 @return: Returns True if successfully updated device
505 @rtype: boolean
506 """
508 # convert device sxp to a dict
509 dev_class = sxp.name(dev_sxp)
510 dev_config = {}
511 for opt_val in dev_sxp[1:]:
512 try:
513 dev_config[opt_val[0]] = opt_val[1]
514 except IndexError:
515 pass
517 # use DevController.reconfigureDevice to change device config
518 dev_control = self.getDeviceController(dev_class)
519 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
521 # update XendConfig with new device info
522 if dev_uuid:
523 self.info.device_update(dev_uuid, dev_sxp)
525 return True
527 def waitForDevices(self):
528 """Wait for this domain's configured devices to connect.
530 @raise VmError: if any device fails to initialise.
531 """
532 for devclass in XendDevices.valid_devices():
533 self.getDeviceController(devclass).waitForDevices()
535 def destroyDevice(self, deviceClass, devid, force = False):
536 try:
537 devid = int(devid)
538 except ValueError:
539 # devid is not a number, let's search for it in xenstore.
540 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
541 for entry in xstransact.List(devicePath):
542 backend = xstransact.Read('%s/%s' % (devicePath, entry),
543 "backend")
544 devName = xstransact.Read(backend, "dev")
545 if devName == devid:
546 # We found the integer matching our devid, use it instead
547 devid = entry
548 break
550 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
552 def getDeviceSxprs(self, deviceClass):
553 if self.state == DOM_STATE_RUNNING:
554 return self.getDeviceController(deviceClass).sxprs()
555 else:
556 sxprs = []
557 dev_num = 0
558 for dev_type, dev_info in self.info.all_devices_sxpr():
559 if dev_type == deviceClass:
560 sxprs.append([dev_num, dev_info])
561 dev_num += 1
562 return sxprs
565 def setMemoryTarget(self, target):
566 """Set the memory target of this domain.
567 @param target: In MiB.
568 """
569 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
570 self.info['name_label'], self.domid, target)
572 if target <= 0:
573 raise XendError('Invalid memory size')
575 self.info['memory_static_min'] = target
576 if self.domid >= 0:
577 self.storeVm("memory", target)
578 self.storeDom("memory/target", target << 10)
579 else:
580 self.info['memory_dynamic_min'] = target
581 xen.xend.XendDomain.instance().managed_config_save(self)
583 def setMemoryMaximum(self, limit):
584 """Set the maximum memory limit of this domain
585 @param limit: In MiB.
586 """
587 log.debug("Setting memory maximum of domain %s (%d) to %d MiB.",
588 self.info['name_label'], self.domid, limit)
590 if limit <= 0:
591 raise XendError('Invalid memory size')
593 self.info['memory_static_max'] = limit
594 if self.domid >= 0:
595 maxmem = int(limit) * 1024
596 try:
597 return xc.domain_setmaxmem(self.domid, maxmem)
598 except Exception, ex:
599 raise XendError(str(ex))
600 else:
601 self.info['memory_dynamic_max'] = limit
602 xen.xend.XendDomain.instance().managed_config_save(self)
605 def getVCPUInfo(self):
606 try:
607 # We include the domain name and ID, to help xm.
608 sxpr = ['domain',
609 ['domid', self.domid],
610 ['name', self.info['name_label']],
611 ['vcpu_count', self.info['vcpus_number']]]
613 for i in range(0, self.info['vcpus_number']):
614 info = xc.vcpu_getinfo(self.domid, i)
616 sxpr.append(['vcpu',
617 ['number', i],
618 ['online', info['online']],
619 ['blocked', info['blocked']],
620 ['running', info['running']],
621 ['cpu_time', info['cpu_time'] / 1e9],
622 ['cpu', info['cpu']],
623 ['cpumap', info['cpumap']]])
625 return sxpr
627 except RuntimeError, exn:
628 raise XendError(str(exn))
630 #
631 # internal functions ... TODO: re-categorised
632 #
634 def _augmentInfo(self, priv):
635 """Augment self.info, as given to us through L{recreate}, with
636 values taken from the store. This recovers those values known
637 to xend but not to the hypervisor.
638 """
639 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
640 if priv:
641 augment_entries.remove('memory')
642 augment_entries.remove('maxmem')
643 augment_entries.remove('vcpus')
644 augment_entries.remove('vcpu_avail')
646 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
647 for k in augment_entries])
649 # make returned lists into a dictionary
650 vm_config = dict(zip(augment_entries, vm_config))
652 for arg in augment_entries:
653 xapicfg = arg
654 val = vm_config[arg]
655 if val != None:
656 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
657 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
658 self.info[xapiarg] = val
659 else:
660 self.info[arg] = val
662 # For dom0, we ignore any stored value for the vcpus fields, and
663 # read the current value from Xen instead. This allows boot-time
664 # settings to take precedence over any entries in the store.
665 if priv:
666 xeninfo = dom_get(self.domid)
667 self.info['vcpus_number'] = xeninfo['online_vcpus']
668 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
670 # read image value
671 image_sxp = self._readVm('image')
672 if image_sxp:
673 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
675 # read devices
676 devices = []
677 for devclass in XendDevices.valid_devices():
678 devconfig = self.getDeviceController(devclass).configurations()
679 if devconfig:
680 devices.extend(devconfig)
682 if not self.info['devices'] and devices is not None:
683 for device in devices:
684 self.info.device_add(device[0], cfg_sxp = device)
686 self._update_consoles()
688 def _update_consoles(self):
689 if self.domid == None or self.domid == 0:
690 return
692 # Update VT100 port if it exists
693 self.console_port = self.readDom('console/port')
694 if self.console_port is not None:
695 serial_consoles = self.info.console_get_all('vt100')
696 if not serial_consoles:
697 cfg = self.info.console_add('vt100', self.console_port)
698 self._createDevice('console', cfg)
699 else:
700 console_uuid = serial_consoles[0].get('uuid')
701 self.info.console_update(console_uuid, 'location',
702 self.console_port)
705 # Update VNC port if it exists and write to xenstore
706 vnc_port = self.readDom('console/vnc-port')
707 if vnc_port is not None:
708 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
709 if dev_type == 'vfb':
710 old_location = dev_info.get('location')
711 listen_host = dev_info.get('vnclisten', 'localhost')
712 new_location = '%s:%s' % (listen_host, str(vnc_port))
713 if old_location == new_location:
714 break
716 dev_info['location'] = new_location
717 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
718 vfb_ctrl = self.getDeviceController('vfb')
719 vfb_ctrl.reconfigureDevice(0, dev_info)
720 break
722 #
723 # Function to update xenstore /vm/*
724 #
726 def _readVm(self, *args):
727 return xstransact.Read(self.vmpath, *args)
729 def _writeVm(self, *args):
730 return xstransact.Write(self.vmpath, *args)
732 def _removeVm(self, *args):
733 return xstransact.Remove(self.vmpath, *args)
735 def _gatherVm(self, *args):
736 return xstransact.Gather(self.vmpath, *args)
738 def storeVm(self, *args):
739 return xstransact.Store(self.vmpath, *args)
741 #
742 # Function to update xenstore /dom/*
743 #
745 def readDom(self, *args):
746 return xstransact.Read(self.dompath, *args)
748 def gatherDom(self, *args):
749 return xstransact.Gather(self.dompath, *args)
751 def _writeDom(self, *args):
752 return xstransact.Write(self.dompath, *args)
754 def _removeDom(self, *args):
755 return xstransact.Remove(self.dompath, *args)
757 def storeDom(self, *args):
758 return xstransact.Store(self.dompath, *args)
760 def _recreateDom(self):
761 complete(self.dompath, lambda t: self._recreateDomFunc(t))
763 def _recreateDomFunc(self, t):
764 t.remove()
765 t.mkdir()
766 t.set_permissions({'dom' : self.domid})
767 t.write('vm', self.vmpath)
769 def _storeDomDetails(self):
770 to_store = {
771 'domid': str(self.domid),
772 'vm': self.vmpath,
773 'name': self.info['name_label'],
774 'console/limit': str(xoptions.get_console_limit() * 1024),
775 'memory/target': str(self.info['memory_static_min'] * 1024),
776 }
778 def f(n, v):
779 if v is not None:
780 if type(v) == bool:
781 to_store[n] = v and "1" or "0"
782 else:
783 to_store[n] = str(v)
785 f('console/port', self.console_port)
786 f('console/ring-ref', self.console_mfn)
787 f('store/port', self.store_port)
788 f('store/ring-ref', self.store_mfn)
790 if arch.type == "x86":
791 f('control/platform-feature-multiprocessor-suspend', True)
793 # elfnotes
794 for n, v in self.info.get_notes().iteritems():
795 n = n.lower().replace('_', '-')
796 if n == 'features':
797 for v in v.split('|'):
798 v = v.replace('_', '-')
799 if v.startswith('!'):
800 f('image/%s/%s' % (n, v[1:]), False)
801 else:
802 f('image/%s/%s' % (n, v), True)
803 else:
804 f('image/%s' % n, v)
806 to_store.update(self._vcpuDomDetails())
808 log.debug("Storing domain details: %s", scrub_password(to_store))
810 self._writeDom(to_store)
812 def _vcpuDomDetails(self):
813 def availability(n):
814 if self.info['vcpu_avail'] & (1 << n):
815 return 'online'
816 else:
817 return 'offline'
819 result = {}
820 for v in range(0, self.info['vcpus_number']):
821 result["cpu/%d/availability" % v] = availability(v)
822 return result
824 #
825 # xenstore watches
826 #
828 def _registerWatches(self):
829 """Register a watch on this VM's entries in the store, and the
830 domain's control/shutdown node, so that when they are changed
831 externally, we keep up to date. This should only be called by {@link
832 #create}, {@link #recreate}, or {@link #restore}, once the domain's
833 details have been written, but before the new instance is returned."""
834 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
835 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
836 self._handleShutdownWatch)
838 def _storeChanged(self, _):
839 log.trace("XendDomainInfo.storeChanged");
841 changed = False
843 # Check whether values in the configuration have
844 # changed in Xenstore.
846 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
848 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
849 for k in cfg_vm])
851 # convert two lists into a python dictionary
852 vm_details = dict(zip(cfg_vm, vm_details))
854 for arg, val in vm_details.items():
855 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
856 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
857 if val != None and val != self.info[xapiarg]:
858 self.info[xapiarg] = val
859 changed= True
861 # Check whether image definition has been updated
862 image_sxp = self._readVm('image')
863 if image_sxp and image_sxp != self.info.image_sxpr():
864 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
865 changed = True
867 if changed:
868 # Update the domain section of the store, as this contains some
869 # parameters derived from the VM configuration.
870 self._storeDomDetails()
872 return 1
874 def _handleShutdownWatch(self, _):
875 log.debug('XendDomainInfo.handleShutdownWatch')
877 reason = self.readDom('control/shutdown')
879 if reason and reason != 'suspend':
880 sst = self.readDom('xend/shutdown_start_time')
881 now = time.time()
882 if sst:
883 self.shutdownStartTime = float(sst)
884 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
885 else:
886 self.shutdownStartTime = now
887 self.storeDom('xend/shutdown_start_time', now)
888 timeout = SHUTDOWN_TIMEOUT
890 log.trace(
891 "Scheduling refreshShutdown on domain %d in %ds.",
892 self.domid, timeout)
893 threading.Timer(timeout, self.refreshShutdown).start()
895 return True
898 #
899 # Public Attributes for the VM
900 #
903 def getDomid(self):
904 return self.domid
906 def setName(self, name):
907 self._checkName(name)
908 self.info['name_label'] = name
909 self.storeVm("name", name)
911 def getName(self):
912 return self.info['name_label']
914 def getDomainPath(self):
915 return self.dompath
917 def getShutdownReason(self):
918 return self.readDom('control/shutdown')
920 def getStorePort(self):
921 """For use only by image.py and XendCheckpoint.py."""
922 return self.store_port
924 def getConsolePort(self):
925 """For use only by image.py and XendCheckpoint.py"""
926 return self.console_port
928 def getFeatures(self):
929 """For use only by image.py."""
930 return self.info['features']
932 def getVCpuCount(self):
933 return self.info['vcpus_number']
935 def setVCpuCount(self, vcpus):
936 if vcpus <= 0:
937 raise XendError('Invalid VCPUs')
939 self.info['vcpu_avail'] = (1 << vcpus) - 1
940 if self.domid >= 0:
941 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
942 # update dom differently depending on whether we are adjusting
943 # vcpu number up or down, otherwise _vcpuDomDetails does not
944 # disable the vcpus
945 if self.info['vcpus_number'] > vcpus:
946 # decreasing
947 self._writeDom(self._vcpuDomDetails())
948 self.info['vcpus_number'] = vcpus
949 else:
950 # same or increasing
951 self.info['vcpus_number'] = vcpus
952 self._writeDom(self._vcpuDomDetails())
953 else:
954 self.info['vcpus_number'] = vcpus
955 xen.xend.XendDomain.instance().managed_config_save(self)
956 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
957 vcpus)
959 def getLabel(self):
960 return security.get_security_info(self.info, 'label')
962 def getMemoryTarget(self):
963 """Get this domain's target memory size, in KB."""
964 return self.info['memory_static_min'] * 1024
966 def getMemoryMaximum(self):
967 """Get this domain's maximum memory size, in KB."""
968 return self.info['memory_static_max'] * 1024
970 def getResume(self):
971 return str(self._resume)
973 def getCap(self):
974 return self.info.get('cpu_cap', 0)
976 def getWeight(self):
977 return self.info.get('cpu_weight', 256)
979 def setResume(self, state):
980 self._resume = state
982 def getRestartCount(self):
983 return self._readVm('xend/restart_count')
985 def refreshShutdown(self, xeninfo = None):
986 """ Checks the domain for whether a shutdown is required.
988 Called from XendDomainInfo and also image.py for HVM images.
989 """
991 # If set at the end of this method, a restart is required, with the
992 # given reason. This restart has to be done out of the scope of
993 # refresh_shutdown_lock.
994 restart_reason = None
996 self.refresh_shutdown_lock.acquire()
997 try:
998 if xeninfo is None:
999 xeninfo = dom_get(self.domid)
1000 if xeninfo is None:
1001 # The domain no longer exists. This will occur if we have
1002 # scheduled a timer to check for shutdown timeouts and the
1003 # shutdown succeeded. It will also occur if someone
1004 # destroys a domain beneath us. We clean up the domain,
1005 # just in case, but we can't clean up the VM, because that
1006 # VM may have migrated to a different domain on this
1007 # machine.
1008 self.cleanupDomain()
1009 self._stateSet(DOM_STATE_HALTED)
1010 return
1012 if xeninfo['dying']:
1013 # Dying means that a domain has been destroyed, but has not
1014 # yet been cleaned up by Xen. This state could persist
1015 # indefinitely if, for example, another domain has some of its
1016 # pages mapped. We might like to diagnose this problem in the
1017 # future, but for now all we do is make sure that it's not us
1018 # holding the pages, by calling cleanupDomain. We can't
1019 # clean up the VM, as above.
1020 self.cleanupDomain()
1021 self._stateSet(DOM_STATE_SHUTDOWN)
1022 return
1024 elif xeninfo['crashed']:
1025 if self.readDom('xend/shutdown_completed'):
1026 # We've seen this shutdown already, but we are preserving
1027 # the domain for debugging. Leave it alone.
1028 return
1030 log.warn('Domain has crashed: name=%s id=%d.',
1031 self.info['name_label'], self.domid)
1032 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1034 if xoptions.get_enable_dump():
1035 try:
1036 self.dumpCore()
1037 except XendError:
1038 # This error has been logged -- there's nothing more
1039 # we can do in this context.
1040 pass
1042 restart_reason = 'crash'
1043 self._stateSet(DOM_STATE_HALTED)
1045 elif xeninfo['shutdown']:
1046 self._stateSet(DOM_STATE_SHUTDOWN)
1047 if self.readDom('xend/shutdown_completed'):
1048 # We've seen this shutdown already, but we are preserving
1049 # the domain for debugging. Leave it alone.
1050 return
1052 else:
1053 reason = shutdown_reason(xeninfo['shutdown_reason'])
1055 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1056 self.info['name_label'], self.domid, reason)
1057 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1059 self._clearRestart()
1061 if reason == 'suspend':
1062 self._stateSet(DOM_STATE_SUSPENDED)
1063 # Don't destroy the domain. XendCheckpoint will do
1064 # this once it has finished. However, stop watching
1065 # the VM path now, otherwise we will end up with one
1066 # watch for the old domain, and one for the new.
1067 self._unwatchVm()
1068 elif reason in ('poweroff', 'reboot'):
1069 restart_reason = reason
1070 else:
1071 self.destroy()
1073 elif self.dompath is None:
1074 # We have yet to manage to call introduceDomain on this
1075 # domain. This can happen if a restore is in progress, or has
1076 # failed. Ignore this domain.
1077 pass
1078 else:
1079 # Domain is alive. If we are shutting it down, then check
1080 # the timeout on that, and destroy it if necessary.
1081 if xeninfo['paused']:
1082 self._stateSet(DOM_STATE_PAUSED)
1083 else:
1084 self._stateSet(DOM_STATE_RUNNING)
1086 if self.shutdownStartTime:
1087 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1088 self.shutdownStartTime)
1089 if timeout < 0:
1090 log.info(
1091 "Domain shutdown timeout expired: name=%s id=%s",
1092 self.info['name_label'], self.domid)
1093 self.destroy()
1094 finally:
1095 self.refresh_shutdown_lock.release()
1097 if restart_reason:
1098 threading.Thread(target = self._maybeRestart,
1099 args = (restart_reason,)).start()
1103 # Restart functions - handling whether we come back up on shutdown.
1106 def _clearRestart(self):
1107 self._removeDom("xend/shutdown_start_time")
1110 def _maybeRestart(self, reason):
1111 # Dispatch to the correct method based upon the configured on_{reason}
1112 # behaviour.
1113 actions = {"destroy" : self.destroy,
1114 "restart" : self._restart,
1115 "preserve" : self._preserve,
1116 "rename-restart" : self._renameRestart}
1118 action_conf = {
1119 'poweroff': 'actions_after_shutdown',
1120 'reboot': 'actions_after_reboot',
1121 'crash': 'actions_after_crash',
1124 action_target = self.info.get(action_conf.get(reason))
1125 func = actions.get(action_target, None)
1126 if func and callable(func):
1127 func()
1128 else:
1129 self.destroy() # default to destroy
1131 def _renameRestart(self):
1132 self._restart(True)
1134 def _restart(self, rename = False):
1135 """Restart the domain after it has exited.
1137 @param rename True if the old domain is to be renamed and preserved,
1138 False if it is to be destroyed.
1139 """
1140 from xen.xend import XendDomain
1142 if self._readVm(RESTART_IN_PROGRESS):
1143 log.error('Xend failed during restart of domain %s. '
1144 'Refusing to restart to avoid loops.',
1145 str(self.domid))
1146 self.destroy()
1147 return
1149 old_domid = self.domid
1150 self._writeVm(RESTART_IN_PROGRESS, 'True')
1152 now = time.time()
1153 rst = self._readVm('xend/previous_restart_time')
1154 if rst:
1155 rst = float(rst)
1156 timeout = now - rst
1157 if timeout < MINIMUM_RESTART_TIME:
1158 log.error(
1159 'VM %s restarting too fast (%f seconds since the last '
1160 'restart). Refusing to restart to avoid loops.',
1161 self.info['name_label'], timeout)
1162 self.destroy()
1163 return
1165 self._writeVm('xend/previous_restart_time', str(now))
1167 try:
1168 if rename:
1169 self._preserveForRestart()
1170 else:
1171 self._unwatchVm()
1172 self.destroyDomain()
1174 # new_dom's VM will be the same as this domain's VM, except where
1175 # the rename flag has instructed us to call preserveForRestart.
1176 # In that case, it is important that we remove the
1177 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1178 # once the new one is available.
1180 new_dom = None
1181 try:
1182 new_dom = XendDomain.instance().domain_create_from_dict(
1183 self.info)
1184 new_dom.unpause()
1185 rst_cnt = self._readVm('xend/restart_count')
1186 rst_cnt = int(rst_cnt) + 1
1187 self._writeVm('xend/restart_count', str(rst_cnt))
1188 new_dom._removeVm(RESTART_IN_PROGRESS)
1189 except:
1190 if new_dom:
1191 new_dom._removeVm(RESTART_IN_PROGRESS)
1192 new_dom.destroy()
1193 else:
1194 self._removeVm(RESTART_IN_PROGRESS)
1195 raise
1196 except:
1197 log.exception('Failed to restart domain %s.', str(old_domid))
1199 def _preserveForRestart(self):
1200 """Preserve a domain that has been shut down, by giving it a new UUID,
1201 cloning the VM details, and giving it a new name. This allows us to
1202 keep this domain for debugging, but restart a new one in its place
1203 preserving the restart semantics (name and UUID preserved).
1204 """
1206 new_uuid = uuid.createString()
1207 new_name = 'Domain-%s' % new_uuid
1208 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1209 self.info['name_label'], self.domid, self.info['uuid'],
1210 new_name, new_uuid)
1211 self._unwatchVm()
1212 self._releaseDevices()
1213 self.info['name_label'] = new_name
1214 self.info['uuid'] = new_uuid
1215 self.vmpath = XS_VMROOT + new_uuid
1216 self._storeVmDetails()
1217 self._preserve()
1220 def _preserve(self):
1221 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1222 self.domid)
1223 self._unwatchVm()
1224 self.storeDom('xend/shutdown_completed', 'True')
1225 self._stateSet(DOM_STATE_HALTED)
1228 # Debugging ..
1231 def dumpCore(self, corefile = None):
1232 """Create a core dump for this domain.
1234 @raise: XendError if core dumping failed.
1235 """
1237 try:
1238 if not corefile:
1239 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1240 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1241 self.info['name_label'], self.domid)
1243 if os.path.isdir(corefile):
1244 raise XendError("Cannot dump core in a directory: %s" %
1245 corefile)
1247 xc.domain_dumpcore(self.domid, corefile)
1248 except RuntimeError, ex:
1249 corefile_incomp = corefile+'-incomplete'
1250 os.rename(corefile, corefile_incomp)
1251 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1252 self.domid, self.info['name_label'])
1253 raise XendError("Failed to dump core: %s" % str(ex))
1256 # Device creation/deletion functions
1259 def _createDevice(self, deviceClass, devConfig):
1260 return self.getDeviceController(deviceClass).createDevice(devConfig)
1262 def _waitForDevice(self, deviceClass, devid):
1263 return self.getDeviceController(deviceClass).waitForDevice(devid)
1265 def _waitForDeviceUUID(self, dev_uuid):
1266 deviceClass, config = self.info['devices'].get(dev_uuid)
1267 self._waitForDevice(deviceClass, config['devid'])
1269 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1270 return self.getDeviceController(deviceClass).reconfigureDevice(
1271 devid, devconfig)
1273 def _createDevices(self):
1274 """Create the devices for a vm.
1276 @raise: VmError for invalid devices
1277 """
1278 ordered_refs = self.info.ordered_device_refs()
1279 for dev_uuid in ordered_refs:
1280 devclass, config = self.info['devices'][dev_uuid]
1281 if devclass in XendDevices.valid_devices():
1282 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1283 dev_uuid = config.get('uuid')
1284 devid = self._createDevice(devclass, config)
1286 # store devid in XendConfig for caching reasons
1287 if dev_uuid in self.info['devices']:
1288 self.info['devices'][dev_uuid][1]['devid'] = devid
1290 if self.image:
1291 self.image.createDeviceModel()
1293 def _releaseDevices(self, suspend = False):
1294 """Release all domain's devices. Nothrow guarantee."""
1295 if suspend and self.image:
1296 self.image.destroy(suspend)
1297 return
1299 while True:
1300 t = xstransact("%s/device" % self.dompath)
1301 for devclass in XendDevices.valid_devices():
1302 for dev in t.list(devclass):
1303 try:
1304 t.remove(dev)
1305 except:
1306 # Log and swallow any exceptions in removal --
1307 # there's nothing more we can do.
1308 log.exception(
1309 "Device release failed: %s; %s; %s",
1310 self.info['name_label'], devclass, dev)
1311 if t.commit():
1312 break
1314 def getDeviceController(self, name):
1315 """Get the device controller for this domain, and if it
1316 doesn't exist, create it.
1318 @param name: device class name
1319 @type name: string
1320 @rtype: subclass of DevController
1321 """
1322 if name not in self._deviceControllers:
1323 devController = XendDevices.make_controller(name, self)
1324 if not devController:
1325 raise XendError("Unknown device type: %s" % name)
1326 self._deviceControllers[name] = devController
1328 return self._deviceControllers[name]
1331 # Migration functions (public)
1334 def testMigrateDevices(self, network, dst):
1335 """ Notify all device about intention of migration
1336 @raise: XendError for a device that cannot be migrated
1337 """
1338 for (n, c) in self.info.all_devices_sxpr():
1339 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1340 if rc != 0:
1341 raise XendError("Device of type '%s' refuses migration." % n)
1343 def migrateDevices(self, network, dst, step, domName=''):
1344 """Notify the devices about migration
1345 """
1346 ctr = 0
1347 try:
1348 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1349 self.migrateDevice(dev_type, dev_conf, network, dst,
1350 step, domName)
1351 ctr = ctr + 1
1352 except:
1353 for dev_type, dev_conf in self.info.all_devices_sxpr():
1354 if ctr == 0:
1355 step = step - 1
1356 ctr = ctr - 1
1357 self._recoverMigrateDevice(dev_type, dev_conf, network,
1358 dst, step, domName)
1359 raise
1361 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1362 step, domName=''):
1363 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1364 network, dst, step, domName)
1366 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1367 dst, step, domName=''):
1368 return self.getDeviceController(deviceClass).recover_migrate(
1369 deviceConfig, network, dst, step, domName)
1372 ## private:
1374 def _constructDomain(self):
1375 """Construct the domain.
1377 @raise: VmError on error
1378 """
1380 log.debug('XendDomainInfo.constructDomain')
1382 self.shutdownStartTime = None
1384 hvm = self.info.is_hvm()
1385 if hvm:
1386 info = xc.xeninfo()
1387 if 'hvm' not in info['xen_caps']:
1388 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1389 "supported by your CPU and enabled in your "
1390 "BIOS?")
1392 self.domid = xc.domain_create(
1393 domid = 0,
1394 ssidref = security.get_security_info(self.info, 'ssidref'),
1395 handle = uuid.fromString(self.info['uuid']),
1396 hvm = int(hvm))
1398 if self.domid < 0:
1399 raise VmError('Creating domain failed: name=%s' %
1400 self.info['name_label'])
1402 self.dompath = GetDomainPath(self.domid)
1404 self._recreateDom()
1406 # Set maximum number of vcpus in domain
1407 xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
1409 # register the domain in the list
1410 from xen.xend import XendDomain
1411 XendDomain.instance().add_domain(self)
1413 def _introduceDomain(self):
1414 assert self.domid is not None
1415 assert self.store_mfn is not None
1416 assert self.store_port is not None
1418 try:
1419 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1420 except RuntimeError, exn:
1421 raise XendError(str(exn))
1424 def _initDomain(self):
1425 log.debug('XendDomainInfo.initDomain: %s %s',
1426 self.domid,
1427 self.info['cpu_weight'])
1429 self._configureBootloader()
1431 try:
1432 self.image = image.create(self, self.info)
1434 localtime = self.info.get('platform_localtime', False)
1435 if localtime:
1436 xc.domain_set_time_offset(self.domid)
1438 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1440 # repin domain vcpus if a restricted cpus list is provided
1441 # this is done prior to memory allocation to aide in memory
1442 # distribution for NUMA systems.
1443 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1444 for v in range(0, self.info['vcpus_number']):
1445 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1447 # Use architecture- and image-specific calculations to determine
1448 # the various headrooms necessary, given the raw configured
1449 # values. maxmem, memory, and shadow are all in KiB.
1450 memory = self.image.getRequiredAvailableMemory(
1451 self.info['memory_static_min'] * 1024)
1452 maxmem = self.image.getRequiredAvailableMemory(
1453 self.info['memory_static_max'] * 1024)
1454 shadow = self.image.getRequiredShadowMemory(
1455 self.info['shadow_memory'] * 1024,
1456 self.info['memory_static_max'] * 1024)
1458 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'],)
1459 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1460 # takes MiB and we must not round down and end up under-providing.
1461 shadow = ((shadow + 1023) / 1024) * 1024
1463 # set memory limit
1464 xc.domain_setmaxmem(self.domid, maxmem)
1466 # Make sure there's enough RAM available for the domain
1467 balloon.free(memory + shadow)
1469 # Set up the shadow memory
1470 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1471 self.info['shadow_memory'] = shadow_cur
1473 self._createChannels()
1475 channel_details = self.image.createImage()
1477 self.store_mfn = channel_details['store_mfn']
1478 if 'console_mfn' in channel_details:
1479 self.console_mfn = channel_details['console_mfn']
1480 if 'notes' in channel_details:
1481 self.info.set_notes(channel_details['notes'])
1483 self._introduceDomain()
1485 self._createDevices()
1487 self.image.cleanupBootloading()
1489 self.info['start_time'] = time.time()
1491 self._stateSet(DOM_STATE_RUNNING)
1492 except VmError, exn:
1493 log.exception("XendDomainInfo.initDomain: exception occurred")
1494 if self.image:
1495 self.image.cleanupBootloading()
1496 raise exn
1497 except RuntimeError, exn:
1498 log.exception("XendDomainInfo.initDomain: exception occurred")
1499 if self.image:
1500 self.image.cleanupBootloading()
1501 raise VmError(str(exn))
1504 def cleanupDomain(self):
1505 """Cleanup domain resources; release devices. Idempotent. Nothrow
1506 guarantee."""
1508 self.refresh_shutdown_lock.acquire()
1509 try:
1510 self.unwatchShutdown()
1511 self._releaseDevices()
1512 bootloader_tidy(self)
1514 if self.image:
1515 try:
1516 self.image.destroy()
1517 except:
1518 log.exception(
1519 "XendDomainInfo.cleanup: image.destroy() failed.")
1520 self.image = None
1522 try:
1523 self._removeDom()
1524 except:
1525 log.exception("Removing domain path failed.")
1527 self._stateSet(DOM_STATE_HALTED)
1528 finally:
1529 self.refresh_shutdown_lock.release()
1532 def unwatchShutdown(self):
1533 """Remove the watch on the domain's control/shutdown node, if any.
1534 Idempotent. Nothrow guarantee. Expects to be protected by the
1535 refresh_shutdown_lock."""
1537 try:
1538 try:
1539 if self.shutdownWatch:
1540 self.shutdownWatch.unwatch()
1541 finally:
1542 self.shutdownWatch = None
1543 except:
1544 log.exception("Unwatching control/shutdown failed.")
1546 def waitForShutdown(self):
1547 self.state_updated.acquire()
1548 try:
1549 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1550 self.state_updated.wait()
1551 finally:
1552 self.state_updated.release()
1556 # TODO: recategorise - called from XendCheckpoint
1559 def completeRestore(self, store_mfn, console_mfn):
1561 log.debug("XendDomainInfo.completeRestore")
1563 self.store_mfn = store_mfn
1564 self.console_mfn = console_mfn
1566 self._introduceDomain()
1567 if self.info.has_hvm():
1568 self.image = image.create(self, self.info)
1569 if self.image:
1570 self.image.createDeviceModel(True)
1571 self.image.register_shutdown_watch()
1572 self._storeDomDetails()
1573 self._registerWatches()
1574 self.refreshShutdown()
1576 log.debug("XendDomainInfo.completeRestore done")
1579 def _endRestore(self):
1580 self.setResume(False)
1583 # VM Destroy
1586 def _prepare_phantom_paths(self):
1587 # get associated devices to destroy
1588 # build list of phantom devices to be removed after normal devices
1589 plist = []
1590 if self.domid is not None:
1591 from xen.xend.xenstore.xstransact import xstransact
1592 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1593 for dev in t.list():
1594 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1595 % (self.dompath, dev))
1596 if backend_phantom_vbd is not None:
1597 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1598 % backend_phantom_vbd)
1599 plist.append(backend_phantom_vbd)
1600 plist.append(frontend_phantom_vbd)
1601 return plist
1603 def _cleanup_phantom_devs(self, plist):
1604 # remove phantom devices
1605 if not plist == []:
1606 time.sleep(2)
1607 for paths in plist:
1608 if paths.find('backend') != -1:
1609 from xen.xend.server import DevController
1610 # Modify online status /before/ updating state (latter is watched by
1611 # drivers, so this ordering avoids a race).
1612 xstransact.Write(paths, 'online', "0")
1613 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1614 # force
1615 xstransact.Remove(paths)
1617 def destroy(self):
1618 """Cleanup VM and destroy domain. Nothrow guarantee."""
1620 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1622 paths = self._prepare_phantom_paths()
1624 self._cleanupVm()
1625 if self.dompath is not None:
1626 self.destroyDomain()
1628 self._cleanup_phantom_devs(paths)
1630 def destroyDomain(self):
1631 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1633 paths = self._prepare_phantom_paths()
1635 try:
1636 if self.domid is not None:
1637 xc.domain_destroy(self.domid)
1638 self.domid = None
1639 for state in DOM_STATES_OLD:
1640 self.info[state] = 0
1641 except:
1642 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1644 from xen.xend import XendDomain
1645 XendDomain.instance().remove_domain(self)
1647 self.cleanupDomain()
1648 self._cleanup_phantom_devs(paths)
1650 def resumeDomain(self):
1651 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1653 if self.domid is None:
1654 return
1655 try:
1656 # could also fetch a parsed note from xenstore
1657 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1658 if not fast:
1659 self._releaseDevices()
1660 self.testDeviceComplete()
1661 self.testvifsComplete()
1662 log.debug("XendDomainInfo.resumeDomain: devices released")
1664 self._resetChannels()
1666 self._removeDom('control/shutdown')
1667 self._removeDom('device-misc/vif/nextDeviceID')
1669 self._createChannels()
1670 self._introduceDomain()
1671 self._storeDomDetails()
1673 self._createDevices()
1674 log.debug("XendDomainInfo.resumeDomain: devices created")
1676 xc.domain_resume(self.domid, fast)
1677 ResumeDomain(self.domid)
1678 except:
1679 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1682 # Channels for xenstore and console
1685 def _createChannels(self):
1686 """Create the channels to the domain.
1687 """
1688 self.store_port = self._createChannel()
1689 self.console_port = self._createChannel()
1692 def _createChannel(self):
1693 """Create an event channel to the domain.
1694 """
1695 try:
1696 if self.domid != None:
1697 return xc.evtchn_alloc_unbound(domid = self.domid,
1698 remote_dom = 0)
1699 except:
1700 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1701 raise
1703 def _resetChannels(self):
1704 """Reset all event channels in the domain.
1705 """
1706 try:
1707 if self.domid != None:
1708 return xc.evtchn_reset(dom = self.domid)
1709 except:
1710 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1711 raise
1715 # Bootloader configuration
1718 def _configureBootloader(self):
1719 """Run the bootloader if we're configured to do so."""
1721 blexec = self.info['PV_bootloader']
1722 bootloader_args = self.info['PV_bootloader_args']
1723 kernel = self.info['PV_kernel']
1724 ramdisk = self.info['PV_ramdisk']
1725 args = self.info['PV_args']
1726 boot = self.info['HVM_boot_policy']
1728 if boot:
1729 # HVM booting.
1730 pass
1731 elif not blexec and kernel:
1732 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1733 # will be picked up by image.py.
1734 pass
1735 else:
1736 # Boot using bootloader
1737 if not blexec or blexec == 'pygrub':
1738 blexec = osdep.pygrub_path
1740 blcfg = None
1741 disks = [x for x in self.info['vbd_refs']
1742 if self.info['devices'][x][1]['bootable']]
1744 if not disks:
1745 msg = "Had a bootloader specified, but no disks are bootable"
1746 log.error(msg)
1747 raise VmError(msg)
1749 devinfo = self.info['devices'][disks[0]]
1750 devtype = devinfo[0]
1751 disk = devinfo[1]['uname']
1753 fn = blkdev_uname_to_file(disk)
1754 mounted = devtype == 'tap' and not os.stat(fn).st_rdev
1755 if mounted:
1756 # This is a file, not a device. pygrub can cope with a
1757 # file if it's raw, but if it's QCOW or other such formats
1758 # used through blktap, then we need to mount it first.
1760 log.info("Mounting %s on %s." %
1761 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1763 vbd = {
1764 'mode': 'RO',
1765 'device': BOOTLOADER_LOOPBACK_DEVICE,
1768 from xen.xend import XendDomain
1769 dom0 = XendDomain.instance().privilegedDomain()
1770 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
1771 fn = BOOTLOADER_LOOPBACK_DEVICE
1773 try:
1774 blcfg = bootloader(blexec, fn, self, False,
1775 bootloader_args, kernel, ramdisk, args)
1776 finally:
1777 if mounted:
1778 log.info("Unmounting %s from %s." %
1779 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1781 dom0.destroyDevice('tap', '/dev/xvdp')
1783 if blcfg is None:
1784 msg = "Had a bootloader specified, but can't find disk"
1785 log.error(msg)
1786 raise VmError(msg)
1788 self.info.update_with_image_sxp(blcfg, True)
1792 # VM Functions
1795 def _readVMDetails(self, params):
1796 """Read the specified parameters from the store.
1797 """
1798 try:
1799 return self._gatherVm(*params)
1800 except ValueError:
1801 # One of the int/float entries in params has a corresponding store
1802 # entry that is invalid. We recover, because older versions of
1803 # Xend may have put the entry there (memory/target, for example),
1804 # but this is in general a bad situation to have reached.
1805 log.exception(
1806 "Store corrupted at %s! Domain %d's configuration may be "
1807 "affected.", self.vmpath, self.domid)
1808 return []
1810 def _cleanupVm(self):
1811 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1813 self._unwatchVm()
1815 try:
1816 self._removeVm()
1817 except:
1818 log.exception("Removing VM path failed.")
1821 def checkLiveMigrateMemory(self):
1822 """ Make sure there's enough memory to migrate this domain """
1823 overhead_kb = 0
1824 if arch.type == "x86":
1825 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1826 # the minimum that Xen would allocate if no value were given.
1827 overhead_kb = self.info['vcpus_number'] * 1024 + \
1828 self.info['memory_static_max'] * 4
1829 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1830 # The domain might already have some shadow memory
1831 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1832 if overhead_kb > 0:
1833 balloon.free(overhead_kb)
1835 def _unwatchVm(self):
1836 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1837 guarantee."""
1838 try:
1839 try:
1840 if self.vmWatch:
1841 self.vmWatch.unwatch()
1842 finally:
1843 self.vmWatch = None
1844 except:
1845 log.exception("Unwatching VM path failed.")
1847 def testDeviceComplete(self):
1848 """ For Block IO migration safety we must ensure that
1849 the device has shutdown correctly, i.e. all blocks are
1850 flushed to disk
1851 """
1852 start = time.time()
1853 while True:
1854 test = 0
1855 diff = time.time() - start
1856 for i in self.getDeviceController('vbd').deviceIDs():
1857 test = 1
1858 log.info("Dev %s still active, looping...", i)
1859 time.sleep(0.1)
1861 if test == 0:
1862 break
1863 if diff >= MIGRATE_TIMEOUT:
1864 log.info("Dev still active but hit max loop timeout")
1865 break
1867 def testvifsComplete(self):
1868 """ In case vifs are released and then created for the same
1869 domain, we need to wait the device shut down.
1870 """
1871 start = time.time()
1872 while True:
1873 test = 0
1874 diff = time.time() - start
1875 for i in self.getDeviceController('vif').deviceIDs():
1876 test = 1
1877 log.info("Dev %s still active, looping...", i)
1878 time.sleep(0.1)
1880 if test == 0:
1881 break
1882 if diff >= MIGRATE_TIMEOUT:
1883 log.info("Dev still active but hit max loop timeout")
1884 break
1886 def _storeVmDetails(self):
1887 to_store = {}
1889 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1890 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1891 if self._infoIsSet(info_key):
1892 to_store[key] = str(self.info[info_key])
1894 image_sxpr = self.info.image_sxpr()
1895 if image_sxpr:
1896 to_store['image'] = sxp.to_string(image_sxpr)
1898 if self._infoIsSet('security'):
1899 secinfo = self.info['security']
1900 to_store['security'] = sxp.to_string(secinfo)
1901 for idx in range(0, len(secinfo)):
1902 if secinfo[idx][0] == 'access_control':
1903 to_store['security/access_control'] = sxp.to_string(
1904 [secinfo[idx][1], secinfo[idx][2]])
1905 for aidx in range(1, len(secinfo[idx])):
1906 if secinfo[idx][aidx][0] == 'label':
1907 to_store['security/access_control/label'] = \
1908 secinfo[idx][aidx][1]
1909 if secinfo[idx][aidx][0] == 'policy':
1910 to_store['security/access_control/policy'] = \
1911 secinfo[idx][aidx][1]
1912 if secinfo[idx][0] == 'ssidref':
1913 to_store['security/ssidref'] = str(secinfo[idx][1])
1916 if not self._readVm('xend/restart_count'):
1917 to_store['xend/restart_count'] = str(0)
1919 log.debug("Storing VM details: %s", scrub_password(to_store))
1921 self._writeVm(to_store)
1922 self._setVmPermissions()
1925 def _setVmPermissions(self):
1926 """Allow the guest domain to read its UUID. We don't allow it to
1927 access any other entry, for security."""
1928 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1929 { 'dom' : self.domid,
1930 'read' : True,
1931 'write' : False })
1934 # Utility functions
1937 def _stateSet(self, state):
1938 self.state_updated.acquire()
1939 try:
1940 if self.state != state:
1941 self.state = state
1942 self.state_updated.notifyAll()
1943 finally:
1944 self.state_updated.release()
1946 def _infoIsSet(self, name):
1947 return name in self.info and self.info[name] is not None
1949 def _checkName(self, name):
1950 """Check if a vm name is valid. Valid names contain alphabetic
1951 characters, digits, or characters in '_-.:/+'.
1952 The same name cannot be used for more than one vm at the same time.
1954 @param name: name
1955 @raise: VmError if invalid
1956 """
1957 from xen.xend import XendDomain
1959 if name is None or name == '':
1960 raise VmError('Missing VM Name')
1962 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
1963 raise VmError('Invalid VM Name')
1965 dom = XendDomain.instance().domain_lookup_nr(name)
1966 if dom and dom.info['uuid'] != self.info['uuid']:
1967 raise VmError("VM name '%s' already exists%s" %
1968 (name,
1969 dom.domid is not None and
1970 (" as domain %s" % str(dom.domid)) or ""))
1973 def update(self, info = None, refresh = True):
1974 """Update with info from xc.domain_getinfo().
1975 """
1976 log.trace("XendDomainInfo.update(%s) on domain %s", info,
1977 str(self.domid))
1979 if not info:
1980 info = dom_get(self.domid)
1981 if not info:
1982 return
1984 #manually update ssidref / security fields
1985 if security.on() and info.has_key('ssidref'):
1986 if (info['ssidref'] != 0) and self.info.has_key('security'):
1987 security_field = self.info['security']
1988 if not security_field:
1989 #create new security element
1990 self.info.update({'security':
1991 [['ssidref', str(info['ssidref'])]]})
1993 #ssidref field not used any longer
1994 if 'ssidref' in info:
1995 info.pop('ssidref')
1997 # make sure state is reset for info
1998 # TODO: we should eventually get rid of old_dom_states
2000 self.info.update_config(info)
2001 self._update_consoles()
2003 if refresh:
2004 self.refreshShutdown(info)
2006 log.trace("XendDomainInfo.update done on domain %s: %s",
2007 str(self.domid), self.info)
2009 def sxpr(self, ignore_store = False, legacy_only = True):
2010 result = self.info.to_sxp(domain = self,
2011 ignore_devices = ignore_store,
2012 legacy_only = legacy_only)
2014 #if not ignore_store and self.dompath:
2015 # vnc_port = self.readDom('console/vnc-port')
2016 # if vnc_port is not None:
2017 # result.append(['device',
2018 # ['console', ['vnc-port', str(vnc_port)]]])
2020 return result
2022 # Xen API
2023 # ----------------------------------------------------------------
2025 def get_uuid(self):
2026 dom_uuid = self.info.get('uuid')
2027 if not dom_uuid: # if it doesn't exist, make one up
2028 dom_uuid = uuid.createString()
2029 self.info['uuid'] = dom_uuid
2030 return dom_uuid
2032 def get_memory_static_max(self):
2033 return self.info.get('memory_static_max', 0)
2034 def get_memory_static_min(self):
2035 return self.info.get('memory_static_min', 0)
2036 def get_memory_dynamic_max(self):
2037 return self.info.get('memory_dynamic_max', 0)
2038 def get_memory_dynamic_min(self):
2039 return self.info.get('memory_dynamic_min', 0)
2041 def get_vcpus_policy(self):
2042 sched_id = xc.sched_id_get()
2043 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
2044 return 'sedf'
2045 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
2046 return 'credit'
2047 else:
2048 return 'unknown'
2049 def get_vcpus_params(self):
2050 if self.getDomid() is None:
2051 return self.info['vcpus_params']
2053 retval = xc.sched_credit_domain_get(self.getDomid())
2054 return retval
2055 def get_power_state(self):
2056 return XEN_API_VM_POWER_STATE[self.state]
2057 def get_platform(self):
2058 return self.info.get('platform', {})
2059 def get_pci_bus(self):
2060 return self.info.get('pci_bus', '')
2061 def get_tools_version(self):
2062 return self.info.get('tools_version', {})
2064 def get_on_shutdown(self):
2065 after_shutdown = self.info.get('actions_after_shutdown')
2066 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2067 return XEN_API_ON_NORMAL_EXIT[-1]
2068 return after_shutdown
2070 def get_on_reboot(self):
2071 after_reboot = self.info.get('actions_after_reboot')
2072 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2073 return XEN_API_ON_NORMAL_EXIT[-1]
2074 return after_reboot
2076 def get_on_suspend(self):
2077 # TODO: not supported
2078 after_suspend = self.info.get('actions_after_suspend')
2079 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2080 return XEN_API_ON_NORMAL_EXIT[-1]
2081 return after_suspend
2083 def get_on_crash(self):
2084 after_crash = self.info.get('actions_after_crash')
2085 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2086 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2087 return after_crash
2089 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2090 """ Get's a device configuration either from XendConfig or
2091 from the DevController.
2093 @param dev_class: device class, either, 'vbd' or 'vif'
2094 @param dev_uuid: device UUID
2096 @rtype: dictionary
2097 """
2098 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2100 # shortcut if the domain isn't started because
2101 # the devcontrollers will have no better information
2102 # than XendConfig.
2103 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2104 if dev_config:
2105 return copy.deepcopy(dev_config)
2106 return None
2108 # instead of using dev_class, we use the dev_type
2109 # that is from XendConfig.
2110 controller = self.getDeviceController(dev_type)
2111 if not controller:
2112 return None
2114 all_configs = controller.getAllDeviceConfigurations()
2115 if not all_configs:
2116 return None
2118 updated_dev_config = copy.deepcopy(dev_config)
2119 for _devid, _devcfg in all_configs.items():
2120 if _devcfg.get('uuid') == dev_uuid:
2121 updated_dev_config.update(_devcfg)
2122 updated_dev_config['id'] = _devid
2123 return updated_dev_config
2125 return updated_dev_config
2127 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2128 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2129 if not config:
2130 return {}
2132 config['VM'] = self.get_uuid()
2134 if dev_class == 'vif':
2135 if not config.has_key('name'):
2136 config['name'] = config.get('vifname', '')
2137 if not config.has_key('MAC'):
2138 config['MAC'] = config.get('mac', '')
2139 if not config.has_key('type'):
2140 config['type'] = 'paravirtualised'
2141 if not config.has_key('device'):
2142 devid = config.get('id')
2143 if devid != None:
2144 config['device'] = 'eth%d' % devid
2145 else:
2146 config['device'] = ''
2148 if not config.has_key('network'):
2149 try:
2150 config['network'] = \
2151 XendNode.instance().bridge_to_network(
2152 config.get('bridge')).uuid
2153 except Exception:
2154 log.exception('bridge_to_network')
2155 # Ignore this for now -- it may happen if the device
2156 # has been specified using the legacy methods, but at
2157 # some point we're going to have to figure out how to
2158 # handle that properly.
2160 config['MTU'] = 1500 # TODO
2162 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2163 xennode = XendNode.instance()
2164 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2165 config['io_read_kbs'] = rx_bps/1024
2166 config['io_write_kbs'] = tx_bps/1024
2167 else:
2168 config['io_read_kbs'] = 0.0
2169 config['io_write_kbs'] = 0.0
2171 if dev_class == 'vbd':
2173 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2174 controller = self.getDeviceController(dev_class)
2175 devid, _1, _2 = controller.getDeviceDetails(config)
2176 xennode = XendNode.instance()
2177 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2178 config['io_read_kbs'] = rd_blkps
2179 config['io_write_kbs'] = wr_blkps
2180 else:
2181 config['io_read_kbs'] = 0.0
2182 config['io_write_kbs'] = 0.0
2184 config['VDI'] = config.get('VDI', '')
2185 config['device'] = config.get('dev', '')
2186 if ':' in config['device']:
2187 vbd_name, vbd_type = config['device'].split(':', 1)
2188 config['device'] = vbd_name
2189 if vbd_type == 'cdrom':
2190 config['type'] = XEN_API_VBD_TYPE[0]
2191 else:
2192 config['type'] = XEN_API_VBD_TYPE[1]
2194 config['driver'] = 'paravirtualised' # TODO
2195 config['image'] = config.get('uname', '')
2197 if config.get('mode', 'r') == 'r':
2198 config['mode'] = 'RO'
2199 else:
2200 config['mode'] = 'RW'
2202 if dev_class == 'vtpm':
2203 if not config.has_key('type'):
2204 config['type'] = 'paravirtualised' # TODO
2205 if not config.has_key('backend'):
2206 config['backend'] = "00000000-0000-0000-0000-000000000000"
2208 return config
2210 def get_dev_property(self, dev_class, dev_uuid, field):
2211 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2212 try:
2213 return config[field]
2214 except KeyError:
2215 raise XendError('Invalid property for device: %s' % field)
2217 def set_dev_property(self, dev_class, dev_uuid, field, value):
2218 self.info['devices'][dev_uuid][1][field] = value
2220 def get_vcpus_util(self):
2221 vcpu_util = {}
2222 xennode = XendNode.instance()
2223 if 'vcpus_number' in self.info and self.domid != None:
2224 for i in range(0, self.info['vcpus_number']):
2225 util = xennode.get_vcpu_util(self.domid, i)
2226 vcpu_util[str(i)] = util
2228 return vcpu_util
2230 def get_consoles(self):
2231 return self.info.get('console_refs', [])
2233 def get_vifs(self):
2234 return self.info.get('vif_refs', [])
2236 def get_vbds(self):
2237 return self.info.get('vbd_refs', [])
2239 def get_vtpms(self):
2240 return self.info.get('vtpm_refs', [])
2242 def create_vbd(self, xenapi_vbd, vdi_image_path):
2243 """Create a VBD using a VDI from XendStorageRepository.
2245 @param xenapi_vbd: vbd struct from the Xen API
2246 @param vdi_image_path: VDI UUID
2247 @rtype: string
2248 @return: uuid of the device
2249 """
2250 xenapi_vbd['image'] = vdi_image_path
2251 log.debug('create_vbd: %s' % xenapi_vbd)
2252 dev_uuid = ''
2253 if vdi_image_path.startswith('tap'):
2254 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2255 else:
2256 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2258 if not dev_uuid:
2259 raise XendError('Failed to create device')
2261 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2262 _, config = self.info['devices'][dev_uuid]
2263 dev_control = None
2265 if vdi_image_path.startswith('tap'):
2266 dev_control = self.getDeviceController('tap')
2267 else:
2268 dev_control = self.getDeviceController('vbd')
2270 config['devid'] = dev_control.createDevice(config)
2272 return dev_uuid
2274 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2275 """Create a VBD using a VDI from XendStorageRepository.
2277 @param xenapi_vbd: vbd struct from the Xen API
2278 @param vdi_image_path: VDI UUID
2279 @rtype: string
2280 @return: uuid of the device
2281 """
2282 xenapi_vbd['image'] = vdi_image_path
2283 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2284 if not dev_uuid:
2285 raise XendError('Failed to create device')
2287 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2288 _, config = self.info['devices'][dev_uuid]
2289 config['devid'] = self.getDeviceController('tap').createDevice(config)
2291 return config['devid']
2293 def create_vif(self, xenapi_vif):
2294 """Create VIF device from the passed struct in Xen API format.
2296 @param xenapi_vif: Xen API VIF Struct.
2297 @rtype: string
2298 @return: UUID
2299 """
2300 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2301 if not dev_uuid:
2302 raise XendError('Failed to create device')
2304 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2305 _, config = self.info['devices'][dev_uuid]
2306 config['devid'] = self.getDeviceController('vif').createDevice(config)
2308 return dev_uuid
2310 def create_vtpm(self, xenapi_vtpm):
2311 """Create a VTPM device from the passed struct in Xen API format.
2313 @return: uuid of the device
2314 @rtype: string
2315 """
2317 if self.state not in (DOM_STATE_HALTED,):
2318 raise VmError("Can only add vTPM to a halted domain.")
2319 if self.get_vtpms() != []:
2320 raise VmError('Domain already has a vTPM.')
2321 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2322 if not dev_uuid:
2323 raise XendError('Failed to create device')
2325 return dev_uuid
2327 def create_console(self, xenapi_console):
2328 """ Create a console device from a Xen API struct.
2330 @return: uuid of device
2331 @rtype: string
2332 """
2333 if self.state not in (DOM_STATE_HALTED,):
2334 raise VmError("Can only add console to a halted domain.")
2336 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2337 if not dev_uuid:
2338 raise XendError('Failed to create device')
2340 return dev_uuid
2342 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2343 if dev_uuid not in self.info['devices']:
2344 raise XendError('Device does not exist')
2346 try:
2347 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2348 _, config = self.info['devices'][dev_uuid]
2349 devid = config.get('devid')
2350 if devid != None:
2351 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2352 else:
2353 raise XendError('Unable to get devid for device: %s:%s' %
2354 (dev_type, dev_uuid))
2355 finally:
2356 del self.info['devices'][dev_uuid]
2357 self.info['%s_refs' % dev_type].remove(dev_uuid)
2359 def destroy_vbd(self, dev_uuid):
2360 self.destroy_device_by_uuid('vbd', dev_uuid)
2362 def destroy_vif(self, dev_uuid):
2363 self.destroy_device_by_uuid('vif', dev_uuid)
2365 def destroy_vtpm(self, dev_uuid):
2366 self.destroy_device_by_uuid('vtpm', dev_uuid)
2368 def has_device(self, dev_class, dev_uuid):
2369 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2371 def __str__(self):
2372 return '<domain id=%s name=%s memory=%s state=%s>' % \
2373 (str(self.domid), self.info['name_label'],
2374 str(self.info['memory_static_min']), DOM_STATES[self.state])
2376 __repr__ = __str__