ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 14977:4789f3b2e1b9

xend: Auto-balloon a couple megabytes before creating an HVM domain as
Xen will allocate 1MB for shadow memory immediately.
Suggested by Daniel Berrange.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Fri Apr 27 15:22:34 2007 +0100 (2007-04-27)
parents 2c90965d0810
children 26643b7b48b9
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 import traceback
34 from types import StringTypes
36 import xen.lowlevel.xc
37 from xen.util import asserts
38 from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype
39 from xen.util import security
41 from xen.xend import balloon, sxp, uuid, image, arch, osdep
42 from xen.xend import XendOptions, XendNode, XendConfig
44 from xen.xend.XendConfig import scrub_password
45 from xen.xend.XendBootloader import bootloader, bootloader_tidy
46 from xen.xend.XendError import XendError, VmError
47 from xen.xend.XendDevices import XendDevices
48 from xen.xend.XendTask import XendTask
49 from xen.xend.xenstore.xstransact import xstransact, complete
50 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, ResumeDomain
51 from xen.xend.xenstore.xswatch import xswatch
52 from xen.xend.XendConstants import *
53 from xen.xend.XendAPIConstants import *
55 from xen.xend.XendVMMetrics import XendVMMetrics
57 MIGRATE_TIMEOUT = 30.0
58 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
60 xc = xen.lowlevel.xc.xc()
61 xoptions = XendOptions.instance()
63 log = logging.getLogger("xend.XendDomainInfo")
64 #log.setLevel(logging.TRACE)
67 def create(config):
68 """Creates and start a VM using the supplied configuration.
70 @param config: A configuration object involving lists of tuples.
71 @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
73 @rtype: XendDomainInfo
74 @return: An up and running XendDomainInfo instance
75 @raise VmError: Invalid configuration or failure to start.
76 """
78 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
79 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config))
80 try:
81 vm.start()
82 except:
83 log.exception('Domain construction failed')
84 vm.destroy()
85 raise
87 return vm
89 def create_from_dict(config_dict):
90 """Creates and start a VM using the supplied configuration.
92 @param config_dict: An configuration dictionary.
94 @rtype: XendDomainInfo
95 @return: An up and running XendDomainInfo instance
96 @raise VmError: Invalid configuration or failure to start.
97 """
99 log.debug("XendDomainInfo.create_from_dict(%s)",
100 scrub_password(config_dict))
101 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
102 try:
103 vm.start()
104 except:
105 log.exception('Domain construction failed')
106 vm.destroy()
107 raise
108 return vm
110 def recreate(info, priv):
111 """Create the VM object for an existing domain. The domain must not
112 be dying, as the paths in the store should already have been removed,
113 and asking us to recreate them causes problems.
115 @param xeninfo: Parsed configuration
116 @type xeninfo: Dictionary
117 @param priv: Is a privileged domain (Dom 0)
118 @type priv: bool
120 @rtype: XendDomainInfo
121 @return: A up and running XendDomainInfo instance
122 @raise VmError: Invalid configuration.
123 @raise XendError: Errors with configuration.
124 """
126 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
128 assert not info['dying']
130 xeninfo = XendConfig.XendConfig(dominfo = info)
131 xeninfo['is_control_domain'] = priv
132 xeninfo['is_a_template'] = False
133 domid = xeninfo['domid']
134 uuid1 = uuid.fromString(xeninfo['uuid'])
135 needs_reinitialising = False
137 dompath = GetDomainPath(domid)
138 if not dompath:
139 raise XendError('No domain path in store for existing '
140 'domain %d' % domid)
142 log.info("Recreating domain %d, UUID %s. at %s" %
143 (domid, xeninfo['uuid'], dompath))
145 # need to verify the path and uuid if not Domain-0
146 # if the required uuid and vm aren't set, then that means
147 # we need to recreate the dom with our own values
148 #
149 # NOTE: this is probably not desirable, really we should just
150 # abort or ignore, but there may be cases where xenstore's
151 # entry disappears (eg. xenstore-rm /)
152 #
153 try:
154 vmpath = xstransact.Read(dompath, "vm")
155 if not vmpath:
156 if not priv:
157 log.warn('/local/domain/%d/vm is missing. recreate is '
158 'confused, trying our best to recover' % domid)
159 needs_reinitialising = True
160 raise XendError('reinit')
162 uuid2_str = xstransact.Read(vmpath, "uuid")
163 if not uuid2_str:
164 log.warn('%s/uuid/ is missing. recreate is confused, '
165 'trying our best to recover' % vmpath)
166 needs_reinitialising = True
167 raise XendError('reinit')
169 uuid2 = uuid.fromString(uuid2_str)
170 if uuid1 != uuid2:
171 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
172 'Trying out best to recover' % domid)
173 needs_reinitialising = True
174 except XendError:
175 pass # our best shot at 'goto' in python :)
177 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv)
179 if needs_reinitialising:
180 vm._recreateDom()
181 vm._removeVm()
182 vm._storeVmDetails()
183 vm._storeDomDetails()
185 vm.image = image.create(vm, vm.info)
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_updated = threading.Condition()
366 self.refresh_shutdown_lock = threading.Condition()
367 self._stateSet(DOM_STATE_HALTED)
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'])
379 self.metrics = XendVMMetrics(uuid.createString(), self)
382 #
383 # Public functions available through XMLRPC
384 #
387 def start(self, is_managed = False):
388 """Attempts to start the VM by do the appropriate
389 initialisation if it not started.
390 """
391 from xen.xend import XendDomain
393 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED):
394 try:
395 XendTask.log_progress(0, 30, self._constructDomain)
396 XendTask.log_progress(31, 60, self._initDomain)
398 XendTask.log_progress(61, 70, self._storeVmDetails)
399 XendTask.log_progress(71, 80, self._storeDomDetails)
400 XendTask.log_progress(81, 90, self._registerWatches)
401 XendTask.log_progress(91, 100, self.refreshShutdown)
403 xendomains = XendDomain.instance()
404 xennode = XendNode.instance()
406 # save running configuration if XendDomains believe domain is
407 # persistent
408 if is_managed:
409 xendomains.managed_config_save(self)
411 if xennode.xenschedinfo() == 'credit':
412 xendomains.domain_sched_credit_set(self.getDomid(),
413 self.getWeight(),
414 self.getCap())
415 except:
416 log.exception('VM start failed')
417 self.destroy()
418 raise
419 else:
420 raise XendError('VM already running')
422 def resume(self):
423 """Resumes a domain that has come back from suspension."""
424 state = self._stateGet()
425 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
426 try:
427 self._constructDomain()
428 self._storeVmDetails()
429 self._createDevices()
430 self._createChannels()
431 self._storeDomDetails()
432 self._endRestore()
433 except:
434 log.exception('VM resume failed')
435 self.destroy()
436 raise
437 else:
438 raise XendError('VM is not susupened; it is %s'
439 % XEN_API_VM_POWER_STATE[state])
441 def shutdown(self, reason):
442 """Shutdown a domain by signalling this via xenstored."""
443 log.debug('XendDomainInfo.shutdown(%s)', reason)
444 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
445 raise XendError('Domain cannot be shutdown')
447 if self.domid == 0:
448 raise XendError('Domain 0 cannot be shutdown')
450 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
451 raise XendError('Invalid reason: %s' % reason)
452 self._removeVm('xend/previous_restart_time')
453 self.storeDom("control/shutdown", reason)
455 # HVM domain shuts itself down only if it has PV drivers
456 if self.info.is_hvm():
457 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
458 if not hvm_pvdrv:
459 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
460 log.info("HVM save:remote shutdown dom %d!", self.domid)
461 xc.domain_shutdown(self.domid, code)
463 def pause(self):
464 """Pause domain
466 @raise XendError: Failed pausing a domain
467 """
468 try:
469 xc.domain_pause(self.domid)
470 self._stateSet(DOM_STATE_PAUSED)
471 except Exception, ex:
472 log.exception(ex)
473 raise XendError("Domain unable to be paused: %s" % str(ex))
475 def unpause(self):
476 """Unpause domain
478 @raise XendError: Failed unpausing a domain
479 """
480 try:
481 xc.domain_unpause(self.domid)
482 self._stateSet(DOM_STATE_RUNNING)
483 except Exception, ex:
484 log.exception(ex)
485 raise XendError("Domain unable to be unpaused: %s" % str(ex))
487 def send_sysrq(self, key):
488 """ Send a Sysrq equivalent key via xenstored."""
489 asserts.isCharConvertible(key)
490 self.storeDom("control/sysrq", '%c' % key)
492 def device_create(self, dev_config):
493 """Create a new device.
495 @param dev_config: device configuration
496 @type dev_config: SXP object (parsed config)
497 """
498 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
499 dev_type = sxp.name(dev_config)
500 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
501 dev_config_dict = self.info['devices'][dev_uuid][1]
502 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
503 dev_config_dict['devid'] = devid = \
504 self._createDevice(dev_type, dev_config_dict)
505 self._waitForDevice(dev_type, devid)
506 return self.getDeviceController(dev_type).sxpr(devid)
508 def device_configure(self, dev_sxp, devid = None):
509 """Configure an existing device.
511 @param dev_config: device configuration
512 @type dev_config: SXP object (parsed config)
513 @param devid: device id
514 @type devid: int
515 @return: Returns True if successfully updated device
516 @rtype: boolean
517 """
519 # convert device sxp to a dict
520 dev_class = sxp.name(dev_sxp)
521 dev_config = {}
522 for opt_val in dev_sxp[1:]:
523 try:
524 dev_config[opt_val[0]] = opt_val[1]
525 except IndexError:
526 pass
528 # use DevController.reconfigureDevice to change device config
529 dev_control = self.getDeviceController(dev_class)
530 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
532 # update XendConfig with new device info
533 if dev_uuid:
534 self.info.device_update(dev_uuid, dev_sxp)
536 return True
538 def waitForDevices(self):
539 """Wait for this domain's configured devices to connect.
541 @raise VmError: if any device fails to initialise.
542 """
543 for devclass in XendDevices.valid_devices():
544 self.getDeviceController(devclass).waitForDevices()
546 def destroyDevice(self, deviceClass, devid, force = False):
547 try:
548 devid = int(devid)
549 except ValueError:
550 # devid is not a number, let's search for it in xenstore.
551 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
552 for entry in xstransact.List(devicePath):
553 backend = xstransact.Read('%s/%s' % (devicePath, entry),
554 "backend")
555 devName = xstransact.Read(backend, "dev")
556 if devName == devid:
557 # We found the integer matching our devid, use it instead
558 devid = entry
559 break
561 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
563 def getDeviceSxprs(self, deviceClass):
564 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
565 return self.getDeviceController(deviceClass).sxprs()
566 else:
567 sxprs = []
568 dev_num = 0
569 for dev_type, dev_info in self.info.all_devices_sxpr():
570 if dev_type == deviceClass:
571 sxprs.append([dev_num, dev_info])
572 dev_num += 1
573 return sxprs
576 def setMemoryTarget(self, target):
577 """Set the memory target of this domain.
578 @param target: In MiB.
579 """
580 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
581 self.info['name_label'], str(self.domid), target)
583 MiB = 1024 * 1024
584 self._safe_set_memory('memory_dynamic_min', target * MiB)
585 self._safe_set_memory('memory_dynamic_max', target * MiB)
587 if self.domid >= 0:
588 self.storeVm("memory", target)
589 self.storeDom("memory/target", target << 10)
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 (%s) to %d MiB.",
597 self.info['name_label'], str(self.domid), limit)
599 if limit <= 0:
600 raise XendError('Invalid memory size')
602 MiB = 1024 * 1024
603 self.info['memory_static_max'] = limit * MiB
605 if self.domid >= 0:
606 maxmem = int(limit) * 1024
607 try:
608 return xc.domain_setmaxmem(self.domid, maxmem)
609 except Exception, ex:
610 raise XendError(str(ex))
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',
863 'rtc/timeoffset']
865 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
866 for k in cfg_vm])
868 # convert two lists into a python dictionary
869 vm_details = dict(zip(cfg_vm, vm_details))
871 for arg, val in vm_details.items():
872 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
873 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
874 if val != None and val != self.info[xapiarg]:
875 self.info[xapiarg] = val
876 changed = True
877 elif arg == "memory":
878 if val != None and val != self.info["static_memory_min"]:
879 self.info["static_memory_min"] = val
880 changed = True
881 elif arg == "maxmem":
882 if val != None and val != self.info["static_memory_max"]:
883 self.info["static_memory_max"] = val
884 changed = True
886 # Check whether image definition has been updated
887 image_sxp = self._readVm('image')
888 if image_sxp and image_sxp != self.info.image_sxpr():
889 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
890 changed = True
892 # Check if the rtc offset has changes
893 if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
894 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
895 changed = True
897 if changed:
898 # Update the domain section of the store, as this contains some
899 # parameters derived from the VM configuration.
900 self._storeDomDetails()
902 return 1
904 def _handleShutdownWatch(self, _):
905 log.debug('XendDomainInfo.handleShutdownWatch')
907 reason = self.readDom('control/shutdown')
909 if reason and reason != 'suspend':
910 sst = self.readDom('xend/shutdown_start_time')
911 now = time.time()
912 if sst:
913 self.shutdownStartTime = float(sst)
914 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
915 else:
916 self.shutdownStartTime = now
917 self.storeDom('xend/shutdown_start_time', now)
918 timeout = SHUTDOWN_TIMEOUT
920 log.trace(
921 "Scheduling refreshShutdown on domain %d in %ds.",
922 self.domid, timeout)
923 threading.Timer(timeout, self.refreshShutdown).start()
925 return True
928 #
929 # Public Attributes for the VM
930 #
933 def getDomid(self):
934 return self.domid
936 def setName(self, name):
937 self._checkName(name)
938 self.info['name_label'] = name
939 self.storeVm("name", name)
941 def getName(self):
942 return self.info['name_label']
944 def getDomainPath(self):
945 return self.dompath
947 def getShutdownReason(self):
948 return self.readDom('control/shutdown')
950 def getStorePort(self):
951 """For use only by image.py and XendCheckpoint.py."""
952 return self.store_port
954 def getConsolePort(self):
955 """For use only by image.py and XendCheckpoint.py"""
956 return self.console_port
958 def getFeatures(self):
959 """For use only by image.py."""
960 return self.info['features']
962 def getVCpuCount(self):
963 return self.info['VCPUs_max']
965 def setVCpuCount(self, vcpus):
966 if vcpus <= 0:
967 raise XendError('Invalid VCPUs')
969 self.info['vcpu_avail'] = (1 << vcpus) - 1
970 if self.domid >= 0:
971 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
972 # update dom differently depending on whether we are adjusting
973 # vcpu number up or down, otherwise _vcpuDomDetails does not
974 # disable the vcpus
975 if self.info['VCPUs_max'] > vcpus:
976 # decreasing
977 self._writeDom(self._vcpuDomDetails())
978 self.info['VCPUs_live'] = vcpus
979 else:
980 # same or increasing
981 self.info['VCPUs_live'] = vcpus
982 self._writeDom(self._vcpuDomDetails())
983 else:
984 self.info['VCPUs_live'] = vcpus
985 xen.xend.XendDomain.instance().managed_config_save(self)
986 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
987 vcpus)
989 def getLabel(self):
990 return security.get_security_info(self.info, 'label')
992 def getMemoryTarget(self):
993 """Get this domain's target memory size, in KB."""
994 return self.info['memory_dynamic_max'] / 1024
996 def getMemoryMaximum(self):
997 """Get this domain's maximum memory size, in KB."""
998 # remember, info now stores memory in bytes
999 return self.info['memory_static_max'] / 1024
1001 def getResume(self):
1002 return str(self._resume)
1004 def getCap(self):
1005 return self.info.get('cpu_cap', 0)
1007 def getWeight(self):
1008 return self.info.get('cpu_weight', 256)
1010 def setResume(self, state):
1011 self._resume = state
1013 def getRestartCount(self):
1014 return self._readVm('xend/restart_count')
1016 def refreshShutdown(self, xeninfo = None):
1017 """ Checks the domain for whether a shutdown is required.
1019 Called from XendDomainInfo and also image.py for HVM images.
1020 """
1022 # If set at the end of this method, a restart is required, with the
1023 # given reason. This restart has to be done out of the scope of
1024 # refresh_shutdown_lock.
1025 restart_reason = None
1027 self.refresh_shutdown_lock.acquire()
1028 try:
1029 if xeninfo is None:
1030 xeninfo = dom_get(self.domid)
1031 if xeninfo is None:
1032 # The domain no longer exists. This will occur if we have
1033 # scheduled a timer to check for shutdown timeouts and the
1034 # shutdown succeeded. It will also occur if someone
1035 # destroys a domain beneath us. We clean up the domain,
1036 # just in case, but we can't clean up the VM, because that
1037 # VM may have migrated to a different domain on this
1038 # machine.
1039 self.cleanupDomain()
1040 self._stateSet(DOM_STATE_HALTED)
1041 return
1043 if xeninfo['dying']:
1044 # Dying means that a domain has been destroyed, but has not
1045 # yet been cleaned up by Xen. This state could persist
1046 # indefinitely if, for example, another domain has some of its
1047 # pages mapped. We might like to diagnose this problem in the
1048 # future, but for now all we do is make sure that it's not us
1049 # holding the pages, by calling cleanupDomain. We can't
1050 # clean up the VM, as above.
1051 self.cleanupDomain()
1052 self._stateSet(DOM_STATE_SHUTDOWN)
1053 return
1055 elif xeninfo['crashed']:
1056 if self.readDom('xend/shutdown_completed'):
1057 # We've seen this shutdown already, but we are preserving
1058 # the domain for debugging. Leave it alone.
1059 return
1061 log.warn('Domain has crashed: name=%s id=%d.',
1062 self.info['name_label'], self.domid)
1063 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1065 if xoptions.get_enable_dump():
1066 try:
1067 self.dumpCore()
1068 except XendError:
1069 # This error has been logged -- there's nothing more
1070 # we can do in this context.
1071 pass
1073 restart_reason = 'crash'
1074 self._stateSet(DOM_STATE_HALTED)
1076 elif xeninfo['shutdown']:
1077 self._stateSet(DOM_STATE_SHUTDOWN)
1078 if self.readDom('xend/shutdown_completed'):
1079 # We've seen this shutdown already, but we are preserving
1080 # the domain for debugging. Leave it alone.
1081 return
1083 else:
1084 reason = shutdown_reason(xeninfo['shutdown_reason'])
1086 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1087 self.info['name_label'], self.domid, reason)
1088 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1090 self._clearRestart()
1092 if reason == 'suspend':
1093 self._stateSet(DOM_STATE_SUSPENDED)
1094 # Don't destroy the domain. XendCheckpoint will do
1095 # this once it has finished. However, stop watching
1096 # the VM path now, otherwise we will end up with one
1097 # watch for the old domain, and one for the new.
1098 self._unwatchVm()
1099 elif reason in ('poweroff', 'reboot'):
1100 restart_reason = reason
1101 else:
1102 self.destroy()
1104 elif self.dompath is None:
1105 # We have yet to manage to call introduceDomain on this
1106 # domain. This can happen if a restore is in progress, or has
1107 # failed. Ignore this domain.
1108 pass
1109 else:
1110 # Domain is alive. If we are shutting it down, then check
1111 # the timeout on that, and destroy it if necessary.
1112 if xeninfo['paused']:
1113 self._stateSet(DOM_STATE_PAUSED)
1114 else:
1115 self._stateSet(DOM_STATE_RUNNING)
1117 if self.shutdownStartTime:
1118 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1119 self.shutdownStartTime)
1120 if timeout < 0:
1121 log.info(
1122 "Domain shutdown timeout expired: name=%s id=%s",
1123 self.info['name_label'], self.domid)
1124 self.destroy()
1125 finally:
1126 self.refresh_shutdown_lock.release()
1128 if restart_reason:
1129 threading.Thread(target = self._maybeRestart,
1130 args = (restart_reason,)).start()
1134 # Restart functions - handling whether we come back up on shutdown.
1137 def _clearRestart(self):
1138 self._removeDom("xend/shutdown_start_time")
1141 def _maybeRestart(self, reason):
1142 # Dispatch to the correct method based upon the configured on_{reason}
1143 # behaviour.
1144 actions = {"destroy" : self.destroy,
1145 "restart" : self._restart,
1146 "preserve" : self._preserve,
1147 "rename-restart" : self._renameRestart}
1149 action_conf = {
1150 'poweroff': 'actions_after_shutdown',
1151 'reboot': 'actions_after_reboot',
1152 'crash': 'actions_after_crash',
1155 action_target = self.info.get(action_conf.get(reason))
1156 func = actions.get(action_target, None)
1157 if func and callable(func):
1158 func()
1159 else:
1160 self.destroy() # default to destroy
1162 def _renameRestart(self):
1163 self._restart(True)
1165 def _restart(self, rename = False):
1166 """Restart the domain after it has exited.
1168 @param rename True if the old domain is to be renamed and preserved,
1169 False if it is to be destroyed.
1170 """
1171 from xen.xend import XendDomain
1173 if self._readVm(RESTART_IN_PROGRESS):
1174 log.error('Xend failed during restart of domain %s. '
1175 'Refusing to restart to avoid loops.',
1176 str(self.domid))
1177 self.destroy()
1178 return
1180 old_domid = self.domid
1181 self._writeVm(RESTART_IN_PROGRESS, 'True')
1183 now = time.time()
1184 rst = self._readVm('xend/previous_restart_time')
1185 if rst:
1186 rst = float(rst)
1187 timeout = now - rst
1188 if timeout < MINIMUM_RESTART_TIME:
1189 log.error(
1190 'VM %s restarting too fast (%f seconds since the last '
1191 'restart). Refusing to restart to avoid loops.',
1192 self.info['name_label'], timeout)
1193 self.destroy()
1194 return
1196 self._writeVm('xend/previous_restart_time', str(now))
1198 try:
1199 if rename:
1200 self._preserveForRestart()
1201 else:
1202 self._unwatchVm()
1203 self.destroyDomain()
1205 # new_dom's VM will be the same as this domain's VM, except where
1206 # the rename flag has instructed us to call preserveForRestart.
1207 # In that case, it is important that we remove the
1208 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1209 # once the new one is available.
1211 new_dom = None
1212 try:
1213 new_dom = XendDomain.instance().domain_create_from_dict(
1214 self.info)
1215 new_dom.unpause()
1216 rst_cnt = self._readVm('xend/restart_count')
1217 rst_cnt = int(rst_cnt) + 1
1218 self._writeVm('xend/restart_count', str(rst_cnt))
1219 new_dom._removeVm(RESTART_IN_PROGRESS)
1220 except:
1221 if new_dom:
1222 new_dom._removeVm(RESTART_IN_PROGRESS)
1223 new_dom.destroy()
1224 else:
1225 self._removeVm(RESTART_IN_PROGRESS)
1226 raise
1227 except:
1228 log.exception('Failed to restart domain %s.', str(old_domid))
1230 def _preserveForRestart(self):
1231 """Preserve a domain that has been shut down, by giving it a new UUID,
1232 cloning the VM details, and giving it a new name. This allows us to
1233 keep this domain for debugging, but restart a new one in its place
1234 preserving the restart semantics (name and UUID preserved).
1235 """
1237 new_uuid = uuid.createString()
1238 new_name = 'Domain-%s' % new_uuid
1239 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1240 self.info['name_label'], self.domid, self.info['uuid'],
1241 new_name, new_uuid)
1242 self._unwatchVm()
1243 self._releaseDevices()
1244 self.info['name_label'] = new_name
1245 self.info['uuid'] = new_uuid
1246 self.vmpath = XS_VMROOT + new_uuid
1247 self._storeVmDetails()
1248 self._preserve()
1251 def _preserve(self):
1252 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1253 self.domid)
1254 self._unwatchVm()
1255 self.storeDom('xend/shutdown_completed', 'True')
1256 self._stateSet(DOM_STATE_HALTED)
1259 # Debugging ..
1262 def dumpCore(self, corefile = None):
1263 """Create a core dump for this domain.
1265 @raise: XendError if core dumping failed.
1266 """
1268 try:
1269 if not corefile:
1270 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1271 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1272 self.info['name_label'], self.domid)
1274 if os.path.isdir(corefile):
1275 raise XendError("Cannot dump core in a directory: %s" %
1276 corefile)
1278 xc.domain_dumpcore(self.domid, corefile)
1279 except RuntimeError, ex:
1280 corefile_incomp = corefile+'-incomplete'
1281 os.rename(corefile, corefile_incomp)
1282 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1283 self.domid, self.info['name_label'])
1284 raise XendError("Failed to dump core: %s" % str(ex))
1287 # Device creation/deletion functions
1290 def _createDevice(self, deviceClass, devConfig):
1291 return self.getDeviceController(deviceClass).createDevice(devConfig)
1293 def _waitForDevice(self, deviceClass, devid):
1294 return self.getDeviceController(deviceClass).waitForDevice(devid)
1296 def _waitForDeviceUUID(self, dev_uuid):
1297 deviceClass, config = self.info['devices'].get(dev_uuid)
1298 self._waitForDevice(deviceClass, config['devid'])
1300 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1301 return self.getDeviceController(deviceClass).reconfigureDevice(
1302 devid, devconfig)
1304 def _createDevices(self):
1305 """Create the devices for a vm.
1307 @raise: VmError for invalid devices
1308 """
1309 ordered_refs = self.info.ordered_device_refs()
1310 for dev_uuid in ordered_refs:
1311 devclass, config = self.info['devices'][dev_uuid]
1312 if devclass in XendDevices.valid_devices():
1313 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1314 dev_uuid = config.get('uuid')
1315 devid = self._createDevice(devclass, config)
1317 # store devid in XendConfig for caching reasons
1318 if dev_uuid in self.info['devices']:
1319 self.info['devices'][dev_uuid][1]['devid'] = devid
1321 if self.image:
1322 self.image.createDeviceModel()
1324 def _releaseDevices(self, suspend = False):
1325 """Release all domain's devices. Nothrow guarantee."""
1326 if suspend and self.image:
1327 self.image.destroy(suspend)
1328 return
1330 while True:
1331 t = xstransact("%s/device" % self.dompath)
1332 for devclass in XendDevices.valid_devices():
1333 for dev in t.list(devclass):
1334 try:
1335 t.remove(dev)
1336 except:
1337 # Log and swallow any exceptions in removal --
1338 # there's nothing more we can do.
1339 log.exception(
1340 "Device release failed: %s; %s; %s",
1341 self.info['name_label'], devclass, dev)
1342 if t.commit():
1343 break
1345 def getDeviceController(self, name):
1346 """Get the device controller for this domain, and if it
1347 doesn't exist, create it.
1349 @param name: device class name
1350 @type name: string
1351 @rtype: subclass of DevController
1352 """
1353 if name not in self._deviceControllers:
1354 devController = XendDevices.make_controller(name, self)
1355 if not devController:
1356 raise XendError("Unknown device type: %s" % name)
1357 self._deviceControllers[name] = devController
1359 return self._deviceControllers[name]
1362 # Migration functions (public)
1365 def testMigrateDevices(self, network, dst):
1366 """ Notify all device about intention of migration
1367 @raise: XendError for a device that cannot be migrated
1368 """
1369 for (n, c) in self.info.all_devices_sxpr():
1370 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1371 if rc != 0:
1372 raise XendError("Device of type '%s' refuses migration." % n)
1374 def migrateDevices(self, network, dst, step, domName=''):
1375 """Notify the devices about migration
1376 """
1377 ctr = 0
1378 try:
1379 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1380 self.migrateDevice(dev_type, dev_conf, network, dst,
1381 step, domName)
1382 ctr = ctr + 1
1383 except:
1384 for dev_type, dev_conf in self.info.all_devices_sxpr():
1385 if ctr == 0:
1386 step = step - 1
1387 ctr = ctr - 1
1388 self._recoverMigrateDevice(dev_type, dev_conf, network,
1389 dst, step, domName)
1390 raise
1392 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1393 step, domName=''):
1394 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1395 network, dst, step, domName)
1397 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1398 dst, step, domName=''):
1399 return self.getDeviceController(deviceClass).recover_migrate(
1400 deviceConfig, network, dst, step, domName)
1403 ## private:
1405 def _constructDomain(self):
1406 """Construct the domain.
1408 @raise: VmError on error
1409 """
1411 log.debug('XendDomainInfo.constructDomain')
1413 self.shutdownStartTime = None
1415 hvm = self.info.is_hvm()
1416 if hvm:
1417 info = xc.xeninfo()
1418 if 'hvm' not in info['xen_caps']:
1419 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1420 "supported by your CPU and enabled in your "
1421 "BIOS?")
1422 # Hack to pre-reserve some memory for HVM setup.
1423 # Needed because Xen allocates 1MB by default immediately.
1424 balloon.free(2*1024) # 2MB should be plenty
1426 self.domid = xc.domain_create(
1427 domid = 0,
1428 ssidref = security.get_security_info(self.info, 'ssidref'),
1429 handle = uuid.fromString(self.info['uuid']),
1430 hvm = int(hvm))
1432 if self.domid < 0:
1433 raise VmError('Creating domain failed: name=%s' %
1434 self.info['name_label'])
1436 self.dompath = GetDomainPath(self.domid)
1438 self._recreateDom()
1440 # Set maximum number of vcpus in domain
1441 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1443 # register the domain in the list
1444 from xen.xend import XendDomain
1445 XendDomain.instance().add_domain(self)
1447 def _introduceDomain(self):
1448 assert self.domid is not None
1449 assert self.store_mfn is not None
1450 assert self.store_port is not None
1452 try:
1453 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1454 except RuntimeError, exn:
1455 raise XendError(str(exn))
1458 def _initDomain(self):
1459 log.debug('XendDomainInfo.initDomain: %s %s',
1460 self.domid,
1461 self.info['cpu_weight'])
1463 self._configureBootloader()
1465 try:
1466 self.image = image.create(self, self.info)
1468 localtime = self.info.get('platform_localtime', False)
1469 if localtime:
1470 xc.domain_set_time_offset(self.domid)
1472 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1474 # repin domain vcpus if a restricted cpus list is provided
1475 # this is done prior to memory allocation to aide in memory
1476 # distribution for NUMA systems.
1477 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1478 for v in range(0, self.info['VCPUs_max']):
1479 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1481 # Use architecture- and image-specific calculations to determine
1482 # the various headrooms necessary, given the raw configured
1483 # values. maxmem, memory, and shadow are all in KiB.
1484 # but memory_static_max etc are all stored in bytes now.
1485 memory = self.image.getRequiredAvailableMemory(
1486 self.info['memory_dynamic_max'] / 1024)
1487 maxmem = self.image.getRequiredAvailableMemory(
1488 self.info['memory_static_max'] / 1024)
1489 shadow = self.image.getRequiredShadowMemory(
1490 self.info['shadow_memory'] / 1024,
1491 self.info['memory_static_max'] / 1024)
1493 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'],)
1494 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1495 # takes MiB and we must not round down and end up under-providing.
1496 shadow = ((shadow + 1023) / 1024) * 1024
1498 # set memory limit
1499 xc.domain_setmaxmem(self.domid, maxmem)
1501 # Make sure there's enough RAM available for the domain
1502 balloon.free(memory + shadow)
1504 # Set up the shadow memory
1505 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1506 self.info['shadow_memory'] = shadow_cur
1508 self._createChannels()
1510 channel_details = self.image.createImage()
1512 self.store_mfn = channel_details['store_mfn']
1513 if 'console_mfn' in channel_details:
1514 self.console_mfn = channel_details['console_mfn']
1515 if 'notes' in channel_details:
1516 self.info.set_notes(channel_details['notes'])
1518 self._introduceDomain()
1520 self._createDevices()
1522 self.image.cleanupBootloading()
1524 self.info['start_time'] = time.time()
1526 self._stateSet(DOM_STATE_RUNNING)
1527 except VmError, exn:
1528 log.exception("XendDomainInfo.initDomain: exception occurred")
1529 if self.image:
1530 self.image.cleanupBootloading()
1531 raise exn
1532 except RuntimeError, exn:
1533 log.exception("XendDomainInfo.initDomain: exception occurred")
1534 if self.image:
1535 self.image.cleanupBootloading()
1536 raise VmError(str(exn))
1539 def cleanupDomain(self):
1540 """Cleanup domain resources; release devices. Idempotent. Nothrow
1541 guarantee."""
1543 self.refresh_shutdown_lock.acquire()
1544 try:
1545 self.unwatchShutdown()
1546 self._releaseDevices()
1547 bootloader_tidy(self)
1549 if self.image:
1550 try:
1551 self.image.destroy()
1552 except:
1553 log.exception(
1554 "XendDomainInfo.cleanup: image.destroy() failed.")
1555 self.image = None
1557 try:
1558 self._removeDom()
1559 except:
1560 log.exception("Removing domain path failed.")
1562 self._stateSet(DOM_STATE_HALTED)
1563 finally:
1564 self.refresh_shutdown_lock.release()
1567 def unwatchShutdown(self):
1568 """Remove the watch on the domain's control/shutdown node, if any.
1569 Idempotent. Nothrow guarantee. Expects to be protected by the
1570 refresh_shutdown_lock."""
1572 try:
1573 try:
1574 if self.shutdownWatch:
1575 self.shutdownWatch.unwatch()
1576 finally:
1577 self.shutdownWatch = None
1578 except:
1579 log.exception("Unwatching control/shutdown failed.")
1581 def waitForShutdown(self):
1582 self.state_updated.acquire()
1583 try:
1584 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1585 self.state_updated.wait()
1586 finally:
1587 self.state_updated.release()
1590 # TODO: recategorise - called from XendCheckpoint
1593 def completeRestore(self, store_mfn, console_mfn):
1595 log.debug("XendDomainInfo.completeRestore")
1597 self.store_mfn = store_mfn
1598 self.console_mfn = console_mfn
1600 self._introduceDomain()
1601 if self.info.is_hvm():
1602 self.image = image.create(self, self.info)
1603 if self.image:
1604 self.image.createDeviceModel(True)
1605 self._storeDomDetails()
1606 self._registerWatches()
1607 self.refreshShutdown()
1609 log.debug("XendDomainInfo.completeRestore done")
1612 def _endRestore(self):
1613 self.setResume(False)
1616 # VM Destroy
1619 def _prepare_phantom_paths(self):
1620 # get associated devices to destroy
1621 # build list of phantom devices to be removed after normal devices
1622 plist = []
1623 if self.domid is not None:
1624 from xen.xend.xenstore.xstransact import xstransact
1625 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1626 for dev in t.list():
1627 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1628 % (self.dompath, dev))
1629 if backend_phantom_vbd is not None:
1630 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1631 % backend_phantom_vbd)
1632 plist.append(backend_phantom_vbd)
1633 plist.append(frontend_phantom_vbd)
1634 return plist
1636 def _cleanup_phantom_devs(self, plist):
1637 # remove phantom devices
1638 if not plist == []:
1639 time.sleep(2)
1640 for paths in plist:
1641 if paths.find('backend') != -1:
1642 from xen.xend.server import DevController
1643 # Modify online status /before/ updating state (latter is watched by
1644 # drivers, so this ordering avoids a race).
1645 xstransact.Write(paths, 'online', "0")
1646 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1647 # force
1648 xstransact.Remove(paths)
1650 def destroy(self):
1651 """Cleanup VM and destroy domain. Nothrow guarantee."""
1653 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1655 paths = self._prepare_phantom_paths()
1657 self._cleanupVm()
1658 if self.dompath is not None:
1659 self.destroyDomain()
1661 self._cleanup_phantom_devs(paths)
1663 if "transient" in self.info["other_config"] \
1664 and bool(self.info["other_config"]["transient"]):
1665 from xen.xend import XendDomain
1666 XendDomain.instance().domain_delete_by_dominfo(self)
1669 def destroyDomain(self):
1670 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1672 paths = self._prepare_phantom_paths()
1674 try:
1675 if self.domid is not None:
1676 xc.domain_destroy(self.domid)
1677 self.domid = None
1678 for state in DOM_STATES_OLD:
1679 self.info[state] = 0
1680 self._stateSet(DOM_STATE_HALTED)
1681 except:
1682 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1684 from xen.xend import XendDomain
1685 XendDomain.instance().remove_domain(self)
1687 self.cleanupDomain()
1688 self._cleanup_phantom_devs(paths)
1691 def resumeDomain(self):
1692 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1694 if self.domid is None:
1695 return
1696 try:
1697 # could also fetch a parsed note from xenstore
1698 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1699 if not fast:
1700 self._releaseDevices()
1701 self.testDeviceComplete()
1702 self.testvifsComplete()
1703 log.debug("XendDomainInfo.resumeDomain: devices released")
1705 self._resetChannels()
1707 self._removeDom('control/shutdown')
1708 self._removeDom('device-misc/vif/nextDeviceID')
1710 self._createChannels()
1711 self._introduceDomain()
1712 self._storeDomDetails()
1714 self._createDevices()
1715 log.debug("XendDomainInfo.resumeDomain: devices created")
1717 xc.domain_resume(self.domid, fast)
1718 ResumeDomain(self.domid)
1719 except:
1720 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1723 # Channels for xenstore and console
1726 def _createChannels(self):
1727 """Create the channels to the domain.
1728 """
1729 self.store_port = self._createChannel()
1730 self.console_port = self._createChannel()
1733 def _createChannel(self):
1734 """Create an event channel to the domain.
1735 """
1736 try:
1737 if self.domid != None:
1738 return xc.evtchn_alloc_unbound(domid = self.domid,
1739 remote_dom = 0)
1740 except:
1741 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1742 raise
1744 def _resetChannels(self):
1745 """Reset all event channels in the domain.
1746 """
1747 try:
1748 if self.domid != None:
1749 return xc.evtchn_reset(dom = self.domid)
1750 except:
1751 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1752 raise
1756 # Bootloader configuration
1759 def _configureBootloader(self):
1760 """Run the bootloader if we're configured to do so."""
1762 blexec = self.info['PV_bootloader']
1763 bootloader_args = self.info['PV_bootloader_args']
1764 kernel = self.info['PV_kernel']
1765 ramdisk = self.info['PV_ramdisk']
1766 args = self.info['PV_args']
1767 boot = self.info['HVM_boot_policy']
1769 if boot:
1770 # HVM booting.
1771 pass
1772 elif not blexec and kernel:
1773 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1774 # will be picked up by image.py.
1775 pass
1776 else:
1777 # Boot using bootloader
1778 if not blexec or blexec == 'pygrub':
1779 blexec = osdep.pygrub_path
1781 blcfg = None
1782 disks = [x for x in self.info['vbd_refs']
1783 if self.info['devices'][x][1]['bootable']]
1785 if not disks:
1786 msg = "Had a bootloader specified, but no disks are bootable"
1787 log.error(msg)
1788 raise VmError(msg)
1790 devinfo = self.info['devices'][disks[0]]
1791 devtype = devinfo[0]
1792 disk = devinfo[1]['uname']
1794 fn = blkdev_uname_to_file(disk)
1795 taptype = blkdev_uname_to_taptype(disk)
1796 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1797 if mounted:
1798 # This is a file, not a device. pygrub can cope with a
1799 # file if it's raw, but if it's QCOW or other such formats
1800 # used through blktap, then we need to mount it first.
1802 log.info("Mounting %s on %s." %
1803 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1805 vbd = {
1806 'mode': 'RO',
1807 'device': BOOTLOADER_LOOPBACK_DEVICE,
1810 from xen.xend import XendDomain
1811 dom0 = XendDomain.instance().privilegedDomain()
1812 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1813 fn = BOOTLOADER_LOOPBACK_DEVICE
1815 try:
1816 blcfg = bootloader(blexec, fn, self, False,
1817 bootloader_args, kernel, ramdisk, args)
1818 finally:
1819 if mounted:
1820 log.info("Unmounting %s from %s." %
1821 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1823 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1825 if blcfg is None:
1826 msg = "Had a bootloader specified, but can't find disk"
1827 log.error(msg)
1828 raise VmError(msg)
1830 self.info.update_with_image_sxp(blcfg, True)
1834 # VM Functions
1837 def _readVMDetails(self, params):
1838 """Read the specified parameters from the store.
1839 """
1840 try:
1841 return self._gatherVm(*params)
1842 except ValueError:
1843 # One of the int/float entries in params has a corresponding store
1844 # entry that is invalid. We recover, because older versions of
1845 # Xend may have put the entry there (memory/target, for example),
1846 # but this is in general a bad situation to have reached.
1847 log.exception(
1848 "Store corrupted at %s! Domain %d's configuration may be "
1849 "affected.", self.vmpath, self.domid)
1850 return []
1852 def _cleanupVm(self):
1853 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1855 self._unwatchVm()
1857 try:
1858 self._removeVm()
1859 except:
1860 log.exception("Removing VM path failed.")
1863 def checkLiveMigrateMemory(self):
1864 """ Make sure there's enough memory to migrate this domain """
1865 overhead_kb = 0
1866 if arch.type == "x86":
1867 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1868 # the minimum that Xen would allocate if no value were given.
1869 overhead_kb = self.info['VCPUs_max'] * 1024 + \
1870 (self.info['memory_static_max'] / 1024 / 1024) * 4
1871 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1872 # The domain might already have some shadow memory
1873 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1874 if overhead_kb > 0:
1875 balloon.free(overhead_kb)
1877 def _unwatchVm(self):
1878 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1879 guarantee."""
1880 try:
1881 try:
1882 if self.vmWatch:
1883 self.vmWatch.unwatch()
1884 finally:
1885 self.vmWatch = None
1886 except:
1887 log.exception("Unwatching VM path failed.")
1889 def testDeviceComplete(self):
1890 """ For Block IO migration safety we must ensure that
1891 the device has shutdown correctly, i.e. all blocks are
1892 flushed to disk
1893 """
1894 start = time.time()
1895 while True:
1896 test = 0
1897 diff = time.time() - start
1898 for i in self.getDeviceController('vbd').deviceIDs():
1899 test = 1
1900 log.info("Dev %s still active, looping...", i)
1901 time.sleep(0.1)
1903 if test == 0:
1904 break
1905 if diff >= MIGRATE_TIMEOUT:
1906 log.info("Dev still active but hit max loop timeout")
1907 break
1909 def testvifsComplete(self):
1910 """ In case vifs are released and then created for the same
1911 domain, we need to wait the device shut down.
1912 """
1913 start = time.time()
1914 while True:
1915 test = 0
1916 diff = time.time() - start
1917 for i in self.getDeviceController('vif').deviceIDs():
1918 test = 1
1919 log.info("Dev %s still active, looping...", i)
1920 time.sleep(0.1)
1922 if test == 0:
1923 break
1924 if diff >= MIGRATE_TIMEOUT:
1925 log.info("Dev still active but hit max loop timeout")
1926 break
1928 def _storeVmDetails(self):
1929 to_store = {}
1931 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1932 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1933 if self._infoIsSet(info_key):
1934 to_store[key] = str(self.info[info_key])
1936 if self._infoIsSet("static_memory_min"):
1937 to_store["memory"] = str(self.info["static_memory_min"])
1938 if self._infoIsSet("static_memory_max"):
1939 to_store["maxmem"] = str(self.info["static_memory_max"])
1941 image_sxpr = self.info.image_sxpr()
1942 if image_sxpr:
1943 to_store['image'] = sxp.to_string(image_sxpr)
1945 if self._infoIsSet('security'):
1946 secinfo = self.info['security']
1947 to_store['security'] = sxp.to_string(secinfo)
1948 for idx in range(0, len(secinfo)):
1949 if secinfo[idx][0] == 'access_control':
1950 to_store['security/access_control'] = sxp.to_string(
1951 [secinfo[idx][1], secinfo[idx][2]])
1952 for aidx in range(1, len(secinfo[idx])):
1953 if secinfo[idx][aidx][0] == 'label':
1954 to_store['security/access_control/label'] = \
1955 secinfo[idx][aidx][1]
1956 if secinfo[idx][aidx][0] == 'policy':
1957 to_store['security/access_control/policy'] = \
1958 secinfo[idx][aidx][1]
1959 if secinfo[idx][0] == 'ssidref':
1960 to_store['security/ssidref'] = str(secinfo[idx][1])
1963 if not self._readVm('xend/restart_count'):
1964 to_store['xend/restart_count'] = str(0)
1966 log.debug("Storing VM details: %s", scrub_password(to_store))
1968 self._writeVm(to_store)
1969 self._setVmPermissions()
1972 def _setVmPermissions(self):
1973 """Allow the guest domain to read its UUID. We don't allow it to
1974 access any other entry, for security."""
1975 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1976 { 'dom' : self.domid,
1977 'read' : True,
1978 'write' : False })
1981 # Utility functions
1984 def __getattr__(self, name):
1985 if name == "state":
1986 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
1987 log.warn("".join(traceback.format_stack()))
1988 return self._stateGet()
1989 else:
1990 raise AttributeError()
1992 def __setattr__(self, name, value):
1993 if name == "state":
1994 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
1995 log.warn("".join(traceback.format_stack()))
1996 self._stateSet(value)
1997 else:
1998 self.__dict__[name] = value
2000 def _stateSet(self, state):
2001 self.state_updated.acquire()
2002 try:
2003 # TODO Not sure this is correct...
2004 # _stateGet is live now. Why not fire event
2005 # even when it hasn't changed?
2006 if self._stateGet() != state:
2007 self.state_updated.notifyAll()
2008 import XendAPI
2009 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2010 'power_state')
2011 finally:
2012 self.state_updated.release()
2014 def _stateGet(self):
2015 # Lets try and reconsitute the state from xc
2016 # first lets try and get the domain info
2017 # from xc - this will tell us if the domain
2018 # exists
2019 info = dom_get(self.getDomid())
2020 if info is None or info['shutdown']:
2021 # We are either HALTED or SUSPENDED
2022 # check saved image exists
2023 from xen.xend import XendDomain
2024 managed_config_path = \
2025 XendDomain.instance()._managed_check_point_path( \
2026 self.get_uuid())
2027 if os.path.exists(managed_config_path):
2028 return XEN_API_VM_POWER_STATE_SUSPENDED
2029 else:
2030 return XEN_API_VM_POWER_STATE_HALTED
2031 else:
2032 # We are either RUNNING or PAUSED
2033 if info['paused']:
2034 return XEN_API_VM_POWER_STATE_PAUSED
2035 else:
2036 return XEN_API_VM_POWER_STATE_RUNNING
2038 def _infoIsSet(self, name):
2039 return name in self.info and self.info[name] is not None
2041 def _checkName(self, name):
2042 """Check if a vm name is valid. Valid names contain alphabetic
2043 characters, digits, or characters in '_-.:/+'.
2044 The same name cannot be used for more than one vm at the same time.
2046 @param name: name
2047 @raise: VmError if invalid
2048 """
2049 from xen.xend import XendDomain
2051 if name is None or name == '':
2052 raise VmError('Missing VM Name')
2054 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2055 raise VmError('Invalid VM Name')
2057 dom = XendDomain.instance().domain_lookup_nr(name)
2058 if dom and dom.info['uuid'] != self.info['uuid']:
2059 raise VmError("VM name '%s' already exists%s" %
2060 (name,
2061 dom.domid is not None and
2062 (" as domain %s" % str(dom.domid)) or ""))
2065 def update(self, info = None, refresh = True):
2066 """Update with info from xc.domain_getinfo().
2067 """
2068 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2069 str(self.domid))
2071 if not info:
2072 info = dom_get(self.domid)
2073 if not info:
2074 return
2076 if info["maxmem_kb"] < 0:
2077 info["maxmem_kb"] = XendNode.instance() \
2078 .physinfo_dict()['total_memory'] * 1024
2080 #manually update ssidref / security fields
2081 if security.on() and info.has_key('ssidref'):
2082 if (info['ssidref'] != 0) and self.info.has_key('security'):
2083 security_field = self.info['security']
2084 if not security_field:
2085 #create new security element
2086 self.info.update({'security':
2087 [['ssidref', str(info['ssidref'])]]})
2089 #ssidref field not used any longer
2090 if 'ssidref' in info:
2091 info.pop('ssidref')
2093 # make sure state is reset for info
2094 # TODO: we should eventually get rid of old_dom_states
2096 self.info.update_config(info)
2097 self._update_consoles()
2099 if refresh:
2100 self.refreshShutdown(info)
2102 log.trace("XendDomainInfo.update done on domain %s: %s",
2103 str(self.domid), self.info)
2105 def sxpr(self, ignore_store = False, legacy_only = True):
2106 result = self.info.to_sxp(domain = self,
2107 ignore_devices = ignore_store,
2108 legacy_only = legacy_only)
2110 #if not ignore_store and self.dompath:
2111 # vnc_port = self.readDom('console/vnc-port')
2112 # if vnc_port is not None:
2113 # result.append(['device',
2114 # ['console', ['vnc-port', str(vnc_port)]]])
2116 return result
2118 # Xen API
2119 # ----------------------------------------------------------------
2121 def get_uuid(self):
2122 dom_uuid = self.info.get('uuid')
2123 if not dom_uuid: # if it doesn't exist, make one up
2124 dom_uuid = uuid.createString()
2125 self.info['uuid'] = dom_uuid
2126 return dom_uuid
2128 def get_memory_static_max(self):
2129 return self.info.get('memory_static_max', 0)
2130 def get_memory_static_min(self):
2131 return self.info.get('memory_static_min', 0)
2132 def get_memory_dynamic_max(self):
2133 return self.info.get('memory_dynamic_max', 0)
2134 def get_memory_dynamic_min(self):
2135 return self.info.get('memory_dynamic_min', 0)
2137 # only update memory-related config values if they maintain sanity
2138 def _safe_set_memory(self, key, newval):
2139 oldval = self.info.get(key, 0)
2140 try:
2141 self.info[key] = newval
2142 self.info._memory_sanity_check()
2143 except Exception, ex:
2144 self.info[key] = oldval
2145 raise
2147 def set_memory_static_max(self, val):
2148 self._safe_set_memory('memory_static_max', val)
2149 def set_memory_static_min(self, val):
2150 self._safe_set_memory('memory_static_min', val)
2151 def set_memory_dynamic_max(self, val):
2152 self._safe_set_memory('memory_dynamic_max', val)
2153 def set_memory_dynamic_min(self, val):
2154 self._safe_set_memory('memory_dynamic_min', val)
2156 def get_vcpus_params(self):
2157 if self.getDomid() is None:
2158 return self.info['vcpus_params']
2160 retval = xc.sched_credit_domain_get(self.getDomid())
2161 return retval
2162 def get_power_state(self):
2163 return XEN_API_VM_POWER_STATE[self._stateGet()]
2164 def get_platform(self):
2165 return self.info.get('platform', {})
2166 def get_pci_bus(self):
2167 return self.info.get('pci_bus', '')
2168 def get_tools_version(self):
2169 return self.info.get('tools_version', {})
2170 def get_metrics(self):
2171 return self.metrics.get_uuid();
2173 def get_on_shutdown(self):
2174 after_shutdown = self.info.get('actions_after_shutdown')
2175 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2176 return XEN_API_ON_NORMAL_EXIT[-1]
2177 return after_shutdown
2179 def get_on_reboot(self):
2180 after_reboot = self.info.get('actions_after_reboot')
2181 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2182 return XEN_API_ON_NORMAL_EXIT[-1]
2183 return after_reboot
2185 def get_on_suspend(self):
2186 # TODO: not supported
2187 after_suspend = self.info.get('actions_after_suspend')
2188 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2189 return XEN_API_ON_NORMAL_EXIT[-1]
2190 return after_suspend
2192 def get_on_crash(self):
2193 after_crash = self.info.get('actions_after_crash')
2194 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2195 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2196 return after_crash
2198 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2199 """ Get's a device configuration either from XendConfig or
2200 from the DevController.
2202 @param dev_class: device class, either, 'vbd' or 'vif'
2203 @param dev_uuid: device UUID
2205 @rtype: dictionary
2206 """
2207 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2209 # shortcut if the domain isn't started because
2210 # the devcontrollers will have no better information
2211 # than XendConfig.
2212 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2213 if dev_config:
2214 return copy.deepcopy(dev_config)
2215 return None
2217 # instead of using dev_class, we use the dev_type
2218 # that is from XendConfig.
2219 controller = self.getDeviceController(dev_type)
2220 if not controller:
2221 return None
2223 all_configs = controller.getAllDeviceConfigurations()
2224 if not all_configs:
2225 return None
2227 updated_dev_config = copy.deepcopy(dev_config)
2228 for _devid, _devcfg in all_configs.items():
2229 if _devcfg.get('uuid') == dev_uuid:
2230 updated_dev_config.update(_devcfg)
2231 updated_dev_config['id'] = _devid
2232 return updated_dev_config
2234 return updated_dev_config
2236 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2237 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2238 if not config:
2239 return {}
2241 config['VM'] = self.get_uuid()
2243 if dev_class == 'vif':
2244 if not config.has_key('name'):
2245 config['name'] = config.get('vifname', '')
2246 if not config.has_key('MAC'):
2247 config['MAC'] = config.get('mac', '')
2248 if not config.has_key('type'):
2249 config['type'] = 'paravirtualised'
2250 if not config.has_key('device'):
2251 devid = config.get('id')
2252 if devid != None:
2253 config['device'] = 'eth%d' % devid
2254 else:
2255 config['device'] = ''
2257 if not config.has_key('network'):
2258 try:
2259 bridge = config.get('bridge', None)
2260 if bridge is None:
2261 from xen.util import Brctl
2262 if_to_br = dict([(i,b)
2263 for (b,ifs) in Brctl.get_state().items()
2264 for i in ifs])
2265 vifname = "vif%s.%s" % (self.getDomid(),
2266 config.get('id'))
2267 bridge = if_to_br.get(vifname, None)
2268 config['network'] = \
2269 XendNode.instance().bridge_to_network(
2270 config.get('bridge')).get_uuid()
2271 except Exception:
2272 log.exception('bridge_to_network')
2273 # Ignore this for now -- it may happen if the device
2274 # has been specified using the legacy methods, but at
2275 # some point we're going to have to figure out how to
2276 # handle that properly.
2278 config['MTU'] = 1500 # TODO
2280 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2281 xennode = XendNode.instance()
2282 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2283 config['io_read_kbs'] = rx_bps/1024
2284 config['io_write_kbs'] = tx_bps/1024
2285 else:
2286 config['io_read_kbs'] = 0.0
2287 config['io_write_kbs'] = 0.0
2289 if dev_class == 'vbd':
2291 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2292 controller = self.getDeviceController(dev_class)
2293 devid, _1, _2 = controller.getDeviceDetails(config)
2294 xennode = XendNode.instance()
2295 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2296 config['io_read_kbs'] = rd_blkps
2297 config['io_write_kbs'] = wr_blkps
2298 else:
2299 config['io_read_kbs'] = 0.0
2300 config['io_write_kbs'] = 0.0
2302 config['VDI'] = config.get('VDI', '')
2303 config['device'] = config.get('dev', '')
2304 if ':' in config['device']:
2305 vbd_name, vbd_type = config['device'].split(':', 1)
2306 config['device'] = vbd_name
2307 if vbd_type == 'cdrom':
2308 config['type'] = XEN_API_VBD_TYPE[0]
2309 else:
2310 config['type'] = XEN_API_VBD_TYPE[1]
2312 config['driver'] = 'paravirtualised' # TODO
2313 config['image'] = config.get('uname', '')
2315 if config.get('mode', 'r') == 'r':
2316 config['mode'] = 'RO'
2317 else:
2318 config['mode'] = 'RW'
2320 if dev_class == 'vtpm':
2321 if not config.has_key('type'):
2322 config['type'] = 'paravirtualised' # TODO
2323 if not config.has_key('backend'):
2324 config['backend'] = "00000000-0000-0000-0000-000000000000"
2326 return config
2328 def get_dev_property(self, dev_class, dev_uuid, field):
2329 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2330 try:
2331 return config[field]
2332 except KeyError:
2333 raise XendError('Invalid property for device: %s' % field)
2335 def set_dev_property(self, dev_class, dev_uuid, field, value):
2336 self.info['devices'][dev_uuid][1][field] = value
2338 def get_vcpus_util(self):
2339 vcpu_util = {}
2340 xennode = XendNode.instance()
2341 if 'VCPUs_max' in self.info and self.domid != None:
2342 for i in range(0, self.info['VCPUs_max']):
2343 util = xennode.get_vcpu_util(self.domid, i)
2344 vcpu_util[str(i)] = util
2346 return vcpu_util
2348 def get_consoles(self):
2349 return self.info.get('console_refs', [])
2351 def get_vifs(self):
2352 return self.info.get('vif_refs', [])
2354 def get_vbds(self):
2355 return self.info.get('vbd_refs', [])
2357 def get_vtpms(self):
2358 return self.info.get('vtpm_refs', [])
2360 def create_vbd(self, xenapi_vbd, vdi_image_path):
2361 """Create a VBD using a VDI from XendStorageRepository.
2363 @param xenapi_vbd: vbd struct from the Xen API
2364 @param vdi_image_path: VDI UUID
2365 @rtype: string
2366 @return: uuid of the device
2367 """
2368 xenapi_vbd['image'] = vdi_image_path
2369 if vdi_image_path.startswith('tap'):
2370 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2371 else:
2372 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2374 if not dev_uuid:
2375 raise XendError('Failed to create device')
2377 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2378 XEN_API_VM_POWER_STATE_PAUSED):
2379 _, config = self.info['devices'][dev_uuid]
2381 if vdi_image_path.startswith('tap'):
2382 dev_control = self.getDeviceController('tap')
2383 else:
2384 dev_control = self.getDeviceController('vbd')
2386 try:
2387 devid = dev_control.createDevice(config)
2388 dev_control.waitForDevice(devid)
2389 self.info.device_update(dev_uuid,
2390 cfg_xenapi = {'devid': devid})
2391 except Exception, exn:
2392 log.exception(exn)
2393 del self.info['devices'][dev_uuid]
2394 self.info['vbd_refs'].remove(dev_uuid)
2395 raise
2397 return dev_uuid
2399 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2400 """Create a VBD using a VDI from XendStorageRepository.
2402 @param xenapi_vbd: vbd struct from the Xen API
2403 @param vdi_image_path: VDI UUID
2404 @rtype: string
2405 @return: uuid of the device
2406 """
2407 xenapi_vbd['image'] = vdi_image_path
2408 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2409 if not dev_uuid:
2410 raise XendError('Failed to create device')
2412 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2413 _, config = self.info['devices'][dev_uuid]
2414 config['devid'] = self.getDeviceController('tap').createDevice(config)
2416 return config['devid']
2418 def create_vif(self, xenapi_vif):
2419 """Create VIF device from the passed struct in Xen API format.
2421 @param xenapi_vif: Xen API VIF Struct.
2422 @rtype: string
2423 @return: UUID
2424 """
2425 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2426 if not dev_uuid:
2427 raise XendError('Failed to create device')
2429 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2430 XEN_API_VM_POWER_STATE_PAUSED):
2432 _, config = self.info['devices'][dev_uuid]
2433 dev_control = self.getDeviceController('vif')
2435 try:
2436 devid = dev_control.createDevice(config)
2437 dev_control.waitForDevice(devid)
2438 self.info.device_update(dev_uuid,
2439 cfg_xenapi = {'devid': devid})
2440 except Exception, exn:
2441 log.exception(exn)
2442 del self.info['devices'][dev_uuid]
2443 self.info['vif_refs'].remove(dev_uuid)
2444 raise
2446 return dev_uuid
2448 def create_vtpm(self, xenapi_vtpm):
2449 """Create a VTPM device from the passed struct in Xen API format.
2451 @return: uuid of the device
2452 @rtype: string
2453 """
2455 if self._stateGet() not in (DOM_STATE_HALTED,):
2456 raise VmError("Can only add vTPM to a halted domain.")
2457 if self.get_vtpms() != []:
2458 raise VmError('Domain already has a vTPM.')
2459 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2460 if not dev_uuid:
2461 raise XendError('Failed to create device')
2463 return dev_uuid
2465 def create_console(self, xenapi_console):
2466 """ Create a console device from a Xen API struct.
2468 @return: uuid of device
2469 @rtype: string
2470 """
2471 if self._stateGet() not in (DOM_STATE_HALTED,):
2472 raise VmError("Can only add console to a halted domain.")
2474 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2475 if not dev_uuid:
2476 raise XendError('Failed to create device')
2478 return dev_uuid
2480 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2481 if dev_uuid not in self.info['devices']:
2482 raise XendError('Device does not exist')
2484 try:
2485 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2486 XEN_API_VM_POWER_STATE_PAUSED):
2487 _, config = self.info['devices'][dev_uuid]
2488 devid = config.get('devid')
2489 if devid != None:
2490 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2491 else:
2492 raise XendError('Unable to get devid for device: %s:%s' %
2493 (dev_type, dev_uuid))
2494 finally:
2495 del self.info['devices'][dev_uuid]
2496 self.info['%s_refs' % dev_type].remove(dev_uuid)
2498 def destroy_vbd(self, dev_uuid):
2499 self.destroy_device_by_uuid('vbd', dev_uuid)
2501 def destroy_vif(self, dev_uuid):
2502 self.destroy_device_by_uuid('vif', dev_uuid)
2504 def destroy_vtpm(self, dev_uuid):
2505 self.destroy_device_by_uuid('vtpm', dev_uuid)
2507 def has_device(self, dev_class, dev_uuid):
2508 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2510 def __str__(self):
2511 return '<domain id=%s name=%s memory=%s state=%s>' % \
2512 (str(self.domid), self.info['name_label'],
2513 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2515 __repr__ = __str__