ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 15223:a717cb2fac90

xend: Fix checkname so that it detects duplicate domains.
Signed-off-by: Mats Petersson <mats.petersson@amd.com>
author kfraser@localhost.localdomain
date Fri May 25 10:53:07 2007 +0100 (2007-05-25)
parents d93e560c1d50
children f2d2d5f18543
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.native_protocol = None
362 self.vmWatch = None
363 self.shutdownWatch = None
364 self.shutdownStartTime = None
365 self.unresponsive = False
366 self._resume = resume
368 self.state_updated = threading.Condition()
369 self.refresh_shutdown_lock = threading.Condition()
370 self._stateSet(DOM_STATE_HALTED)
372 self._deviceControllers = {}
374 for state in DOM_STATES_OLD:
375 self.info[state] = 0
377 if augment:
378 self._augmentInfo(priv)
380 self._checkName(self.info['name_label'])
382 self.metrics = XendVMMetrics(uuid.createString(), self)
385 #
386 # Public functions available through XMLRPC
387 #
390 def start(self, is_managed = False):
391 """Attempts to start the VM by do the appropriate
392 initialisation if it not started.
393 """
394 from xen.xend import XendDomain
396 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED):
397 try:
398 XendTask.log_progress(0, 30, self._constructDomain)
399 XendTask.log_progress(31, 60, self._initDomain)
401 XendTask.log_progress(61, 70, self._storeVmDetails)
402 XendTask.log_progress(71, 80, self._storeDomDetails)
403 XendTask.log_progress(81, 90, self._registerWatches)
404 XendTask.log_progress(91, 100, self.refreshShutdown)
406 xendomains = XendDomain.instance()
407 xennode = XendNode.instance()
409 # save running configuration if XendDomains believe domain is
410 # persistent
411 if is_managed:
412 xendomains.managed_config_save(self)
414 if xennode.xenschedinfo() == 'credit':
415 xendomains.domain_sched_credit_set(self.getDomid(),
416 self.getWeight(),
417 self.getCap())
418 except:
419 log.exception('VM start failed')
420 self.destroy()
421 raise
422 else:
423 raise XendError('VM already running')
425 def resume(self):
426 """Resumes a domain that has come back from suspension."""
427 state = self._stateGet()
428 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
429 try:
430 self._constructDomain()
431 self._storeVmDetails()
432 self._createDevices()
433 self._createChannels()
434 self._storeDomDetails()
435 self._endRestore()
436 except:
437 log.exception('VM resume failed')
438 self.destroy()
439 raise
440 else:
441 raise XendError('VM is not susupened; it is %s'
442 % XEN_API_VM_POWER_STATE[state])
444 def shutdown(self, reason):
445 """Shutdown a domain by signalling this via xenstored."""
446 log.debug('XendDomainInfo.shutdown(%s)', reason)
447 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
448 raise XendError('Domain cannot be shutdown')
450 if self.domid == 0:
451 raise XendError('Domain 0 cannot be shutdown')
453 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
454 raise XendError('Invalid reason: %s' % reason)
455 self._removeVm('xend/previous_restart_time')
456 self.storeDom("control/shutdown", reason)
458 # HVM domain shuts itself down only if it has PV drivers
459 if self.info.is_hvm():
460 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
461 if not hvm_pvdrv:
462 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
463 log.info("HVM save:remote shutdown dom %d!", self.domid)
464 xc.domain_shutdown(self.domid, code)
466 def pause(self):
467 """Pause domain
469 @raise XendError: Failed pausing a domain
470 """
471 try:
472 xc.domain_pause(self.domid)
473 self._stateSet(DOM_STATE_PAUSED)
474 except Exception, ex:
475 log.exception(ex)
476 raise XendError("Domain unable to be paused: %s" % str(ex))
478 def unpause(self):
479 """Unpause domain
481 @raise XendError: Failed unpausing a domain
482 """
483 try:
484 xc.domain_unpause(self.domid)
485 self._stateSet(DOM_STATE_RUNNING)
486 except Exception, ex:
487 log.exception(ex)
488 raise XendError("Domain unable to be unpaused: %s" % str(ex))
490 def send_sysrq(self, key):
491 """ Send a Sysrq equivalent key via xenstored."""
492 asserts.isCharConvertible(key)
493 self.storeDom("control/sysrq", '%c' % key)
495 def device_create(self, dev_config):
496 """Create a new device.
498 @param dev_config: device configuration
499 @type dev_config: SXP object (parsed config)
500 """
501 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
502 dev_type = sxp.name(dev_config)
503 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
504 dev_config_dict = self.info['devices'][dev_uuid][1]
505 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
506 dev_config_dict['devid'] = devid = \
507 self._createDevice(dev_type, dev_config_dict)
508 self._waitForDevice(dev_type, devid)
509 return self.getDeviceController(dev_type).sxpr(devid)
511 def device_configure(self, dev_sxp, devid = None):
512 """Configure an existing device.
514 @param dev_config: device configuration
515 @type dev_config: SXP object (parsed config)
516 @param devid: device id
517 @type devid: int
518 @return: Returns True if successfully updated device
519 @rtype: boolean
520 """
522 # convert device sxp to a dict
523 dev_class = sxp.name(dev_sxp)
524 dev_config = {}
525 for opt_val in dev_sxp[1:]:
526 try:
527 dev_config[opt_val[0]] = opt_val[1]
528 except IndexError:
529 pass
531 # use DevController.reconfigureDevice to change device config
532 dev_control = self.getDeviceController(dev_class)
533 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
535 # update XendConfig with new device info
536 if dev_uuid:
537 self.info.device_update(dev_uuid, dev_sxp)
539 return True
541 def waitForDevices(self):
542 """Wait for this domain's configured devices to connect.
544 @raise VmError: if any device fails to initialise.
545 """
546 for devclass in XendDevices.valid_devices():
547 self.getDeviceController(devclass).waitForDevices()
549 def destroyDevice(self, deviceClass, devid, force = False):
550 found = True # Assume devid is an integer.
551 try:
552 devid = int(devid)
553 except ValueError:
554 # devid is not a number, let's search for it in xenstore.
555 devicePath = '%s/device/%s' % (self.dompath, deviceClass)
556 found = False
557 for entry in xstransact.List(devicePath):
558 log.debug("Attempting to find devid at %s/%s", devicePath, entry)
559 backend = xstransact.Read('%s/%s' % (devicePath, entry),
560 "backend")
561 if backend != None:
562 devName = '%s/%s' % (deviceClass, entry)
563 log.debug("devName=%s", devName)
564 if devName == devid:
565 # We found the integer matching our devid, use it instead
566 devid = int(entry)
567 found = True
568 break
570 if not found:
571 log.debug("Could not find the device %s", devid)
572 return None
573 log.debug("devid = %s", devid)
574 return self.getDeviceController(deviceClass).destroyDevice(devid, force)
576 def getDeviceSxprs(self, deviceClass):
577 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
578 return self.getDeviceController(deviceClass).sxprs()
579 else:
580 sxprs = []
581 dev_num = 0
582 for dev_type, dev_info in self.info.all_devices_sxpr():
583 if dev_type == deviceClass:
584 sxprs.append([dev_num, dev_info])
585 dev_num += 1
586 return sxprs
589 def setMemoryTarget(self, target):
590 """Set the memory target of this domain.
591 @param target: In MiB.
592 """
593 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
594 self.info['name_label'], str(self.domid), target)
596 MiB = 1024 * 1024
597 self._safe_set_memory('memory_dynamic_min', target * MiB)
598 self._safe_set_memory('memory_dynamic_max', target * MiB)
600 if self.domid >= 0:
601 self.storeVm("memory", target)
602 self.storeDom("memory/target", target << 10)
603 xen.xend.XendDomain.instance().managed_config_save(self)
605 def setMemoryMaximum(self, limit):
606 """Set the maximum memory limit of this domain
607 @param limit: In MiB.
608 """
609 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
610 self.info['name_label'], str(self.domid), limit)
612 if limit <= 0:
613 raise XendError('Invalid memory size')
615 MiB = 1024 * 1024
616 self.info['memory_static_max'] = limit * MiB
618 if self.domid >= 0:
619 maxmem = int(limit) * 1024
620 try:
621 return xc.domain_setmaxmem(self.domid, maxmem)
622 except Exception, ex:
623 raise XendError(str(ex))
624 xen.xend.XendDomain.instance().managed_config_save(self)
627 def getVCPUInfo(self):
628 try:
629 # We include the domain name and ID, to help xm.
630 sxpr = ['domain',
631 ['domid', self.domid],
632 ['name', self.info['name_label']],
633 ['vcpu_count', self.info['VCPUs_max']]]
635 for i in range(0, self.info['VCPUs_max']):
636 info = xc.vcpu_getinfo(self.domid, i)
638 sxpr.append(['vcpu',
639 ['number', i],
640 ['online', info['online']],
641 ['blocked', info['blocked']],
642 ['running', info['running']],
643 ['cpu_time', info['cpu_time'] / 1e9],
644 ['cpu', info['cpu']],
645 ['cpumap', info['cpumap']]])
647 return sxpr
649 except RuntimeError, exn:
650 raise XendError(str(exn))
653 def getDomInfo(self):
654 return dom_get(self.domid)
656 #
657 # internal functions ... TODO: re-categorised
658 #
660 def _augmentInfo(self, priv):
661 """Augment self.info, as given to us through L{recreate}, with
662 values taken from the store. This recovers those values known
663 to xend but not to the hypervisor.
664 """
665 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
666 if priv:
667 augment_entries.remove('memory')
668 augment_entries.remove('maxmem')
669 augment_entries.remove('vcpus')
670 augment_entries.remove('vcpu_avail')
672 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
673 for k in augment_entries])
675 # make returned lists into a dictionary
676 vm_config = dict(zip(augment_entries, vm_config))
678 for arg in augment_entries:
679 val = vm_config[arg]
680 if val != None:
681 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
682 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
683 self.info[xapiarg] = val
684 elif arg == "memory":
685 self.info["static_memory_min"] = val
686 elif arg == "maxmem":
687 self.info["static_memory_max"] = val
688 else:
689 self.info[arg] = val
691 # For dom0, we ignore any stored value for the vcpus fields, and
692 # read the current value from Xen instead. This allows boot-time
693 # settings to take precedence over any entries in the store.
694 if priv:
695 xeninfo = dom_get(self.domid)
696 self.info['VCPUs_max'] = xeninfo['online_vcpus']
697 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
699 # read image value
700 image_sxp = self._readVm('image')
701 if image_sxp:
702 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
704 # read devices
705 devices = []
706 for devclass in XendDevices.valid_devices():
707 devconfig = self.getDeviceController(devclass).configurations()
708 if devconfig:
709 devices.extend(devconfig)
711 if not self.info['devices'] and devices is not None:
712 for device in devices:
713 self.info.device_add(device[0], cfg_sxp = device)
715 self._update_consoles()
717 def _update_consoles(self):
718 if self.domid == None or self.domid == 0:
719 return
721 # Update VT100 port if it exists
722 self.console_port = self.readDom('console/port')
723 if self.console_port is not None:
724 serial_consoles = self.info.console_get_all('vt100')
725 if not serial_consoles:
726 cfg = self.info.console_add('vt100', self.console_port)
727 self._createDevice('console', cfg)
728 else:
729 console_uuid = serial_consoles[0].get('uuid')
730 self.info.console_update(console_uuid, 'location',
731 self.console_port)
734 # Update VNC port if it exists and write to xenstore
735 vnc_port = self.readDom('console/vnc-port')
736 if vnc_port is not None:
737 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
738 if dev_type == 'vfb':
739 old_location = dev_info.get('location')
740 listen_host = dev_info.get('vnclisten', 'localhost')
741 new_location = '%s:%s' % (listen_host, str(vnc_port))
742 if old_location == new_location:
743 break
745 dev_info['location'] = new_location
746 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
747 vfb_ctrl = self.getDeviceController('vfb')
748 vfb_ctrl.reconfigureDevice(0, dev_info)
749 break
751 #
752 # Function to update xenstore /vm/*
753 #
755 def _readVm(self, *args):
756 return xstransact.Read(self.vmpath, *args)
758 def _writeVm(self, *args):
759 return xstransact.Write(self.vmpath, *args)
761 def _removeVm(self, *args):
762 return xstransact.Remove(self.vmpath, *args)
764 def _gatherVm(self, *args):
765 return xstransact.Gather(self.vmpath, *args)
767 def storeVm(self, *args):
768 return xstransact.Store(self.vmpath, *args)
770 #
771 # Function to update xenstore /dom/*
772 #
774 def readDom(self, *args):
775 return xstransact.Read(self.dompath, *args)
777 def gatherDom(self, *args):
778 return xstransact.Gather(self.dompath, *args)
780 def _writeDom(self, *args):
781 return xstransact.Write(self.dompath, *args)
783 def _removeDom(self, *args):
784 return xstransact.Remove(self.dompath, *args)
786 def storeDom(self, *args):
787 return xstransact.Store(self.dompath, *args)
789 def _recreateDom(self):
790 complete(self.dompath, lambda t: self._recreateDomFunc(t))
792 def _recreateDomFunc(self, t):
793 t.remove()
794 t.mkdir()
795 t.set_permissions({'dom' : self.domid})
796 t.write('vm', self.vmpath)
798 def _storeDomDetails(self):
799 to_store = {
800 'domid': str(self.domid),
801 'vm': self.vmpath,
802 'name': self.info['name_label'],
803 'console/limit': str(xoptions.get_console_limit() * 1024),
804 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
805 }
807 def f(n, v):
808 if v is not None:
809 if type(v) == bool:
810 to_store[n] = v and "1" or "0"
811 else:
812 to_store[n] = str(v)
814 f('console/port', self.console_port)
815 f('console/ring-ref', self.console_mfn)
816 f('store/port', self.store_port)
817 f('store/ring-ref', self.store_mfn)
819 if arch.type == "x86":
820 f('control/platform-feature-multiprocessor-suspend', True)
822 # elfnotes
823 for n, v in self.info.get_notes().iteritems():
824 n = n.lower().replace('_', '-')
825 if n == 'features':
826 for v in v.split('|'):
827 v = v.replace('_', '-')
828 if v.startswith('!'):
829 f('image/%s/%s' % (n, v[1:]), False)
830 else:
831 f('image/%s/%s' % (n, v), True)
832 else:
833 f('image/%s' % n, v)
835 to_store.update(self._vcpuDomDetails())
837 log.debug("Storing domain details: %s", scrub_password(to_store))
839 self._writeDom(to_store)
841 def _vcpuDomDetails(self):
842 def availability(n):
843 if self.info['vcpu_avail'] & (1 << n):
844 return 'online'
845 else:
846 return 'offline'
848 result = {}
849 for v in range(0, self.info['VCPUs_max']):
850 result["cpu/%d/availability" % v] = availability(v)
851 return result
853 #
854 # xenstore watches
855 #
857 def _registerWatches(self):
858 """Register a watch on this VM's entries in the store, and the
859 domain's control/shutdown node, so that when they are changed
860 externally, we keep up to date. This should only be called by {@link
861 #create}, {@link #recreate}, or {@link #restore}, once the domain's
862 details have been written, but before the new instance is returned."""
863 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
864 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
865 self._handleShutdownWatch)
867 def _storeChanged(self, _):
868 log.trace("XendDomainInfo.storeChanged");
870 changed = False
872 # Check whether values in the configuration have
873 # changed in Xenstore.
875 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
876 'rtc/timeoffset']
878 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
879 for k in cfg_vm])
881 # convert two lists into a python dictionary
882 vm_details = dict(zip(cfg_vm, vm_details))
884 if vm_details['rtc/timeoffset'] == None:
885 vm_details['rtc/timeoffset'] = "0"
887 for arg, val in vm_details.items():
888 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
889 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
890 if val != None and val != self.info[xapiarg]:
891 self.info[xapiarg] = val
892 changed = True
893 elif arg == "memory":
894 if val != None and val != self.info["static_memory_min"]:
895 self.info["static_memory_min"] = val
896 changed = True
897 elif arg == "maxmem":
898 if val != None and val != self.info["static_memory_max"]:
899 self.info["static_memory_max"] = val
900 changed = True
902 # Check whether image definition has been updated
903 image_sxp = self._readVm('image')
904 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
905 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
906 changed = True
908 # Check if the rtc offset has changes
909 if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
910 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
911 changed = True
913 if changed:
914 # Update the domain section of the store, as this contains some
915 # parameters derived from the VM configuration.
916 self._storeDomDetails()
918 return 1
920 def _handleShutdownWatch(self, _):
921 log.debug('XendDomainInfo.handleShutdownWatch')
923 reason = self.readDom('control/shutdown')
925 if reason and reason != 'suspend':
926 sst = self.readDom('xend/shutdown_start_time')
927 now = time.time()
928 if sst:
929 self.shutdownStartTime = float(sst)
930 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
931 else:
932 self.shutdownStartTime = now
933 self.storeDom('xend/shutdown_start_time', now)
934 timeout = SHUTDOWN_TIMEOUT
936 log.trace(
937 "Scheduling refreshShutdown on domain %d in %ds.",
938 self.domid, timeout)
939 threading.Timer(timeout, self.refreshShutdown).start()
941 return True
944 #
945 # Public Attributes for the VM
946 #
949 def getDomid(self):
950 return self.domid
952 def setName(self, name):
953 self._checkName(name)
954 self.info['name_label'] = name
955 self.storeVm("name", name)
957 def getName(self):
958 return self.info['name_label']
960 def getDomainPath(self):
961 return self.dompath
963 def getShutdownReason(self):
964 return self.readDom('control/shutdown')
966 def getStorePort(self):
967 """For use only by image.py and XendCheckpoint.py."""
968 return self.store_port
970 def getConsolePort(self):
971 """For use only by image.py and XendCheckpoint.py"""
972 return self.console_port
974 def getFeatures(self):
975 """For use only by image.py."""
976 return self.info['features']
978 def getVCpuCount(self):
979 return self.info['VCPUs_max']
981 def setVCpuCount(self, vcpus):
982 if vcpus <= 0:
983 raise XendError('Invalid VCPUs')
985 self.info['vcpu_avail'] = (1 << vcpus) - 1
986 if self.domid >= 0:
987 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
988 # update dom differently depending on whether we are adjusting
989 # vcpu number up or down, otherwise _vcpuDomDetails does not
990 # disable the vcpus
991 if self.info['VCPUs_max'] > vcpus:
992 # decreasing
993 self._writeDom(self._vcpuDomDetails())
994 self.info['VCPUs_live'] = vcpus
995 else:
996 # same or increasing
997 self.info['VCPUs_live'] = vcpus
998 self._writeDom(self._vcpuDomDetails())
999 else:
1000 self.info['VCPUs_live'] = vcpus
1001 xen.xend.XendDomain.instance().managed_config_save(self)
1002 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1003 vcpus)
1005 def getLabel(self):
1006 return security.get_security_info(self.info, 'label')
1008 def getMemoryTarget(self):
1009 """Get this domain's target memory size, in KB."""
1010 return self.info['memory_dynamic_max'] / 1024
1012 def getMemoryMaximum(self):
1013 """Get this domain's maximum memory size, in KB."""
1014 # remember, info now stores memory in bytes
1015 return self.info['memory_static_max'] / 1024
1017 def getResume(self):
1018 return str(self._resume)
1020 def getCap(self):
1021 return self.info.get('cpu_cap', 0)
1023 def setCap(self, cpu_cap):
1024 self.info['cpu_cap'] = cpu_cap
1026 def getWeight(self):
1027 return self.info.get('cpu_weight', 256)
1029 def setWeight(self, cpu_weight):
1030 self.info['cpu_weight'] = cpu_weight
1032 def setResume(self, state):
1033 self._resume = state
1035 def getRestartCount(self):
1036 return self._readVm('xend/restart_count')
1038 def refreshShutdown(self, xeninfo = None):
1039 """ Checks the domain for whether a shutdown is required.
1041 Called from XendDomainInfo and also image.py for HVM images.
1042 """
1044 # If set at the end of this method, a restart is required, with the
1045 # given reason. This restart has to be done out of the scope of
1046 # refresh_shutdown_lock.
1047 restart_reason = None
1049 self.refresh_shutdown_lock.acquire()
1050 try:
1051 if xeninfo is None:
1052 xeninfo = dom_get(self.domid)
1053 if xeninfo is None:
1054 # The domain no longer exists. This will occur if we have
1055 # scheduled a timer to check for shutdown timeouts and the
1056 # shutdown succeeded. It will also occur if someone
1057 # destroys a domain beneath us. We clean up the domain,
1058 # just in case, but we can't clean up the VM, because that
1059 # VM may have migrated to a different domain on this
1060 # machine.
1061 self.cleanupDomain()
1062 self._stateSet(DOM_STATE_HALTED)
1063 return
1065 if xeninfo['dying']:
1066 # Dying means that a domain has been destroyed, but has not
1067 # yet been cleaned up by Xen. This state could persist
1068 # indefinitely if, for example, another domain has some of its
1069 # pages mapped. We might like to diagnose this problem in the
1070 # future, but for now all we do is make sure that it's not us
1071 # holding the pages, by calling cleanupDomain. We can't
1072 # clean up the VM, as above.
1073 self.cleanupDomain()
1074 self._stateSet(DOM_STATE_SHUTDOWN)
1075 return
1077 elif xeninfo['crashed']:
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 log.warn('Domain has crashed: name=%s id=%d.',
1084 self.info['name_label'], self.domid)
1085 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1087 if xoptions.get_enable_dump():
1088 try:
1089 self.dumpCore()
1090 except XendError:
1091 # This error has been logged -- there's nothing more
1092 # we can do in this context.
1093 pass
1095 restart_reason = 'crash'
1096 self._stateSet(DOM_STATE_HALTED)
1098 elif xeninfo['shutdown']:
1099 self._stateSet(DOM_STATE_SHUTDOWN)
1100 if self.readDom('xend/shutdown_completed'):
1101 # We've seen this shutdown already, but we are preserving
1102 # the domain for debugging. Leave it alone.
1103 return
1105 else:
1106 reason = shutdown_reason(xeninfo['shutdown_reason'])
1108 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1109 self.info['name_label'], self.domid, reason)
1110 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1112 self._clearRestart()
1114 if reason == 'suspend':
1115 self._stateSet(DOM_STATE_SUSPENDED)
1116 # Don't destroy the domain. XendCheckpoint will do
1117 # this once it has finished. However, stop watching
1118 # the VM path now, otherwise we will end up with one
1119 # watch for the old domain, and one for the new.
1120 self._unwatchVm()
1121 elif reason in ('poweroff', 'reboot'):
1122 restart_reason = reason
1123 else:
1124 self.destroy()
1126 elif self.dompath is None:
1127 # We have yet to manage to call introduceDomain on this
1128 # domain. This can happen if a restore is in progress, or has
1129 # failed. Ignore this domain.
1130 pass
1131 else:
1132 # Domain is alive.
1133 if xeninfo['paused']:
1134 self._stateSet(DOM_STATE_PAUSED)
1135 else:
1136 self._stateSet(DOM_STATE_RUNNING)
1138 if self.shutdownStartTime and not self.unresponsive:
1139 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1140 self.shutdownStartTime)
1141 if timeout < 0:
1142 # The domain is not responding to shutdown requests.
1143 # Log a message, and rename the domain to indicate the
1144 # state; we keep the domain running, however, to
1145 # allow corrective action.
1146 log.info(
1147 "Domain shutdown timeout expired: name=%s id=%s",
1148 self.info['name_label'], self.domid)
1149 self.setName('unresponsive-' + self.getName())
1150 self.unresponsive = True
1151 finally:
1152 self.refresh_shutdown_lock.release()
1154 if restart_reason:
1155 threading.Thread(target = self._maybeRestart,
1156 args = (restart_reason,)).start()
1160 # Restart functions - handling whether we come back up on shutdown.
1163 def _clearRestart(self):
1164 self._removeDom("xend/shutdown_start_time")
1167 def _maybeRestart(self, reason):
1168 # Dispatch to the correct method based upon the configured on_{reason}
1169 # behaviour.
1170 actions = {"destroy" : self.destroy,
1171 "restart" : self._restart,
1172 "preserve" : self._preserve,
1173 "rename-restart" : self._renameRestart}
1175 action_conf = {
1176 'poweroff': 'actions_after_shutdown',
1177 'reboot': 'actions_after_reboot',
1178 'crash': 'actions_after_crash',
1181 action_target = self.info.get(action_conf.get(reason))
1182 func = actions.get(action_target, None)
1183 if func and callable(func):
1184 func()
1185 else:
1186 self.destroy() # default to destroy
1188 def _renameRestart(self):
1189 self._restart(True)
1191 def _restart(self, rename = False):
1192 """Restart the domain after it has exited.
1194 @param rename True if the old domain is to be renamed and preserved,
1195 False if it is to be destroyed.
1196 """
1197 from xen.xend import XendDomain
1199 if self._readVm(RESTART_IN_PROGRESS):
1200 log.error('Xend failed during restart of domain %s. '
1201 'Refusing to restart to avoid loops.',
1202 str(self.domid))
1203 self.destroy()
1204 return
1206 old_domid = self.domid
1207 self._writeVm(RESTART_IN_PROGRESS, 'True')
1209 now = time.time()
1210 rst = self._readVm('xend/previous_restart_time')
1211 if rst:
1212 rst = float(rst)
1213 timeout = now - rst
1214 if timeout < MINIMUM_RESTART_TIME:
1215 log.error(
1216 'VM %s restarting too fast (%f seconds since the last '
1217 'restart). Refusing to restart to avoid loops.',
1218 self.info['name_label'], timeout)
1219 self.destroy()
1220 return
1222 self._writeVm('xend/previous_restart_time', str(now))
1224 try:
1225 if rename:
1226 self._preserveForRestart()
1227 else:
1228 self._unwatchVm()
1229 self.destroyDomain()
1231 # new_dom's VM will be the same as this domain's VM, except where
1232 # the rename flag has instructed us to call preserveForRestart.
1233 # In that case, it is important that we remove the
1234 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1235 # once the new one is available.
1237 new_dom = None
1238 try:
1239 new_dom = XendDomain.instance().domain_create_from_dict(
1240 self.info)
1241 new_dom.unpause()
1242 rst_cnt = self._readVm('xend/restart_count')
1243 rst_cnt = int(rst_cnt) + 1
1244 self._writeVm('xend/restart_count', str(rst_cnt))
1245 new_dom._removeVm(RESTART_IN_PROGRESS)
1246 except:
1247 if new_dom:
1248 new_dom._removeVm(RESTART_IN_PROGRESS)
1249 new_dom.destroy()
1250 else:
1251 self._removeVm(RESTART_IN_PROGRESS)
1252 raise
1253 except:
1254 log.exception('Failed to restart domain %s.', str(old_domid))
1256 def _preserveForRestart(self):
1257 """Preserve a domain that has been shut down, by giving it a new UUID,
1258 cloning the VM details, and giving it a new name. This allows us to
1259 keep this domain for debugging, but restart a new one in its place
1260 preserving the restart semantics (name and UUID preserved).
1261 """
1263 new_uuid = uuid.createString()
1264 new_name = 'Domain-%s' % new_uuid
1265 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1266 self.info['name_label'], self.domid, self.info['uuid'],
1267 new_name, new_uuid)
1268 self._unwatchVm()
1269 self._releaseDevices()
1270 self.info['name_label'] = new_name
1271 self.info['uuid'] = new_uuid
1272 self.vmpath = XS_VMROOT + new_uuid
1273 self._storeVmDetails()
1274 self._preserve()
1277 def _preserve(self):
1278 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1279 self.domid)
1280 self._unwatchVm()
1281 self.storeDom('xend/shutdown_completed', 'True')
1282 self._stateSet(DOM_STATE_HALTED)
1285 # Debugging ..
1288 def dumpCore(self, corefile = None):
1289 """Create a core dump for this domain.
1291 @raise: XendError if core dumping failed.
1292 """
1294 try:
1295 if not corefile:
1296 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1297 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1298 self.info['name_label'], self.domid)
1300 if os.path.isdir(corefile):
1301 raise XendError("Cannot dump core in a directory: %s" %
1302 corefile)
1304 xc.domain_dumpcore(self.domid, corefile)
1305 except RuntimeError, ex:
1306 corefile_incomp = corefile+'-incomplete'
1307 os.rename(corefile, corefile_incomp)
1308 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1309 self.domid, self.info['name_label'])
1310 raise XendError("Failed to dump core: %s" % str(ex))
1313 # Device creation/deletion functions
1316 def _createDevice(self, deviceClass, devConfig):
1317 return self.getDeviceController(deviceClass).createDevice(devConfig)
1319 def _waitForDevice(self, deviceClass, devid):
1320 return self.getDeviceController(deviceClass).waitForDevice(devid)
1322 def _waitForDeviceUUID(self, dev_uuid):
1323 deviceClass, config = self.info['devices'].get(dev_uuid)
1324 self._waitForDevice(deviceClass, config['devid'])
1326 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1327 return self.getDeviceController(deviceClass).reconfigureDevice(
1328 devid, devconfig)
1330 def _createDevices(self):
1331 """Create the devices for a vm.
1333 @raise: VmError for invalid devices
1334 """
1335 ordered_refs = self.info.ordered_device_refs()
1336 for dev_uuid in ordered_refs:
1337 devclass, config = self.info['devices'][dev_uuid]
1338 if devclass in XendDevices.valid_devices():
1339 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1340 dev_uuid = config.get('uuid')
1341 devid = self._createDevice(devclass, config)
1343 # store devid in XendConfig for caching reasons
1344 if dev_uuid in self.info['devices']:
1345 self.info['devices'][dev_uuid][1]['devid'] = devid
1347 if self.image:
1348 self.image.createDeviceModel()
1350 def _releaseDevices(self, suspend = False):
1351 """Release all domain's devices. Nothrow guarantee."""
1352 if suspend and self.image:
1353 self.image.destroy(suspend)
1354 return
1356 t = xstransact("%s/device" % self.dompath)
1357 for devclass in XendDevices.valid_devices():
1358 for dev in t.list(devclass):
1359 try:
1360 log.debug("Removing %s", dev);
1361 self.destroyDevice(devclass, dev, False);
1362 except:
1363 # Log and swallow any exceptions in removal --
1364 # there's nothing more we can do.
1365 log.exception("Device release failed: %s; %s; %s",
1366 self.info['name_label'], devclass, dev)
1370 def getDeviceController(self, name):
1371 """Get the device controller for this domain, and if it
1372 doesn't exist, create it.
1374 @param name: device class name
1375 @type name: string
1376 @rtype: subclass of DevController
1377 """
1378 if name not in self._deviceControllers:
1379 devController = XendDevices.make_controller(name, self)
1380 if not devController:
1381 raise XendError("Unknown device type: %s" % name)
1382 self._deviceControllers[name] = devController
1384 return self._deviceControllers[name]
1387 # Migration functions (public)
1390 def testMigrateDevices(self, network, dst):
1391 """ Notify all device about intention of migration
1392 @raise: XendError for a device that cannot be migrated
1393 """
1394 for (n, c) in self.info.all_devices_sxpr():
1395 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1396 if rc != 0:
1397 raise XendError("Device of type '%s' refuses migration." % n)
1399 def migrateDevices(self, network, dst, step, domName=''):
1400 """Notify the devices about migration
1401 """
1402 ctr = 0
1403 try:
1404 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1405 self.migrateDevice(dev_type, dev_conf, network, dst,
1406 step, domName)
1407 ctr = ctr + 1
1408 except:
1409 for dev_type, dev_conf in self.info.all_devices_sxpr():
1410 if ctr == 0:
1411 step = step - 1
1412 ctr = ctr - 1
1413 self._recoverMigrateDevice(dev_type, dev_conf, network,
1414 dst, step, domName)
1415 raise
1417 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1418 step, domName=''):
1419 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1420 network, dst, step, domName)
1422 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1423 dst, step, domName=''):
1424 return self.getDeviceController(deviceClass).recover_migrate(
1425 deviceConfig, network, dst, step, domName)
1428 ## private:
1430 def _constructDomain(self):
1431 """Construct the domain.
1433 @raise: VmError on error
1434 """
1436 log.debug('XendDomainInfo.constructDomain')
1438 self.shutdownStartTime = None
1439 self.unresponsive = False
1441 hvm = self.info.is_hvm()
1442 if hvm:
1443 info = xc.xeninfo()
1444 if 'hvm' not in info['xen_caps']:
1445 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1446 "supported by your CPU and enabled in your "
1447 "BIOS?")
1449 # Hack to pre-reserve some memory for initial domain creation.
1450 # There is an implicit memory overhead for any domain creation. This
1451 # overhead is greater for some types of domain than others. For
1452 # example, an x86 HVM domain will have a default shadow-pagetable
1453 # allocation of 1MB. We free up 2MB here to be on the safe side.
1454 balloon.free(2*1024) # 2MB should be plenty
1456 self.domid = xc.domain_create(
1457 domid = 0,
1458 ssidref = security.get_security_info(self.info, 'ssidref'),
1459 handle = uuid.fromString(self.info['uuid']),
1460 hvm = int(hvm))
1462 if self.domid < 0:
1463 raise VmError('Creating domain failed: name=%s' %
1464 self.info['name_label'])
1466 self.dompath = GetDomainPath(self.domid)
1468 self._recreateDom()
1470 # Set maximum number of vcpus in domain
1471 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1473 # register the domain in the list
1474 from xen.xend import XendDomain
1475 XendDomain.instance().add_domain(self)
1477 def _introduceDomain(self):
1478 assert self.domid is not None
1479 assert self.store_mfn is not None
1480 assert self.store_port is not None
1482 try:
1483 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1484 except RuntimeError, exn:
1485 raise XendError(str(exn))
1488 def _initDomain(self):
1489 log.debug('XendDomainInfo.initDomain: %s %s',
1490 self.domid,
1491 self.info['cpu_weight'])
1493 self._configureBootloader()
1495 try:
1496 self.image = image.create(self, self.info)
1498 if self.info['platform'].get('localtime', 0):
1499 xc.domain_set_time_offset(self.domid)
1501 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1503 # repin domain vcpus if a restricted cpus list is provided
1504 # this is done prior to memory allocation to aide in memory
1505 # distribution for NUMA systems.
1506 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1507 for v in range(0, self.info['VCPUs_max']):
1508 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1510 # Use architecture- and image-specific calculations to determine
1511 # the various headrooms necessary, given the raw configured
1512 # values. maxmem, memory, and shadow are all in KiB.
1513 # but memory_static_max etc are all stored in bytes now.
1514 memory = self.image.getRequiredAvailableMemory(
1515 self.info['memory_dynamic_max'] / 1024)
1516 maxmem = self.image.getRequiredAvailableMemory(
1517 self.info['memory_static_max'] / 1024)
1518 shadow = self.image.getRequiredShadowMemory(
1519 self.info['shadow_memory'] / 1024,
1520 self.info['memory_static_max'] / 1024)
1522 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'],)
1523 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1524 # takes MiB and we must not round down and end up under-providing.
1525 shadow = ((shadow + 1023) / 1024) * 1024
1527 # set memory limit
1528 xc.domain_setmaxmem(self.domid, maxmem)
1530 # Make sure there's enough RAM available for the domain
1531 balloon.free(memory + shadow)
1533 # Set up the shadow memory
1534 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1535 self.info['shadow_memory'] = shadow_cur
1537 self._createChannels()
1539 channel_details = self.image.createImage()
1541 self.store_mfn = channel_details['store_mfn']
1542 if 'console_mfn' in channel_details:
1543 self.console_mfn = channel_details['console_mfn']
1544 if 'notes' in channel_details:
1545 self.info.set_notes(channel_details['notes'])
1546 if 'native_protocol' in channel_details:
1547 self.native_protocol = channel_details['native_protocol'];
1549 self._introduceDomain()
1551 self._createDevices()
1553 self.image.cleanupBootloading()
1555 self.info['start_time'] = time.time()
1557 self._stateSet(DOM_STATE_RUNNING)
1558 except VmError, exn:
1559 log.exception("XendDomainInfo.initDomain: exception occurred")
1560 if self.image:
1561 self.image.cleanupBootloading()
1562 raise exn
1563 except RuntimeError, exn:
1564 log.exception("XendDomainInfo.initDomain: exception occurred")
1565 if self.image:
1566 self.image.cleanupBootloading()
1567 raise VmError(str(exn))
1570 def cleanupDomain(self):
1571 """Cleanup domain resources; release devices. Idempotent. Nothrow
1572 guarantee."""
1574 self.refresh_shutdown_lock.acquire()
1575 try:
1576 self.unwatchShutdown()
1577 self._releaseDevices()
1578 bootloader_tidy(self)
1580 if self.image:
1581 try:
1582 self.image.destroy()
1583 except:
1584 log.exception(
1585 "XendDomainInfo.cleanup: image.destroy() failed.")
1586 self.image = None
1588 try:
1589 self._removeDom()
1590 except:
1591 log.exception("Removing domain path failed.")
1593 self._stateSet(DOM_STATE_HALTED)
1594 finally:
1595 self.refresh_shutdown_lock.release()
1598 def unwatchShutdown(self):
1599 """Remove the watch on the domain's control/shutdown node, if any.
1600 Idempotent. Nothrow guarantee. Expects to be protected by the
1601 refresh_shutdown_lock."""
1603 try:
1604 try:
1605 if self.shutdownWatch:
1606 self.shutdownWatch.unwatch()
1607 finally:
1608 self.shutdownWatch = None
1609 except:
1610 log.exception("Unwatching control/shutdown failed.")
1612 def waitForShutdown(self):
1613 self.state_updated.acquire()
1614 try:
1615 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1616 self.state_updated.wait()
1617 finally:
1618 self.state_updated.release()
1621 # TODO: recategorise - called from XendCheckpoint
1624 def completeRestore(self, store_mfn, console_mfn):
1626 log.debug("XendDomainInfo.completeRestore")
1628 self.store_mfn = store_mfn
1629 self.console_mfn = console_mfn
1631 self._introduceDomain()
1632 if self.info.is_hvm():
1633 self.image = image.create(self, self.info)
1634 if self.image:
1635 self.image.createDeviceModel(True)
1636 self._storeDomDetails()
1637 self._registerWatches()
1638 self.refreshShutdown()
1640 log.debug("XendDomainInfo.completeRestore done")
1643 def _endRestore(self):
1644 self.setResume(False)
1647 # VM Destroy
1650 def _prepare_phantom_paths(self):
1651 # get associated devices to destroy
1652 # build list of phantom devices to be removed after normal devices
1653 plist = []
1654 if self.domid is not None:
1655 from xen.xend.xenstore.xstransact import xstransact
1656 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1657 for dev in t.list():
1658 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1659 % (self.dompath, dev))
1660 if backend_phantom_vbd is not None:
1661 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1662 % backend_phantom_vbd)
1663 plist.append(backend_phantom_vbd)
1664 plist.append(frontend_phantom_vbd)
1665 return plist
1667 def _cleanup_phantom_devs(self, plist):
1668 # remove phantom devices
1669 if not plist == []:
1670 time.sleep(2)
1671 for paths in plist:
1672 if paths.find('backend') != -1:
1673 from xen.xend.server import DevController
1674 # Modify online status /before/ updating state (latter is watched by
1675 # drivers, so this ordering avoids a race).
1676 xstransact.Write(paths, 'online', "0")
1677 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1678 # force
1679 xstransact.Remove(paths)
1681 def destroy(self):
1682 """Cleanup VM and destroy domain. Nothrow guarantee."""
1684 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1686 paths = self._prepare_phantom_paths()
1688 self._cleanupVm()
1689 if self.dompath is not None:
1690 xc.domain_destroy_hook(self.domid)
1691 self.destroyDomain()
1693 self._cleanup_phantom_devs(paths)
1695 if "transient" in self.info["other_config"] \
1696 and bool(self.info["other_config"]["transient"]):
1697 from xen.xend import XendDomain
1698 XendDomain.instance().domain_delete_by_dominfo(self)
1701 def destroyDomain(self):
1702 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1704 paths = self._prepare_phantom_paths()
1706 try:
1707 if self.domid is not None:
1708 xc.domain_destroy(self.domid)
1709 for state in DOM_STATES_OLD:
1710 self.info[state] = 0
1711 self._stateSet(DOM_STATE_HALTED)
1712 except:
1713 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1715 from xen.xend import XendDomain
1716 XendDomain.instance().remove_domain(self)
1718 self.cleanupDomain()
1719 self._cleanup_phantom_devs(paths)
1722 def resumeDomain(self):
1723 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1725 if self.domid is None:
1726 return
1727 try:
1728 # could also fetch a parsed note from xenstore
1729 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1730 if not fast:
1731 self._releaseDevices()
1732 self.testDeviceComplete()
1733 self.testvifsComplete()
1734 log.debug("XendDomainInfo.resumeDomain: devices released")
1736 self._resetChannels()
1738 self._removeDom('control/shutdown')
1739 self._removeDom('device-misc/vif/nextDeviceID')
1741 self._createChannels()
1742 self._introduceDomain()
1743 self._storeDomDetails()
1745 self._createDevices()
1746 log.debug("XendDomainInfo.resumeDomain: devices created")
1748 xc.domain_resume(self.domid, fast)
1749 ResumeDomain(self.domid)
1750 except:
1751 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1754 # Channels for xenstore and console
1757 def _createChannels(self):
1758 """Create the channels to the domain.
1759 """
1760 self.store_port = self._createChannel()
1761 self.console_port = self._createChannel()
1764 def _createChannel(self):
1765 """Create an event channel to the domain.
1766 """
1767 try:
1768 if self.domid != None:
1769 return xc.evtchn_alloc_unbound(domid = self.domid,
1770 remote_dom = 0)
1771 except:
1772 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1773 raise
1775 def _resetChannels(self):
1776 """Reset all event channels in the domain.
1777 """
1778 try:
1779 if self.domid != None:
1780 return xc.evtchn_reset(dom = self.domid)
1781 except:
1782 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1783 raise
1787 # Bootloader configuration
1790 def _configureBootloader(self):
1791 """Run the bootloader if we're configured to do so."""
1793 blexec = self.info['PV_bootloader']
1794 bootloader_args = self.info['PV_bootloader_args']
1795 kernel = self.info['PV_kernel']
1796 ramdisk = self.info['PV_ramdisk']
1797 args = self.info['PV_args']
1798 boot = self.info['HVM_boot_policy']
1800 if boot:
1801 # HVM booting.
1802 pass
1803 elif not blexec and kernel:
1804 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1805 # will be picked up by image.py.
1806 pass
1807 else:
1808 # Boot using bootloader
1809 if not blexec or blexec == 'pygrub':
1810 blexec = osdep.pygrub_path
1812 blcfg = None
1813 disks = [x for x in self.info['vbd_refs']
1814 if self.info['devices'][x][1]['bootable']]
1816 if not disks:
1817 msg = "Had a bootloader specified, but no disks are bootable"
1818 log.error(msg)
1819 raise VmError(msg)
1821 devinfo = self.info['devices'][disks[0]]
1822 devtype = devinfo[0]
1823 disk = devinfo[1]['uname']
1825 fn = blkdev_uname_to_file(disk)
1826 taptype = blkdev_uname_to_taptype(disk)
1827 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1828 if mounted:
1829 # This is a file, not a device. pygrub can cope with a
1830 # file if it's raw, but if it's QCOW or other such formats
1831 # used through blktap, then we need to mount it first.
1833 log.info("Mounting %s on %s." %
1834 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1836 vbd = {
1837 'mode': 'RO',
1838 'device': BOOTLOADER_LOOPBACK_DEVICE,
1841 from xen.xend import XendDomain
1842 dom0 = XendDomain.instance().privilegedDomain()
1843 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1844 fn = BOOTLOADER_LOOPBACK_DEVICE
1846 try:
1847 blcfg = bootloader(blexec, fn, self, False,
1848 bootloader_args, kernel, ramdisk, args)
1849 finally:
1850 if mounted:
1851 log.info("Unmounting %s from %s." %
1852 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1854 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1856 if blcfg is None:
1857 msg = "Had a bootloader specified, but can't find disk"
1858 log.error(msg)
1859 raise VmError(msg)
1861 self.info.update_with_image_sxp(blcfg, True)
1865 # VM Functions
1868 def _readVMDetails(self, params):
1869 """Read the specified parameters from the store.
1870 """
1871 try:
1872 return self._gatherVm(*params)
1873 except ValueError:
1874 # One of the int/float entries in params has a corresponding store
1875 # entry that is invalid. We recover, because older versions of
1876 # Xend may have put the entry there (memory/target, for example),
1877 # but this is in general a bad situation to have reached.
1878 log.exception(
1879 "Store corrupted at %s! Domain %d's configuration may be "
1880 "affected.", self.vmpath, self.domid)
1881 return []
1883 def _cleanupVm(self):
1884 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1886 self._unwatchVm()
1888 try:
1889 self._removeVm()
1890 except:
1891 log.exception("Removing VM path failed.")
1894 def checkLiveMigrateMemory(self):
1895 """ Make sure there's enough memory to migrate this domain """
1896 overhead_kb = 0
1897 if arch.type == "x86":
1898 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1899 # the minimum that Xen would allocate if no value were given.
1900 overhead_kb = self.info['VCPUs_max'] * 1024 + \
1901 (self.info['memory_static_max'] / 1024 / 1024) * 4
1902 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1903 # The domain might already have some shadow memory
1904 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1905 if overhead_kb > 0:
1906 balloon.free(overhead_kb)
1908 def _unwatchVm(self):
1909 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1910 guarantee."""
1911 try:
1912 try:
1913 if self.vmWatch:
1914 self.vmWatch.unwatch()
1915 finally:
1916 self.vmWatch = None
1917 except:
1918 log.exception("Unwatching VM path failed.")
1920 def testDeviceComplete(self):
1921 """ For Block IO migration safety we must ensure that
1922 the device has shutdown correctly, i.e. all blocks are
1923 flushed to disk
1924 """
1925 start = time.time()
1926 while True:
1927 test = 0
1928 diff = time.time() - start
1929 for i in self.getDeviceController('vbd').deviceIDs():
1930 test = 1
1931 log.info("Dev %s still active, looping...", i)
1932 time.sleep(0.1)
1934 if test == 0:
1935 break
1936 if diff >= MIGRATE_TIMEOUT:
1937 log.info("Dev still active but hit max loop timeout")
1938 break
1940 def testvifsComplete(self):
1941 """ In case vifs are released and then created for the same
1942 domain, we need to wait the device shut down.
1943 """
1944 start = time.time()
1945 while True:
1946 test = 0
1947 diff = time.time() - start
1948 for i in self.getDeviceController('vif').deviceIDs():
1949 test = 1
1950 log.info("Dev %s still active, looping...", i)
1951 time.sleep(0.1)
1953 if test == 0:
1954 break
1955 if diff >= MIGRATE_TIMEOUT:
1956 log.info("Dev still active but hit max loop timeout")
1957 break
1959 def _storeVmDetails(self):
1960 to_store = {}
1962 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1963 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1964 if self._infoIsSet(info_key):
1965 to_store[key] = str(self.info[info_key])
1967 if self._infoIsSet("static_memory_min"):
1968 to_store["memory"] = str(self.info["static_memory_min"])
1969 if self._infoIsSet("static_memory_max"):
1970 to_store["maxmem"] = str(self.info["static_memory_max"])
1972 image_sxpr = self.info.image_sxpr()
1973 if image_sxpr:
1974 to_store['image'] = sxp.to_string(image_sxpr)
1976 if self._infoIsSet('security'):
1977 secinfo = self.info['security']
1978 to_store['security'] = sxp.to_string(secinfo)
1979 for idx in range(0, len(secinfo)):
1980 if secinfo[idx][0] == 'access_control':
1981 to_store['security/access_control'] = sxp.to_string(
1982 [secinfo[idx][1], secinfo[idx][2]])
1983 for aidx in range(1, len(secinfo[idx])):
1984 if secinfo[idx][aidx][0] == 'label':
1985 to_store['security/access_control/label'] = \
1986 secinfo[idx][aidx][1]
1987 if secinfo[idx][aidx][0] == 'policy':
1988 to_store['security/access_control/policy'] = \
1989 secinfo[idx][aidx][1]
1990 if secinfo[idx][0] == 'ssidref':
1991 to_store['security/ssidref'] = str(secinfo[idx][1])
1994 if not self._readVm('xend/restart_count'):
1995 to_store['xend/restart_count'] = str(0)
1997 log.debug("Storing VM details: %s", scrub_password(to_store))
1999 self._writeVm(to_store)
2000 self._setVmPermissions()
2003 def _setVmPermissions(self):
2004 """Allow the guest domain to read its UUID. We don't allow it to
2005 access any other entry, for security."""
2006 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2007 { 'dom' : self.domid,
2008 'read' : True,
2009 'write' : False })
2012 # Utility functions
2015 def __getattr__(self, name):
2016 if name == "state":
2017 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2018 log.warn("".join(traceback.format_stack()))
2019 return self._stateGet()
2020 else:
2021 raise AttributeError()
2023 def __setattr__(self, name, value):
2024 if name == "state":
2025 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2026 log.warn("".join(traceback.format_stack()))
2027 self._stateSet(value)
2028 else:
2029 self.__dict__[name] = value
2031 def _stateSet(self, state):
2032 self.state_updated.acquire()
2033 try:
2034 # TODO Not sure this is correct...
2035 # _stateGet is live now. Why not fire event
2036 # even when it hasn't changed?
2037 if self._stateGet() != state:
2038 self.state_updated.notifyAll()
2039 import XendAPI
2040 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2041 'power_state')
2042 finally:
2043 self.state_updated.release()
2045 def _stateGet(self):
2046 # Lets try and reconsitute the state from xc
2047 # first lets try and get the domain info
2048 # from xc - this will tell us if the domain
2049 # exists
2050 info = dom_get(self.getDomid())
2051 if info is None or info['shutdown']:
2052 # We are either HALTED or SUSPENDED
2053 # check saved image exists
2054 from xen.xend import XendDomain
2055 managed_config_path = \
2056 XendDomain.instance()._managed_check_point_path( \
2057 self.get_uuid())
2058 if os.path.exists(managed_config_path):
2059 return XEN_API_VM_POWER_STATE_SUSPENDED
2060 else:
2061 return XEN_API_VM_POWER_STATE_HALTED
2062 else:
2063 # We are either RUNNING or PAUSED
2064 if info['paused']:
2065 return XEN_API_VM_POWER_STATE_PAUSED
2066 else:
2067 return XEN_API_VM_POWER_STATE_RUNNING
2069 def _infoIsSet(self, name):
2070 return name in self.info and self.info[name] is not None
2072 def _checkName(self, name):
2073 """Check if a vm name is valid. Valid names contain alphabetic
2074 characters, digits, or characters in '_-.:/+'.
2075 The same name cannot be used for more than one vm at the same time.
2077 @param name: name
2078 @raise: VmError if invalid
2079 """
2080 from xen.xend import XendDomain
2082 if name is None or name == '':
2083 raise VmError('Missing VM Name')
2085 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2086 raise VmError('Invalid VM Name')
2088 dom = XendDomain.instance().domain_lookup_nr(name)
2089 if dom and dom.domid != self.domid:
2090 raise VmError("VM name '%s' already exists%s" %
2091 (name,
2092 dom.domid is not None and
2093 (" as domain %s" % str(dom.domid)) or ""))
2096 def update(self, info = None, refresh = True):
2097 """Update with info from xc.domain_getinfo().
2098 """
2099 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2100 str(self.domid))
2102 if not info:
2103 info = dom_get(self.domid)
2104 if not info:
2105 return
2107 if info["maxmem_kb"] < 0:
2108 info["maxmem_kb"] = XendNode.instance() \
2109 .physinfo_dict()['total_memory'] * 1024
2111 #manually update ssidref / security fields
2112 if security.on() and info.has_key('ssidref'):
2113 if (info['ssidref'] != 0) and self.info.has_key('security'):
2114 security_field = self.info['security']
2115 if not security_field:
2116 #create new security element
2117 self.info.update({'security':
2118 [['ssidref', str(info['ssidref'])]]})
2120 #ssidref field not used any longer
2121 if 'ssidref' in info:
2122 info.pop('ssidref')
2124 # make sure state is reset for info
2125 # TODO: we should eventually get rid of old_dom_states
2127 self.info.update_config(info)
2128 self._update_consoles()
2130 if refresh:
2131 self.refreshShutdown(info)
2133 log.trace("XendDomainInfo.update done on domain %s: %s",
2134 str(self.domid), self.info)
2136 def sxpr(self, ignore_store = False, legacy_only = True):
2137 result = self.info.to_sxp(domain = self,
2138 ignore_devices = ignore_store,
2139 legacy_only = legacy_only)
2141 #if not ignore_store and self.dompath:
2142 # vnc_port = self.readDom('console/vnc-port')
2143 # if vnc_port is not None:
2144 # result.append(['device',
2145 # ['console', ['vnc-port', str(vnc_port)]]])
2147 return result
2149 # Xen API
2150 # ----------------------------------------------------------------
2152 def get_uuid(self):
2153 dom_uuid = self.info.get('uuid')
2154 if not dom_uuid: # if it doesn't exist, make one up
2155 dom_uuid = uuid.createString()
2156 self.info['uuid'] = dom_uuid
2157 return dom_uuid
2159 def get_memory_static_max(self):
2160 return self.info.get('memory_static_max', 0)
2161 def get_memory_static_min(self):
2162 return self.info.get('memory_static_min', 0)
2163 def get_memory_dynamic_max(self):
2164 return self.info.get('memory_dynamic_max', 0)
2165 def get_memory_dynamic_min(self):
2166 return self.info.get('memory_dynamic_min', 0)
2168 # only update memory-related config values if they maintain sanity
2169 def _safe_set_memory(self, key, newval):
2170 oldval = self.info.get(key, 0)
2171 try:
2172 self.info[key] = newval
2173 self.info._memory_sanity_check()
2174 except Exception, ex:
2175 self.info[key] = oldval
2176 raise
2178 def set_memory_static_max(self, val):
2179 self._safe_set_memory('memory_static_max', val)
2180 def set_memory_static_min(self, val):
2181 self._safe_set_memory('memory_static_min', val)
2182 def set_memory_dynamic_max(self, val):
2183 self._safe_set_memory('memory_dynamic_max', val)
2184 def set_memory_dynamic_min(self, val):
2185 self._safe_set_memory('memory_dynamic_min', val)
2187 def get_vcpus_params(self):
2188 if self.getDomid() is None:
2189 return self.info['vcpus_params']
2191 retval = xc.sched_credit_domain_get(self.getDomid())
2192 return retval
2193 def get_power_state(self):
2194 return XEN_API_VM_POWER_STATE[self._stateGet()]
2195 def get_platform(self):
2196 return self.info.get('platform', {})
2197 def get_pci_bus(self):
2198 return self.info.get('pci_bus', '')
2199 def get_tools_version(self):
2200 return self.info.get('tools_version', {})
2201 def get_metrics(self):
2202 return self.metrics.get_uuid();
2204 def get_on_shutdown(self):
2205 after_shutdown = self.info.get('actions_after_shutdown')
2206 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2207 return XEN_API_ON_NORMAL_EXIT[-1]
2208 return after_shutdown
2210 def get_on_reboot(self):
2211 after_reboot = self.info.get('actions_after_reboot')
2212 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2213 return XEN_API_ON_NORMAL_EXIT[-1]
2214 return after_reboot
2216 def get_on_suspend(self):
2217 # TODO: not supported
2218 after_suspend = self.info.get('actions_after_suspend')
2219 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2220 return XEN_API_ON_NORMAL_EXIT[-1]
2221 return after_suspend
2223 def get_on_crash(self):
2224 after_crash = self.info.get('actions_after_crash')
2225 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2226 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2227 return after_crash
2229 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2230 """ Get's a device configuration either from XendConfig or
2231 from the DevController.
2233 @param dev_class: device class, either, 'vbd' or 'vif'
2234 @param dev_uuid: device UUID
2236 @rtype: dictionary
2237 """
2238 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2240 # shortcut if the domain isn't started because
2241 # the devcontrollers will have no better information
2242 # than XendConfig.
2243 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2244 if dev_config:
2245 return copy.deepcopy(dev_config)
2246 return None
2248 # instead of using dev_class, we use the dev_type
2249 # that is from XendConfig.
2250 controller = self.getDeviceController(dev_type)
2251 if not controller:
2252 return None
2254 all_configs = controller.getAllDeviceConfigurations()
2255 if not all_configs:
2256 return None
2258 updated_dev_config = copy.deepcopy(dev_config)
2259 for _devid, _devcfg in all_configs.items():
2260 if _devcfg.get('uuid') == dev_uuid:
2261 updated_dev_config.update(_devcfg)
2262 updated_dev_config['id'] = _devid
2263 return updated_dev_config
2265 return updated_dev_config
2267 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2268 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2269 if not config:
2270 return {}
2272 config['VM'] = self.get_uuid()
2274 if dev_class == 'vif':
2275 if not config.has_key('name'):
2276 config['name'] = config.get('vifname', '')
2277 if not config.has_key('MAC'):
2278 config['MAC'] = config.get('mac', '')
2279 if not config.has_key('type'):
2280 config['type'] = 'paravirtualised'
2281 if not config.has_key('device'):
2282 devid = config.get('id')
2283 if devid != None:
2284 config['device'] = 'eth%d' % devid
2285 else:
2286 config['device'] = ''
2288 if not config.has_key('network'):
2289 try:
2290 bridge = config.get('bridge', None)
2291 if bridge is None:
2292 from xen.util import Brctl
2293 if_to_br = dict([(i,b)
2294 for (b,ifs) in Brctl.get_state().items()
2295 for i in ifs])
2296 vifname = "vif%s.%s" % (self.getDomid(),
2297 config.get('id'))
2298 bridge = if_to_br.get(vifname, None)
2299 config['network'] = \
2300 XendNode.instance().bridge_to_network(
2301 config.get('bridge')).get_uuid()
2302 except Exception:
2303 log.exception('bridge_to_network')
2304 # Ignore this for now -- it may happen if the device
2305 # has been specified using the legacy methods, but at
2306 # some point we're going to have to figure out how to
2307 # handle that properly.
2309 config['MTU'] = 1500 # TODO
2311 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2312 xennode = XendNode.instance()
2313 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2314 config['io_read_kbs'] = rx_bps/1024
2315 config['io_write_kbs'] = tx_bps/1024
2316 else:
2317 config['io_read_kbs'] = 0.0
2318 config['io_write_kbs'] = 0.0
2320 if dev_class == 'vbd':
2322 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2323 controller = self.getDeviceController(dev_class)
2324 devid, _1, _2 = controller.getDeviceDetails(config)
2325 xennode = XendNode.instance()
2326 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2327 config['io_read_kbs'] = rd_blkps
2328 config['io_write_kbs'] = wr_blkps
2329 else:
2330 config['io_read_kbs'] = 0.0
2331 config['io_write_kbs'] = 0.0
2333 config['VDI'] = config.get('VDI', '')
2334 config['device'] = config.get('dev', '')
2335 if ':' in config['device']:
2336 vbd_name, vbd_type = config['device'].split(':', 1)
2337 config['device'] = vbd_name
2338 if vbd_type == 'cdrom':
2339 config['type'] = XEN_API_VBD_TYPE[0]
2340 else:
2341 config['type'] = XEN_API_VBD_TYPE[1]
2343 config['driver'] = 'paravirtualised' # TODO
2344 config['image'] = config.get('uname', '')
2346 if config.get('mode', 'r') == 'r':
2347 config['mode'] = 'RO'
2348 else:
2349 config['mode'] = 'RW'
2351 if dev_class == 'vtpm':
2352 if not config.has_key('type'):
2353 config['type'] = 'paravirtualised' # TODO
2354 if not config.has_key('backend'):
2355 config['backend'] = "00000000-0000-0000-0000-000000000000"
2357 return config
2359 def get_dev_property(self, dev_class, dev_uuid, field):
2360 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2361 try:
2362 return config[field]
2363 except KeyError:
2364 raise XendError('Invalid property for device: %s' % field)
2366 def set_dev_property(self, dev_class, dev_uuid, field, value):
2367 self.info['devices'][dev_uuid][1][field] = value
2369 def get_vcpus_util(self):
2370 vcpu_util = {}
2371 xennode = XendNode.instance()
2372 if 'VCPUs_max' in self.info and self.domid != None:
2373 for i in range(0, self.info['VCPUs_max']):
2374 util = xennode.get_vcpu_util(self.domid, i)
2375 vcpu_util[str(i)] = util
2377 return vcpu_util
2379 def get_consoles(self):
2380 return self.info.get('console_refs', [])
2382 def get_vifs(self):
2383 return self.info.get('vif_refs', [])
2385 def get_vbds(self):
2386 return self.info.get('vbd_refs', [])
2388 def get_vtpms(self):
2389 return self.info.get('vtpm_refs', [])
2391 def create_vbd(self, xenapi_vbd, vdi_image_path):
2392 """Create a VBD using a VDI from XendStorageRepository.
2394 @param xenapi_vbd: vbd struct from the Xen API
2395 @param vdi_image_path: VDI UUID
2396 @rtype: string
2397 @return: uuid of the device
2398 """
2399 xenapi_vbd['image'] = vdi_image_path
2400 if vdi_image_path.startswith('tap'):
2401 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2402 else:
2403 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2405 if not dev_uuid:
2406 raise XendError('Failed to create device')
2408 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2409 XEN_API_VM_POWER_STATE_PAUSED):
2410 _, config = self.info['devices'][dev_uuid]
2412 if vdi_image_path.startswith('tap'):
2413 dev_control = self.getDeviceController('tap')
2414 else:
2415 dev_control = self.getDeviceController('vbd')
2417 try:
2418 devid = dev_control.createDevice(config)
2419 dev_control.waitForDevice(devid)
2420 self.info.device_update(dev_uuid,
2421 cfg_xenapi = {'devid': devid})
2422 except Exception, exn:
2423 log.exception(exn)
2424 del self.info['devices'][dev_uuid]
2425 self.info['vbd_refs'].remove(dev_uuid)
2426 raise
2428 return dev_uuid
2430 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2431 """Create a VBD using a VDI from XendStorageRepository.
2433 @param xenapi_vbd: vbd struct from the Xen API
2434 @param vdi_image_path: VDI UUID
2435 @rtype: string
2436 @return: uuid of the device
2437 """
2438 xenapi_vbd['image'] = vdi_image_path
2439 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2440 if not dev_uuid:
2441 raise XendError('Failed to create device')
2443 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2444 _, config = self.info['devices'][dev_uuid]
2445 config['devid'] = self.getDeviceController('tap').createDevice(config)
2447 return config['devid']
2449 def create_vif(self, xenapi_vif):
2450 """Create VIF device from the passed struct in Xen API format.
2452 @param xenapi_vif: Xen API VIF Struct.
2453 @rtype: string
2454 @return: UUID
2455 """
2456 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2457 if not dev_uuid:
2458 raise XendError('Failed to create device')
2460 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2461 XEN_API_VM_POWER_STATE_PAUSED):
2463 _, config = self.info['devices'][dev_uuid]
2464 dev_control = self.getDeviceController('vif')
2466 try:
2467 devid = dev_control.createDevice(config)
2468 dev_control.waitForDevice(devid)
2469 self.info.device_update(dev_uuid,
2470 cfg_xenapi = {'devid': devid})
2471 except Exception, exn:
2472 log.exception(exn)
2473 del self.info['devices'][dev_uuid]
2474 self.info['vif_refs'].remove(dev_uuid)
2475 raise
2477 return dev_uuid
2479 def create_vtpm(self, xenapi_vtpm):
2480 """Create a VTPM device from the passed struct in Xen API format.
2482 @return: uuid of the device
2483 @rtype: string
2484 """
2486 if self._stateGet() not in (DOM_STATE_HALTED,):
2487 raise VmError("Can only add vTPM to a halted domain.")
2488 if self.get_vtpms() != []:
2489 raise VmError('Domain already has a vTPM.')
2490 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2491 if not dev_uuid:
2492 raise XendError('Failed to create device')
2494 return dev_uuid
2496 def create_console(self, xenapi_console):
2497 """ Create a console device from a Xen API struct.
2499 @return: uuid of device
2500 @rtype: string
2501 """
2502 if self._stateGet() not in (DOM_STATE_HALTED,):
2503 raise VmError("Can only add console to a halted domain.")
2505 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2506 if not dev_uuid:
2507 raise XendError('Failed to create device')
2509 return dev_uuid
2511 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2512 if dev_uuid not in self.info['devices']:
2513 raise XendError('Device does not exist')
2515 try:
2516 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2517 XEN_API_VM_POWER_STATE_PAUSED):
2518 _, config = self.info['devices'][dev_uuid]
2519 devid = config.get('devid')
2520 if devid != None:
2521 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2522 else:
2523 raise XendError('Unable to get devid for device: %s:%s' %
2524 (dev_type, dev_uuid))
2525 finally:
2526 del self.info['devices'][dev_uuid]
2527 self.info['%s_refs' % dev_type].remove(dev_uuid)
2529 def destroy_vbd(self, dev_uuid):
2530 self.destroy_device_by_uuid('vbd', dev_uuid)
2532 def destroy_vif(self, dev_uuid):
2533 self.destroy_device_by_uuid('vif', dev_uuid)
2535 def destroy_vtpm(self, dev_uuid):
2536 self.destroy_device_by_uuid('vtpm', dev_uuid)
2538 def has_device(self, dev_class, dev_uuid):
2539 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2541 def __str__(self):
2542 return '<domain id=%s name=%s memory=%s state=%s>' % \
2543 (str(self.domid), self.info['name_label'],
2544 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2546 __repr__ = __str__