ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 14131:64d80037e524

Save elfnotes in VM sxpr under image/notes, and load them on restore.
Signed-off-by: Brendan Cully <brendan@cs.ubc.ca>
author kfraser@localhost.localdomain
date Mon Feb 26 09:59:56 2007 +0000 (2007-02-26)
parents a20ec270998b
children 0b5da89e2b3d
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 notes: OS image notes
306 @type notes: dictionary
307 @ivar vmWatch: reference to a watch on the xenstored vmpath
308 @type vmWatch: xen.xend.xenstore.xswatch
309 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
310 @type shutdownWatch: xen.xend.xenstore.xswatch
311 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
312 @type shutdownStartTime: float or None
313 @ivar state: Domain state
314 @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
315 @ivar state_updated: lock for self.state
316 @type state_updated: threading.Condition
317 @ivar refresh_shutdown_lock: lock for polling shutdown state
318 @type refresh_shutdown_lock: threading.Condition
319 @ivar _deviceControllers: device controller cache for this domain
320 @type _deviceControllers: dict 'string' to DevControllers
321 """
323 def __init__(self, info, domid = None, dompath = None, augment = False,
324 priv = False, resume = False):
325 """Constructor for a domain
327 @param info: parsed configuration
328 @type info: dictionary
329 @keyword domid: Set initial domain id (if any)
330 @type domid: int
331 @keyword dompath: Set initial dompath (if any)
332 @type dompath: string
333 @keyword augment: Augment given info with xenstored VM info
334 @type augment: bool
335 @keyword priv: Is a privileged domain (Dom 0)
336 @type priv: bool
337 @keyword resume: Is this domain being resumed?
338 @type resume: bool
339 """
341 self.info = info
342 if domid == None:
343 self.domid = self.info.get('domid')
344 else:
345 self.domid = domid
347 #REMOVE: uuid is now generated in XendConfig
348 #if not self._infoIsSet('uuid'):
349 # self.info['uuid'] = uuid.toString(uuid.create())
351 self.vmpath = XS_VMROOT + self.info['uuid']
352 self.dompath = dompath
354 self.image = None
355 self.store_port = None
356 self.store_mfn = None
357 self.console_port = None
358 self.console_mfn = None
360 self.vmWatch = None
361 self.shutdownWatch = None
362 self.shutdownStartTime = None
363 self._resume = resume
365 self.state = DOM_STATE_HALTED
366 self.state_updated = threading.Condition()
367 self.refresh_shutdown_lock = threading.Condition()
369 self._deviceControllers = {}
371 for state in DOM_STATES_OLD:
372 self.info[state] = 0
374 if augment:
375 self._augmentInfo(priv)
377 self._checkName(self.info['name_label'])
380 #
381 # Public functions available through XMLRPC
382 #
385 def start(self, is_managed = False):
386 """Attempts to start the VM by do the appropriate
387 initialisation if it not started.
388 """
389 from xen.xend import XendDomain
391 if self.state == DOM_STATE_HALTED:
392 try:
393 XendTask.log_progress(0, 30, self._constructDomain)
394 XendTask.log_progress(31, 60, self._initDomain)
396 XendTask.log_progress(61, 70, self._storeVmDetails)
397 XendTask.log_progress(71, 80, self._storeDomDetails)
398 XendTask.log_progress(81, 90, self._registerWatches)
399 XendTask.log_progress(91, 100, self.refreshShutdown)
401 # save running configuration if XendDomains believe domain is
402 # persistent
403 if is_managed:
404 xendomains = XendDomain.instance()
405 xendomains.managed_config_save(self)
406 except:
407 log.exception('VM start failed')
408 self.destroy()
409 raise
410 else:
411 raise XendError('VM already running')
413 def resume(self):
414 """Resumes a domain that has come back from suspension."""
415 if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED):
416 try:
417 self._constructDomain()
418 self._storeVmDetails()
419 self._createDevices()
420 self._createChannels()
421 self._storeDomDetails()
422 self._endRestore()
423 except:
424 log.exception('VM resume failed')
425 raise
426 else:
427 raise XendError('VM already running')
429 def shutdown(self, reason):
430 """Shutdown a domain by signalling this via xenstored."""
431 log.debug('XendDomainInfo.shutdown(%s)', reason)
432 if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
433 raise XendError('Domain cannot be shutdown')
435 if self.domid == 0:
436 raise XendError('Domain 0 cannot be shutdown')
438 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
439 raise XendError('Invalid reason: %s' % reason)
440 self._removeVm('xend/previous_restart_time')
441 self.storeDom("control/shutdown", reason)
443 ## shutdown hypercall for hvm domain desides xenstore write
444 image_cfg = self.info.get('image', {})
445 hvm = image_cfg.has_key('hvm')
446 if hvm:
447 for code in DOMAIN_SHUTDOWN_REASONS.keys():
448 if DOMAIN_SHUTDOWN_REASONS[code] == reason:
449 break
450 xc.domain_shutdown(self.domid, code)
453 def pause(self):
454 """Pause domain
456 @raise XendError: Failed pausing a domain
457 """
458 try:
459 xc.domain_pause(self.domid)
460 self._stateSet(DOM_STATE_PAUSED)
461 except Exception, ex:
462 log.exception(ex)
463 raise XendError("Domain unable to be paused: %s" % str(ex))
465 def unpause(self):
466 """Unpause domain
468 @raise XendError: Failed unpausing a domain
469 """
470 try:
471 xc.domain_unpause(self.domid)
472 self._stateSet(DOM_STATE_RUNNING)
473 except Exception, ex:
474 log.exception(ex)
475 raise XendError("Domain unable to be unpaused: %s" % str(ex))
477 def send_sysrq(self, key):
478 """ Send a Sysrq equivalent key via xenstored."""
479 asserts.isCharConvertible(key)
480 self.storeDom("control/sysrq", '%c' % key)
482 def device_create(self, dev_config):
483 """Create a new device.
485 @param dev_config: device configuration
486 @type dev_config: SXP object (parsed config)
487 """
488 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
489 dev_type = sxp.name(dev_config)
490 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
491 dev_config_dict = self.info['devices'][dev_uuid][1]
492 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
493 dev_config_dict['devid'] = devid = \
494 self._createDevice(dev_type, dev_config_dict)
495 self._waitForDevice(dev_type, devid)
496 return self.getDeviceController(dev_type).sxpr(devid)
498 def device_configure(self, dev_sxp, devid = None):
499 """Configure an existing device.
501 @param dev_config: device configuration
502 @type dev_config: SXP object (parsed config)
503 @param devid: device id
504 @type devid: int
505 @return: Returns True if successfully updated device
506 @rtype: boolean
507 """
509 # convert device sxp to a dict
510 dev_class = sxp.name(dev_sxp)
511 dev_config = {}
512 for opt_val in dev_sxp[1:]:
513 try:
514 dev_config[opt_val[0]] = opt_val[1]
515 except IndexError:
516 pass
518 # use DevController.reconfigureDevice to change device config
519 dev_control = self.getDeviceController(dev_class)
520 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
522 # update XendConfig with new device info
523 if dev_uuid:
524 self.info.device_update(dev_uuid, dev_sxp)
526 return True
528 def waitForDevices(self):
529 """Wait for this domain's configured devices to connect.
531 @raise VmError: if any device fails to initialise.
532 """
533 for devclass in XendDevices.valid_devices():
534 self.getDeviceController(devclass).waitForDevices()
536 def destroyDevice(self, deviceClass, devid, force = False):
537 try:
538 devid = int(devid)
539 except ValueError:
540 # devid is not a number, let's search for it in xenstore.
541 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
542 for entry in xstransact.List(devicePath):
543 backend = xstransact.Read('%s/%s' % (devicePath, entry),
544 "backend")
545 devName = xstransact.Read(backend, "dev")
546 if devName == devid:
547 # We found the integer matching our devid, use it instead
548 devid = entry
549 break
551 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
553 def getDeviceSxprs(self, deviceClass):
554 if self.state == DOM_STATE_RUNNING:
555 return self.getDeviceController(deviceClass).sxprs()
556 else:
557 sxprs = []
558 dev_num = 0
559 for dev_type, dev_info in self.info.all_devices_sxpr():
560 if dev_type == deviceClass:
561 sxprs.append([dev_num, dev_info])
562 dev_num += 1
563 return sxprs
566 def setMemoryTarget(self, target):
567 """Set the memory target of this domain.
568 @param target: In MiB.
569 """
570 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
571 self.info['name_label'], self.domid, target)
573 if target <= 0:
574 raise XendError('Invalid memory size')
576 self.info['memory_static_min'] = target
577 if self.domid >= 0:
578 self.storeVm("memory", target)
579 self.storeDom("memory/target", target << 10)
580 else:
581 self.info['memory_dynamic_min'] = target
582 xen.xend.XendDomain.instance().managed_config_save(self)
584 def setMemoryMaximum(self, limit):
585 """Set the maximum memory limit of this domain
586 @param limit: In MiB.
587 """
588 log.debug("Setting memory maximum of domain %s (%d) to %d MiB.",
589 self.info['name_label'], self.domid, limit)
591 if limit <= 0:
592 raise XendError('Invalid memory size')
594 self.info['memory_static_max'] = limit
595 if self.domid >= 0:
596 maxmem = int(limit) * 1024
597 try:
598 return xc.domain_setmaxmem(self.domid, maxmem)
599 except Exception, ex:
600 raise XendError(str(ex))
601 else:
602 self.info['memory_dynamic_max'] = limit
603 xen.xend.XendDomain.instance().managed_config_save(self)
606 def getVCPUInfo(self):
607 try:
608 # We include the domain name and ID, to help xm.
609 sxpr = ['domain',
610 ['domid', self.domid],
611 ['name', self.info['name_label']],
612 ['vcpu_count', self.info['vcpus_number']]]
614 for i in range(0, self.info['vcpus_number']):
615 info = xc.vcpu_getinfo(self.domid, i)
617 sxpr.append(['vcpu',
618 ['number', i],
619 ['online', info['online']],
620 ['blocked', info['blocked']],
621 ['running', info['running']],
622 ['cpu_time', info['cpu_time'] / 1e9],
623 ['cpu', info['cpu']],
624 ['cpumap', info['cpumap']]])
626 return sxpr
628 except RuntimeError, exn:
629 raise XendError(str(exn))
631 #
632 # internal functions ... TODO: re-categorised
633 #
635 def _augmentInfo(self, priv):
636 """Augment self.info, as given to us through L{recreate}, with
637 values taken from the store. This recovers those values known
638 to xend but not to the hypervisor.
639 """
640 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
641 if priv:
642 augment_entries.remove('memory')
643 augment_entries.remove('maxmem')
644 augment_entries.remove('vcpus')
645 augment_entries.remove('vcpu_avail')
647 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
648 for k in augment_entries])
650 # make returned lists into a dictionary
651 vm_config = dict(zip(augment_entries, vm_config))
653 for arg in augment_entries:
654 xapicfg = arg
655 val = vm_config[arg]
656 if val != None:
657 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
658 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
659 self.info[xapiarg] = val
660 else:
661 self.info[arg] = val
663 # For dom0, we ignore any stored value for the vcpus fields, and
664 # read the current value from Xen instead. This allows boot-time
665 # settings to take precedence over any entries in the store.
666 if priv:
667 xeninfo = dom_get(self.domid)
668 self.info['vcpus_number'] = xeninfo['online_vcpus']
669 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
671 # read image value
672 image_sxp = self._readVm('image')
673 if image_sxp:
674 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
676 # read devices
677 devices = []
678 for devclass in XendDevices.valid_devices():
679 devconfig = self.getDeviceController(devclass).configurations()
680 if devconfig:
681 devices.extend(devconfig)
683 if not self.info['devices'] and devices is not None:
684 for device in devices:
685 self.info.device_add(device[0], cfg_sxp = device)
687 self._update_consoles()
689 def _update_consoles(self):
690 if self.domid == None or self.domid == 0:
691 return
693 # Update VT100 port if it exists
694 self.console_port = self.readDom('console/port')
695 if self.console_port is not None:
696 serial_consoles = self.info.console_get_all('vt100')
697 if not serial_consoles:
698 cfg = self.info.console_add('vt100', self.console_port)
699 self._createDevice('console', cfg)
700 else:
701 console_uuid = serial_consoles[0].get('uuid')
702 self.info.console_update(console_uuid, 'location',
703 self.console_port)
706 # Update VNC port if it exists and write to xenstore
707 vnc_port = self.readDom('console/vnc-port')
708 if vnc_port is not None:
709 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
710 if dev_type == 'vfb':
711 old_location = dev_info.get('location')
712 listen_host = dev_info.get('vnclisten', 'localhost')
713 new_location = '%s:%s' % (listen_host, str(vnc_port))
714 if old_location == new_location:
715 break
717 dev_info['location'] = new_location
718 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
719 vfb_ctrl = self.getDeviceController('vfb')
720 vfb_ctrl.reconfigureDevice(0, dev_info)
721 break
723 #
724 # Function to update xenstore /vm/*
725 #
727 def _readVm(self, *args):
728 return xstransact.Read(self.vmpath, *args)
730 def _writeVm(self, *args):
731 return xstransact.Write(self.vmpath, *args)
733 def _removeVm(self, *args):
734 return xstransact.Remove(self.vmpath, *args)
736 def _gatherVm(self, *args):
737 return xstransact.Gather(self.vmpath, *args)
739 def storeVm(self, *args):
740 return xstransact.Store(self.vmpath, *args)
742 #
743 # Function to update xenstore /dom/*
744 #
746 def readDom(self, *args):
747 return xstransact.Read(self.dompath, *args)
749 def gatherDom(self, *args):
750 return xstransact.Gather(self.dompath, *args)
752 def _writeDom(self, *args):
753 return xstransact.Write(self.dompath, *args)
755 def _removeDom(self, *args):
756 return xstransact.Remove(self.dompath, *args)
758 def storeDom(self, *args):
759 return xstransact.Store(self.dompath, *args)
761 def _recreateDom(self):
762 complete(self.dompath, lambda t: self._recreateDomFunc(t))
764 def _recreateDomFunc(self, t):
765 t.remove()
766 t.mkdir()
767 t.set_permissions({'dom' : self.domid})
768 t.write('vm', self.vmpath)
770 def _storeDomDetails(self):
771 to_store = {
772 'domid': str(self.domid),
773 'vm': self.vmpath,
774 'name': self.info['name_label'],
775 'console/limit': str(xoptions.get_console_limit() * 1024),
776 'memory/target': str(self.info['memory_static_min'] * 1024)
777 }
779 def f(n, v):
780 if v is not None:
781 if type(v) == bool:
782 to_store[n] = v and "1" or "0"
783 else:
784 to_store[n] = str(v)
786 f('console/port', self.console_port)
787 f('console/ring-ref', self.console_mfn)
788 f('store/port', self.store_port)
789 f('store/ring-ref', self.store_mfn)
791 # elfnotes
792 for n, v in self.info.get_notes().iteritems():
793 n = n.lower().replace('_', '-')
794 if n == 'features':
795 for v in v.split('|'):
796 v = v.replace('_', '-')
797 if v.startswith('!'):
798 f('image/%s/%s' % (n, v[1:]), False)
799 else:
800 f('image/%s/%s' % (n, v), True)
801 else:
802 f('image/%s' % n, v)
804 to_store.update(self._vcpuDomDetails())
806 log.debug("Storing domain details: %s", scrub_password(to_store))
808 self._writeDom(to_store)
810 def _vcpuDomDetails(self):
811 def availability(n):
812 if self.info['vcpu_avail'] & (1 << n):
813 return 'online'
814 else:
815 return 'offline'
817 result = {}
818 for v in range(0, self.info['vcpus_number']):
819 result["cpu/%d/availability" % v] = availability(v)
820 return result
822 #
823 # xenstore watches
824 #
826 def _registerWatches(self):
827 """Register a watch on this VM's entries in the store, and the
828 domain's control/shutdown node, so that when they are changed
829 externally, we keep up to date. This should only be called by {@link
830 #create}, {@link #recreate}, or {@link #restore}, once the domain's
831 details have been written, but before the new instance is returned."""
832 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
833 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
834 self._handleShutdownWatch)
836 def _storeChanged(self, _):
837 log.trace("XendDomainInfo.storeChanged");
839 changed = False
841 # Check whether values in the configuration have
842 # changed in Xenstore.
844 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
846 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
847 for k in cfg_vm])
849 # convert two lists into a python dictionary
850 vm_details = dict(zip(cfg_vm, vm_details))
852 for arg, val in vm_details.items():
853 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
854 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
855 if val != None and val != self.info[xapiarg]:
856 self.info[xapiarg] = val
857 changed= True
859 # Check whether image definition has been updated
860 image_sxp = self._readVm('image')
861 if image_sxp and image_sxp != self.info.image_sxpr():
862 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
863 changed = True
865 if changed:
866 # Update the domain section of the store, as this contains some
867 # parameters derived from the VM configuration.
868 self._storeDomDetails()
870 return 1
872 def _handleShutdownWatch(self, _):
873 log.debug('XendDomainInfo.handleShutdownWatch')
875 reason = self.readDom('control/shutdown')
877 if reason and reason != 'suspend':
878 sst = self.readDom('xend/shutdown_start_time')
879 now = time.time()
880 if sst:
881 self.shutdownStartTime = float(sst)
882 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
883 else:
884 self.shutdownStartTime = now
885 self.storeDom('xend/shutdown_start_time', now)
886 timeout = SHUTDOWN_TIMEOUT
888 log.trace(
889 "Scheduling refreshShutdown on domain %d in %ds.",
890 self.domid, timeout)
891 threading.Timer(timeout, self.refreshShutdown).start()
893 return True
896 #
897 # Public Attributes for the VM
898 #
901 def getDomid(self):
902 return self.domid
904 def setName(self, name):
905 self._checkName(name)
906 self.info['name_label'] = name
907 self.storeVm("name", name)
909 def getName(self):
910 return self.info['name_label']
912 def getDomainPath(self):
913 return self.dompath
915 def getShutdownReason(self):
916 return self.readDom('control/shutdown')
918 def getStorePort(self):
919 """For use only by image.py and XendCheckpoint.py."""
920 return self.store_port
922 def getConsolePort(self):
923 """For use only by image.py and XendCheckpoint.py"""
924 return self.console_port
926 def getFeatures(self):
927 """For use only by image.py."""
928 return self.info['features']
930 def getVCpuCount(self):
931 return self.info['vcpus_number']
933 def setVCpuCount(self, vcpus):
934 self.info['vcpu_avail'] = (1 << vcpus) - 1
935 if self.domid >= 0:
936 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
937 # update dom differently depending on whether we are adjusting
938 # vcpu number up or down, otherwise _vcpuDomDetails does not
939 # disable the vcpus
940 if self.info['vcpus_number'] > vcpus:
941 # decreasing
942 self._writeDom(self._vcpuDomDetails())
943 self.info['vcpus_number'] = vcpus
944 else:
945 # same or increasing
946 self.info['vcpus_number'] = vcpus
947 self._writeDom(self._vcpuDomDetails())
948 else:
949 self.info['vcpus_number'] = vcpus
950 xen.xend.XendDomain.instance().managed_config_save(self)
951 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
952 vcpus)
954 def getLabel(self):
955 return security.get_security_info(self.info, 'label')
957 def getMemoryTarget(self):
958 """Get this domain's target memory size, in KB."""
959 return self.info['memory_static_min'] * 1024
961 def getMemoryMaximum(self):
962 """Get this domain's maximum memory size, in KB."""
963 return self.info['memory_static_max'] * 1024
965 def getResume(self):
966 return str(self._resume)
968 def getCap(self):
969 return self.info.get('cpu_cap', 0)
971 def getWeight(self):
972 return self.info.get('cpu_weight', 256)
974 def setResume(self, state):
975 self._resume = state
977 def getRestartCount(self):
978 return self._readVm('xend/restart_count')
980 def refreshShutdown(self, xeninfo = None):
981 """ Checks the domain for whether a shutdown is required.
983 Called from XendDomainInfo and also image.py for HVM images.
984 """
986 # If set at the end of this method, a restart is required, with the
987 # given reason. This restart has to be done out of the scope of
988 # refresh_shutdown_lock.
989 restart_reason = None
991 self.refresh_shutdown_lock.acquire()
992 try:
993 if xeninfo is None:
994 xeninfo = dom_get(self.domid)
995 if xeninfo is None:
996 # The domain no longer exists. This will occur if we have
997 # scheduled a timer to check for shutdown timeouts and the
998 # shutdown succeeded. It will also occur if someone
999 # destroys a domain beneath us. We clean up the domain,
1000 # just in case, but we can't clean up the VM, because that
1001 # VM may have migrated to a different domain on this
1002 # machine.
1003 self.cleanupDomain()
1004 self._stateSet(DOM_STATE_HALTED)
1005 return
1007 if xeninfo['dying']:
1008 # Dying means that a domain has been destroyed, but has not
1009 # yet been cleaned up by Xen. This state could persist
1010 # indefinitely if, for example, another domain has some of its
1011 # pages mapped. We might like to diagnose this problem in the
1012 # future, but for now all we do is make sure that it's not us
1013 # holding the pages, by calling cleanupDomain. We can't
1014 # clean up the VM, as above.
1015 self.cleanupDomain()
1016 self._stateSet(DOM_STATE_SHUTDOWN)
1017 return
1019 elif xeninfo['crashed']:
1020 if self.readDom('xend/shutdown_completed'):
1021 # We've seen this shutdown already, but we are preserving
1022 # the domain for debugging. Leave it alone.
1023 return
1025 log.warn('Domain has crashed: name=%s id=%d.',
1026 self.info['name_label'], self.domid)
1027 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1029 if xoptions.get_enable_dump():
1030 try:
1031 self.dumpCore()
1032 except XendError:
1033 # This error has been logged -- there's nothing more
1034 # we can do in this context.
1035 pass
1037 restart_reason = 'crash'
1038 self._stateSet(DOM_STATE_HALTED)
1040 elif xeninfo['shutdown']:
1041 self._stateSet(DOM_STATE_SHUTDOWN)
1042 if self.readDom('xend/shutdown_completed'):
1043 # We've seen this shutdown already, but we are preserving
1044 # the domain for debugging. Leave it alone.
1045 return
1047 else:
1048 reason = shutdown_reason(xeninfo['shutdown_reason'])
1050 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1051 self.info['name_label'], self.domid, reason)
1052 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1054 self._clearRestart()
1056 if reason == 'suspend':
1057 self._stateSet(DOM_STATE_SUSPENDED)
1058 # Don't destroy the domain. XendCheckpoint will do
1059 # this once it has finished. However, stop watching
1060 # the VM path now, otherwise we will end up with one
1061 # watch for the old domain, and one for the new.
1062 self._unwatchVm()
1063 elif reason in ('poweroff', 'reboot'):
1064 restart_reason = reason
1065 else:
1066 self.destroy()
1068 elif self.dompath is None:
1069 # We have yet to manage to call introduceDomain on this
1070 # domain. This can happen if a restore is in progress, or has
1071 # failed. Ignore this domain.
1072 pass
1073 else:
1074 # Domain is alive. If we are shutting it down, then check
1075 # the timeout on that, and destroy it if necessary.
1076 if xeninfo['paused']:
1077 self._stateSet(DOM_STATE_PAUSED)
1078 else:
1079 self._stateSet(DOM_STATE_RUNNING)
1081 if self.shutdownStartTime:
1082 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1083 self.shutdownStartTime)
1084 if timeout < 0:
1085 log.info(
1086 "Domain shutdown timeout expired: name=%s id=%s",
1087 self.info['name_label'], self.domid)
1088 self.destroy()
1089 finally:
1090 self.refresh_shutdown_lock.release()
1092 if restart_reason:
1093 threading.Thread(target = self._maybeRestart,
1094 args = (restart_reason,)).start()
1098 # Restart functions - handling whether we come back up on shutdown.
1101 def _clearRestart(self):
1102 self._removeDom("xend/shutdown_start_time")
1105 def _maybeRestart(self, reason):
1106 # Dispatch to the correct method based upon the configured on_{reason}
1107 # behaviour.
1108 actions = {"destroy" : self.destroy,
1109 "restart" : self._restart,
1110 "preserve" : self._preserve,
1111 "rename-restart" : self._renameRestart}
1113 action_conf = {
1114 'poweroff': 'actions_after_shutdown',
1115 'reboot': 'actions_after_reboot',
1116 'crash': 'actions_after_crash',
1119 action_target = self.info.get(action_conf.get(reason))
1120 func = actions.get(action_target, None)
1121 if func and callable(func):
1122 func()
1123 else:
1124 self.destroy() # default to destroy
1126 def _renameRestart(self):
1127 self._restart(True)
1129 def _restart(self, rename = False):
1130 """Restart the domain after it has exited.
1132 @param rename True if the old domain is to be renamed and preserved,
1133 False if it is to be destroyed.
1134 """
1135 from xen.xend import XendDomain
1137 if self._readVm(RESTART_IN_PROGRESS):
1138 log.error('Xend failed during restart of domain %s. '
1139 'Refusing to restart to avoid loops.',
1140 str(self.domid))
1141 self.destroy()
1142 return
1144 old_domid = self.domid
1145 self._writeVm(RESTART_IN_PROGRESS, 'True')
1147 now = time.time()
1148 rst = self._readVm('xend/previous_restart_time')
1149 if rst:
1150 rst = float(rst)
1151 timeout = now - rst
1152 if timeout < MINIMUM_RESTART_TIME:
1153 log.error(
1154 'VM %s restarting too fast (%f seconds since the last '
1155 'restart). Refusing to restart to avoid loops.',
1156 self.info['name_label'], timeout)
1157 self.destroy()
1158 return
1160 self._writeVm('xend/previous_restart_time', str(now))
1162 try:
1163 if rename:
1164 self._preserveForRestart()
1165 else:
1166 self._unwatchVm()
1167 self.destroyDomain()
1169 # new_dom's VM will be the same as this domain's VM, except where
1170 # the rename flag has instructed us to call preserveForRestart.
1171 # In that case, it is important that we remove the
1172 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1173 # once the new one is available.
1175 new_dom = None
1176 try:
1177 new_dom = XendDomain.instance().domain_create_from_dict(
1178 self.info)
1179 new_dom.unpause()
1180 rst_cnt = self._readVm('xend/restart_count')
1181 rst_cnt = int(rst_cnt) + 1
1182 self._writeVm('xend/restart_count', str(rst_cnt))
1183 new_dom._removeVm(RESTART_IN_PROGRESS)
1184 except:
1185 if new_dom:
1186 new_dom._removeVm(RESTART_IN_PROGRESS)
1187 new_dom.destroy()
1188 else:
1189 self._removeVm(RESTART_IN_PROGRESS)
1190 raise
1191 except:
1192 log.exception('Failed to restart domain %s.', str(old_domid))
1194 def _preserveForRestart(self):
1195 """Preserve a domain that has been shut down, by giving it a new UUID,
1196 cloning the VM details, and giving it a new name. This allows us to
1197 keep this domain for debugging, but restart a new one in its place
1198 preserving the restart semantics (name and UUID preserved).
1199 """
1201 new_uuid = uuid.createString()
1202 new_name = 'Domain-%s' % new_uuid
1203 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1204 self.info['name_label'], self.domid, self.info['uuid'],
1205 new_name, new_uuid)
1206 self._unwatchVm()
1207 self._releaseDevices()
1208 self.info['name_label'] = new_name
1209 self.info['uuid'] = new_uuid
1210 self.vmpath = XS_VMROOT + new_uuid
1211 self._storeVmDetails()
1212 self._preserve()
1215 def _preserve(self):
1216 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1217 self.domid)
1218 self._unwatchVm()
1219 self.storeDom('xend/shutdown_completed', 'True')
1220 self._stateSet(DOM_STATE_HALTED)
1223 # Debugging ..
1226 def dumpCore(self, corefile = None):
1227 """Create a core dump for this domain.
1229 @raise: XendError if core dumping failed.
1230 """
1232 try:
1233 if not corefile:
1234 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1235 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1236 self.info['name_label'], self.domid)
1238 if os.path.isdir(corefile):
1239 raise XendError("Cannot dump core in a directory: %s" %
1240 corefile)
1242 xc.domain_dumpcore(self.domid, corefile)
1243 except RuntimeError, ex:
1244 corefile_incomp = corefile+'-incomplete'
1245 os.rename(corefile, corefile_incomp)
1246 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1247 self.domid, self.info['name_label'])
1248 raise XendError("Failed to dump core: %s" % str(ex))
1251 # Device creation/deletion functions
1254 def _createDevice(self, deviceClass, devConfig):
1255 return self.getDeviceController(deviceClass).createDevice(devConfig)
1257 def _waitForDevice(self, deviceClass, devid):
1258 return self.getDeviceController(deviceClass).waitForDevice(devid)
1260 def _waitForDeviceUUID(self, dev_uuid):
1261 deviceClass, config = self.info['devices'].get(dev_uuid)
1262 self._waitForDevice(deviceClass, config['devid'])
1264 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1265 return self.getDeviceController(deviceClass).reconfigureDevice(
1266 devid, devconfig)
1268 def _createDevices(self):
1269 """Create the devices for a vm.
1271 @raise: VmError for invalid devices
1272 """
1273 ordered_refs = self.info.ordered_device_refs()
1274 for dev_uuid in ordered_refs:
1275 devclass, config = self.info['devices'][dev_uuid]
1276 if devclass in XendDevices.valid_devices():
1277 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1278 dev_uuid = config.get('uuid')
1279 devid = self._createDevice(devclass, config)
1281 # store devid in XendConfig for caching reasons
1282 if dev_uuid in self.info['devices']:
1283 self.info['devices'][dev_uuid][1]['devid'] = devid
1285 if self.image:
1286 self.image.createDeviceModel()
1288 def _releaseDevices(self, suspend = False):
1289 """Release all domain's devices. Nothrow guarantee."""
1290 if suspend and self.image:
1291 self.image.destroy(suspend)
1292 return
1294 while True:
1295 t = xstransact("%s/device" % self.dompath)
1296 for devclass in XendDevices.valid_devices():
1297 for dev in t.list(devclass):
1298 try:
1299 t.remove(dev)
1300 except:
1301 # Log and swallow any exceptions in removal --
1302 # there's nothing more we can do.
1303 log.exception(
1304 "Device release failed: %s; %s; %s",
1305 self.info['name_label'], devclass, dev)
1306 if t.commit():
1307 break
1309 def getDeviceController(self, name):
1310 """Get the device controller for this domain, and if it
1311 doesn't exist, create it.
1313 @param name: device class name
1314 @type name: string
1315 @rtype: subclass of DevController
1316 """
1317 if name not in self._deviceControllers:
1318 devController = XendDevices.make_controller(name, self)
1319 if not devController:
1320 raise XendError("Unknown device type: %s" % name)
1321 self._deviceControllers[name] = devController
1323 return self._deviceControllers[name]
1326 # Migration functions (public)
1329 def testMigrateDevices(self, network, dst):
1330 """ Notify all device about intention of migration
1331 @raise: XendError for a device that cannot be migrated
1332 """
1333 for (n, c) in self.info.all_devices_sxpr():
1334 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1335 if rc != 0:
1336 raise XendError("Device of type '%s' refuses migration." % n)
1338 def migrateDevices(self, network, dst, step, domName=''):
1339 """Notify the devices about migration
1340 """
1341 ctr = 0
1342 try:
1343 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1344 self.migrateDevice(dev_type, dev_conf, network, dst,
1345 step, domName)
1346 ctr = ctr + 1
1347 except:
1348 for dev_type, dev_conf in self.info.all_devices_sxpr():
1349 if ctr == 0:
1350 step = step - 1
1351 ctr = ctr - 1
1352 self._recoverMigrateDevice(dev_type, dev_conf, network,
1353 dst, step, domName)
1354 raise
1356 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1357 step, domName=''):
1358 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1359 network, dst, step, domName)
1361 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1362 dst, step, domName=''):
1363 return self.getDeviceController(deviceClass).recover_migrate(
1364 deviceConfig, network, dst, step, domName)
1367 ## private:
1369 def _constructDomain(self):
1370 """Construct the domain.
1372 @raise: VmError on error
1373 """
1375 log.debug('XendDomainInfo.constructDomain')
1377 self.shutdownStartTime = None
1379 image_cfg = self.info.get('image', {})
1380 hvm = image_cfg.has_key('hvm')
1382 if hvm:
1383 info = xc.xeninfo()
1384 if 'hvm' not in info['xen_caps']:
1385 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1386 "supported by your CPU and enabled in your "
1387 "BIOS?")
1389 self.domid = xc.domain_create(
1390 domid = 0,
1391 ssidref = security.get_security_info(self.info, 'ssidref'),
1392 handle = uuid.fromString(self.info['uuid']),
1393 hvm = int(hvm))
1395 if self.domid < 0:
1396 raise VmError('Creating domain failed: name=%s' %
1397 self.info['name_label'])
1399 self.dompath = GetDomainPath(self.domid)
1401 self._recreateDom()
1403 # Set maximum number of vcpus in domain
1404 xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
1406 # register the domain in the list
1407 from xen.xend import XendDomain
1408 XendDomain.instance().add_domain(self)
1410 def _introduceDomain(self):
1411 assert self.domid is not None
1412 assert self.store_mfn is not None
1413 assert self.store_port is not None
1415 try:
1416 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1417 except RuntimeError, exn:
1418 raise XendError(str(exn))
1421 def _initDomain(self):
1422 log.debug('XendDomainInfo.initDomain: %s %s',
1423 self.domid,
1424 self.info['cpu_weight'])
1426 self._configureBootloader()
1428 if not self._infoIsSet('image'):
1429 raise VmError('Missing image in configuration')
1431 try:
1432 self.image = image.create(self,
1433 self.info,
1434 self.info['image'],
1435 self.info['devices'])
1437 localtime = self.info.get('platform_localtime', False)
1438 if localtime:
1439 xc.domain_set_time_offset(self.domid)
1441 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1443 # repin domain vcpus if a restricted cpus list is provided
1444 # this is done prior to memory allocation to aide in memory
1445 # distribution for NUMA systems.
1446 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1447 for v in range(0, self.info['vcpus_number']):
1448 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1450 # Use architecture- and image-specific calculations to determine
1451 # the various headrooms necessary, given the raw configured
1452 # values. maxmem, memory, and shadow are all in KiB.
1453 memory = self.image.getRequiredAvailableMemory(
1454 self.info['memory_static_min'] * 1024)
1455 maxmem = self.image.getRequiredAvailableMemory(
1456 self.info['memory_static_max'] * 1024)
1457 shadow = self.image.getRequiredShadowMemory(
1458 self.info['shadow_memory'] * 1024,
1459 self.info['memory_static_max'] * 1024)
1461 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'],)
1462 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1463 # takes MiB and we must not round down and end up under-providing.
1464 shadow = ((shadow + 1023) / 1024) * 1024
1466 # set memory limit
1467 xc.domain_setmaxmem(self.domid, maxmem)
1469 # Make sure there's enough RAM available for the domain
1470 balloon.free(memory + shadow)
1472 # Set up the shadow memory
1473 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1474 self.info['shadow_memory'] = shadow_cur
1476 self._createChannels()
1478 channel_details = self.image.createImage()
1480 self.store_mfn = channel_details['store_mfn']
1481 if 'console_mfn' in channel_details:
1482 self.console_mfn = channel_details['console_mfn']
1483 if 'notes' in channel_details:
1484 self.info.set_notes(channel_details['notes'])
1486 self._introduceDomain()
1488 self._createDevices()
1490 self.image.cleanupBootloading()
1492 self.info['start_time'] = time.time()
1494 self._stateSet(DOM_STATE_RUNNING)
1495 except RuntimeError, exn:
1496 log.exception("XendDomainInfo.initDomain: exception occurred")
1497 self.image.cleanupBootloading()
1498 raise VmError(str(exn))
1501 def cleanupDomain(self):
1502 """Cleanup domain resources; release devices. Idempotent. Nothrow
1503 guarantee."""
1505 self.refresh_shutdown_lock.acquire()
1506 try:
1507 self.unwatchShutdown()
1508 self._releaseDevices()
1509 bootloader_tidy(self)
1511 if self.image:
1512 try:
1513 self.image.destroy()
1514 except:
1515 log.exception(
1516 "XendDomainInfo.cleanup: image.destroy() failed.")
1517 self.image = None
1519 try:
1520 self._removeDom()
1521 except:
1522 log.exception("Removing domain path failed.")
1524 self._stateSet(DOM_STATE_HALTED)
1525 finally:
1526 self.refresh_shutdown_lock.release()
1529 def unwatchShutdown(self):
1530 """Remove the watch on the domain's control/shutdown node, if any.
1531 Idempotent. Nothrow guarantee. Expects to be protected by the
1532 refresh_shutdown_lock."""
1534 try:
1535 try:
1536 if self.shutdownWatch:
1537 self.shutdownWatch.unwatch()
1538 finally:
1539 self.shutdownWatch = None
1540 except:
1541 log.exception("Unwatching control/shutdown failed.")
1543 def waitForShutdown(self):
1544 self.state_updated.acquire()
1545 try:
1546 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1547 self.state_updated.wait()
1548 finally:
1549 self.state_updated.release()
1553 # TODO: recategorise - called from XendCheckpoint
1556 def completeRestore(self, store_mfn, console_mfn):
1558 log.debug("XendDomainInfo.completeRestore")
1560 self.store_mfn = store_mfn
1561 self.console_mfn = console_mfn
1563 self._introduceDomain()
1564 image_cfg = self.info.get('image', {})
1565 hvm = image_cfg.has_key('hvm')
1566 if hvm:
1567 self.image = image.create(self,
1568 self.info,
1569 self.info['image'],
1570 self.info['devices'])
1571 if self.image:
1572 self.image.createDeviceModel(True)
1573 self.image.register_shutdown_watch()
1574 self._storeDomDetails()
1575 self._registerWatches()
1576 self.refreshShutdown()
1578 log.debug("XendDomainInfo.completeRestore done")
1581 def _endRestore(self):
1582 self.setResume(False)
1585 # VM Destroy
1588 def _prepare_phantom_paths(self):
1589 # get associated devices to destroy
1590 # build list of phantom devices to be removed after normal devices
1591 plist = []
1592 if self.domid is not None:
1593 from xen.xend.xenstore.xstransact import xstransact
1594 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1595 for dev in t.list():
1596 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1597 % (self.dompath, dev))
1598 if backend_phantom_vbd is not None:
1599 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1600 % backend_phantom_vbd)
1601 plist.append(backend_phantom_vbd)
1602 plist.append(frontend_phantom_vbd)
1603 return plist
1605 def _cleanup_phantom_devs(self, plist):
1606 # remove phantom devices
1607 if not plist == []:
1608 time.sleep(2)
1609 for paths in plist:
1610 if paths.find('backend') != -1:
1611 from xen.xend.server import DevController
1612 # Modify online status /before/ updating state (latter is watched by
1613 # drivers, so this ordering avoids a race).
1614 xstransact.Write(paths, 'online', "0")
1615 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1616 # force
1617 xstransact.Remove(paths)
1619 def destroy(self):
1620 """Cleanup VM and destroy domain. Nothrow guarantee."""
1622 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1624 paths = self._prepare_phantom_paths()
1626 self._cleanupVm()
1627 if self.dompath is not None:
1628 self.destroyDomain()
1630 self._cleanup_phantom_devs(paths)
1632 def destroyDomain(self):
1633 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1635 paths = self._prepare_phantom_paths()
1637 try:
1638 if self.domid is not None:
1639 xc.domain_destroy(self.domid)
1640 self.domid = None
1641 for state in DOM_STATES_OLD:
1642 self.info[state] = 0
1643 except:
1644 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1646 from xen.xend import XendDomain
1647 XendDomain.instance().remove_domain(self)
1649 self.cleanupDomain()
1650 self._cleanup_phantom_devs(paths)
1652 def resumeDomain(self):
1653 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1655 try:
1656 if self.domid is not None:
1657 xc.domain_resume(self.domid)
1658 ResumeDomain(self.domid)
1659 except:
1660 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1663 # Channels for xenstore and console
1666 def _createChannels(self):
1667 """Create the channels to the domain.
1668 """
1669 self.store_port = self._createChannel()
1670 self.console_port = self._createChannel()
1673 def _createChannel(self):
1674 """Create an event channel to the domain.
1675 """
1676 try:
1677 if self.domid != None:
1678 return xc.evtchn_alloc_unbound(domid = self.domid,
1679 remote_dom = 0)
1680 except:
1681 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1682 raise
1684 def _resetChannels(self):
1685 """Reset all event channels in the domain.
1686 """
1687 try:
1688 if self.domid != None:
1689 return xc.evtchn_reset(dom = self.domid)
1690 except:
1691 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1692 raise
1696 # Bootloader configuration
1699 def _configureBootloader(self):
1700 """Run the bootloader if we're configured to do so."""
1702 blexec = self.info['PV_bootloader']
1703 bootloader_args = self.info['PV_bootloader_args']
1704 kernel = self.info['PV_kernel']
1705 ramdisk = self.info['PV_ramdisk']
1706 args = self.info['PV_args']
1707 boot = self.info['HVM_boot_policy']
1709 if boot:
1710 # HVM booting.
1711 self.info['image']['type'] = 'hvm'
1712 if not 'devices' in self.info['image']:
1713 self.info['image']['devices'] = {}
1714 self.info['image']['devices']['boot'] = \
1715 self.info['HVM_boot_params'].get('order', 'dc')
1716 elif not blexec and kernel:
1717 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1718 # will be picked up by image.py.
1719 pass
1720 else:
1721 # Boot using bootloader
1722 if not blexec or blexec == 'pygrub':
1723 blexec = osdep.pygrub_path
1725 blcfg = None
1726 disks = [x for x in self.info['vbd_refs']
1727 if self.info['devices'][x][1]['bootable']]
1729 if not disks:
1730 msg = "Had a bootloader specified, but no disks are bootable"
1731 log.error(msg)
1732 raise VmError(msg)
1734 devinfo = self.info['devices'][disks[0]]
1735 devtype = devinfo[0]
1736 disk = devinfo[1]['uname']
1738 fn = blkdev_uname_to_file(disk)
1739 mounted = devtype == 'tap' and not os.stat(fn).st_rdev
1740 if mounted:
1741 # This is a file, not a device. pygrub can cope with a
1742 # file if it's raw, but if it's QCOW or other such formats
1743 # used through blktap, then we need to mount it first.
1745 log.info("Mounting %s on %s." %
1746 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1748 vbd = {
1749 'mode': 'RO',
1750 'device': BOOTLOADER_LOOPBACK_DEVICE,
1753 from xen.xend import XendDomain
1754 dom0 = XendDomain.instance().privilegedDomain()
1755 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
1756 fn = BOOTLOADER_LOOPBACK_DEVICE
1758 try:
1759 blcfg = bootloader(blexec, fn, self, False,
1760 bootloader_args, kernel, ramdisk, args)
1761 finally:
1762 if mounted:
1763 log.info("Unmounting %s from %s." %
1764 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1766 dom0.destroyDevice('tap', '/dev/xvdp')
1768 if blcfg is None:
1769 msg = "Had a bootloader specified, but can't find disk"
1770 log.error(msg)
1771 raise VmError(msg)
1773 self.info.update_with_image_sxp(blcfg, True)
1777 # VM Functions
1780 def _readVMDetails(self, params):
1781 """Read the specified parameters from the store.
1782 """
1783 try:
1784 return self._gatherVm(*params)
1785 except ValueError:
1786 # One of the int/float entries in params has a corresponding store
1787 # entry that is invalid. We recover, because older versions of
1788 # Xend may have put the entry there (memory/target, for example),
1789 # but this is in general a bad situation to have reached.
1790 log.exception(
1791 "Store corrupted at %s! Domain %d's configuration may be "
1792 "affected.", self.vmpath, self.domid)
1793 return []
1795 def _cleanupVm(self):
1796 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1798 self._unwatchVm()
1800 try:
1801 self._removeVm()
1802 except:
1803 log.exception("Removing VM path failed.")
1806 def checkLiveMigrateMemory(self):
1807 """ Make sure there's enough memory to migrate this domain """
1808 overhead_kb = 0
1809 if arch.type == "x86":
1810 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1811 # the minimum that Xen would allocate if no value were given.
1812 overhead_kb = self.info['vcpus_number'] * 1024 + \
1813 self.info['memory_static_max'] * 4
1814 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1815 # The domain might already have some shadow memory
1816 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1817 if overhead_kb > 0:
1818 balloon.free(overhead_kb)
1820 def _unwatchVm(self):
1821 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1822 guarantee."""
1823 try:
1824 try:
1825 if self.vmWatch:
1826 self.vmWatch.unwatch()
1827 finally:
1828 self.vmWatch = None
1829 except:
1830 log.exception("Unwatching VM path failed.")
1832 def testDeviceComplete(self):
1833 """ For Block IO migration safety we must ensure that
1834 the device has shutdown correctly, i.e. all blocks are
1835 flushed to disk
1836 """
1837 start = time.time()
1838 while True:
1839 test = 0
1840 diff = time.time() - start
1841 for i in self.getDeviceController('vbd').deviceIDs():
1842 test = 1
1843 log.info("Dev %s still active, looping...", i)
1844 time.sleep(0.1)
1846 if test == 0:
1847 break
1848 if diff >= MIGRATE_TIMEOUT:
1849 log.info("Dev still active but hit max loop timeout")
1850 break
1852 def testvifsComplete(self):
1853 """ In case vifs are released and then created for the same
1854 domain, we need to wait the device shut down.
1855 """
1856 start = time.time()
1857 while True:
1858 test = 0
1859 diff = time.time() - start
1860 for i in self.getDeviceController('vif').deviceIDs():
1861 test = 1
1862 log.info("Dev %s still active, looping...", i)
1863 time.sleep(0.1)
1865 if test == 0:
1866 break
1867 if diff >= MIGRATE_TIMEOUT:
1868 log.info("Dev still active but hit max loop timeout")
1869 break
1871 def _storeVmDetails(self):
1872 to_store = {}
1874 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1875 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1876 if self._infoIsSet(info_key):
1877 to_store[key] = str(self.info[info_key])
1879 if self.info.get('image'):
1880 image_sxpr = self.info.image_sxpr()
1881 if image_sxpr:
1882 to_store['image'] = sxp.to_string(image_sxpr)
1884 if self._infoIsSet('security'):
1885 secinfo = self.info['security']
1886 to_store['security'] = sxp.to_string(secinfo)
1887 for idx in range(0, len(secinfo)):
1888 if secinfo[idx][0] == 'access_control':
1889 to_store['security/access_control'] = sxp.to_string(
1890 [secinfo[idx][1], secinfo[idx][2]])
1891 for aidx in range(1, len(secinfo[idx])):
1892 if secinfo[idx][aidx][0] == 'label':
1893 to_store['security/access_control/label'] = \
1894 secinfo[idx][aidx][1]
1895 if secinfo[idx][aidx][0] == 'policy':
1896 to_store['security/access_control/policy'] = \
1897 secinfo[idx][aidx][1]
1898 if secinfo[idx][0] == 'ssidref':
1899 to_store['security/ssidref'] = str(secinfo[idx][1])
1902 if not self._readVm('xend/restart_count'):
1903 to_store['xend/restart_count'] = str(0)
1905 log.debug("Storing VM details: %s", scrub_password(to_store))
1907 self._writeVm(to_store)
1908 self._setVmPermissions()
1911 def _setVmPermissions(self):
1912 """Allow the guest domain to read its UUID. We don't allow it to
1913 access any other entry, for security."""
1914 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1915 { 'dom' : self.domid,
1916 'read' : True,
1917 'write' : False })
1920 # Utility functions
1923 def _stateSet(self, state):
1924 self.state_updated.acquire()
1925 try:
1926 if self.state != state:
1927 self.state = state
1928 self.state_updated.notifyAll()
1929 finally:
1930 self.state_updated.release()
1932 def _infoIsSet(self, name):
1933 return name in self.info and self.info[name] is not None
1935 def _checkName(self, name):
1936 """Check if a vm name is valid. Valid names contain alphabetic
1937 characters, digits, or characters in '_-.:/+'.
1938 The same name cannot be used for more than one vm at the same time.
1940 @param name: name
1941 @raise: VmError if invalid
1942 """
1943 from xen.xend import XendDomain
1945 if name is None or name == '':
1946 raise VmError('Missing VM Name')
1948 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
1949 raise VmError('Invalid VM Name')
1951 dom = XendDomain.instance().domain_lookup_nr(name)
1952 if dom and dom.info['uuid'] != self.info['uuid']:
1953 raise VmError("VM name '%s' already exists%s" %
1954 (name,
1955 dom.domid is not None and
1956 (" as domain %s" % str(dom.domid)) or ""))
1959 def update(self, info = None, refresh = True):
1960 """Update with info from xc.domain_getinfo().
1961 """
1962 log.trace("XendDomainInfo.update(%s) on domain %s", info,
1963 str(self.domid))
1965 if not info:
1966 info = dom_get(self.domid)
1967 if not info:
1968 return
1970 #manually update ssidref / security fields
1971 if security.on() and info.has_key('ssidref'):
1972 if (info['ssidref'] != 0) and self.info.has_key('security'):
1973 security_field = self.info['security']
1974 if not security_field:
1975 #create new security element
1976 self.info.update({'security':
1977 [['ssidref', str(info['ssidref'])]]})
1979 #ssidref field not used any longer
1980 if 'ssidref' in info:
1981 info.pop('ssidref')
1983 # make sure state is reset for info
1984 # TODO: we should eventually get rid of old_dom_states
1986 self.info.update_config(info)
1987 self._update_consoles()
1989 if refresh:
1990 self.refreshShutdown(info)
1992 log.trace("XendDomainInfo.update done on domain %s: %s",
1993 str(self.domid), self.info)
1995 def sxpr(self, ignore_store = False, legacy_only = True):
1996 result = self.info.to_sxp(domain = self,
1997 ignore_devices = ignore_store,
1998 legacy_only = legacy_only)
2000 #if not ignore_store and self.dompath:
2001 # vnc_port = self.readDom('console/vnc-port')
2002 # if vnc_port is not None:
2003 # result.append(['device',
2004 # ['console', ['vnc-port', str(vnc_port)]]])
2006 return result
2008 # Xen API
2009 # ----------------------------------------------------------------
2011 def get_uuid(self):
2012 dom_uuid = self.info.get('uuid')
2013 if not dom_uuid: # if it doesn't exist, make one up
2014 dom_uuid = uuid.createString()
2015 self.info['uuid'] = dom_uuid
2016 return dom_uuid
2018 def get_memory_static_max(self):
2019 return self.info.get('memory_static_max', 0)
2020 def get_memory_static_min(self):
2021 return self.info.get('memory_static_min', 0)
2022 def get_memory_dynamic_max(self):
2023 return self.info.get('memory_dynamic_max', 0)
2024 def get_memory_dynamic_min(self):
2025 return self.info.get('memory_dynamic_min', 0)
2027 def get_vcpus_policy(self):
2028 sched_id = xc.sched_id_get()
2029 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
2030 return 'sedf'
2031 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
2032 return 'credit'
2033 else:
2034 return 'unknown'
2035 def get_vcpus_params(self):
2036 if self.getDomid() is None:
2037 return self.info['vcpus_params']
2039 retval = xc.sched_credit_domain_get(self.getDomid())
2040 return retval
2041 def get_power_state(self):
2042 return XEN_API_VM_POWER_STATE[self.state]
2043 def get_platform_std_vga(self):
2044 return self.info.get('platform_std_vga', False)
2045 def get_platform_serial(self):
2046 return self.info.get('platform_serial', '')
2047 def get_platform_localtime(self):
2048 return self.info.get('platform_localtime', False)
2049 def get_platform_clock_offset(self):
2050 return self.info.get('platform_clock_offset', False)
2051 def get_platform_enable_audio(self):
2052 return self.info.get('platform_enable_audio', False)
2053 def get_platform_keymap(self):
2054 return self.info.get('platform_keymap', '')
2055 def get_pci_bus(self):
2056 return self.info.get('pci_bus', '')
2057 def get_tools_version(self):
2058 return self.info.get('tools_version', {})
2060 def get_on_shutdown(self):
2061 after_shutdown = self.info.get('action_after_shutdown')
2062 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2063 return XEN_API_ON_NORMAL_EXIT[-1]
2064 return after_shutdown
2066 def get_on_reboot(self):
2067 after_reboot = self.info.get('action_after_reboot')
2068 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2069 return XEN_API_ON_NORMAL_EXIT[-1]
2070 return after_reboot
2072 def get_on_suspend(self):
2073 # TODO: not supported
2074 after_suspend = self.info.get('action_after_suspend')
2075 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2076 return XEN_API_ON_NORMAL_EXIT[-1]
2077 return after_suspend
2079 def get_on_crash(self):
2080 after_crash = self.info.get('action_after_crash')
2081 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2082 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2083 return after_crash
2085 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2086 """ Get's a device configuration either from XendConfig or
2087 from the DevController.
2089 @param dev_class: device class, either, 'vbd' or 'vif'
2090 @param dev_uuid: device UUID
2092 @rtype: dictionary
2093 """
2094 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2096 # shortcut if the domain isn't started because
2097 # the devcontrollers will have no better information
2098 # than XendConfig.
2099 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2100 if dev_config:
2101 return copy.deepcopy(dev_config)
2102 return None
2104 # instead of using dev_class, we use the dev_type
2105 # that is from XendConfig.
2106 controller = self.getDeviceController(dev_type)
2107 if not controller:
2108 return None
2110 all_configs = controller.getAllDeviceConfigurations()
2111 if not all_configs:
2112 return None
2114 updated_dev_config = copy.deepcopy(dev_config)
2115 for _devid, _devcfg in all_configs.items():
2116 if _devcfg.get('uuid') == dev_uuid:
2117 updated_dev_config.update(_devcfg)
2118 updated_dev_config['id'] = _devid
2119 return updated_dev_config
2121 return updated_dev_config
2123 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2124 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2125 if not config:
2126 return {}
2128 config['VM'] = self.get_uuid()
2130 if dev_class == 'vif':
2131 if not config.has_key('name'):
2132 config['name'] = config.get('vifname', '')
2133 if not config.has_key('MAC'):
2134 config['MAC'] = config.get('mac', '')
2135 if not config.has_key('type'):
2136 config['type'] = 'paravirtualised'
2137 if not config.has_key('device'):
2138 devid = config.get('id')
2139 if devid != None:
2140 config['device'] = 'eth%d' % devid
2141 else:
2142 config['device'] = ''
2144 if not config.has_key('network'):
2145 try:
2146 config['network'] = \
2147 XendNode.instance().bridge_to_network(
2148 config.get('bridge')).uuid
2149 except Exception:
2150 log.exception('bridge_to_network')
2151 # Ignore this for now -- it may happen if the device
2152 # has been specified using the legacy methods, but at
2153 # some point we're going to have to figure out how to
2154 # handle that properly.
2156 config['MTU'] = 1500 # TODO
2158 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2159 xennode = XendNode.instance()
2160 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2161 config['io_read_kbs'] = rx_bps/1024
2162 config['io_write_kbs'] = tx_bps/1024
2163 else:
2164 config['io_read_kbs'] = 0.0
2165 config['io_write_kbs'] = 0.0
2167 if dev_class == 'vbd':
2169 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2170 controller = self.getDeviceController(dev_class)
2171 devid, _1, _2 = controller.getDeviceDetails(config)
2172 xennode = XendNode.instance()
2173 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2174 config['io_read_kbs'] = rd_blkps
2175 config['io_write_kbs'] = wr_blkps
2176 else:
2177 config['io_read_kbs'] = 0.0
2178 config['io_write_kbs'] = 0.0
2180 config['VDI'] = config.get('VDI', '')
2181 config['device'] = config.get('dev', '')
2182 if ':' in config['device']:
2183 vbd_name, vbd_type = config['device'].split(':', 1)
2184 config['device'] = vbd_name
2185 if vbd_type == 'cdrom':
2186 config['type'] = XEN_API_VBD_TYPE[0]
2187 else:
2188 config['type'] = XEN_API_VBD_TYPE[1]
2190 config['driver'] = 'paravirtualised' # TODO
2191 config['image'] = config.get('uname', '')
2193 if config.get('mode', 'r') == 'r':
2194 config['mode'] = 'RO'
2195 else:
2196 config['mode'] = 'RW'
2198 if dev_class == 'vtpm':
2199 if not config.has_key('type'):
2200 config['type'] = 'paravirtualised' # TODO
2201 if not config.has_key('backend'):
2202 config['backend'] = "00000000-0000-0000-0000-000000000000"
2204 return config
2206 def get_dev_property(self, dev_class, dev_uuid, field):
2207 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2208 try:
2209 return config[field]
2210 except KeyError:
2211 raise XendError('Invalid property for device: %s' % field)
2213 def set_dev_property(self, dev_class, dev_uuid, field, value):
2214 self.info['devices'][dev_uuid][1][field] = value
2216 def get_vcpus_util(self):
2217 vcpu_util = {}
2218 xennode = XendNode.instance()
2219 if 'vcpus_number' in self.info and self.domid != None:
2220 for i in range(0, self.info['vcpus_number']):
2221 util = xennode.get_vcpu_util(self.domid, i)
2222 vcpu_util[str(i)] = util
2224 return vcpu_util
2226 def get_consoles(self):
2227 return self.info.get('console_refs', [])
2229 def get_vifs(self):
2230 return self.info.get('vif_refs', [])
2232 def get_vbds(self):
2233 return self.info.get('vbd_refs', [])
2235 def get_vtpms(self):
2236 return self.info.get('vtpm_refs', [])
2238 def create_vbd(self, xenapi_vbd, vdi_image_path):
2239 """Create a VBD using a VDI from XendStorageRepository.
2241 @param xenapi_vbd: vbd struct from the Xen API
2242 @param vdi_image_path: VDI UUID
2243 @rtype: string
2244 @return: uuid of the device
2245 """
2246 xenapi_vbd['image'] = vdi_image_path
2247 log.debug('create_vbd: %s' % xenapi_vbd)
2248 dev_uuid = ''
2249 if vdi_image_path.startswith('tap'):
2250 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2251 else:
2252 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2254 if not dev_uuid:
2255 raise XendError('Failed to create device')
2257 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2258 _, config = self.info['devices'][dev_uuid]
2259 dev_control = None
2261 if vdi_image_path.startswith('tap'):
2262 dev_control = self.getDeviceController('tap')
2263 else:
2264 dev_control = self.getDeviceController('vbd')
2266 config['devid'] = dev_control.createDevice(config)
2268 return dev_uuid
2270 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2271 """Create a VBD using a VDI from XendStorageRepository.
2273 @param xenapi_vbd: vbd struct from the Xen API
2274 @param vdi_image_path: VDI UUID
2275 @rtype: string
2276 @return: uuid of the device
2277 """
2278 xenapi_vbd['image'] = vdi_image_path
2279 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2280 if not dev_uuid:
2281 raise XendError('Failed to create device')
2283 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2284 _, config = self.info['devices'][dev_uuid]
2285 config['devid'] = self.getDeviceController('tap').createDevice(config)
2287 return config['devid']
2289 def create_vif(self, xenapi_vif):
2290 """Create VIF device from the passed struct in Xen API format.
2292 @param xenapi_vif: Xen API VIF Struct.
2293 @rtype: string
2294 @return: UUID
2295 """
2296 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2297 if not dev_uuid:
2298 raise XendError('Failed to create device')
2300 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2301 _, config = self.info['devices'][dev_uuid]
2302 config['devid'] = self.getDeviceController('vif').createDevice(config)
2304 return dev_uuid
2306 def create_vtpm(self, xenapi_vtpm):
2307 """Create a VTPM device from the passed struct in Xen API format.
2309 @return: uuid of the device
2310 @rtype: string
2311 """
2313 if self.state not in (DOM_STATE_HALTED,):
2314 raise VmError("Can only add vTPM to a halted domain.")
2315 if self.get_vtpms() != []:
2316 raise VmError('Domain already has a vTPM.')
2317 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2318 if not dev_uuid:
2319 raise XendError('Failed to create device')
2321 return dev_uuid
2323 def create_console(self, xenapi_console):
2324 """ Create a console device from a Xen API struct.
2326 @return: uuid of device
2327 @rtype: string
2328 """
2329 if self.state not in (DOM_STATE_HALTED,):
2330 raise VmError("Can only add console to a halted domain.")
2332 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2333 if not dev_uuid:
2334 raise XendError('Failed to create device')
2336 return dev_uuid
2338 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2339 if dev_uuid not in self.info['devices']:
2340 raise XendError('Device does not exist')
2342 try:
2343 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2344 _, config = self.info['devices'][dev_uuid]
2345 devid = config.get('devid')
2346 if devid != None:
2347 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2348 else:
2349 raise XendError('Unable to get devid for device: %s:%s' %
2350 (dev_type, dev_uuid))
2351 finally:
2352 del self.info['devices'][dev_uuid]
2353 self.info['%s_refs' % dev_type].remove(dev_uuid)
2355 def destroy_vbd(self, dev_uuid):
2356 self.destroy_device_by_uuid('vbd', dev_uuid)
2358 def destroy_vif(self, dev_uuid):
2359 self.destroy_device_by_uuid('vif', dev_uuid)
2361 def destroy_vtpm(self, dev_uuid):
2362 self.destroy_device_by_uuid('vtpm', dev_uuid)
2364 def has_device(self, dev_class, dev_uuid):
2365 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2367 def __str__(self):
2368 return '<domain id=%s name=%s memory=%s state=%s>' % \
2369 (str(self.domid), self.info['name_label'],
2370 str(self.info['memory_static_min']), DOM_STATES[self.state])
2372 __repr__ = __str__