ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 14558:f4ce943f25f6

Fix vbd_create etc, add devices to paused domains

signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
author Tom Wilkie <tom.wilkie@gmail.com>
date Mon Mar 26 13:29:27 2007 +0100 (2007-03-26)
parents eb52a7f22e72
children 15c74855ba68
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005-2007 XenSource Ltd
17 #============================================================================
19 """Representation of a single domain.
20 Includes support for domain construction, using
21 open-ended configurations.
23 Author: Mike Wray <mike.wray@hp.com>
25 """
27 import logging
28 import time
29 import threading
30 import re
31 import copy
32 import os
33 from types import StringTypes
35 import xen.lowlevel.xc
36 from xen.util import asserts
37 from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype
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 from xen.xend.XendVMMetrics import XendVMMetrics
56 MIGRATE_TIMEOUT = 30.0
57 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
59 xc = xen.lowlevel.xc.xc()
60 xoptions = XendOptions.instance()
62 log = logging.getLogger("xend.XendDomainInfo")
63 #log.setLevel(logging.TRACE)
66 def create(config):
67 """Creates and start a VM using the supplied configuration.
69 @param config: A configuration object involving lists of tuples.
70 @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
72 @rtype: XendDomainInfo
73 @return: An up and running XendDomainInfo instance
74 @raise VmError: Invalid configuration or failure to start.
75 """
77 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
78 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config))
79 try:
80 vm.start()
81 except:
82 log.exception('Domain construction failed')
83 vm.destroy()
84 raise
86 return vm
88 def create_from_dict(config_dict):
89 """Creates and start a VM using the supplied configuration.
91 @param config_dict: An configuration dictionary.
93 @rtype: XendDomainInfo
94 @return: An up and running XendDomainInfo instance
95 @raise VmError: Invalid configuration or failure to start.
96 """
98 log.debug("XendDomainInfo.create_from_dict(%s)",
99 scrub_password(config_dict))
100 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
101 try:
102 vm.start()
103 except:
104 log.exception('Domain construction failed')
105 vm.destroy()
106 raise
107 return vm
109 def recreate(info, priv):
110 """Create the VM object for an existing domain. The domain must not
111 be dying, as the paths in the store should already have been removed,
112 and asking us to recreate them causes problems.
114 @param xeninfo: Parsed configuration
115 @type xeninfo: Dictionary
116 @param priv: Is a privileged domain (Dom 0)
117 @type priv: bool
119 @rtype: XendDomainInfo
120 @return: A up and running XendDomainInfo instance
121 @raise VmError: Invalid configuration.
122 @raise XendError: Errors with configuration.
123 """
125 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
127 assert not info['dying']
129 xeninfo = XendConfig.XendConfig(dominfo = info)
130 xeninfo['is_control_domain'] = priv
131 xeninfo['is_a_template'] = False
132 domid = xeninfo['domid']
133 uuid1 = uuid.fromString(xeninfo['uuid'])
134 needs_reinitialising = False
136 dompath = GetDomainPath(domid)
137 if not dompath:
138 raise XendError('No domain path in store for existing '
139 'domain %d' % domid)
141 log.info("Recreating domain %d, UUID %s. at %s" %
142 (domid, xeninfo['uuid'], dompath))
144 # need to verify the path and uuid if not Domain-0
145 # if the required uuid and vm aren't set, then that means
146 # we need to recreate the dom with our own values
147 #
148 # NOTE: this is probably not desirable, really we should just
149 # abort or ignore, but there may be cases where xenstore's
150 # entry disappears (eg. xenstore-rm /)
151 #
152 try:
153 vmpath = xstransact.Read(dompath, "vm")
154 if not vmpath:
155 log.warn('/local/domain/%d/vm is missing. recreate is '
156 'confused, trying our best to recover' % domid)
157 needs_reinitialising = True
158 raise XendError('reinit')
160 uuid2_str = xstransact.Read(vmpath, "uuid")
161 if not uuid2_str:
162 log.warn('%s/uuid/ is missing. recreate is confused, '
163 'trying our best to recover' % vmpath)
164 needs_reinitialising = True
165 raise XendError('reinit')
167 uuid2 = uuid.fromString(uuid2_str)
168 if uuid1 != uuid2:
169 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
170 'Trying out best to recover' % domid)
171 needs_reinitialising = True
172 except XendError:
173 pass # our best shot at 'goto' in python :)
175 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv)
177 if needs_reinitialising:
178 vm._recreateDom()
179 vm._removeVm()
180 vm._storeVmDetails()
181 vm._storeDomDetails()
183 vm.image = image.create(vm, vm.info)
184 vm.image.recreate()
186 vm._registerWatches()
187 vm.refreshShutdown(xeninfo)
189 # register the domain in the list
190 from xen.xend import XendDomain
191 XendDomain.instance().add_domain(vm)
193 return vm
196 def restore(config):
197 """Create a domain and a VM object to do a restore.
199 @param config: Domain SXP configuration
200 @type config: list of lists. (see C{create})
202 @rtype: XendDomainInfo
203 @return: A up and running XendDomainInfo instance
204 @raise VmError: Invalid configuration or failure to start.
205 @raise XendError: Errors with configuration.
206 """
208 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
209 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
210 resume = True)
211 try:
212 vm.resume()
213 return vm
214 except:
215 vm.destroy()
216 raise
218 def createDormant(domconfig):
219 """Create a dormant/inactive XenDomainInfo without creating VM.
220 This is for creating instances of persistent domains that are not
221 yet start.
223 @param domconfig: Parsed configuration
224 @type domconfig: XendConfig object
226 @rtype: XendDomainInfo
227 @return: A up and running XendDomainInfo instance
228 @raise XendError: Errors with configuration.
229 """
231 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
233 # domid does not make sense for non-running domains.
234 domconfig.pop('domid', None)
235 vm = XendDomainInfo(domconfig)
236 return vm
238 def domain_by_name(name):
239 """Get domain by name
241 @params name: Name of the domain
242 @type name: string
243 @return: XendDomainInfo or None
244 """
245 from xen.xend import XendDomain
246 return XendDomain.instance().domain_lookup_by_name_nr(name)
249 def shutdown_reason(code):
250 """Get a shutdown reason from a code.
252 @param code: shutdown code
253 @type code: int
254 @return: shutdown reason
255 @rtype: string
256 """
257 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
259 def dom_get(dom):
260 """Get info from xen for an existing domain.
262 @param dom: domain id
263 @type dom: int
264 @return: info or None
265 @rtype: dictionary
266 """
267 try:
268 domlist = xc.domain_getinfo(dom, 1)
269 if domlist and dom == domlist[0]['domid']:
270 return domlist[0]
271 except Exception, err:
272 # ignore missing domain
273 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
274 return None
277 class XendDomainInfo:
278 """An object represents a domain.
280 @TODO: try to unify dom and domid, they mean the same thing, but
281 xc refers to it as dom, and everywhere else, including
282 xenstore it is domid. The best way is to change xc's
283 python interface.
285 @ivar info: Parsed configuration
286 @type info: dictionary
287 @ivar domid: Domain ID (if VM has started)
288 @type domid: int or None
289 @ivar vmpath: XenStore path to this VM.
290 @type vmpath: string
291 @ivar dompath: XenStore path to this Domain.
292 @type dompath: string
293 @ivar image: Reference to the VM Image.
294 @type image: xen.xend.image.ImageHandler
295 @ivar store_port: event channel to xenstored
296 @type store_port: int
297 @ivar console_port: event channel to xenconsoled
298 @type console_port: int
299 @ivar store_mfn: xenstored mfn
300 @type store_mfn: int
301 @ivar console_mfn: xenconsoled mfn
302 @type console_mfn: int
303 @ivar notes: OS image notes
304 @type notes: dictionary
305 @ivar vmWatch: reference to a watch on the xenstored vmpath
306 @type vmWatch: xen.xend.xenstore.xswatch
307 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
308 @type shutdownWatch: xen.xend.xenstore.xswatch
309 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
310 @type shutdownStartTime: float or None
311 @ivar state: Domain state
312 @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
313 @ivar state_updated: lock for self.state
314 @type state_updated: threading.Condition
315 @ivar refresh_shutdown_lock: lock for polling shutdown state
316 @type refresh_shutdown_lock: threading.Condition
317 @ivar _deviceControllers: device controller cache for this domain
318 @type _deviceControllers: dict 'string' to DevControllers
319 """
321 def __init__(self, info, domid = None, dompath = None, augment = False,
322 priv = False, resume = False):
323 """Constructor for a domain
325 @param info: parsed configuration
326 @type info: dictionary
327 @keyword domid: Set initial domain id (if any)
328 @type domid: int
329 @keyword dompath: Set initial dompath (if any)
330 @type dompath: string
331 @keyword augment: Augment given info with xenstored VM info
332 @type augment: bool
333 @keyword priv: Is a privileged domain (Dom 0)
334 @type priv: bool
335 @keyword resume: Is this domain being resumed?
336 @type resume: bool
337 """
339 self.info = info
340 if domid == None:
341 self.domid = self.info.get('domid')
342 else:
343 self.domid = domid
345 #REMOVE: uuid is now generated in XendConfig
346 #if not self._infoIsSet('uuid'):
347 # self.info['uuid'] = uuid.toString(uuid.create())
349 self.vmpath = XS_VMROOT + self.info['uuid']
350 self.dompath = dompath
352 self.image = None
353 self.store_port = None
354 self.store_mfn = None
355 self.console_port = None
356 self.console_mfn = None
358 self.vmWatch = None
359 self.shutdownWatch = None
360 self.shutdownStartTime = None
361 self._resume = resume
363 self.state = DOM_STATE_HALTED
364 self.state_updated = threading.Condition()
365 self.refresh_shutdown_lock = threading.Condition()
367 self._deviceControllers = {}
369 for state in DOM_STATES_OLD:
370 self.info[state] = 0
372 if augment:
373 self._augmentInfo(priv)
375 self._checkName(self.info['name_label'])
377 self.metrics = XendVMMetrics(uuid.createString(), self)
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 if self.info.is_hvm():
452 for code in DOMAIN_SHUTDOWN_REASONS.keys():
453 if DOMAIN_SHUTDOWN_REASONS[code] == reason:
454 break
455 xc.domain_shutdown(self.domid, code)
458 def pause(self):
459 """Pause domain
461 @raise XendError: Failed pausing a domain
462 """
463 try:
464 xc.domain_pause(self.domid)
465 self._stateSet(DOM_STATE_PAUSED)
466 except Exception, ex:
467 log.exception(ex)
468 raise XendError("Domain unable to be paused: %s" % str(ex))
470 def unpause(self):
471 """Unpause domain
473 @raise XendError: Failed unpausing a domain
474 """
475 try:
476 xc.domain_unpause(self.domid)
477 self._stateSet(DOM_STATE_RUNNING)
478 except Exception, ex:
479 log.exception(ex)
480 raise XendError("Domain unable to be unpaused: %s" % str(ex))
482 def send_sysrq(self, key):
483 """ Send a Sysrq equivalent key via xenstored."""
484 asserts.isCharConvertible(key)
485 self.storeDom("control/sysrq", '%c' % key)
487 def device_create(self, dev_config):
488 """Create a new device.
490 @param dev_config: device configuration
491 @type dev_config: SXP object (parsed config)
492 """
493 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
494 dev_type = sxp.name(dev_config)
495 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
496 dev_config_dict = self.info['devices'][dev_uuid][1]
497 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
498 dev_config_dict['devid'] = devid = \
499 self._createDevice(dev_type, dev_config_dict)
500 self._waitForDevice(dev_type, devid)
501 return self.getDeviceController(dev_type).sxpr(devid)
503 def device_configure(self, dev_sxp, devid = None):
504 """Configure an existing device.
506 @param dev_config: device configuration
507 @type dev_config: SXP object (parsed config)
508 @param devid: device id
509 @type devid: int
510 @return: Returns True if successfully updated device
511 @rtype: boolean
512 """
514 # convert device sxp to a dict
515 dev_class = sxp.name(dev_sxp)
516 dev_config = {}
517 for opt_val in dev_sxp[1:]:
518 try:
519 dev_config[opt_val[0]] = opt_val[1]
520 except IndexError:
521 pass
523 # use DevController.reconfigureDevice to change device config
524 dev_control = self.getDeviceController(dev_class)
525 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
527 # update XendConfig with new device info
528 if dev_uuid:
529 self.info.device_update(dev_uuid, dev_sxp)
531 return True
533 def waitForDevices(self):
534 """Wait for this domain's configured devices to connect.
536 @raise VmError: if any device fails to initialise.
537 """
538 for devclass in XendDevices.valid_devices():
539 self.getDeviceController(devclass).waitForDevices()
541 def destroyDevice(self, deviceClass, devid, force = False):
542 try:
543 devid = int(devid)
544 except ValueError:
545 # devid is not a number, let's search for it in xenstore.
546 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
547 for entry in xstransact.List(devicePath):
548 backend = xstransact.Read('%s/%s' % (devicePath, entry),
549 "backend")
550 devName = xstransact.Read(backend, "dev")
551 if devName == devid:
552 # We found the integer matching our devid, use it instead
553 devid = entry
554 break
556 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
558 def getDeviceSxprs(self, deviceClass):
559 if self.state == DOM_STATE_RUNNING \
560 or self.state == DOM_STATE_PAUSED:
561 return self.getDeviceController(deviceClass).sxprs()
562 else:
563 sxprs = []
564 dev_num = 0
565 for dev_type, dev_info in self.info.all_devices_sxpr():
566 if dev_type == deviceClass:
567 sxprs.append([dev_num, dev_info])
568 dev_num += 1
569 return sxprs
572 def setMemoryTarget(self, target):
573 """Set the memory target of this domain.
574 @param target: In MiB.
575 """
576 log.debug("Setting memory target of domain %s (%d) to %d MiB.",
577 self.info['name_label'], self.domid, target)
579 if target <= 0:
580 raise XendError('Invalid memory size')
582 MiB = 1024 * 1024
583 self.info['memory_dynamic_min'] = target * MiB
584 self.info['memory_dynamic_max'] = target * MiB
586 if self.domid >= 0:
587 self.storeVm("memory", target)
588 self.storeDom("memory/target", target << 10)
589 else:
590 xen.xend.XendDomain.instance().managed_config_save(self)
592 def setMemoryMaximum(self, limit):
593 """Set the maximum memory limit of this domain
594 @param limit: In MiB.
595 """
596 log.debug("Setting memory maximum of domain %s (%d) to %d MiB.",
597 self.info['name_label'], self.domid, limit)
599 if limit <= 0:
600 raise XendError('Invalid memory size')
602 self.info['memory_static_max'] = limit
603 if self.domid >= 0:
604 maxmem = int(limit) * 1024
605 try:
606 return xc.domain_setmaxmem(self.domid, maxmem)
607 except Exception, ex:
608 raise XendError(str(ex))
609 else:
610 self.info['memory_dynamic_max'] = limit
611 xen.xend.XendDomain.instance().managed_config_save(self)
614 def getVCPUInfo(self):
615 try:
616 # We include the domain name and ID, to help xm.
617 sxpr = ['domain',
618 ['domid', self.domid],
619 ['name', self.info['name_label']],
620 ['vcpu_count', self.info['VCPUs_max']]]
622 for i in range(0, self.info['VCPUs_max']):
623 info = xc.vcpu_getinfo(self.domid, i)
625 sxpr.append(['vcpu',
626 ['number', i],
627 ['online', info['online']],
628 ['blocked', info['blocked']],
629 ['running', info['running']],
630 ['cpu_time', info['cpu_time'] / 1e9],
631 ['cpu', info['cpu']],
632 ['cpumap', info['cpumap']]])
634 return sxpr
636 except RuntimeError, exn:
637 raise XendError(str(exn))
640 def getDomInfo(self):
641 return dom_get(self.domid)
643 #
644 # internal functions ... TODO: re-categorised
645 #
647 def _augmentInfo(self, priv):
648 """Augment self.info, as given to us through L{recreate}, with
649 values taken from the store. This recovers those values known
650 to xend but not to the hypervisor.
651 """
652 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
653 if priv:
654 augment_entries.remove('memory')
655 augment_entries.remove('maxmem')
656 augment_entries.remove('vcpus')
657 augment_entries.remove('vcpu_avail')
659 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
660 for k in augment_entries])
662 # make returned lists into a dictionary
663 vm_config = dict(zip(augment_entries, vm_config))
665 for arg in augment_entries:
666 val = vm_config[arg]
667 if val != None:
668 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
669 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
670 self.info[xapiarg] = val
671 elif arg == "memory":
672 self.info["static_memory_min"] = val
673 elif arg == "maxmem":
674 self.info["static_memory_max"] = val
675 else:
676 self.info[arg] = val
678 # For dom0, we ignore any stored value for the vcpus fields, and
679 # read the current value from Xen instead. This allows boot-time
680 # settings to take precedence over any entries in the store.
681 if priv:
682 xeninfo = dom_get(self.domid)
683 self.info['VCPUs_max'] = xeninfo['online_vcpus']
684 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
686 # read image value
687 image_sxp = self._readVm('image')
688 if image_sxp:
689 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
691 # read devices
692 devices = []
693 for devclass in XendDevices.valid_devices():
694 devconfig = self.getDeviceController(devclass).configurations()
695 if devconfig:
696 devices.extend(devconfig)
698 if not self.info['devices'] and devices is not None:
699 for device in devices:
700 self.info.device_add(device[0], cfg_sxp = device)
702 self._update_consoles()
704 def _update_consoles(self):
705 if self.domid == None or self.domid == 0:
706 return
708 # Update VT100 port if it exists
709 self.console_port = self.readDom('console/port')
710 if self.console_port is not None:
711 serial_consoles = self.info.console_get_all('vt100')
712 if not serial_consoles:
713 cfg = self.info.console_add('vt100', self.console_port)
714 self._createDevice('console', cfg)
715 else:
716 console_uuid = serial_consoles[0].get('uuid')
717 self.info.console_update(console_uuid, 'location',
718 self.console_port)
721 # Update VNC port if it exists and write to xenstore
722 vnc_port = self.readDom('console/vnc-port')
723 if vnc_port is not None:
724 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
725 if dev_type == 'vfb':
726 old_location = dev_info.get('location')
727 listen_host = dev_info.get('vnclisten', 'localhost')
728 new_location = '%s:%s' % (listen_host, str(vnc_port))
729 if old_location == new_location:
730 break
732 dev_info['location'] = new_location
733 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
734 vfb_ctrl = self.getDeviceController('vfb')
735 vfb_ctrl.reconfigureDevice(0, dev_info)
736 break
738 #
739 # Function to update xenstore /vm/*
740 #
742 def _readVm(self, *args):
743 return xstransact.Read(self.vmpath, *args)
745 def _writeVm(self, *args):
746 return xstransact.Write(self.vmpath, *args)
748 def _removeVm(self, *args):
749 return xstransact.Remove(self.vmpath, *args)
751 def _gatherVm(self, *args):
752 return xstransact.Gather(self.vmpath, *args)
754 def storeVm(self, *args):
755 return xstransact.Store(self.vmpath, *args)
757 #
758 # Function to update xenstore /dom/*
759 #
761 def readDom(self, *args):
762 return xstransact.Read(self.dompath, *args)
764 def gatherDom(self, *args):
765 return xstransact.Gather(self.dompath, *args)
767 def _writeDom(self, *args):
768 return xstransact.Write(self.dompath, *args)
770 def _removeDom(self, *args):
771 return xstransact.Remove(self.dompath, *args)
773 def storeDom(self, *args):
774 return xstransact.Store(self.dompath, *args)
776 def _recreateDom(self):
777 complete(self.dompath, lambda t: self._recreateDomFunc(t))
779 def _recreateDomFunc(self, t):
780 t.remove()
781 t.mkdir()
782 t.set_permissions({'dom' : self.domid})
783 t.write('vm', self.vmpath)
785 def _storeDomDetails(self):
786 to_store = {
787 'domid': str(self.domid),
788 'vm': self.vmpath,
789 'name': self.info['name_label'],
790 'console/limit': str(xoptions.get_console_limit() * 1024),
791 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
792 }
794 def f(n, v):
795 if v is not None:
796 if type(v) == bool:
797 to_store[n] = v and "1" or "0"
798 else:
799 to_store[n] = str(v)
801 f('console/port', self.console_port)
802 f('console/ring-ref', self.console_mfn)
803 f('store/port', self.store_port)
804 f('store/ring-ref', self.store_mfn)
806 if arch.type == "x86":
807 f('control/platform-feature-multiprocessor-suspend', True)
809 # elfnotes
810 for n, v in self.info.get_notes().iteritems():
811 n = n.lower().replace('_', '-')
812 if n == 'features':
813 for v in v.split('|'):
814 v = v.replace('_', '-')
815 if v.startswith('!'):
816 f('image/%s/%s' % (n, v[1:]), False)
817 else:
818 f('image/%s/%s' % (n, v), True)
819 else:
820 f('image/%s' % n, v)
822 to_store.update(self._vcpuDomDetails())
824 log.debug("Storing domain details: %s", scrub_password(to_store))
826 self._writeDom(to_store)
828 def _vcpuDomDetails(self):
829 def availability(n):
830 if self.info['vcpu_avail'] & (1 << n):
831 return 'online'
832 else:
833 return 'offline'
835 result = {}
836 for v in range(0, self.info['VCPUs_max']):
837 result["cpu/%d/availability" % v] = availability(v)
838 return result
840 #
841 # xenstore watches
842 #
844 def _registerWatches(self):
845 """Register a watch on this VM's entries in the store, and the
846 domain's control/shutdown node, so that when they are changed
847 externally, we keep up to date. This should only be called by {@link
848 #create}, {@link #recreate}, or {@link #restore}, once the domain's
849 details have been written, but before the new instance is returned."""
850 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
851 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
852 self._handleShutdownWatch)
854 def _storeChanged(self, _):
855 log.trace("XendDomainInfo.storeChanged");
857 changed = False
859 # Check whether values in the configuration have
860 # changed in Xenstore.
862 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
864 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
865 for k in cfg_vm])
867 # convert two lists into a python dictionary
868 vm_details = dict(zip(cfg_vm, vm_details))
870 for arg, val in vm_details.items():
871 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
872 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
873 if val != None and val != self.info[xapiarg]:
874 self.info[xapiarg] = val
875 changed = True
876 elif arg == "memory":
877 if val != None and val != self.info["static_memory_min"]:
878 self.info["static_memory_min"] = val
879 changed = True
880 elif arg == "maxmem":
881 if val != None and val != self.info["static_memory_max"]:
882 self.info["static_memory_max"] = val
883 changed = True
885 # Check whether image definition has been updated
886 image_sxp = self._readVm('image')
887 if image_sxp and image_sxp != self.info.image_sxpr():
888 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
889 changed = True
891 if changed:
892 # Update the domain section of the store, as this contains some
893 # parameters derived from the VM configuration.
894 self._storeDomDetails()
896 return 1
898 def _handleShutdownWatch(self, _):
899 log.debug('XendDomainInfo.handleShutdownWatch')
901 reason = self.readDom('control/shutdown')
903 if reason and reason != 'suspend':
904 sst = self.readDom('xend/shutdown_start_time')
905 now = time.time()
906 if sst:
907 self.shutdownStartTime = float(sst)
908 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
909 else:
910 self.shutdownStartTime = now
911 self.storeDom('xend/shutdown_start_time', now)
912 timeout = SHUTDOWN_TIMEOUT
914 log.trace(
915 "Scheduling refreshShutdown on domain %d in %ds.",
916 self.domid, timeout)
917 threading.Timer(timeout, self.refreshShutdown).start()
919 return True
922 #
923 # Public Attributes for the VM
924 #
927 def getDomid(self):
928 return self.domid
930 def setName(self, name):
931 self._checkName(name)
932 self.info['name_label'] = name
933 self.storeVm("name", name)
935 def getName(self):
936 return self.info['name_label']
938 def getDomainPath(self):
939 return self.dompath
941 def getShutdownReason(self):
942 return self.readDom('control/shutdown')
944 def getStorePort(self):
945 """For use only by image.py and XendCheckpoint.py."""
946 return self.store_port
948 def getConsolePort(self):
949 """For use only by image.py and XendCheckpoint.py"""
950 return self.console_port
952 def getFeatures(self):
953 """For use only by image.py."""
954 return self.info['features']
956 def getVCpuCount(self):
957 return self.info['VCPUs_max']
959 def setVCpuCount(self, vcpus):
960 if vcpus <= 0:
961 raise XendError('Invalid VCPUs')
963 self.info['vcpu_avail'] = (1 << vcpus) - 1
964 if self.domid >= 0:
965 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
966 # update dom differently depending on whether we are adjusting
967 # vcpu number up or down, otherwise _vcpuDomDetails does not
968 # disable the vcpus
969 if self.info['VCPUs_max'] > vcpus:
970 # decreasing
971 self._writeDom(self._vcpuDomDetails())
972 self.info['VCPUs_live'] = vcpus
973 else:
974 # same or increasing
975 self.info['VCPUs_live'] = vcpus
976 self._writeDom(self._vcpuDomDetails())
977 else:
978 self.info['VCPUs_live'] = vcpus
979 xen.xend.XendDomain.instance().managed_config_save(self)
980 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
981 vcpus)
983 def getLabel(self):
984 return security.get_security_info(self.info, 'label')
986 def getMemoryTarget(self):
987 """Get this domain's target memory size, in KB."""
988 return self.info['memory_dynamic_max'] / 1024
990 def getMemoryMaximum(self):
991 """Get this domain's maximum memory size, in KB."""
992 # remember, info now stores memory in bytes
993 return self.info['memory_static_max'] / 1024
995 def getResume(self):
996 return str(self._resume)
998 def getCap(self):
999 return self.info.get('cpu_cap', 0)
1001 def getWeight(self):
1002 return self.info.get('cpu_weight', 256)
1004 def setResume(self, state):
1005 self._resume = state
1007 def getRestartCount(self):
1008 return self._readVm('xend/restart_count')
1010 def refreshShutdown(self, xeninfo = None):
1011 """ Checks the domain for whether a shutdown is required.
1013 Called from XendDomainInfo and also image.py for HVM images.
1014 """
1016 # If set at the end of this method, a restart is required, with the
1017 # given reason. This restart has to be done out of the scope of
1018 # refresh_shutdown_lock.
1019 restart_reason = None
1021 self.refresh_shutdown_lock.acquire()
1022 try:
1023 if xeninfo is None:
1024 xeninfo = dom_get(self.domid)
1025 if xeninfo is None:
1026 # The domain no longer exists. This will occur if we have
1027 # scheduled a timer to check for shutdown timeouts and the
1028 # shutdown succeeded. It will also occur if someone
1029 # destroys a domain beneath us. We clean up the domain,
1030 # just in case, but we can't clean up the VM, because that
1031 # VM may have migrated to a different domain on this
1032 # machine.
1033 self.cleanupDomain()
1034 self._stateSet(DOM_STATE_HALTED)
1035 return
1037 if xeninfo['dying']:
1038 # Dying means that a domain has been destroyed, but has not
1039 # yet been cleaned up by Xen. This state could persist
1040 # indefinitely if, for example, another domain has some of its
1041 # pages mapped. We might like to diagnose this problem in the
1042 # future, but for now all we do is make sure that it's not us
1043 # holding the pages, by calling cleanupDomain. We can't
1044 # clean up the VM, as above.
1045 self.cleanupDomain()
1046 self._stateSet(DOM_STATE_SHUTDOWN)
1047 return
1049 elif xeninfo['crashed']:
1050 if self.readDom('xend/shutdown_completed'):
1051 # We've seen this shutdown already, but we are preserving
1052 # the domain for debugging. Leave it alone.
1053 return
1055 log.warn('Domain has crashed: name=%s id=%d.',
1056 self.info['name_label'], self.domid)
1057 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1059 if xoptions.get_enable_dump():
1060 try:
1061 self.dumpCore()
1062 except XendError:
1063 # This error has been logged -- there's nothing more
1064 # we can do in this context.
1065 pass
1067 restart_reason = 'crash'
1068 self._stateSet(DOM_STATE_HALTED)
1070 elif xeninfo['shutdown']:
1071 self._stateSet(DOM_STATE_SHUTDOWN)
1072 if self.readDom('xend/shutdown_completed'):
1073 # We've seen this shutdown already, but we are preserving
1074 # the domain for debugging. Leave it alone.
1075 return
1077 else:
1078 reason = shutdown_reason(xeninfo['shutdown_reason'])
1080 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1081 self.info['name_label'], self.domid, reason)
1082 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1084 self._clearRestart()
1086 if reason == 'suspend':
1087 self._stateSet(DOM_STATE_SUSPENDED)
1088 # Don't destroy the domain. XendCheckpoint will do
1089 # this once it has finished. However, stop watching
1090 # the VM path now, otherwise we will end up with one
1091 # watch for the old domain, and one for the new.
1092 self._unwatchVm()
1093 elif reason in ('poweroff', 'reboot'):
1094 restart_reason = reason
1095 else:
1096 self.destroy()
1098 elif self.dompath is None:
1099 # We have yet to manage to call introduceDomain on this
1100 # domain. This can happen if a restore is in progress, or has
1101 # failed. Ignore this domain.
1102 pass
1103 else:
1104 # Domain is alive. If we are shutting it down, then check
1105 # the timeout on that, and destroy it if necessary.
1106 if xeninfo['paused']:
1107 self._stateSet(DOM_STATE_PAUSED)
1108 else:
1109 self._stateSet(DOM_STATE_RUNNING)
1111 if self.shutdownStartTime:
1112 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1113 self.shutdownStartTime)
1114 if timeout < 0:
1115 log.info(
1116 "Domain shutdown timeout expired: name=%s id=%s",
1117 self.info['name_label'], self.domid)
1118 self.destroy()
1119 finally:
1120 self.refresh_shutdown_lock.release()
1122 if restart_reason:
1123 threading.Thread(target = self._maybeRestart,
1124 args = (restart_reason,)).start()
1128 # Restart functions - handling whether we come back up on shutdown.
1131 def _clearRestart(self):
1132 self._removeDom("xend/shutdown_start_time")
1135 def _maybeRestart(self, reason):
1136 # Dispatch to the correct method based upon the configured on_{reason}
1137 # behaviour.
1138 actions = {"destroy" : self.destroy,
1139 "restart" : self._restart,
1140 "preserve" : self._preserve,
1141 "rename-restart" : self._renameRestart}
1143 action_conf = {
1144 'poweroff': 'actions_after_shutdown',
1145 'reboot': 'actions_after_reboot',
1146 'crash': 'actions_after_crash',
1149 action_target = self.info.get(action_conf.get(reason))
1150 func = actions.get(action_target, None)
1151 if func and callable(func):
1152 func()
1153 else:
1154 self.destroy() # default to destroy
1156 def _renameRestart(self):
1157 self._restart(True)
1159 def _restart(self, rename = False):
1160 """Restart the domain after it has exited.
1162 @param rename True if the old domain is to be renamed and preserved,
1163 False if it is to be destroyed.
1164 """
1165 from xen.xend import XendDomain
1167 if self._readVm(RESTART_IN_PROGRESS):
1168 log.error('Xend failed during restart of domain %s. '
1169 'Refusing to restart to avoid loops.',
1170 str(self.domid))
1171 self.destroy()
1172 return
1174 old_domid = self.domid
1175 self._writeVm(RESTART_IN_PROGRESS, 'True')
1177 now = time.time()
1178 rst = self._readVm('xend/previous_restart_time')
1179 if rst:
1180 rst = float(rst)
1181 timeout = now - rst
1182 if timeout < MINIMUM_RESTART_TIME:
1183 log.error(
1184 'VM %s restarting too fast (%f seconds since the last '
1185 'restart). Refusing to restart to avoid loops.',
1186 self.info['name_label'], timeout)
1187 self.destroy()
1188 return
1190 self._writeVm('xend/previous_restart_time', str(now))
1192 try:
1193 if rename:
1194 self._preserveForRestart()
1195 else:
1196 self._unwatchVm()
1197 self.destroyDomain()
1199 # new_dom's VM will be the same as this domain's VM, except where
1200 # the rename flag has instructed us to call preserveForRestart.
1201 # In that case, it is important that we remove the
1202 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1203 # once the new one is available.
1205 new_dom = None
1206 try:
1207 new_dom = XendDomain.instance().domain_create_from_dict(
1208 self.info)
1209 new_dom.unpause()
1210 rst_cnt = self._readVm('xend/restart_count')
1211 rst_cnt = int(rst_cnt) + 1
1212 self._writeVm('xend/restart_count', str(rst_cnt))
1213 new_dom._removeVm(RESTART_IN_PROGRESS)
1214 except:
1215 if new_dom:
1216 new_dom._removeVm(RESTART_IN_PROGRESS)
1217 new_dom.destroy()
1218 else:
1219 self._removeVm(RESTART_IN_PROGRESS)
1220 raise
1221 except:
1222 log.exception('Failed to restart domain %s.', str(old_domid))
1224 def _preserveForRestart(self):
1225 """Preserve a domain that has been shut down, by giving it a new UUID,
1226 cloning the VM details, and giving it a new name. This allows us to
1227 keep this domain for debugging, but restart a new one in its place
1228 preserving the restart semantics (name and UUID preserved).
1229 """
1231 new_uuid = uuid.createString()
1232 new_name = 'Domain-%s' % new_uuid
1233 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1234 self.info['name_label'], self.domid, self.info['uuid'],
1235 new_name, new_uuid)
1236 self._unwatchVm()
1237 self._releaseDevices()
1238 self.info['name_label'] = new_name
1239 self.info['uuid'] = new_uuid
1240 self.vmpath = XS_VMROOT + new_uuid
1241 self._storeVmDetails()
1242 self._preserve()
1245 def _preserve(self):
1246 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1247 self.domid)
1248 self._unwatchVm()
1249 self.storeDom('xend/shutdown_completed', 'True')
1250 self._stateSet(DOM_STATE_HALTED)
1253 # Debugging ..
1256 def dumpCore(self, corefile = None):
1257 """Create a core dump for this domain.
1259 @raise: XendError if core dumping failed.
1260 """
1262 try:
1263 if not corefile:
1264 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1265 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1266 self.info['name_label'], self.domid)
1268 if os.path.isdir(corefile):
1269 raise XendError("Cannot dump core in a directory: %s" %
1270 corefile)
1272 xc.domain_dumpcore(self.domid, corefile)
1273 except RuntimeError, ex:
1274 corefile_incomp = corefile+'-incomplete'
1275 os.rename(corefile, corefile_incomp)
1276 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1277 self.domid, self.info['name_label'])
1278 raise XendError("Failed to dump core: %s" % str(ex))
1281 # Device creation/deletion functions
1284 def _createDevice(self, deviceClass, devConfig):
1285 return self.getDeviceController(deviceClass).createDevice(devConfig)
1287 def _waitForDevice(self, deviceClass, devid):
1288 return self.getDeviceController(deviceClass).waitForDevice(devid)
1290 def _waitForDeviceUUID(self, dev_uuid):
1291 deviceClass, config = self.info['devices'].get(dev_uuid)
1292 self._waitForDevice(deviceClass, config['devid'])
1294 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1295 return self.getDeviceController(deviceClass).reconfigureDevice(
1296 devid, devconfig)
1298 def _createDevices(self):
1299 """Create the devices for a vm.
1301 @raise: VmError for invalid devices
1302 """
1303 ordered_refs = self.info.ordered_device_refs()
1304 for dev_uuid in ordered_refs:
1305 devclass, config = self.info['devices'][dev_uuid]
1306 if devclass in XendDevices.valid_devices():
1307 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1308 dev_uuid = config.get('uuid')
1309 devid = self._createDevice(devclass, config)
1311 # store devid in XendConfig for caching reasons
1312 if dev_uuid in self.info['devices']:
1313 self.info['devices'][dev_uuid][1]['devid'] = devid
1315 if self.image:
1316 self.image.createDeviceModel()
1318 def _releaseDevices(self, suspend = False):
1319 """Release all domain's devices. Nothrow guarantee."""
1320 if suspend and self.image:
1321 self.image.destroy(suspend)
1322 return
1324 while True:
1325 t = xstransact("%s/device" % self.dompath)
1326 for devclass in XendDevices.valid_devices():
1327 for dev in t.list(devclass):
1328 try:
1329 t.remove(dev)
1330 except:
1331 # Log and swallow any exceptions in removal --
1332 # there's nothing more we can do.
1333 log.exception(
1334 "Device release failed: %s; %s; %s",
1335 self.info['name_label'], devclass, dev)
1336 if t.commit():
1337 break
1339 def getDeviceController(self, name):
1340 """Get the device controller for this domain, and if it
1341 doesn't exist, create it.
1343 @param name: device class name
1344 @type name: string
1345 @rtype: subclass of DevController
1346 """
1347 if name not in self._deviceControllers:
1348 devController = XendDevices.make_controller(name, self)
1349 if not devController:
1350 raise XendError("Unknown device type: %s" % name)
1351 self._deviceControllers[name] = devController
1353 return self._deviceControllers[name]
1356 # Migration functions (public)
1359 def testMigrateDevices(self, network, dst):
1360 """ Notify all device about intention of migration
1361 @raise: XendError for a device that cannot be migrated
1362 """
1363 for (n, c) in self.info.all_devices_sxpr():
1364 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1365 if rc != 0:
1366 raise XendError("Device of type '%s' refuses migration." % n)
1368 def migrateDevices(self, network, dst, step, domName=''):
1369 """Notify the devices about migration
1370 """
1371 ctr = 0
1372 try:
1373 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1374 self.migrateDevice(dev_type, dev_conf, network, dst,
1375 step, domName)
1376 ctr = ctr + 1
1377 except:
1378 for dev_type, dev_conf in self.info.all_devices_sxpr():
1379 if ctr == 0:
1380 step = step - 1
1381 ctr = ctr - 1
1382 self._recoverMigrateDevice(dev_type, dev_conf, network,
1383 dst, step, domName)
1384 raise
1386 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1387 step, domName=''):
1388 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1389 network, dst, step, domName)
1391 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1392 dst, step, domName=''):
1393 return self.getDeviceController(deviceClass).recover_migrate(
1394 deviceConfig, network, dst, step, domName)
1397 ## private:
1399 def _constructDomain(self):
1400 """Construct the domain.
1402 @raise: VmError on error
1403 """
1405 log.debug('XendDomainInfo.constructDomain')
1407 self.shutdownStartTime = None
1409 hvm = self.info.is_hvm()
1410 if hvm:
1411 info = xc.xeninfo()
1412 if 'hvm' not in info['xen_caps']:
1413 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1414 "supported by your CPU and enabled in your "
1415 "BIOS?")
1417 self.domid = xc.domain_create(
1418 domid = 0,
1419 ssidref = security.get_security_info(self.info, 'ssidref'),
1420 handle = uuid.fromString(self.info['uuid']),
1421 hvm = int(hvm))
1423 if self.domid < 0:
1424 raise VmError('Creating domain failed: name=%s' %
1425 self.info['name_label'])
1427 self.dompath = GetDomainPath(self.domid)
1429 self._recreateDom()
1431 # Set maximum number of vcpus in domain
1432 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1434 # register the domain in the list
1435 from xen.xend import XendDomain
1436 XendDomain.instance().add_domain(self)
1438 def _introduceDomain(self):
1439 assert self.domid is not None
1440 assert self.store_mfn is not None
1441 assert self.store_port is not None
1443 try:
1444 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1445 except RuntimeError, exn:
1446 raise XendError(str(exn))
1449 def _initDomain(self):
1450 log.debug('XendDomainInfo.initDomain: %s %s',
1451 self.domid,
1452 self.info['cpu_weight'])
1454 self._configureBootloader()
1456 try:
1457 self.image = image.create(self, self.info)
1459 localtime = self.info.get('platform_localtime', False)
1460 if localtime:
1461 xc.domain_set_time_offset(self.domid)
1463 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1465 # repin domain vcpus if a restricted cpus list is provided
1466 # this is done prior to memory allocation to aide in memory
1467 # distribution for NUMA systems.
1468 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1469 for v in range(0, self.info['VCPUs_max']):
1470 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1472 # Use architecture- and image-specific calculations to determine
1473 # the various headrooms necessary, given the raw configured
1474 # values. maxmem, memory, and shadow are all in KiB.
1475 # but memory_static_max etc are all stored in bytes now.
1476 memory = self.image.getRequiredAvailableMemory(
1477 self.info['memory_dynamic_max'] / 1024)
1478 maxmem = self.image.getRequiredAvailableMemory(
1479 self.info['memory_static_max'] / 1024)
1480 shadow = self.image.getRequiredShadowMemory(
1481 self.info['shadow_memory'] / 1024,
1482 self.info['memory_static_max'] / 1024)
1484 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'],)
1485 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1486 # takes MiB and we must not round down and end up under-providing.
1487 shadow = ((shadow + 1023) / 1024) * 1024
1489 # set memory limit
1490 xc.domain_setmaxmem(self.domid, maxmem)
1492 # Make sure there's enough RAM available for the domain
1493 balloon.free(memory + shadow)
1495 # Set up the shadow memory
1496 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1497 self.info['shadow_memory'] = shadow_cur
1499 self._createChannels()
1501 channel_details = self.image.createImage()
1503 self.store_mfn = channel_details['store_mfn']
1504 if 'console_mfn' in channel_details:
1505 self.console_mfn = channel_details['console_mfn']
1506 if 'notes' in channel_details:
1507 self.info.set_notes(channel_details['notes'])
1509 self._introduceDomain()
1511 self._createDevices()
1513 self.image.cleanupBootloading()
1515 self.info['start_time'] = time.time()
1517 self._stateSet(DOM_STATE_RUNNING)
1518 except VmError, exn:
1519 log.exception("XendDomainInfo.initDomain: exception occurred")
1520 if self.image:
1521 self.image.cleanupBootloading()
1522 raise exn
1523 except RuntimeError, exn:
1524 log.exception("XendDomainInfo.initDomain: exception occurred")
1525 if self.image:
1526 self.image.cleanupBootloading()
1527 raise VmError(str(exn))
1530 def cleanupDomain(self):
1531 """Cleanup domain resources; release devices. Idempotent. Nothrow
1532 guarantee."""
1534 self.refresh_shutdown_lock.acquire()
1535 try:
1536 self.unwatchShutdown()
1537 self._releaseDevices()
1538 bootloader_tidy(self)
1540 if self.image:
1541 try:
1542 self.image.destroy()
1543 except:
1544 log.exception(
1545 "XendDomainInfo.cleanup: image.destroy() failed.")
1546 self.image = None
1548 try:
1549 self._removeDom()
1550 except:
1551 log.exception("Removing domain path failed.")
1553 self._stateSet(DOM_STATE_HALTED)
1554 finally:
1555 self.refresh_shutdown_lock.release()
1558 def unwatchShutdown(self):
1559 """Remove the watch on the domain's control/shutdown node, if any.
1560 Idempotent. Nothrow guarantee. Expects to be protected by the
1561 refresh_shutdown_lock."""
1563 try:
1564 try:
1565 if self.shutdownWatch:
1566 self.shutdownWatch.unwatch()
1567 finally:
1568 self.shutdownWatch = None
1569 except:
1570 log.exception("Unwatching control/shutdown failed.")
1572 def waitForShutdown(self):
1573 self.state_updated.acquire()
1574 try:
1575 while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1576 self.state_updated.wait()
1577 finally:
1578 self.state_updated.release()
1582 # TODO: recategorise - called from XendCheckpoint
1585 def completeRestore(self, store_mfn, console_mfn):
1587 log.debug("XendDomainInfo.completeRestore")
1589 self.store_mfn = store_mfn
1590 self.console_mfn = console_mfn
1592 self._introduceDomain()
1593 if self.info.is_hvm():
1594 self.image = image.create(self, self.info)
1595 if self.image:
1596 self.image.createDeviceModel(True)
1597 self.image.register_shutdown_watch()
1598 self._storeDomDetails()
1599 self._registerWatches()
1600 self.refreshShutdown()
1602 log.debug("XendDomainInfo.completeRestore done")
1605 def _endRestore(self):
1606 self.setResume(False)
1609 # VM Destroy
1612 def _prepare_phantom_paths(self):
1613 # get associated devices to destroy
1614 # build list of phantom devices to be removed after normal devices
1615 plist = []
1616 if self.domid is not None:
1617 from xen.xend.xenstore.xstransact import xstransact
1618 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1619 for dev in t.list():
1620 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1621 % (self.dompath, dev))
1622 if backend_phantom_vbd is not None:
1623 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1624 % backend_phantom_vbd)
1625 plist.append(backend_phantom_vbd)
1626 plist.append(frontend_phantom_vbd)
1627 return plist
1629 def _cleanup_phantom_devs(self, plist):
1630 # remove phantom devices
1631 if not plist == []:
1632 time.sleep(2)
1633 for paths in plist:
1634 if paths.find('backend') != -1:
1635 from xen.xend.server import DevController
1636 # Modify online status /before/ updating state (latter is watched by
1637 # drivers, so this ordering avoids a race).
1638 xstransact.Write(paths, 'online', "0")
1639 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1640 # force
1641 xstransact.Remove(paths)
1643 def destroy(self):
1644 """Cleanup VM and destroy domain. Nothrow guarantee."""
1646 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1648 paths = self._prepare_phantom_paths()
1650 self._cleanupVm()
1651 if self.dompath is not None:
1652 self.destroyDomain()
1654 self._cleanup_phantom_devs(paths)
1656 if "transient" in self.info["other_config"] \
1657 and bool(self.info["other_config"]["transient"]):
1658 from xen.xend import XendDomain
1659 XendDomain.instance().domain_delete_by_dominfo(self)
1662 def destroyDomain(self):
1663 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1665 paths = self._prepare_phantom_paths()
1667 try:
1668 if self.domid is not None:
1669 xc.domain_destroy(self.domid)
1670 self.domid = None
1671 for state in DOM_STATES_OLD:
1672 self.info[state] = 0
1673 self._stateSet(DOM_STATE_HALTED)
1674 except:
1675 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1677 from xen.xend import XendDomain
1678 XendDomain.instance().remove_domain(self)
1680 self.cleanupDomain()
1681 self._cleanup_phantom_devs(paths)
1684 def resumeDomain(self):
1685 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1687 if self.domid is None:
1688 return
1689 try:
1690 # could also fetch a parsed note from xenstore
1691 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1692 if not fast:
1693 self._releaseDevices()
1694 self.testDeviceComplete()
1695 self.testvifsComplete()
1696 log.debug("XendDomainInfo.resumeDomain: devices released")
1698 self._resetChannels()
1700 self._removeDom('control/shutdown')
1701 self._removeDom('device-misc/vif/nextDeviceID')
1703 self._createChannels()
1704 self._introduceDomain()
1705 self._storeDomDetails()
1707 self._createDevices()
1708 log.debug("XendDomainInfo.resumeDomain: devices created")
1710 xc.domain_resume(self.domid, fast)
1711 ResumeDomain(self.domid)
1712 except:
1713 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1716 # Channels for xenstore and console
1719 def _createChannels(self):
1720 """Create the channels to the domain.
1721 """
1722 self.store_port = self._createChannel()
1723 self.console_port = self._createChannel()
1726 def _createChannel(self):
1727 """Create an event channel to the domain.
1728 """
1729 try:
1730 if self.domid != None:
1731 return xc.evtchn_alloc_unbound(domid = self.domid,
1732 remote_dom = 0)
1733 except:
1734 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1735 raise
1737 def _resetChannels(self):
1738 """Reset all event channels in the domain.
1739 """
1740 try:
1741 if self.domid != None:
1742 return xc.evtchn_reset(dom = self.domid)
1743 except:
1744 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1745 raise
1749 # Bootloader configuration
1752 def _configureBootloader(self):
1753 """Run the bootloader if we're configured to do so."""
1755 blexec = self.info['PV_bootloader']
1756 bootloader_args = self.info['PV_bootloader_args']
1757 kernel = self.info['PV_kernel']
1758 ramdisk = self.info['PV_ramdisk']
1759 args = self.info['PV_args']
1760 boot = self.info['HVM_boot_policy']
1762 if boot:
1763 # HVM booting.
1764 pass
1765 elif not blexec and kernel:
1766 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1767 # will be picked up by image.py.
1768 pass
1769 else:
1770 # Boot using bootloader
1771 if not blexec or blexec == 'pygrub':
1772 blexec = osdep.pygrub_path
1774 blcfg = None
1775 disks = [x for x in self.info['vbd_refs']
1776 if self.info['devices'][x][1]['bootable']]
1778 if not disks:
1779 msg = "Had a bootloader specified, but no disks are bootable"
1780 log.error(msg)
1781 raise VmError(msg)
1783 devinfo = self.info['devices'][disks[0]]
1784 devtype = devinfo[0]
1785 disk = devinfo[1]['uname']
1787 fn = blkdev_uname_to_file(disk)
1788 taptype = blkdev_uname_to_taptype(disk)
1789 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1790 if mounted:
1791 # This is a file, not a device. pygrub can cope with a
1792 # file if it's raw, but if it's QCOW or other such formats
1793 # used through blktap, then we need to mount it first.
1795 log.info("Mounting %s on %s." %
1796 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1798 vbd = {
1799 'mode': 'RO',
1800 'device': BOOTLOADER_LOOPBACK_DEVICE,
1803 from xen.xend import XendDomain
1804 dom0 = XendDomain.instance().privilegedDomain()
1805 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1806 fn = BOOTLOADER_LOOPBACK_DEVICE
1808 try:
1809 blcfg = bootloader(blexec, fn, self, False,
1810 bootloader_args, kernel, ramdisk, args)
1811 finally:
1812 if mounted:
1813 log.info("Unmounting %s from %s." %
1814 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1816 dom0.destroyDevice('tap', '/dev/xvdp')
1818 if blcfg is None:
1819 msg = "Had a bootloader specified, but can't find disk"
1820 log.error(msg)
1821 raise VmError(msg)
1823 self.info.update_with_image_sxp(blcfg, True)
1827 # VM Functions
1830 def _readVMDetails(self, params):
1831 """Read the specified parameters from the store.
1832 """
1833 try:
1834 return self._gatherVm(*params)
1835 except ValueError:
1836 # One of the int/float entries in params has a corresponding store
1837 # entry that is invalid. We recover, because older versions of
1838 # Xend may have put the entry there (memory/target, for example),
1839 # but this is in general a bad situation to have reached.
1840 log.exception(
1841 "Store corrupted at %s! Domain %d's configuration may be "
1842 "affected.", self.vmpath, self.domid)
1843 return []
1845 def _cleanupVm(self):
1846 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1848 self._unwatchVm()
1850 try:
1851 self._removeVm()
1852 except:
1853 log.exception("Removing VM path failed.")
1856 def checkLiveMigrateMemory(self):
1857 """ Make sure there's enough memory to migrate this domain """
1858 overhead_kb = 0
1859 if arch.type == "x86":
1860 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1861 # the minimum that Xen would allocate if no value were given.
1862 overhead_kb = self.info['VCPUs_max'] * 1024 + \
1863 (self.info['memory_static_max'] / 1024 / 1024) * 4
1864 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1865 # The domain might already have some shadow memory
1866 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1867 if overhead_kb > 0:
1868 balloon.free(overhead_kb)
1870 def _unwatchVm(self):
1871 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1872 guarantee."""
1873 try:
1874 try:
1875 if self.vmWatch:
1876 self.vmWatch.unwatch()
1877 finally:
1878 self.vmWatch = None
1879 except:
1880 log.exception("Unwatching VM path failed.")
1882 def testDeviceComplete(self):
1883 """ For Block IO migration safety we must ensure that
1884 the device has shutdown correctly, i.e. all blocks are
1885 flushed to disk
1886 """
1887 start = time.time()
1888 while True:
1889 test = 0
1890 diff = time.time() - start
1891 for i in self.getDeviceController('vbd').deviceIDs():
1892 test = 1
1893 log.info("Dev %s still active, looping...", i)
1894 time.sleep(0.1)
1896 if test == 0:
1897 break
1898 if diff >= MIGRATE_TIMEOUT:
1899 log.info("Dev still active but hit max loop timeout")
1900 break
1902 def testvifsComplete(self):
1903 """ In case vifs are released and then created for the same
1904 domain, we need to wait the device shut down.
1905 """
1906 start = time.time()
1907 while True:
1908 test = 0
1909 diff = time.time() - start
1910 for i in self.getDeviceController('vif').deviceIDs():
1911 test = 1
1912 log.info("Dev %s still active, looping...", i)
1913 time.sleep(0.1)
1915 if test == 0:
1916 break
1917 if diff >= MIGRATE_TIMEOUT:
1918 log.info("Dev still active but hit max loop timeout")
1919 break
1921 def _storeVmDetails(self):
1922 to_store = {}
1924 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1925 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1926 if self._infoIsSet(info_key):
1927 to_store[key] = str(self.info[info_key])
1929 if self._infoIsSet("static_memory_min"):
1930 to_store["memory"] = str(self.info["static_memory_min"])
1931 if self._infoIsSet("static_memory_max"):
1932 to_store["maxmem"] = str(self.info["static_memory_max"])
1934 image_sxpr = self.info.image_sxpr()
1935 if image_sxpr:
1936 to_store['image'] = sxp.to_string(image_sxpr)
1938 if self._infoIsSet('security'):
1939 secinfo = self.info['security']
1940 to_store['security'] = sxp.to_string(secinfo)
1941 for idx in range(0, len(secinfo)):
1942 if secinfo[idx][0] == 'access_control':
1943 to_store['security/access_control'] = sxp.to_string(
1944 [secinfo[idx][1], secinfo[idx][2]])
1945 for aidx in range(1, len(secinfo[idx])):
1946 if secinfo[idx][aidx][0] == 'label':
1947 to_store['security/access_control/label'] = \
1948 secinfo[idx][aidx][1]
1949 if secinfo[idx][aidx][0] == 'policy':
1950 to_store['security/access_control/policy'] = \
1951 secinfo[idx][aidx][1]
1952 if secinfo[idx][0] == 'ssidref':
1953 to_store['security/ssidref'] = str(secinfo[idx][1])
1956 if not self._readVm('xend/restart_count'):
1957 to_store['xend/restart_count'] = str(0)
1959 log.debug("Storing VM details: %s", scrub_password(to_store))
1961 self._writeVm(to_store)
1962 self._setVmPermissions()
1965 def _setVmPermissions(self):
1966 """Allow the guest domain to read its UUID. We don't allow it to
1967 access any other entry, for security."""
1968 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1969 { 'dom' : self.domid,
1970 'read' : True,
1971 'write' : False })
1974 # Utility functions
1977 def _stateSet(self, state):
1978 self.state_updated.acquire()
1979 try:
1980 if self.state != state:
1981 self.state = state
1982 self.state_updated.notifyAll()
1983 finally:
1984 self.state_updated.release()
1986 def _infoIsSet(self, name):
1987 return name in self.info and self.info[name] is not None
1989 def _checkName(self, name):
1990 """Check if a vm name is valid. Valid names contain alphabetic
1991 characters, digits, or characters in '_-.:/+'.
1992 The same name cannot be used for more than one vm at the same time.
1994 @param name: name
1995 @raise: VmError if invalid
1996 """
1997 from xen.xend import XendDomain
1999 if name is None or name == '':
2000 raise VmError('Missing VM Name')
2002 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2003 raise VmError('Invalid VM Name')
2005 dom = XendDomain.instance().domain_lookup_nr(name)
2006 if dom and dom.info['uuid'] != self.info['uuid']:
2007 raise VmError("VM name '%s' already exists%s" %
2008 (name,
2009 dom.domid is not None and
2010 (" as domain %s" % str(dom.domid)) or ""))
2013 def update(self, info = None, refresh = True):
2014 """Update with info from xc.domain_getinfo().
2015 """
2016 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2017 str(self.domid))
2019 if not info:
2020 info = dom_get(self.domid)
2021 if not info:
2022 return
2024 if info["maxmem_kb"] < 0:
2025 info["maxmem_kb"] = XendNode.instance() \
2026 .physinfo_dict()['total_memory'] * 1024
2028 #manually update ssidref / security fields
2029 if security.on() and info.has_key('ssidref'):
2030 if (info['ssidref'] != 0) and self.info.has_key('security'):
2031 security_field = self.info['security']
2032 if not security_field:
2033 #create new security element
2034 self.info.update({'security':
2035 [['ssidref', str(info['ssidref'])]]})
2037 #ssidref field not used any longer
2038 if 'ssidref' in info:
2039 info.pop('ssidref')
2041 # make sure state is reset for info
2042 # TODO: we should eventually get rid of old_dom_states
2044 self.info.update_config(info)
2045 self._update_consoles()
2047 if refresh:
2048 self.refreshShutdown(info)
2050 log.trace("XendDomainInfo.update done on domain %s: %s",
2051 str(self.domid), self.info)
2053 def sxpr(self, ignore_store = False, legacy_only = True):
2054 result = self.info.to_sxp(domain = self,
2055 ignore_devices = ignore_store,
2056 legacy_only = legacy_only)
2058 #if not ignore_store and self.dompath:
2059 # vnc_port = self.readDom('console/vnc-port')
2060 # if vnc_port is not None:
2061 # result.append(['device',
2062 # ['console', ['vnc-port', str(vnc_port)]]])
2064 return result
2066 # Xen API
2067 # ----------------------------------------------------------------
2069 def get_uuid(self):
2070 dom_uuid = self.info.get('uuid')
2071 if not dom_uuid: # if it doesn't exist, make one up
2072 dom_uuid = uuid.createString()
2073 self.info['uuid'] = dom_uuid
2074 return dom_uuid
2076 def get_memory_static_max(self):
2077 return self.info.get('memory_static_max', 0)
2078 def get_memory_static_min(self):
2079 return self.info.get('memory_static_min', 0)
2080 def get_memory_dynamic_max(self):
2081 return self.info.get('memory_dynamic_max', 0)
2082 def get_memory_dynamic_min(self):
2083 return self.info.get('memory_dynamic_min', 0)
2085 def set_memory_static_max(self, val):
2086 self.info['memory_static_max'] = val
2087 def set_memory_static_min(self, val):
2088 self.info['memory_static_min'] = val
2089 def set_memory_dynamic_max(self, val):
2090 self.info['memory_dynamic_max'] = val
2091 def set_memory_dynamic_min(self, val):
2092 self.info['memory_dynamic_min'] = val
2094 def get_vcpus_params(self):
2095 if self.getDomid() is None:
2096 return self.info['vcpus_params']
2098 retval = xc.sched_credit_domain_get(self.getDomid())
2099 return retval
2100 def get_power_state(self):
2101 return XEN_API_VM_POWER_STATE[self.state]
2102 def get_platform(self):
2103 return self.info.get('platform', {})
2104 def get_pci_bus(self):
2105 return self.info.get('pci_bus', '')
2106 def get_tools_version(self):
2107 return self.info.get('tools_version', {})
2108 def get_metrics(self):
2109 return self.metrics.get_uuid();
2111 def get_on_shutdown(self):
2112 after_shutdown = self.info.get('actions_after_shutdown')
2113 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2114 return XEN_API_ON_NORMAL_EXIT[-1]
2115 return after_shutdown
2117 def get_on_reboot(self):
2118 after_reboot = self.info.get('actions_after_reboot')
2119 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2120 return XEN_API_ON_NORMAL_EXIT[-1]
2121 return after_reboot
2123 def get_on_suspend(self):
2124 # TODO: not supported
2125 after_suspend = self.info.get('actions_after_suspend')
2126 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2127 return XEN_API_ON_NORMAL_EXIT[-1]
2128 return after_suspend
2130 def get_on_crash(self):
2131 after_crash = self.info.get('actions_after_crash')
2132 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2133 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2134 return after_crash
2136 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2137 """ Get's a device configuration either from XendConfig or
2138 from the DevController.
2140 @param dev_class: device class, either, 'vbd' or 'vif'
2141 @param dev_uuid: device UUID
2143 @rtype: dictionary
2144 """
2145 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2147 # shortcut if the domain isn't started because
2148 # the devcontrollers will have no better information
2149 # than XendConfig.
2150 if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
2151 if dev_config:
2152 return copy.deepcopy(dev_config)
2153 return None
2155 # instead of using dev_class, we use the dev_type
2156 # that is from XendConfig.
2157 controller = self.getDeviceController(dev_type)
2158 if not controller:
2159 return None
2161 all_configs = controller.getAllDeviceConfigurations()
2162 if not all_configs:
2163 return None
2165 updated_dev_config = copy.deepcopy(dev_config)
2166 for _devid, _devcfg in all_configs.items():
2167 if _devcfg.get('uuid') == dev_uuid:
2168 updated_dev_config.update(_devcfg)
2169 updated_dev_config['id'] = _devid
2170 return updated_dev_config
2172 return updated_dev_config
2174 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2175 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2176 if not config:
2177 return {}
2179 config['VM'] = self.get_uuid()
2181 if dev_class == 'vif':
2182 if not config.has_key('name'):
2183 config['name'] = config.get('vifname', '')
2184 if not config.has_key('MAC'):
2185 config['MAC'] = config.get('mac', '')
2186 if not config.has_key('type'):
2187 config['type'] = 'paravirtualised'
2188 if not config.has_key('device'):
2189 devid = config.get('id')
2190 if devid != None:
2191 config['device'] = 'eth%d' % devid
2192 else:
2193 config['device'] = ''
2195 if not config.has_key('network'):
2196 try:
2197 config['network'] = \
2198 XendNode.instance().bridge_to_network(
2199 config.get('bridge')).uuid
2200 except Exception:
2201 log.exception('bridge_to_network')
2202 # Ignore this for now -- it may happen if the device
2203 # has been specified using the legacy methods, but at
2204 # some point we're going to have to figure out how to
2205 # handle that properly.
2207 config['MTU'] = 1500 # TODO
2209 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2210 xennode = XendNode.instance()
2211 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2212 config['io_read_kbs'] = rx_bps/1024
2213 config['io_write_kbs'] = tx_bps/1024
2214 else:
2215 config['io_read_kbs'] = 0.0
2216 config['io_write_kbs'] = 0.0
2218 if dev_class == 'vbd':
2220 if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
2221 controller = self.getDeviceController(dev_class)
2222 devid, _1, _2 = controller.getDeviceDetails(config)
2223 xennode = XendNode.instance()
2224 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2225 config['io_read_kbs'] = rd_blkps
2226 config['io_write_kbs'] = wr_blkps
2227 else:
2228 config['io_read_kbs'] = 0.0
2229 config['io_write_kbs'] = 0.0
2231 config['VDI'] = config.get('VDI', '')
2232 config['device'] = config.get('dev', '')
2233 if ':' in config['device']:
2234 vbd_name, vbd_type = config['device'].split(':', 1)
2235 config['device'] = vbd_name
2236 if vbd_type == 'cdrom':
2237 config['type'] = XEN_API_VBD_TYPE[0]
2238 else:
2239 config['type'] = XEN_API_VBD_TYPE[1]
2241 config['driver'] = 'paravirtualised' # TODO
2242 config['image'] = config.get('uname', '')
2244 if config.get('mode', 'r') == 'r':
2245 config['mode'] = 'RO'
2246 else:
2247 config['mode'] = 'RW'
2249 if dev_class == 'vtpm':
2250 if not config.has_key('type'):
2251 config['type'] = 'paravirtualised' # TODO
2252 if not config.has_key('backend'):
2253 config['backend'] = "00000000-0000-0000-0000-000000000000"
2255 return config
2257 def get_dev_property(self, dev_class, dev_uuid, field):
2258 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2259 try:
2260 return config[field]
2261 except KeyError:
2262 raise XendError('Invalid property for device: %s' % field)
2264 def set_dev_property(self, dev_class, dev_uuid, field, value):
2265 self.info['devices'][dev_uuid][1][field] = value
2267 def get_vcpus_util(self):
2268 vcpu_util = {}
2269 xennode = XendNode.instance()
2270 if 'VCPUs_max' in self.info and self.domid != None:
2271 for i in range(0, self.info['VCPUs_max']):
2272 util = xennode.get_vcpu_util(self.domid, i)
2273 vcpu_util[str(i)] = util
2275 return vcpu_util
2277 def get_consoles(self):
2278 return self.info.get('console_refs', [])
2280 def get_vifs(self):
2281 return self.info.get('vif_refs', [])
2283 def get_vbds(self):
2284 return self.info.get('vbd_refs', [])
2286 def get_vtpms(self):
2287 return self.info.get('vtpm_refs', [])
2289 def create_vbd(self, xenapi_vbd, vdi_image_path):
2290 """Create a VBD using a VDI from XendStorageRepository.
2292 @param xenapi_vbd: vbd struct from the Xen API
2293 @param vdi_image_path: VDI UUID
2294 @rtype: string
2295 @return: uuid of the device
2296 """
2297 xenapi_vbd['image'] = vdi_image_path
2298 if vdi_image_path.startswith('tap'):
2299 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2300 else:
2301 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2303 if not dev_uuid:
2304 raise XendError('Failed to create device')
2306 if self.state == XEN_API_VM_POWER_STATE_RUNNING or \
2307 self.state == XEN_API_VM_POWER_STATE_PAUSED:
2308 _, config = self.info['devices'][dev_uuid]
2310 if vdi_image_path.startswith('tap'):
2311 dev_control = self.getDeviceController('tap')
2312 else:
2313 dev_control = self.getDeviceController('vbd')
2315 try:
2316 devid = dev_control.createDevice(config)
2317 dev_control.waitForDevice(devid)
2318 self.info.device_update(dev_uuid,
2319 cfg_xenapi = {'devid': devid})
2320 except Exception, exn:
2321 log.exception(exn)
2322 del self.info['devices'][dev_uuid]
2323 self.info['vbd_refs'].remove(dev_uuid)
2324 raise
2326 return dev_uuid
2328 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2329 """Create a VBD using a VDI from XendStorageRepository.
2331 @param xenapi_vbd: vbd struct from the Xen API
2332 @param vdi_image_path: VDI UUID
2333 @rtype: string
2334 @return: uuid of the device
2335 """
2336 xenapi_vbd['image'] = vdi_image_path
2337 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2338 if not dev_uuid:
2339 raise XendError('Failed to create device')
2341 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2342 _, config = self.info['devices'][dev_uuid]
2343 config['devid'] = self.getDeviceController('tap').createDevice(config)
2345 return config['devid']
2347 def create_vif(self, xenapi_vif):
2348 """Create VIF device from the passed struct in Xen API format.
2350 @param xenapi_vif: Xen API VIF Struct.
2351 @rtype: string
2352 @return: UUID
2353 """
2354 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2355 if not dev_uuid:
2356 raise XendError('Failed to create device')
2358 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2360 _, config = self.info['devices'][dev_uuid]
2361 dev_control = self.getDeviceController('vif')
2363 try:
2364 devid = dev_control.createDevice(config)
2365 dev_control.waitForDevice(devid)
2366 self.info.device_update(dev_uuid,
2367 cfg_xenapi = {'devid': devid})
2368 except Exception, exn:
2369 log.exception(exn)
2370 del self.info['devices'][dev_uuid]
2371 self.info['vif_refs'].remove(dev_uuid)
2372 raise
2374 return dev_uuid
2376 def create_vtpm(self, xenapi_vtpm):
2377 """Create a VTPM device from the passed struct in Xen API format.
2379 @return: uuid of the device
2380 @rtype: string
2381 """
2383 if self.state not in (DOM_STATE_HALTED,):
2384 raise VmError("Can only add vTPM to a halted domain.")
2385 if self.get_vtpms() != []:
2386 raise VmError('Domain already has a vTPM.')
2387 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2388 if not dev_uuid:
2389 raise XendError('Failed to create device')
2391 return dev_uuid
2393 def create_console(self, xenapi_console):
2394 """ Create a console device from a Xen API struct.
2396 @return: uuid of device
2397 @rtype: string
2398 """
2399 if self.state not in (DOM_STATE_HALTED,):
2400 raise VmError("Can only add console to a halted domain.")
2402 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2403 if not dev_uuid:
2404 raise XendError('Failed to create device')
2406 return dev_uuid
2408 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2409 if dev_uuid not in self.info['devices']:
2410 raise XendError('Device does not exist')
2412 try:
2413 if self.state == XEN_API_VM_POWER_STATE_RUNNING:
2414 _, config = self.info['devices'][dev_uuid]
2415 devid = config.get('devid')
2416 if devid != None:
2417 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2418 else:
2419 raise XendError('Unable to get devid for device: %s:%s' %
2420 (dev_type, dev_uuid))
2421 finally:
2422 del self.info['devices'][dev_uuid]
2423 self.info['%s_refs' % dev_type].remove(dev_uuid)
2425 def destroy_vbd(self, dev_uuid):
2426 self.destroy_device_by_uuid('vbd', dev_uuid)
2428 def destroy_vif(self, dev_uuid):
2429 self.destroy_device_by_uuid('vif', dev_uuid)
2431 def destroy_vtpm(self, dev_uuid):
2432 self.destroy_device_by_uuid('vtpm', dev_uuid)
2434 def has_device(self, dev_class, dev_uuid):
2435 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2437 def __str__(self):
2438 return '<domain id=%s name=%s memory=%s state=%s>' % \
2439 (str(self.domid), self.info['name_label'],
2440 str(self.info['memory_dynamic_max']), DOM_STATES[self.state])
2442 __repr__ = __str__