direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 14132:0261e86db3b1

Fix xm vcpu-set command for when wrong number of VCPU is given

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Ewan Mellor <ewan@xensource.com>
date Mon Feb 26 15:41:35 2007 +0000 (2007-02-26)
parents 0b5da89e2b3d
children 270a5e2ead43
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 xendomains = XendDomain.instance()
402 xennode = XendNode.instance()
404 # save running configuration if XendDomains believe domain is
405 # persistent
406 if is_managed:
407 xendomains.managed_config_save(self)
409 if xennode.xenschedinfo() == 'credit':
410 xendomains.domain_sched_credit_set(self.getDomid(),
411 self.getWeight(),
412 self.getCap())
413 except:
414 log.exception('VM start failed')
415 self.destroy()
416 raise
417 else:
418 raise XendError('VM already running')
420 def resume(self):
421 """Resumes a domain that has come back from suspension."""
422 if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED):
423 try:
424 self._constructDomain()
425 self._storeVmDetails()
426 self._createDevices()
427 self._createChannels()
428 self._storeDomDetails()
429 self._endRestore()
430 except:
431 log.exception('VM resume failed')
432 raise
433 else:
434 raise XendError('VM already running')
436 def shutdown(self, reason):
437 """Shutdown a domain by signalling this via xenstored."""
438 log.debug('XendDomainInfo.shutdown(%s)', reason)
439 if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
440 raise XendError('Domain cannot be shutdown')
442 if self.domid == 0:
443 raise XendError('Domain 0 cannot be shutdown')
445 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
446 raise XendError('Invalid reason: %s' % reason)
447 self._removeVm('xend/previous_restart_time')
448 self.storeDom("control/shutdown", reason)
450 ## shutdown hypercall for hvm domain desides xenstore write
451 image_cfg = self.info.get('image', {})
452 hvm = image_cfg.has_key('hvm')
453 if hvm:
454 for code in DOMAIN_SHUTDOWN_REASONS.keys():
455 if DOMAIN_SHUTDOWN_REASONS[code] == reason:
456 break
457 xc.domain_shutdown(self.domid, code)
460 def pause(self):
461 """Pause domain
463 @raise XendError: Failed pausing a domain
464 """
465 try:
466 xc.domain_pause(self.domid)
467 self._stateSet(DOM_STATE_PAUSED)
468 except Exception, ex:
469 log.exception(ex)
470 raise XendError("Domain unable to be paused: %s" % str(ex))
472 def unpause(self):
473 """Unpause domain
475 @raise XendError: Failed unpausing a domain
476 """
477 try:
478 xc.domain_unpause(self.domid)
479 self._stateSet(DOM_STATE_RUNNING)
480 except Exception, ex:
481 log.exception(ex)
482 raise XendError("Domain unable to be unpaused: %s" % str(ex))
484 def send_sysrq(self, key):
485 """ Send a Sysrq equivalent key via xenstored."""
486 asserts.isCharConvertible(key)
487 self.storeDom("control/sysrq", '%c' % key)
489 def device_create(self, dev_config):
490 """Create a new device.
492 @param dev_config: device configuration
493 @type dev_config: SXP object (parsed config)
494 """
495 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
496 dev_type = sxp.name(dev_config)
497 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
498 dev_config_dict = self.info['devices'][dev_uuid][1]
499 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
500 dev_config_dict['devid'] = devid = \
501 self._createDevice(dev_type, dev_config_dict)
502 self._waitForDevice(dev_type, devid)
503 return self.getDeviceController(dev_type).sxpr(devid)
505 def device_configure(self, dev_sxp, devid = None):
506 """Configure an existing device.
508 @param dev_config: device configuration
509 @type dev_config: SXP object (parsed config)
510 @param devid: device id
511 @type devid: int
512 @return: Returns True if successfully updated device
513 @rtype: boolean
514 """
516 # convert device sxp to a dict
517 dev_class = sxp.name(dev_sxp)
518 dev_config = {}
519 for opt_val in dev_sxp[1:]:
520 try:
521 dev_config[opt_val[0]] = opt_val[1]
522 except IndexError:
523 pass
525 # use DevController.reconfigureDevice to change device config
526 dev_control = self.getDeviceController(dev_class)
527 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
529 # update XendConfig with new device info
530 if dev_uuid:
531 self.info.device_update(dev_uuid, dev_sxp)
533 return True
535 def waitForDevices(self):
536 """Wait for this domain's configured devices to connect.
538 @raise VmError: if any device fails to initialise.
539 """
540 for devclass in XendDevices.valid_devices():
541 self.getDeviceController(devclass).waitForDevices()
543 def destroyDevice(self, deviceClass, devid, force = False):
544 try:
545 devid = int(devid)
546 except ValueError:
547 # devid is not a number, let's search for it in xenstore.
548 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
549 for entry in xstransact.List(devicePath):
550 backend = xstransact.Read('%s/%s' % (devicePath, entry),
551 "backend")
552 devName = xstransact.Read(backend, "dev")
553 if devName == devid:
554 # We found the integer matching our devid, use it instead
555 devid = entry
556 break
558 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
560 def getDeviceSxprs(self, deviceClass):
561 if self.state == DOM_STATE_RUNNING:
562 return self.getDeviceController(deviceClass).sxprs()
563 else:
564 sxprs = []
565 dev_num = 0
566 for dev_type, dev_info in self.info.all_devices_sxpr():
567 if dev_type == deviceClass:
568 sxprs.append([dev_num, dev_info])
569 dev_num += 1
570 return sxprs
573 def setMemoryTarget(self, target):
574 """Set the memory target of this domain.
575 @param target: In MiB.
576 """
577 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
578 self.info['name_label'], self.domid, target)
580 if target <= 0:
581 raise XendError('Invalid memory size')
583 self.info['memory_static_min'] = target
584 if self.domid >= 0:
585 self.storeVm("memory", target)
586 self.storeDom("memory/target", target << 10)
587 else:
588 self.info['memory_dynamic_min'] = target
589 xen.xend.XendDomain.instance().managed_config_save(self)
591 def setMemoryMaximum(self, limit):
592 """Set the maximum memory limit of this domain
593 @param limit: In MiB.
594 """
595 log.debug("Setting memory maximum of domain %s (%d) to %d MiB.",
596 self.info['name_label'], self.domid, limit)
598 if limit <= 0:
599 raise XendError('Invalid memory size')
601 self.info['memory_static_max'] = limit
602 if self.domid >= 0:
603 maxmem = int(limit) * 1024
604 try:
605 return xc.domain_setmaxmem(self.domid, maxmem)
606 except Exception, ex:
607 raise XendError(str(ex))
608 else:
609 self.info['memory_dynamic_max'] = limit
610 xen.xend.XendDomain.instance().managed_config_save(self)
613 def getVCPUInfo(self):
614 try:
615 # We include the domain name and ID, to help xm.
616 sxpr = ['domain',
617 ['domid', self.domid],
618 ['name', self.info['name_label']],
619 ['vcpu_count', self.info['vcpus_number']]]
621 for i in range(0, self.info['vcpus_number']):
622 info = xc.vcpu_getinfo(self.domid, i)
624 sxpr.append(['vcpu',
625 ['number', i],
626 ['online', info['online']],
627 ['blocked', info['blocked']],
628 ['running', info['running']],
629 ['cpu_time', info['cpu_time'] / 1e9],
630 ['cpu', info['cpu']],
631 ['cpumap', info['cpumap']]])
633 return sxpr
635 except RuntimeError, exn:
636 raise XendError(str(exn))
638 #
639 # internal functions ... TODO: re-categorised
640 #
642 def _augmentInfo(self, priv):
643 """Augment self.info, as given to us through L{recreate}, with
644 values taken from the store. This recovers those values known
645 to xend but not to the hypervisor.
646 """
647 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
648 if priv:
649 augment_entries.remove('memory')
650 augment_entries.remove('maxmem')
651 augment_entries.remove('vcpus')
652 augment_entries.remove('vcpu_avail')
654 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
655 for k in augment_entries])
657 # make returned lists into a dictionary
658 vm_config = dict(zip(augment_entries, vm_config))
660 for arg in augment_entries:
661 xapicfg = arg
662 val = vm_config[arg]
663 if val != None:
664 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
665 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
666 self.info[xapiarg] = val
667 else:
668 self.info[arg] = val
670 # For dom0, we ignore any stored value for the vcpus fields, and
671 # read the current value from Xen instead. This allows boot-time
672 # settings to take precedence over any entries in the store.
673 if priv:
674 xeninfo = dom_get(self.domid)
675 self.info['vcpus_number'] = xeninfo['online_vcpus']
676 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
678 # read image value
679 image_sxp = self._readVm('image')
680 if image_sxp:
681 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
683 # read devices
684 devices = []
685 for devclass in XendDevices.valid_devices():
686 devconfig = self.getDeviceController(devclass).configurations()
687 if devconfig:
688 devices.extend(devconfig)
690 if not self.info['devices'] and devices is not None:
691 for device in devices:
692 self.info.device_add(device[0], cfg_sxp = device)
694 self._update_consoles()
696 def _update_consoles(self):
697 if self.domid == None or self.domid == 0:
698 return
700 # Update VT100 port if it exists
701 self.console_port = self.readDom('console/port')
702 if self.console_port is not None:
703 serial_consoles = self.info.console_get_all('vt100')
704 if not serial_consoles:
705 cfg = self.info.console_add('vt100', self.console_port)
706 self._createDevice('console', cfg)
707 else:
708 console_uuid = serial_consoles[0].get('uuid')
709 self.info.console_update(console_uuid, 'location',
710 self.console_port)
713 # Update VNC port if it exists and write to xenstore
714 vnc_port = self.readDom('console/vnc-port')
715 if vnc_port is not None:
716 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
717 if dev_type == 'vfb':
718 old_location = dev_info.get('location')
719 listen_host = dev_info.get('vnclisten', 'localhost')
720 new_location = '%s:%s' % (listen_host, str(vnc_port))
721 if old_location == new_location:
722 break
724 dev_info['location'] = new_location
725 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
726 vfb_ctrl = self.getDeviceController('vfb')
727 vfb_ctrl.reconfigureDevice(0, dev_info)
728 break
730 #
731 # Function to update xenstore /vm/*
732 #
734 def _readVm(self, *args):
735 return xstransact.Read(self.vmpath, *args)
737 def _writeVm(self, *args):
738 return xstransact.Write(self.vmpath, *args)
740 def _removeVm(self, *args):
741 return xstransact.Remove(self.vmpath, *args)
743 def _gatherVm(self, *args):
744 return xstransact.Gather(self.vmpath, *args)
746 def storeVm(self, *args):
747 return xstransact.Store(self.vmpath, *args)
749 #
750 # Function to update xenstore /dom/*
751 #
753 def readDom(self, *args):
754 return xstransact.Read(self.dompath, *args)
756 def gatherDom(self, *args):
757 return xstransact.Gather(self.dompath, *args)
759 def _writeDom(self, *args):
760 return xstransact.Write(self.dompath, *args)
762 def _removeDom(self, *args):
763 return xstransact.Remove(self.dompath, *args)
765 def storeDom(self, *args):
766 return xstransact.Store(self.dompath, *args)
768 def _recreateDom(self):
769 complete(self.dompath, lambda t: self._recreateDomFunc(t))
771 def _recreateDomFunc(self, t):
772 t.remove()
773 t.mkdir()
774 t.set_permissions({'dom' : self.domid})
775 t.write('vm', self.vmpath)
777 def _storeDomDetails(self):
778 to_store = {
779 'domid': str(self.domid),
780 'vm': self.vmpath,
781 'name': self.info['name_label'],
782 'console/limit': str(xoptions.get_console_limit() * 1024),
783 'memory/target': str(self.info['memory_static_min'] * 1024)
784 }
786 def f(n, v):
787 if v is not None:
788 if type(v) == bool:
789 to_store[n] = v and "1" or "0"
790 else:
791 to_store[n] = str(v)
793 f('console/port', self.console_port)
794 f('console/ring-ref', self.console_mfn)
795 f('store/port', self.store_port)
796 f('store/ring-ref', self.store_mfn)
798 # elfnotes
799 for n, v in self.info.get_notes().iteritems():
800 n = n.lower().replace('_', '-')
801 if n == 'features':
802 for v in v.split('|'):
803 v = v.replace('_', '-')
804 if v.startswith('!'):
805 f('image/%s/%s' % (n, v[1:]), False)
806 else:
807 f('image/%s/%s' % (n, v), True)
808 else:
809 f('image/%s' % n, v)
811 to_store.update(self._vcpuDomDetails())
813 log.debug("Storing domain details: %s", scrub_password(to_store))
815 self._writeDom(to_store)
817 def _vcpuDomDetails(self):
818 def availability(n):
819 if self.info['vcpu_avail'] & (1 << n):
820 return 'online'
821 else:
822 return 'offline'
824 result = {}
825 for v in range(0, self.info['vcpus_number']):
826 result["cpu/%d/availability" % v] = availability(v)
827 return result
829 #
830 # xenstore watches
831 #
833 def _registerWatches(self):
834 """Register a watch on this VM's entries in the store, and the
835 domain's control/shutdown node, so that when they are changed
836 externally, we keep up to date. This should only be called by {@link
837 #create}, {@link #recreate}, or {@link #restore}, once the domain's
838 details have been written, but before the new instance is returned."""
839 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
840 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
841 self._handleShutdownWatch)
843 def _storeChanged(self, _):
844 log.trace("XendDomainInfo.storeChanged");
846 changed = False
848 # Check whether values in the configuration have
849 # changed in Xenstore.
851 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
853 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
854 for k in cfg_vm])
856 # convert two lists into a python dictionary
857 vm_details = dict(zip(cfg_vm, vm_details))
859 for arg, val in vm_details.items():
860 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
861 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
862 if val != None and val != self.info[xapiarg]:
863 self.info[xapiarg] = val
864 changed= True
866 # Check whether image definition has been updated
867 image_sxp = self._readVm('image')
868 if image_sxp and image_sxp != self.info.image_sxpr():
869 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
870 changed = True
872 if changed:
873 # Update the domain section of the store, as this contains some
874 # parameters derived from the VM configuration.
875 self._storeDomDetails()
877 return 1
879 def _handleShutdownWatch(self, _):
880 log.debug('XendDomainInfo.handleShutdownWatch')
882 reason = self.readDom('control/shutdown')
884 if reason and reason != 'suspend':
885 sst = self.readDom('xend/shutdown_start_time')
886 now = time.time()
887 if sst:
888 self.shutdownStartTime = float(sst)
889 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
890 else:
891 self.shutdownStartTime = now
892 self.storeDom('xend/shutdown_start_time', now)
893 timeout = SHUTDOWN_TIMEOUT
895 log.trace(
896 "Scheduling refreshShutdown on domain %d in %ds.",
897 self.domid, timeout)
898 threading.Timer(timeout, self.refreshShutdown).start()
900 return True
903 #
904 # Public Attributes for the VM
905 #
908 def getDomid(self):
909 return self.domid
911 def setName(self, name):
912 self._checkName(name)
913 self.info['name_label'] = name
914 self.storeVm("name", name)
916 def getName(self):
917 return self.info['name_label']
919 def getDomainPath(self):
920 return self.dompath
922 def getShutdownReason(self):
923 return self.readDom('control/shutdown')
925 def getStorePort(self):
926 """For use only by image.py and XendCheckpoint.py."""
927 return self.store_port
929 def getConsolePort(self):
930 """For use only by image.py and XendCheckpoint.py"""
931 return self.console_port
933 def getFeatures(self):
934 """For use only by image.py."""
935 return self.info['features']
937 def getVCpuCount(self):
938 return self.info['vcpus_number']
940 def setVCpuCount(self, vcpus):
941 if vcpus <= 0:
942 raise XendError('Invalid VCPUs')
944 self.info['vcpu_avail'] = (1 << vcpus) - 1
945 if self.domid >= 0:
946 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
947 # update dom differently depending on whether we are adjusting
948 # vcpu number up or down, otherwise _vcpuDomDetails does not
949 # disable the vcpus
950 if self.info['vcpus_number'] > vcpus:
951 # decreasing
952 self._writeDom(self._vcpuDomDetails())
953 self.info['vcpus_number'] = vcpus
954 else:
955 # same or increasing
956 self.info['vcpus_number'] = vcpus
957 self._writeDom(self._vcpuDomDetails())
958 else:
959 self.info['vcpus_number'] = vcpus
960 xen.xend.XendDomain.instance().managed_config_save(self)
961 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
962 vcpus)
964 def getLabel(self):
965 return security.get_security_info(self.info, 'label')
967 def getMemoryTarget(self):
968 """Get this domain's target memory size, in KB."""
969 return self.info['memory_static_min'] * 1024
971 def getMemoryMaximum(self):
972 """Get this domain's maximum memory size, in KB."""
973 return self.info['memory_static_max'] * 1024
975 def getResume(self):
976 return str(self._resume)
978 def getCap(self):
979 return self.info.get('cpu_cap', 0)
981 def getWeight(self):
982 return self.info.get('cpu_weight', 256)
984 def setResume(self, state):
985 self._resume = state
987 def getRestartCount(self):
988 return self._readVm('xend/restart_count')
990 def refreshShutdown(self, xeninfo = None):
991 """ Checks the domain for whether a shutdown is required.
993 Called from XendDomainInfo and also image.py for HVM images.
994 """
996 # If set at the end of this method, a restart is required, with the
997 # given reason. This restart has to be done out of the scope of
998 # refresh_shutdown_lock.
999 restart_reason = None
1001 self.refresh_shutdown_lock.acquire()
1002 try:
1003 if xeninfo is None:
1004 xeninfo = dom_get(self.domid)
1005 if xeninfo is None:
1006 # The domain no longer exists. This will occur if we have
1007 # scheduled a timer to check for shutdown timeouts and the
1008 # shutdown succeeded. It will also occur if someone
1009 # destroys a domain beneath us. We clean up the domain,
1010 # just in case, but we can't clean up the VM, because that
1011 # VM may have migrated to a different domain on this
1012 # machine.
1013 self.cleanupDomain()
1014 self._stateSet(DOM_STATE_HALTED)
1015 return
1017 if xeninfo['dying']:
1018 # Dying means that a domain has been destroyed, but has not
1019 # yet been cleaned up by Xen. This state could persist
1020 # indefinitely if, for example, another domain has some of its
1021 # pages mapped. We might like to diagnose this problem in the
1022 # future, but for now all we do is make sure that it's not us
1023 # holding the pages, by calling cleanupDomain. We can't
1024 # clean up the VM, as above.
1025 self.cleanupDomain()
1026 self._stateSet(DOM_STATE_SHUTDOWN)
1027 return
1029 elif xeninfo['crashed']:
1030 if self.readDom('xend/shutdown_completed'):
1031 # We've seen this shutdown already, but we are preserving
1032 # the domain for debugging. Leave it alone.
1033 return
1035 log.warn('Domain has crashed: name=%s id=%d.',
1036 self.info['name_label'], self.domid)
1037 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1039 if xoptions.get_enable_dump():
1040 try:
1041 self.dumpCore()
1042 except XendError:
1043 # This error has been logged -- there's nothing more
1044 # we can do in this context.
1045 pass
1047 restart_reason = 'crash'
1048 self._stateSet(DOM_STATE_HALTED)
1050 elif xeninfo['shutdown']:
1051 self._stateSet(DOM_STATE_SHUTDOWN)
1052 if self.readDom('xend/shutdown_completed'):
1053 # We've seen this shutdown already, but we are preserving
1054 # the domain for debugging. Leave it alone.
1055 return
1057 else:
1058 reason = shutdown_reason(xeninfo['shutdown_reason'])
1060 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1061 self.info['name_label'], self.domid, reason)
1062 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1064 self._clearRestart()
1066 if reason == 'suspend':
1067 self._stateSet(DOM_STATE_SUSPENDED)
1068 # Don't destroy the domain. XendCheckpoint will do
1069 # this once it has finished. However, stop watching
1070 # the VM path now, otherwise we will end up with one
1071 # watch for the old domain, and one for the new.
1072 self._unwatchVm()
1073 elif reason in ('poweroff', 'reboot'):
1074 restart_reason = reason
1075 else:
1076 self.destroy()
1078 elif self.dompath is None:
1079 # We have yet to manage to call introduceDomain on this
1080 # domain. This can happen if a restore is in progress, or has
1081 # failed. Ignore this domain.
1082 pass
1083 else:
1084 # Domain is alive. If we are shutting it down, then check
1085 # the timeout on that, and destroy it if necessary.
1086 if xeninfo['paused']:
1087 self._stateSet(DOM_STATE_PAUSED)
1088 else:
1089 self._stateSet(DOM_STATE_RUNNING)
1091 if self.shutdownStartTime:
1092 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1093 self.shutdownStartTime)
1094 if timeout < 0:
1095 log.info(
1096 "Domain shutdown timeout expired: name=%s id=%s",
1097 self.info['name_label'], self.domid)
1098 self.destroy()
1099 finally:
1100 self.refresh_shutdown_lock.release()
1102 if restart_reason:
1103 threading.Thread(target = self._maybeRestart,
1104 args = (restart_reason,)).start()
1108 # Restart functions - handling whether we come back up on shutdown.
1111 def _clearRestart(self):
1112 self._removeDom("xend/shutdown_start_time")
1115 def _maybeRestart(self, reason):
1116 # Dispatch to the correct method based upon the configured on_{reason}
1117 # behaviour.
1118 actions = {"destroy" : self.destroy,
1119 "restart" : self._restart,
1120 "preserve" : self._preserve,
1121 "rename-restart" : self._renameRestart}
1123 action_conf = {
1124 'poweroff': 'actions_after_shutdown',
1125 'reboot': 'actions_after_reboot',
1126 'crash': 'actions_after_crash',
1129 action_target = self.info.get(action_conf.get(reason))
1130 func = actions.get(action_target, None)
1131 if func and callable(func):
1132 func()
1133 else:
1134 self.destroy() # default to destroy
1136 def _renameRestart(self):
1137 self._restart(True)
1139 def _restart(self, rename = False):
1140 """Restart the domain after it has exited.
1142 @param rename True if the old domain is to be renamed and preserved,
1143 False if it is to be destroyed.
1144 """
1145 from xen.xend import XendDomain
1147 if self._readVm(RESTART_IN_PROGRESS):
1148 log.error('Xend failed during restart of domain %s. '
1149 'Refusing to restart to avoid loops.',
1150 str(self.domid))
1151 self.destroy()
1152 return
1154 old_domid = self.domid
1155 self._writeVm(RESTART_IN_PROGRESS, 'True')
1157 now = time.time()
1158 rst = self._readVm('xend/previous_restart_time')
1159 if rst:
1160 rst = float(rst)
1161 timeout = now - rst
1162 if timeout < MINIMUM_RESTART_TIME:
1163 log.error(
1164 'VM %s restarting too fast (%f seconds since the last '
1165 'restart). Refusing to restart to avoid loops.',
1166 self.info['name_label'], timeout)
1167 self.destroy()
1168 return
1170 self._writeVm('xend/previous_restart_time', str(now))
1172 try:
1173 if rename:
1174 self._preserveForRestart()
1175 else:
1176 self._unwatchVm()
1177 self.destroyDomain()
1179 # new_dom's VM will be the same as this domain's VM, except where
1180 # the rename flag has instructed us to call preserveForRestart.
1181 # In that case, it is important that we remove the
1182 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1183 # once the new one is available.
1185 new_dom = None
1186 try:
1187 new_dom = XendDomain.instance().domain_create_from_dict(
1188 self.info)
1189 new_dom.unpause()
1190 rst_cnt = self._readVm('xend/restart_count')
1191 rst_cnt = int(rst_cnt) + 1
1192 self._writeVm('xend/restart_count', str(rst_cnt))
1193 new_dom._removeVm(RESTART_IN_PROGRESS)
1194 except:
1195 if new_dom:
1196 new_dom._removeVm(RESTART_IN_PROGRESS)
1197 new_dom.destroy()
1198 else:
1199 self._removeVm(RESTART_IN_PROGRESS)
1200 raise
1201 except:
1202 log.exception('Failed to restart domain %s.', str(old_domid))
1204 def _preserveForRestart(self):
1205 """Preserve a domain that has been shut down, by giving it a new UUID,
1206 cloning the VM details, and giving it a new name. This allows us to
1207 keep this domain for debugging, but restart a new one in its place
1208 preserving the restart semantics (name and UUID preserved).
1209 """
1211 new_uuid = uuid.createString()
1212 new_name = 'Domain-%s' % new_uuid
1213 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1214 self.info['name_label'], self.domid, self.info['uuid'],
1215 new_name, new_uuid)
1216 self._unwatchVm()
1217 self._releaseDevices()
1218 self.info['name_label'] = new_name
1219 self.info['uuid'] = new_uuid
1220 self.vmpath = XS_VMROOT + new_uuid
1221 self._storeVmDetails()
1222 self._preserve()
1225 def _preserve(self):
1226 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1227 self.domid)
1228 self._unwatchVm()
1229 self.storeDom('xend/shutdown_completed', 'True')
1230 self._stateSet(DOM_STATE_HALTED)
1233 # Debugging ..
1236 def dumpCore(self, corefile = None):
1237 """Create a core dump for this domain.
1239 @raise: XendError if core dumping failed.
1240 """
1242 try:
1243 if not corefile:
1244 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1245 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1246 self.info['name_label'], self.domid)
1248 if os.path.isdir(corefile):
1249 raise XendError("Cannot dump core in a directory: %s" %
1250 corefile)
1252 xc.domain_dumpcore(self.domid, corefile)
1253 except RuntimeError, ex:
1254 corefile_incomp = corefile+'-incomplete'
1255 os.rename(corefile, corefile_incomp)
1256 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1257 self.domid, self.info['name_label'])
1258 raise XendError("Failed to dump core: %s" % str(ex))
1261 # Device creation/deletion functions
1264 def _createDevice(self, deviceClass, devConfig):
1265 return self.getDeviceController(deviceClass).createDevice(devConfig)
1267 def _waitForDevice(self, deviceClass, devid):
1268 return self.getDeviceController(deviceClass).waitForDevice(devid)
1270 def _waitForDeviceUUID(self, dev_uuid):
1271 deviceClass, config = self.info['devices'].get(dev_uuid)
1272 self._waitForDevice(deviceClass, config['devid'])
1274 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1275 return self.getDeviceController(deviceClass).reconfigureDevice(
1276 devid, devconfig)
1278 def _createDevices(self):
1279 """Create the devices for a vm.
1281 @raise: VmError for invalid devices
1282 """
1283 ordered_refs = self.info.ordered_device_refs()
1284 for dev_uuid in ordered_refs:
1285 devclass, config = self.info['devices'][dev_uuid]
1286 if devclass in XendDevices.valid_devices():
1287 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1288 dev_uuid = config.get('uuid')
1289 devid = self._createDevice(devclass, config)
1291 # store devid in XendConfig for caching reasons
1292 if dev_uuid in self.info['devices']:
1293 self.info['devices'][dev_uuid][1]['devid'] = devid
1295 if self.image:
1296 self.image.createDeviceModel()
1298 def _releaseDevices(self, suspend = False):
1299 """Release all domain's devices. Nothrow guarantee."""
1300 if suspend and self.image:
1301 self.image.destroy(suspend)
1302 return
1304 while True:
1305 t = xstransact("%s/device" % self.dompath)
1306 for devclass in XendDevices.valid_devices():
1307 for dev in t.list(devclass):
1308 try:
1309 t.remove(dev)
1310 except:
1311 # Log and swallow any exceptions in removal --
1312 # there's nothing more we can do.
1313 log.exception(
1314 "Device release failed: %s; %s; %s",
1315 self.info['name_label'], devclass, dev)
1316 if t.commit():
1317 break
1319 def getDeviceController(self, name):
1320 """Get the device controller for this domain, and if it
1321 doesn't exist, create it.
1323 @param name: device class name
1324 @type name: string
1325 @rtype: subclass of DevController
1326 """
1327 if name not in self._deviceControllers:
1328 devController = XendDevices.make_controller(name, self)
1329 if not devController:
1330 raise XendError("Unknown device type: %s" % name)
1331 self._deviceControllers[name] = devController
1333 return self._deviceControllers[name]
1336 # Migration functions (public)
1339 def testMigrateDevices(self, network, dst):
1340 """ Notify all device about intention of migration
1341 @raise: XendError for a device that cannot be migrated
1342 """
1343 for (n, c) in self.info.all_devices_sxpr():
1344 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1345 if rc != 0:
1346 raise XendError("Device of type '%s' refuses migration." % n)
1348 def migrateDevices(self, network, dst, step, domName=''):
1349 """Notify the devices about migration
1350 """
1351 ctr = 0
1352 try:
1353 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1354 self.migrateDevice(dev_type, dev_conf, network, dst,
1355 step, domName)
1356 ctr = ctr + 1
1357 except:
1358 for dev_type, dev_conf in self.info.all_devices_sxpr():
1359 if ctr == 0:
1360 step = step - 1
1361 ctr = ctr - 1
1362 self._recoverMigrateDevice(dev_type, dev_conf, network,
1363 dst, step, domName)
1364 raise
1366 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1367 step, domName=''):
1368 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1369 network, dst, step, domName)
1371 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1372 dst, step, domName=''):
1373 return self.getDeviceController(deviceClass).recover_migrate(
1374 deviceConfig, network, dst, step, domName)
1377 ## private:
1379 def _constructDomain(self):
1380 """Construct the domain.
1382 @raise: VmError on error
1383 """
1385 log.debug('XendDomainInfo.constructDomain')
1387 self.shutdownStartTime = None
1389 image_cfg = self.info.get('image', {})
1390 hvm = image_cfg.has_key('hvm')
1392 if hvm:
1393 info = xc.xeninfo()
1394 if 'hvm' not in info['xen_caps']:
1395 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1396 "supported by your CPU and enabled in your "
1397 "BIOS?")
1399 self.domid = xc.domain_create(
1400 domid = 0,
1401 ssidref = security.get_security_info(self.info, 'ssidref'),
1402 handle = uuid.fromString(self.info['uuid']),
1403 hvm = int(hvm))
1405 if self.domid < 0:
1406 raise VmError('Creating domain failed: name=%s' %
1407 self.info['name_label'])
1409 self.dompath = GetDomainPath(self.domid)
1411 self._recreateDom()
1413 # Set maximum number of vcpus in domain
1414 xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
1416 # register the domain in the list
1417 from xen.xend import XendDomain
1418 XendDomain.instance().add_domain(self)
1420 def _introduceDomain(self):
1421 assert self.domid is not None
1422 assert self.store_mfn is not None
1423 assert self.store_port is not None
1425 try:
1426 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1427 except RuntimeError, exn:
1428 raise XendError(str(exn))
1431 def _initDomain(self):
1432 log.debug('XendDomainInfo.initDomain: %s %s',
1433 self.domid,
1434 self.info['cpu_weight'])
1436 self._configureBootloader()
1438 if not self._infoIsSet('image'):
1439 raise VmError('Missing image in configuration')
1441 try:
1442 self.image = image.create(self,
1443 self.info,
1444 self.info['image'],
1445 self.info['devices'])
1447 localtime = self.info.get('platform_localtime', False)
1448 if localtime:
1449 xc.domain_set_time_offset(self.domid)
1451 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1453 # repin domain vcpus if a restricted cpus list is provided
1454 # this is done prior to memory allocation to aide in memory
1455 # distribution for NUMA systems.
1456 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1457 for v in range(0, self.info['vcpus_number']):
1458 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1460 # Use architecture- and image-specific calculations to determine
1461 # the various headrooms necessary, given the raw configured
1462 # values. maxmem, memory, and shadow are all in KiB.
1463 memory = self.image.getRequiredAvailableMemory(
1464 self.info['memory_static_min'] * 1024)
1465 maxmem = self.image.getRequiredAvailableMemory(
1466 self.info['memory_static_max'] * 1024)
1467 shadow = self.image.getRequiredShadowMemory(
1468 self.info['shadow_memory'] * 1024,
1469 self.info['memory_static_max'] * 1024)
1471 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'],)
1472 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1473 # takes MiB and we must not round down and end up under-providing.
1474 shadow = ((shadow + 1023) / 1024) * 1024
1476 # set memory limit
1477 xc.domain_setmaxmem(self.domid, maxmem)
1479 # Make sure there's enough RAM available for the domain
1480 balloon.free(memory + shadow)
1482 # Set up the shadow memory
1483 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1484 self.info['shadow_memory'] = shadow_cur
1486 self._createChannels()
1488 channel_details = self.image.createImage()
1490 self.store_mfn = channel_details['store_mfn']
1491 if 'console_mfn' in channel_details:
1492 self.console_mfn = channel_details['console_mfn']
1493 if 'notes' in channel_details:
1494 self.info.set_notes(channel_details['notes'])
1496 self._introduceDomain()
1498 self._createDevices()
1500 self.image.cleanupBootloading()
1502 self.info['start_time'] = time.time()
1504 self._stateSet(DOM_STATE_RUNNING)
1505 except RuntimeError, exn:
1506 log.exception("XendDomainInfo.initDomain: exception occurred")
1507 self.image.cleanupBootloading()
1508 raise VmError(str(exn))
1511 def cleanupDomain(self):
1512 """Cleanup domain resources; release devices. Idempotent. Nothrow
1513 guarantee."""
1515 self.refresh_shutdown_lock.acquire()
1516 try:
1517 self.unwatchShutdown()
1518 self._releaseDevices()
1519 bootloader_tidy(self)
1521 if self.image:
1522 try:
1523 self.image.destroy()
1524 except:
1525 log.exception(
1526 "XendDomainInfo.cleanup: image.destroy() failed.")
1527 self.image = None
1529 try:
1530 self._removeDom()
1531 except:
1532 log.exception("Removing domain path failed.")
1534 self._stateSet(DOM_STATE_HALTED)
1535 finally:
1536 self.refresh_shutdown_lock.release()
1539 def unwatchShutdown(self):
1540 """Remove the watch on the domain's control/shutdown node, if any.
1541 Idempotent. Nothrow guarantee. Expects to be protected by the
1542 refresh_shutdown_lock."""
1544 try:
1545 try:
1546 if self.shutdownWatch:
1547 self.shutdownWatch.unwatch()
1548 finally:
1549 self.shutdownWatch = None
1550 except:
1551 log.exception("Unwatching control/shutdown failed.")
1553 def waitForShutdown(self):
1554 self.state_updated.acquire()
1555 try:
1556 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1557 self.state_updated.wait()
1558 finally:
1559 self.state_updated.release()
1563 # TODO: recategorise - called from XendCheckpoint
1566 def completeRestore(self, store_mfn, console_mfn):
1568 log.debug("XendDomainInfo.completeRestore")
1570 self.store_mfn = store_mfn
1571 self.console_mfn = console_mfn
1573 self._introduceDomain()
1574 image_cfg = self.info.get('image', {})
1575 hvm = image_cfg.has_key('hvm')
1576 if hvm:
1577 self.image = image.create(self,
1578 self.info,
1579 self.info['image'],
1580 self.info['devices'])
1581 if self.image:
1582 self.image.createDeviceModel(True)
1583 self.image.register_shutdown_watch()
1584 self._storeDomDetails()
1585 self._registerWatches()
1586 self.refreshShutdown()
1588 log.debug("XendDomainInfo.completeRestore done")
1591 def _endRestore(self):
1592 self.setResume(False)
1595 # VM Destroy
1598 def _prepare_phantom_paths(self):
1599 # get associated devices to destroy
1600 # build list of phantom devices to be removed after normal devices
1601 plist = []
1602 if self.domid is not None:
1603 from xen.xend.xenstore.xstransact import xstransact
1604 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1605 for dev in t.list():
1606 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1607 % (self.dompath, dev))
1608 if backend_phantom_vbd is not None:
1609 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1610 % backend_phantom_vbd)
1611 plist.append(backend_phantom_vbd)
1612 plist.append(frontend_phantom_vbd)
1613 return plist
1615 def _cleanup_phantom_devs(self, plist):
1616 # remove phantom devices
1617 if not plist == []:
1618 time.sleep(2)
1619 for paths in plist:
1620 if paths.find('backend') != -1:
1621 from xen.xend.server import DevController
1622 # Modify online status /before/ updating state (latter is watched by
1623 # drivers, so this ordering avoids a race).
1624 xstransact.Write(paths, 'online', "0")
1625 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1626 # force
1627 xstransact.Remove(paths)
1629 def destroy(self):
1630 """Cleanup VM and destroy domain. Nothrow guarantee."""
1632 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1634 paths = self._prepare_phantom_paths()
1636 self._cleanupVm()
1637 if self.dompath is not None:
1638 self.destroyDomain()
1640 self._cleanup_phantom_devs(paths)
1642 def destroyDomain(self):
1643 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1645 paths = self._prepare_phantom_paths()
1647 try:
1648 if self.domid is not None:
1649 xc.domain_destroy(self.domid)
1650 self.domid = None
1651 for state in DOM_STATES_OLD:
1652 self.info[state] = 0
1653 except:
1654 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1656 from xen.xend import XendDomain
1657 XendDomain.instance().remove_domain(self)
1659 self.cleanupDomain()
1660 self._cleanup_phantom_devs(paths)
1662 def resumeDomain(self):
1663 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1665 try:
1666 if self.domid is not None:
1667 xc.domain_resume(self.domid)
1668 ResumeDomain(self.domid)
1669 except:
1670 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1673 # Channels for xenstore and console
1676 def _createChannels(self):
1677 """Create the channels to the domain.
1678 """
1679 self.store_port = self._createChannel()
1680 self.console_port = self._createChannel()
1683 def _createChannel(self):
1684 """Create an event channel to the domain.
1685 """
1686 try:
1687 if self.domid != None:
1688 return xc.evtchn_alloc_unbound(domid = self.domid,
1689 remote_dom = 0)
1690 except:
1691 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1692 raise
1694 def _resetChannels(self):
1695 """Reset all event channels in the domain.
1696 """
1697 try:
1698 if self.domid != None:
1699 return xc.evtchn_reset(dom = self.domid)
1700 except:
1701 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1702 raise
1706 # Bootloader configuration
1709 def _configureBootloader(self):
1710 """Run the bootloader if we're configured to do so."""
1712 blexec = self.info['PV_bootloader']
1713 bootloader_args = self.info['PV_bootloader_args']
1714 kernel = self.info['PV_kernel']
1715 ramdisk = self.info['PV_ramdisk']
1716 args = self.info['PV_args']
1717 boot = self.info['HVM_boot_policy']
1719 if boot:
1720 # HVM booting.
1721 self.info['image']['type'] = 'hvm'
1722 if not 'devices' in self.info['image']:
1723 self.info['image']['devices'] = {}
1724 self.info['image']['devices']['boot'] = \
1725 self.info['HVM_boot_params'].get('order', 'dc')
1726 elif not blexec and kernel:
1727 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1728 # will be picked up by image.py.
1729 pass
1730 else:
1731 # Boot using bootloader
1732 if not blexec or blexec == 'pygrub':
1733 blexec = osdep.pygrub_path
1735 blcfg = None
1736 disks = [x for x in self.info['vbd_refs']
1737 if self.info['devices'][x][1]['bootable']]
1739 if not disks:
1740 msg = "Had a bootloader specified, but no disks are bootable"
1741 log.error(msg)
1742 raise VmError(msg)
1744 devinfo = self.info['devices'][disks[0]]
1745 devtype = devinfo[0]
1746 disk = devinfo[1]['uname']
1748 fn = blkdev_uname_to_file(disk)
1749 mounted = devtype == 'tap' and not os.stat(fn).st_rdev
1750 if mounted:
1751 # This is a file, not a device. pygrub can cope with a
1752 # file if it's raw, but if it's QCOW or other such formats
1753 # used through blktap, then we need to mount it first.
1755 log.info("Mounting %s on %s." %
1756 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1758 vbd = {
1759 'mode': 'RO',
1760 'device': BOOTLOADER_LOOPBACK_DEVICE,
1763 from xen.xend import XendDomain
1764 dom0 = XendDomain.instance().privilegedDomain()
1765 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
1766 fn = BOOTLOADER_LOOPBACK_DEVICE
1768 try:
1769 blcfg = bootloader(blexec, fn, self, False,
1770 bootloader_args, kernel, ramdisk, args)
1771 finally:
1772 if mounted:
1773 log.info("Unmounting %s from %s." %
1774 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1776 dom0.destroyDevice('tap', '/dev/xvdp')
1778 if blcfg is None:
1779 msg = "Had a bootloader specified, but can't find disk"
1780 log.error(msg)
1781 raise VmError(msg)
1783 self.info.update_with_image_sxp(blcfg, True)
1787 # VM Functions
1790 def _readVMDetails(self, params):
1791 """Read the specified parameters from the store.
1792 """
1793 try:
1794 return self._gatherVm(*params)
1795 except ValueError:
1796 # One of the int/float entries in params has a corresponding store
1797 # entry that is invalid. We recover, because older versions of
1798 # Xend may have put the entry there (memory/target, for example),
1799 # but this is in general a bad situation to have reached.
1800 log.exception(
1801 "Store corrupted at %s! Domain %d's configuration may be "
1802 "affected.", self.vmpath, self.domid)
1803 return []
1805 def _cleanupVm(self):
1806 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1808 self._unwatchVm()
1810 try:
1811 self._removeVm()
1812 except:
1813 log.exception("Removing VM path failed.")
1816 def checkLiveMigrateMemory(self):
1817 """ Make sure there's enough memory to migrate this domain """
1818 overhead_kb = 0
1819 if arch.type == "x86":
1820 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1821 # the minimum that Xen would allocate if no value were given.
1822 overhead_kb = self.info['vcpus_number'] * 1024 + \
1823 self.info['memory_static_max'] * 4
1824 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1825 # The domain might already have some shadow memory
1826 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1827 if overhead_kb > 0:
1828 balloon.free(overhead_kb)
1830 def _unwatchVm(self):
1831 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1832 guarantee."""
1833 try:
1834 try:
1835 if self.vmWatch:
1836 self.vmWatch.unwatch()
1837 finally:
1838 self.vmWatch = None
1839 except:
1840 log.exception("Unwatching VM path failed.")
1842 def testDeviceComplete(self):
1843 """ For Block IO migration safety we must ensure that
1844 the device has shutdown correctly, i.e. all blocks are
1845 flushed to disk
1846 """
1847 start = time.time()
1848 while True:
1849 test = 0
1850 diff = time.time() - start
1851 for i in self.getDeviceController('vbd').deviceIDs():
1852 test = 1
1853 log.info("Dev %s still active, looping...", i)
1854 time.sleep(0.1)
1856 if test == 0:
1857 break
1858 if diff >= MIGRATE_TIMEOUT:
1859 log.info("Dev still active but hit max loop timeout")
1860 break
1862 def testvifsComplete(self):
1863 """ In case vifs are released and then created for the same
1864 domain, we need to wait the device shut down.
1865 """
1866 start = time.time()
1867 while True:
1868 test = 0
1869 diff = time.time() - start
1870 for i in self.getDeviceController('vif').deviceIDs():
1871 test = 1
1872 log.info("Dev %s still active, looping...", i)
1873 time.sleep(0.1)
1875 if test == 0:
1876 break
1877 if diff >= MIGRATE_TIMEOUT:
1878 log.info("Dev still active but hit max loop timeout")
1879 break
1881 def _storeVmDetails(self):
1882 to_store = {}
1884 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1885 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1886 if self._infoIsSet(info_key):
1887 to_store[key] = str(self.info[info_key])
1889 if self.info.get('image'):
1890 image_sxpr = self.info.image_sxpr()
1891 if image_sxpr:
1892 to_store['image'] = sxp.to_string(image_sxpr)
1894 if self._infoIsSet('security'):
1895 secinfo = self.info['security']
1896 to_store['security'] = sxp.to_string(secinfo)
1897 for idx in range(0, len(secinfo)):
1898 if secinfo[idx][0] == 'access_control':
1899 to_store['security/access_control'] = sxp.to_string(
1900 [secinfo[idx][1], secinfo[idx][2]])
1901 for aidx in range(1, len(secinfo[idx])):
1902 if secinfo[idx][aidx][0] == 'label':
1903 to_store['security/access_control/label'] = \
1904 secinfo[idx][aidx][1]
1905 if secinfo[idx][aidx][0] == 'policy':
1906 to_store['security/access_control/policy'] = \
1907 secinfo[idx][aidx][1]
1908 if secinfo[idx][0] == 'ssidref':
1909 to_store['security/ssidref'] = str(secinfo[idx][1])
1912 if not self._readVm('xend/restart_count'):
1913 to_store['xend/restart_count'] = str(0)
1915 log.debug("Storing VM details: %s", scrub_password(to_store))
1917 self._writeVm(to_store)
1918 self._setVmPermissions()
1921 def _setVmPermissions(self):
1922 """Allow the guest domain to read its UUID. We don't allow it to
1923 access any other entry, for security."""
1924 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1925 { 'dom' : self.domid,
1926 'read' : True,
1927 'write' : False })
1930 # Utility functions
1933 def _stateSet(self, state):
1934 self.state_updated.acquire()
1935 try:
1936 if self.state != state:
1937 self.state = state
1938 self.state_updated.notifyAll()
1939 finally:
1940 self.state_updated.release()
1942 def _infoIsSet(self, name):
1943 return name in self.info and self.info[name] is not None
1945 def _checkName(self, name):
1946 """Check if a vm name is valid. Valid names contain alphabetic
1947 characters, digits, or characters in '_-.:/+'.
1948 The same name cannot be used for more than one vm at the same time.
1950 @param name: name
1951 @raise: VmError if invalid
1952 """
1953 from xen.xend import XendDomain
1955 if name is None or name == '':
1956 raise VmError('Missing VM Name')
1958 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
1959 raise VmError('Invalid VM Name')
1961 dom = XendDomain.instance().domain_lookup_nr(name)
1962 if dom and dom.info['uuid'] != self.info['uuid']:
1963 raise VmError("VM name '%s' already exists%s" %
1964 (name,
1965 dom.domid is not None and
1966 (" as domain %s" % str(dom.domid)) or ""))
1969 def update(self, info = None, refresh = True):
1970 """Update with info from xc.domain_getinfo().
1971 """
1972 log.trace("XendDomainInfo.update(%s) on domain %s", info,
1973 str(self.domid))
1975 if not info:
1976 info = dom_get(self.domid)
1977 if not info:
1978 return
1980 #manually update ssidref / security fields
1981 if security.on() and info.has_key('ssidref'):
1982 if (info['ssidref'] != 0) and self.info.has_key('security'):
1983 security_field = self.info['security']
1984 if not security_field:
1985 #create new security element
1986 self.info.update({'security':
1987 [['ssidref', str(info['ssidref'])]]})
1989 #ssidref field not used any longer
1990 if 'ssidref' in info:
1991 info.pop('ssidref')
1993 # make sure state is reset for info
1994 # TODO: we should eventually get rid of old_dom_states
1996 self.info.update_config(info)
1997 self._update_consoles()
1999 if refresh:
2000 self.refreshShutdown(info)
2002 log.trace("XendDomainInfo.update done on domain %s: %s",
2003 str(self.domid), self.info)
2005 def sxpr(self, ignore_store = False, legacy_only = True):
2006 result = self.info.to_sxp(domain = self,
2007 ignore_devices = ignore_store,
2008 legacy_only = legacy_only)
2010 #if not ignore_store and self.dompath:
2011 # vnc_port = self.readDom('console/vnc-port')
2012 # if vnc_port is not None:
2013 # result.append(['device',
2014 # ['console', ['vnc-port', str(vnc_port)]]])
2016 return result
2018 # Xen API
2019 # ----------------------------------------------------------------
2021 def get_uuid(self):
2022 dom_uuid = self.info.get('uuid')
2023 if not dom_uuid: # if it doesn't exist, make one up
2024 dom_uuid = uuid.createString()
2025 self.info['uuid'] = dom_uuid
2026 return dom_uuid
2028 def get_memory_static_max(self):
2029 return self.info.get('memory_static_max', 0)
2030 def get_memory_static_min(self):
2031 return self.info.get('memory_static_min', 0)
2032 def get_memory_dynamic_max(self):
2033 return self.info.get('memory_dynamic_max', 0)
2034 def get_memory_dynamic_min(self):
2035 return self.info.get('memory_dynamic_min', 0)
2037 def get_vcpus_policy(self):
2038 sched_id = xc.sched_id_get()
2039 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
2040 return 'sedf'
2041 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
2042 return 'credit'
2043 else:
2044 return 'unknown'
2045 def get_vcpus_params(self):
2046 if self.getDomid() is None:
2047 return self.info['vcpus_params']
2049 retval = xc.sched_credit_domain_get(self.getDomid())
2050 return retval
2051 def get_power_state(self):
2052 return XEN_API_VM_POWER_STATE[self.state]
2053 def get_platform_std_vga(self):
2054 return self.info.get('platform_std_vga', False)
2055 def get_platform_serial(self):
2056 return self.info.get('platform_serial', '')
2057 def get_platform_localtime(self):
2058 return self.info.get('platform_localtime', False)
2059 def get_platform_clock_offset(self):
2060 return self.info.get('platform_clock_offset', False)
2061 def get_platform_enable_audio(self):
2062 return self.info.get('platform_enable_audio', False)
2063 def get_platform_keymap(self):
2064 return self.info.get('platform_keymap', '')
2065 def get_pci_bus(self):
2066 return self.info.get('pci_bus', '')
2067 def get_tools_version(self):
2068 return self.info.get('tools_version', {})
2070 def get_on_shutdown(self):
2071 after_shutdown = self.info.get('action_after_shutdown')
2072 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2073 return XEN_API_ON_NORMAL_EXIT[-1]
2074 return after_shutdown
2076 def get_on_reboot(self):
2077 after_reboot = self.info.get('action_after_reboot')
2078 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2079 return XEN_API_ON_NORMAL_EXIT[-1]
2080 return after_reboot
2082 def get_on_suspend(self):
2083 # TODO: not supported
2084 after_suspend = self.info.get('action_after_suspend')
2085 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2086 return XEN_API_ON_NORMAL_EXIT[-1]
2087 return after_suspend
2089 def get_on_crash(self):
2090 after_crash = self.info.get('action_after_crash')
2091 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2092 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2093 return after_crash
2095 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2096 """ Get's a device configuration either from XendConfig or
2097 from the DevController.
2099 @param dev_class: device class, either, 'vbd' or 'vif'
2100 @param dev_uuid: device UUID
2102 @rtype: dictionary
2103 """
2104 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2106 # shortcut if the domain isn't started because
2107 # the devcontrollers will have no better information
2108 # than XendConfig.
2109 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2110 if dev_config:
2111 return copy.deepcopy(dev_config)
2112 return None
2114 # instead of using dev_class, we use the dev_type
2115 # that is from XendConfig.
2116 controller = self.getDeviceController(dev_type)
2117 if not controller:
2118 return None
2120 all_configs = controller.getAllDeviceConfigurations()
2121 if not all_configs:
2122 return None
2124 updated_dev_config = copy.deepcopy(dev_config)
2125 for _devid, _devcfg in all_configs.items():
2126 if _devcfg.get('uuid') == dev_uuid:
2127 updated_dev_config.update(_devcfg)
2128 updated_dev_config['id'] = _devid
2129 return updated_dev_config
2131 return updated_dev_config
2133 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2134 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2135 if not config:
2136 return {}
2138 config['VM'] = self.get_uuid()
2140 if dev_class == 'vif':
2141 if not config.has_key('name'):
2142 config['name'] = config.get('vifname', '')
2143 if not config.has_key('MAC'):
2144 config['MAC'] = config.get('mac', '')
2145 if not config.has_key('type'):
2146 config['type'] = 'paravirtualised'
2147 if not config.has_key('device'):
2148 devid = config.get('id')
2149 if devid != None:
2150 config['device'] = 'eth%d' % devid
2151 else:
2152 config['device'] = ''
2154 if not config.has_key('network'):
2155 try:
2156 config['network'] = \
2157 XendNode.instance().bridge_to_network(
2158 config.get('bridge')).uuid
2159 except Exception:
2160 log.exception('bridge_to_network')
2161 # Ignore this for now -- it may happen if the device
2162 # has been specified using the legacy methods, but at
2163 # some point we're going to have to figure out how to
2164 # handle that properly.
2166 config['MTU'] = 1500 # TODO
2168 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2169 xennode = XendNode.instance()
2170 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2171 config['io_read_kbs'] = rx_bps/1024
2172 config['io_write_kbs'] = tx_bps/1024
2173 else:
2174 config['io_read_kbs'] = 0.0
2175 config['io_write_kbs'] = 0.0
2177 if dev_class == 'vbd':
2179 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2180 controller = self.getDeviceController(dev_class)
2181 devid, _1, _2 = controller.getDeviceDetails(config)
2182 xennode = XendNode.instance()
2183 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2184 config['io_read_kbs'] = rd_blkps
2185 config['io_write_kbs'] = wr_blkps
2186 else:
2187 config['io_read_kbs'] = 0.0
2188 config['io_write_kbs'] = 0.0
2190 config['VDI'] = config.get('VDI', '')
2191 config['device'] = config.get('dev', '')
2192 if ':' in config['device']:
2193 vbd_name, vbd_type = config['device'].split(':', 1)
2194 config['device'] = vbd_name
2195 if vbd_type == 'cdrom':
2196 config['type'] = XEN_API_VBD_TYPE[0]
2197 else:
2198 config['type'] = XEN_API_VBD_TYPE[1]
2200 config['driver'] = 'paravirtualised' # TODO
2201 config['image'] = config.get('uname', '')
2203 if config.get('mode', 'r') == 'r':
2204 config['mode'] = 'RO'
2205 else:
2206 config['mode'] = 'RW'
2208 if dev_class == 'vtpm':
2209 if not config.has_key('type'):
2210 config['type'] = 'paravirtualised' # TODO
2211 if not config.has_key('backend'):
2212 config['backend'] = "00000000-0000-0000-0000-000000000000"
2214 return config
2216 def get_dev_property(self, dev_class, dev_uuid, field):
2217 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2218 try:
2219 return config[field]
2220 except KeyError:
2221 raise XendError('Invalid property for device: %s' % field)
2223 def set_dev_property(self, dev_class, dev_uuid, field, value):
2224 self.info['devices'][dev_uuid][1][field] = value
2226 def get_vcpus_util(self):
2227 vcpu_util = {}
2228 xennode = XendNode.instance()
2229 if 'vcpus_number' in self.info and self.domid != None:
2230 for i in range(0, self.info['vcpus_number']):
2231 util = xennode.get_vcpu_util(self.domid, i)
2232 vcpu_util[str(i)] = util
2234 return vcpu_util
2236 def get_consoles(self):
2237 return self.info.get('console_refs', [])
2239 def get_vifs(self):
2240 return self.info.get('vif_refs', [])
2242 def get_vbds(self):
2243 return self.info.get('vbd_refs', [])
2245 def get_vtpms(self):
2246 return self.info.get('vtpm_refs', [])
2248 def create_vbd(self, xenapi_vbd, vdi_image_path):
2249 """Create a VBD using a VDI from XendStorageRepository.
2251 @param xenapi_vbd: vbd struct from the Xen API
2252 @param vdi_image_path: VDI UUID
2253 @rtype: string
2254 @return: uuid of the device
2255 """
2256 xenapi_vbd['image'] = vdi_image_path
2257 log.debug('create_vbd: %s' % xenapi_vbd)
2258 dev_uuid = ''
2259 if vdi_image_path.startswith('tap'):
2260 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2261 else:
2262 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2264 if not dev_uuid:
2265 raise XendError('Failed to create device')
2267 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2268 _, config = self.info['devices'][dev_uuid]
2269 dev_control = None
2271 if vdi_image_path.startswith('tap'):
2272 dev_control = self.getDeviceController('tap')
2273 else:
2274 dev_control = self.getDeviceController('vbd')
2276 config['devid'] = dev_control.createDevice(config)
2278 return dev_uuid
2280 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2281 """Create a VBD using a VDI from XendStorageRepository.
2283 @param xenapi_vbd: vbd struct from the Xen API
2284 @param vdi_image_path: VDI UUID
2285 @rtype: string
2286 @return: uuid of the device
2287 """
2288 xenapi_vbd['image'] = vdi_image_path
2289 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2290 if not dev_uuid:
2291 raise XendError('Failed to create device')
2293 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2294 _, config = self.info['devices'][dev_uuid]
2295 config['devid'] = self.getDeviceController('tap').createDevice(config)
2297 return config['devid']
2299 def create_vif(self, xenapi_vif):
2300 """Create VIF device from the passed struct in Xen API format.
2302 @param xenapi_vif: Xen API VIF Struct.
2303 @rtype: string
2304 @return: UUID
2305 """
2306 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2307 if not dev_uuid:
2308 raise XendError('Failed to create device')
2310 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2311 _, config = self.info['devices'][dev_uuid]
2312 config['devid'] = self.getDeviceController('vif').createDevice(config)
2314 return dev_uuid
2316 def create_vtpm(self, xenapi_vtpm):
2317 """Create a VTPM device from the passed struct in Xen API format.
2319 @return: uuid of the device
2320 @rtype: string
2321 """
2323 if self.state not in (DOM_STATE_HALTED,):
2324 raise VmError("Can only add vTPM to a halted domain.")
2325 if self.get_vtpms() != []:
2326 raise VmError('Domain already has a vTPM.')
2327 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2328 if not dev_uuid:
2329 raise XendError('Failed to create device')
2331 return dev_uuid
2333 def create_console(self, xenapi_console):
2334 """ Create a console device from a Xen API struct.
2336 @return: uuid of device
2337 @rtype: string
2338 """
2339 if self.state not in (DOM_STATE_HALTED,):
2340 raise VmError("Can only add console to a halted domain.")
2342 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2343 if not dev_uuid:
2344 raise XendError('Failed to create device')
2346 return dev_uuid
2348 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2349 if dev_uuid not in self.info['devices']:
2350 raise XendError('Device does not exist')
2352 try:
2353 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2354 _, config = self.info['devices'][dev_uuid]
2355 devid = config.get('devid')
2356 if devid != None:
2357 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2358 else:
2359 raise XendError('Unable to get devid for device: %s:%s' %
2360 (dev_type, dev_uuid))
2361 finally:
2362 del self.info['devices'][dev_uuid]
2363 self.info['%s_refs' % dev_type].remove(dev_uuid)
2365 def destroy_vbd(self, dev_uuid):
2366 self.destroy_device_by_uuid('vbd', dev_uuid)
2368 def destroy_vif(self, dev_uuid):
2369 self.destroy_device_by_uuid('vif', dev_uuid)
2371 def destroy_vtpm(self, dev_uuid):
2372 self.destroy_device_by_uuid('vtpm', dev_uuid)
2374 def has_device(self, dev_class, dev_uuid):
2375 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2377 def __str__(self):
2378 return '<domain id=%s name=%s memory=%s state=%s>' % \
2379 (str(self.domid), self.info['name_label'],
2380 str(self.info['memory_static_min']), DOM_STATES[self.state])
2382 __repr__ = __str__