ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 15513:3196b63a7301

xend: Fix xm block/network-attach command for inactive managed domain
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Fri Jul 06 14:41:05 2007 +0100 (2007-07-06)
parents 168b143a1a88
children 259bb15b2d1e
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._resume = resume
367 self.state_updated = threading.Condition()
368 self.refresh_shutdown_lock = threading.Condition()
369 self._stateSet(DOM_STATE_HALTED)
371 self._deviceControllers = {}
373 for state in DOM_STATES_OLD:
374 self.info[state] = 0
376 if augment:
377 self._augmentInfo(priv)
379 self._checkName(self.info['name_label'])
381 self.metrics = XendVMMetrics(uuid.createString(), self)
384 #
385 # Public functions available through XMLRPC
386 #
389 def start(self, is_managed = False):
390 """Attempts to start the VM by do the appropriate
391 initialisation if it not started.
392 """
393 from xen.xend import XendDomain
395 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED):
396 try:
397 XendTask.log_progress(0, 30, self._constructDomain)
398 XendTask.log_progress(31, 60, self._initDomain)
400 XendTask.log_progress(61, 70, self._storeVmDetails)
401 XendTask.log_progress(71, 80, self._storeDomDetails)
402 XendTask.log_progress(81, 90, self._registerWatches)
403 XendTask.log_progress(91, 100, self.refreshShutdown)
405 xendomains = XendDomain.instance()
406 xennode = XendNode.instance()
408 # save running configuration if XendDomains believe domain is
409 # persistent
410 if is_managed:
411 xendomains.managed_config_save(self)
413 if xennode.xenschedinfo() == 'credit':
414 xendomains.domain_sched_credit_set(self.getDomid(),
415 self.getWeight(),
416 self.getCap())
417 except:
418 log.exception('VM start failed')
419 self.destroy()
420 raise
421 else:
422 raise XendError('VM already running')
424 def resume(self):
425 """Resumes a domain that has come back from suspension."""
426 state = self._stateGet()
427 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
428 try:
429 self._constructDomain()
430 self._storeVmDetails()
431 self._createDevices()
432 self._createChannels()
433 self._storeDomDetails()
434 self._endRestore()
435 except:
436 log.exception('VM resume failed')
437 self.destroy()
438 raise
439 else:
440 raise XendError('VM is not susupened; it is %s'
441 % XEN_API_VM_POWER_STATE[state])
443 def shutdown(self, reason):
444 """Shutdown a domain by signalling this via xenstored."""
445 log.debug('XendDomainInfo.shutdown(%s)', reason)
446 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
447 raise XendError('Domain cannot be shutdown')
449 if self.domid == 0:
450 raise XendError('Domain 0 cannot be shutdown')
452 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
453 raise XendError('Invalid reason: %s' % reason)
454 self._removeVm('xend/previous_restart_time')
455 self.storeDom("control/shutdown", reason)
457 # HVM domain shuts itself down only if it has PV drivers
458 if self.info.is_hvm():
459 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
460 if not hvm_pvdrv:
461 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
462 log.info("HVM save:remote shutdown dom %d!", self.domid)
463 xc.domain_shutdown(self.domid, code)
465 def pause(self):
466 """Pause domain
468 @raise XendError: Failed pausing a domain
469 """
470 try:
471 xc.domain_pause(self.domid)
472 self._stateSet(DOM_STATE_PAUSED)
473 except Exception, ex:
474 log.exception(ex)
475 raise XendError("Domain unable to be paused: %s" % str(ex))
477 def unpause(self):
478 """Unpause domain
480 @raise XendError: Failed unpausing a domain
481 """
482 try:
483 xc.domain_unpause(self.domid)
484 self._stateSet(DOM_STATE_RUNNING)
485 except Exception, ex:
486 log.exception(ex)
487 raise XendError("Domain unable to be unpaused: %s" % str(ex))
489 def send_sysrq(self, key):
490 """ Send a Sysrq equivalent key via xenstored."""
491 asserts.isCharConvertible(key)
492 self.storeDom("control/sysrq", '%c' % key)
494 def device_create(self, dev_config):
495 """Create a new device.
497 @param dev_config: device configuration
498 @type dev_config: SXP object (parsed config)
499 """
500 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
501 dev_type = sxp.name(dev_config)
502 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
503 dev_config_dict = self.info['devices'][dev_uuid][1]
504 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
506 if self.domid is not None:
507 try:
508 dev_config_dict['devid'] = devid = \
509 self._createDevice(dev_type, dev_config_dict)
510 self._waitForDevice(dev_type, devid)
511 except VmError, ex:
512 raise ex
513 else:
514 devid = None
516 xen.xend.XendDomain.instance().managed_config_save(self)
517 return self.getDeviceController(dev_type).sxpr(devid)
519 def device_configure(self, dev_sxp, devid = None):
520 """Configure an existing device.
522 @param dev_config: device configuration
523 @type dev_config: SXP object (parsed config)
524 @param devid: device id
525 @type devid: int
526 @return: Returns True if successfully updated device
527 @rtype: boolean
528 """
530 # convert device sxp to a dict
531 dev_class = sxp.name(dev_sxp)
532 dev_config = {}
533 for opt_val in dev_sxp[1:]:
534 try:
535 dev_config[opt_val[0]] = opt_val[1]
536 except IndexError:
537 pass
539 # use DevController.reconfigureDevice to change device config
540 dev_control = self.getDeviceController(dev_class)
541 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
543 # update XendConfig with new device info
544 if dev_uuid:
545 self.info.device_update(dev_uuid, dev_sxp)
547 return True
549 def waitForDevices(self):
550 """Wait for this domain's configured devices to connect.
552 @raise VmError: if any device fails to initialise.
553 """
554 for devclass in XendDevices.valid_devices():
555 self.getDeviceController(devclass).waitForDevices()
557 def destroyDevice(self, deviceClass, devid, force = False):
558 try:
559 dev = int(devid)
560 except ValueError:
561 # devid is not a number but a string containing either device
562 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
563 dev = type(devid) is str and devid.split('/')[-1] or None
564 if dev == None:
565 log.debug("Could not find the device %s", devid)
566 return None
568 log.debug("dev = %s", dev)
569 return self.getDeviceController(deviceClass).destroyDevice(dev, force)
571 def getDeviceSxprs(self, deviceClass):
572 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
573 return self.getDeviceController(deviceClass).sxprs()
574 else:
575 sxprs = []
576 dev_num = 0
577 for dev_type, dev_info in self.info.all_devices_sxpr():
578 if dev_type == deviceClass:
579 sxprs.append([dev_num, dev_info])
580 dev_num += 1
581 return sxprs
584 def setMemoryTarget(self, target):
585 """Set the memory target of this domain.
586 @param target: In MiB.
587 """
588 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
589 self.info['name_label'], str(self.domid), target)
591 MiB = 1024 * 1024
592 self._safe_set_memory('memory_dynamic_min', target * MiB)
593 self._safe_set_memory('memory_dynamic_max', target * MiB)
595 if self.domid >= 0:
596 self.storeVm("memory", target)
597 self.storeDom("memory/target", target << 10)
598 xen.xend.XendDomain.instance().managed_config_save(self)
600 def setMemoryMaximum(self, limit):
601 """Set the maximum memory limit of this domain
602 @param limit: In MiB.
603 """
604 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
605 self.info['name_label'], str(self.domid), limit)
607 if limit <= 0:
608 raise XendError('Invalid memory size')
610 MiB = 1024 * 1024
611 self._safe_set_memory('memory_static_max', limit * MiB)
613 if self.domid >= 0:
614 maxmem = int(limit) * 1024
615 try:
616 return xc.domain_setmaxmem(self.domid, maxmem)
617 except Exception, ex:
618 raise XendError(str(ex))
619 xen.xend.XendDomain.instance().managed_config_save(self)
622 def getVCPUInfo(self):
623 try:
624 # We include the domain name and ID, to help xm.
625 sxpr = ['domain',
626 ['domid', self.domid],
627 ['name', self.info['name_label']],
628 ['vcpu_count', self.info['VCPUs_max']]]
630 for i in range(0, self.info['VCPUs_max']):
631 info = xc.vcpu_getinfo(self.domid, i)
633 sxpr.append(['vcpu',
634 ['number', i],
635 ['online', info['online']],
636 ['blocked', info['blocked']],
637 ['running', info['running']],
638 ['cpu_time', info['cpu_time'] / 1e9],
639 ['cpu', info['cpu']],
640 ['cpumap', info['cpumap']]])
642 return sxpr
644 except RuntimeError, exn:
645 raise XendError(str(exn))
648 def getDomInfo(self):
649 return dom_get(self.domid)
651 #
652 # internal functions ... TODO: re-categorised
653 #
655 def _augmentInfo(self, priv):
656 """Augment self.info, as given to us through L{recreate}, with
657 values taken from the store. This recovers those values known
658 to xend but not to the hypervisor.
659 """
660 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
661 if priv:
662 augment_entries.remove('memory')
663 augment_entries.remove('maxmem')
664 augment_entries.remove('vcpus')
665 augment_entries.remove('vcpu_avail')
667 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
668 for k in augment_entries])
670 # make returned lists into a dictionary
671 vm_config = dict(zip(augment_entries, vm_config))
673 for arg in augment_entries:
674 val = vm_config[arg]
675 if val != None:
676 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
677 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
678 self.info[xapiarg] = val
679 elif arg == "memory":
680 self.info["static_memory_min"] = val
681 elif arg == "maxmem":
682 self.info["static_memory_max"] = val
683 else:
684 self.info[arg] = val
686 # For dom0, we ignore any stored value for the vcpus fields, and
687 # read the current value from Xen instead. This allows boot-time
688 # settings to take precedence over any entries in the store.
689 if priv:
690 xeninfo = dom_get(self.domid)
691 self.info['VCPUs_max'] = xeninfo['online_vcpus']
692 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
694 # read image value
695 image_sxp = self._readVm('image')
696 if image_sxp:
697 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
699 # read devices
700 devices = []
701 for devclass in XendDevices.valid_devices():
702 devconfig = self.getDeviceController(devclass).configurations()
703 if devconfig:
704 devices.extend(devconfig)
706 if not self.info['devices'] and devices is not None:
707 for device in devices:
708 self.info.device_add(device[0], cfg_sxp = device)
710 self._update_consoles()
712 def _update_consoles(self):
713 if self.domid == None or self.domid == 0:
714 return
716 # Update VT100 port if it exists
717 self.console_port = self.readDom('console/port')
718 if self.console_port is not None:
719 serial_consoles = self.info.console_get_all('vt100')
720 if not serial_consoles:
721 cfg = self.info.console_add('vt100', self.console_port)
722 self._createDevice('console', cfg)
723 else:
724 console_uuid = serial_consoles[0].get('uuid')
725 self.info.console_update(console_uuid, 'location',
726 self.console_port)
729 # Update VNC port if it exists and write to xenstore
730 vnc_port = self.readDom('console/vnc-port')
731 if vnc_port is not None:
732 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
733 if dev_type == 'vfb':
734 old_location = dev_info.get('location')
735 listen_host = dev_info.get('vnclisten', 'localhost')
736 new_location = '%s:%s' % (listen_host, str(vnc_port))
737 if old_location == new_location:
738 break
740 dev_info['location'] = new_location
741 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
742 vfb_ctrl = self.getDeviceController('vfb')
743 vfb_ctrl.reconfigureDevice(0, dev_info)
744 break
746 #
747 # Function to update xenstore /vm/*
748 #
750 def _readVm(self, *args):
751 return xstransact.Read(self.vmpath, *args)
753 def _writeVm(self, *args):
754 return xstransact.Write(self.vmpath, *args)
756 def _removeVm(self, *args):
757 return xstransact.Remove(self.vmpath, *args)
759 def _gatherVm(self, *args):
760 return xstransact.Gather(self.vmpath, *args)
762 def storeVm(self, *args):
763 return xstransact.Store(self.vmpath, *args)
765 #
766 # Function to update xenstore /dom/*
767 #
769 def readDom(self, *args):
770 return xstransact.Read(self.dompath, *args)
772 def gatherDom(self, *args):
773 return xstransact.Gather(self.dompath, *args)
775 def _writeDom(self, *args):
776 return xstransact.Write(self.dompath, *args)
778 def _removeDom(self, *args):
779 return xstransact.Remove(self.dompath, *args)
781 def storeDom(self, *args):
782 return xstransact.Store(self.dompath, *args)
784 def _recreateDom(self):
785 complete(self.dompath, lambda t: self._recreateDomFunc(t))
787 def _recreateDomFunc(self, t):
788 t.remove()
789 t.mkdir()
790 t.set_permissions({'dom' : self.domid})
791 t.write('vm', self.vmpath)
793 def _storeDomDetails(self):
794 to_store = {
795 'domid': str(self.domid),
796 'vm': self.vmpath,
797 'name': self.info['name_label'],
798 'console/limit': str(xoptions.get_console_limit() * 1024),
799 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
800 }
802 def f(n, v):
803 if v is not None:
804 if type(v) == bool:
805 to_store[n] = v and "1" or "0"
806 else:
807 to_store[n] = str(v)
809 f('console/port', self.console_port)
810 f('console/ring-ref', self.console_mfn)
811 f('store/port', self.store_port)
812 f('store/ring-ref', self.store_mfn)
814 if arch.type == "x86":
815 f('control/platform-feature-multiprocessor-suspend', True)
817 # elfnotes
818 for n, v in self.info.get_notes().iteritems():
819 n = n.lower().replace('_', '-')
820 if n == 'features':
821 for v in v.split('|'):
822 v = v.replace('_', '-')
823 if v.startswith('!'):
824 f('image/%s/%s' % (n, v[1:]), False)
825 else:
826 f('image/%s/%s' % (n, v), True)
827 else:
828 f('image/%s' % n, v)
830 to_store.update(self._vcpuDomDetails())
832 log.debug("Storing domain details: %s", scrub_password(to_store))
834 self._writeDom(to_store)
836 def _vcpuDomDetails(self):
837 def availability(n):
838 if self.info['vcpu_avail'] & (1 << n):
839 return 'online'
840 else:
841 return 'offline'
843 result = {}
844 for v in range(0, self.info['VCPUs_max']):
845 result["cpu/%d/availability" % v] = availability(v)
846 return result
848 #
849 # xenstore watches
850 #
852 def _registerWatches(self):
853 """Register a watch on this VM's entries in the store, and the
854 domain's control/shutdown node, so that when they are changed
855 externally, we keep up to date. This should only be called by {@link
856 #create}, {@link #recreate}, or {@link #restore}, once the domain's
857 details have been written, but before the new instance is returned."""
858 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
859 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
860 self._handleShutdownWatch)
862 def _storeChanged(self, _):
863 log.trace("XendDomainInfo.storeChanged");
865 changed = False
867 # Check whether values in the configuration have
868 # changed in Xenstore.
870 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
871 'rtc/timeoffset']
873 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
874 for k in cfg_vm])
876 # convert two lists into a python dictionary
877 vm_details = dict(zip(cfg_vm, vm_details))
879 if vm_details['rtc/timeoffset'] == None:
880 vm_details['rtc/timeoffset'] = "0"
882 for arg, val in vm_details.items():
883 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
884 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
885 if val != None and val != self.info[xapiarg]:
886 self.info[xapiarg] = val
887 changed = True
888 elif arg == "memory":
889 if val != None and val != self.info["static_memory_min"]:
890 self.info["static_memory_min"] = val
891 changed = True
892 elif arg == "maxmem":
893 if val != None and val != self.info["static_memory_max"]:
894 self.info["static_memory_max"] = val
895 changed = True
897 # Check whether image definition has been updated
898 image_sxp = self._readVm('image')
899 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
900 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
901 changed = True
903 # Check if the rtc offset has changes
904 if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
905 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
906 changed = True
908 if changed:
909 # Update the domain section of the store, as this contains some
910 # parameters derived from the VM configuration.
911 self._storeDomDetails()
913 return 1
915 def _handleShutdownWatch(self, _):
916 log.debug('XendDomainInfo.handleShutdownWatch')
918 reason = self.readDom('control/shutdown')
920 if reason and reason != 'suspend':
921 sst = self.readDom('xend/shutdown_start_time')
922 now = time.time()
923 if sst:
924 self.shutdownStartTime = float(sst)
925 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
926 else:
927 self.shutdownStartTime = now
928 self.storeDom('xend/shutdown_start_time', now)
929 timeout = SHUTDOWN_TIMEOUT
931 log.trace(
932 "Scheduling refreshShutdown on domain %d in %ds.",
933 self.domid, timeout)
934 threading.Timer(timeout, self.refreshShutdown).start()
936 return True
939 #
940 # Public Attributes for the VM
941 #
944 def getDomid(self):
945 return self.domid
947 def setName(self, name):
948 self._checkName(name)
949 self.info['name_label'] = name
950 self.storeVm("name", name)
952 def getName(self):
953 return self.info['name_label']
955 def getDomainPath(self):
956 return self.dompath
958 def getShutdownReason(self):
959 return self.readDom('control/shutdown')
961 def getStorePort(self):
962 """For use only by image.py and XendCheckpoint.py."""
963 return self.store_port
965 def getConsolePort(self):
966 """For use only by image.py and XendCheckpoint.py"""
967 return self.console_port
969 def getFeatures(self):
970 """For use only by image.py."""
971 return self.info['features']
973 def getVCpuCount(self):
974 return self.info['VCPUs_max']
976 def setVCpuCount(self, vcpus):
977 if vcpus <= 0:
978 raise XendError('Invalid VCPUs')
980 self.info['vcpu_avail'] = (1 << vcpus) - 1
981 if self.domid >= 0:
982 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
983 # update dom differently depending on whether we are adjusting
984 # vcpu number up or down, otherwise _vcpuDomDetails does not
985 # disable the vcpus
986 if self.info['VCPUs_max'] > vcpus:
987 # decreasing
988 self._writeDom(self._vcpuDomDetails())
989 self.info['VCPUs_live'] = vcpus
990 else:
991 # same or increasing
992 self.info['VCPUs_live'] = vcpus
993 self._writeDom(self._vcpuDomDetails())
994 else:
995 self.info['VCPUs_max'] = vcpus
996 xen.xend.XendDomain.instance().managed_config_save(self)
997 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
998 vcpus)
1000 def getLabel(self):
1001 return security.get_security_info(self.info, 'label')
1003 def getMemoryTarget(self):
1004 """Get this domain's target memory size, in KB."""
1005 return self.info['memory_dynamic_max'] / 1024
1007 def getMemoryMaximum(self):
1008 """Get this domain's maximum memory size, in KB."""
1009 # remember, info now stores memory in bytes
1010 return self.info['memory_static_max'] / 1024
1012 def getResume(self):
1013 return str(self._resume)
1015 def getCap(self):
1016 return self.info.get('cpu_cap', 0)
1018 def setCap(self, cpu_cap):
1019 self.info['cpu_cap'] = cpu_cap
1021 def getWeight(self):
1022 return self.info.get('cpu_weight', 256)
1024 def setWeight(self, cpu_weight):
1025 self.info['cpu_weight'] = cpu_weight
1027 def setResume(self, state):
1028 self._resume = state
1030 def getRestartCount(self):
1031 return self._readVm('xend/restart_count')
1033 def refreshShutdown(self, xeninfo = None):
1034 """ Checks the domain for whether a shutdown is required.
1036 Called from XendDomainInfo and also image.py for HVM images.
1037 """
1039 # If set at the end of this method, a restart is required, with the
1040 # given reason. This restart has to be done out of the scope of
1041 # refresh_shutdown_lock.
1042 restart_reason = None
1044 self.refresh_shutdown_lock.acquire()
1045 try:
1046 if xeninfo is None:
1047 xeninfo = dom_get(self.domid)
1048 if xeninfo is None:
1049 # The domain no longer exists. This will occur if we have
1050 # scheduled a timer to check for shutdown timeouts and the
1051 # shutdown succeeded. It will also occur if someone
1052 # destroys a domain beneath us. We clean up the domain,
1053 # just in case, but we can't clean up the VM, because that
1054 # VM may have migrated to a different domain on this
1055 # machine.
1056 self.cleanupDomain()
1057 self._stateSet(DOM_STATE_HALTED)
1058 return
1060 if xeninfo['dying']:
1061 # Dying means that a domain has been destroyed, but has not
1062 # yet been cleaned up by Xen. This state could persist
1063 # indefinitely if, for example, another domain has some of its
1064 # pages mapped. We might like to diagnose this problem in the
1065 # future, but for now all we do is make sure that it's not us
1066 # holding the pages, by calling cleanupDomain. We can't
1067 # clean up the VM, as above.
1068 self.cleanupDomain()
1069 self._stateSet(DOM_STATE_SHUTDOWN)
1070 return
1072 elif xeninfo['crashed']:
1073 if self.readDom('xend/shutdown_completed'):
1074 # We've seen this shutdown already, but we are preserving
1075 # the domain for debugging. Leave it alone.
1076 return
1078 log.warn('Domain has crashed: name=%s id=%d.',
1079 self.info['name_label'], self.domid)
1080 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1082 if xoptions.get_enable_dump():
1083 try:
1084 self.dumpCore()
1085 except XendError:
1086 # This error has been logged -- there's nothing more
1087 # we can do in this context.
1088 pass
1090 restart_reason = 'crash'
1091 self._stateSet(DOM_STATE_HALTED)
1093 elif xeninfo['shutdown']:
1094 self._stateSet(DOM_STATE_SHUTDOWN)
1095 if self.readDom('xend/shutdown_completed'):
1096 # We've seen this shutdown already, but we are preserving
1097 # the domain for debugging. Leave it alone.
1098 return
1100 else:
1101 reason = shutdown_reason(xeninfo['shutdown_reason'])
1103 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1104 self.info['name_label'], self.domid, reason)
1105 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1107 self._clearRestart()
1109 if reason == 'suspend':
1110 self._stateSet(DOM_STATE_SUSPENDED)
1111 # Don't destroy the domain. XendCheckpoint will do
1112 # this once it has finished. However, stop watching
1113 # the VM path now, otherwise we will end up with one
1114 # watch for the old domain, and one for the new.
1115 self._unwatchVm()
1116 elif reason in ('poweroff', 'reboot'):
1117 restart_reason = reason
1118 else:
1119 self.destroy()
1121 elif self.dompath is None:
1122 # We have yet to manage to call introduceDomain on this
1123 # domain. This can happen if a restore is in progress, or has
1124 # failed. Ignore this domain.
1125 pass
1126 else:
1127 # Domain is alive. If we are shutting it down, log a message
1128 # if it seems unresponsive.
1129 if xeninfo['paused']:
1130 self._stateSet(DOM_STATE_PAUSED)
1131 else:
1132 self._stateSet(DOM_STATE_RUNNING)
1134 if self.shutdownStartTime:
1135 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1136 self.shutdownStartTime)
1137 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1138 log.info(
1139 "Domain shutdown timeout expired: name=%s id=%s",
1140 self.info['name_label'], self.domid)
1141 self.storeDom('xend/unresponsive', 'True')
1142 finally:
1143 self.refresh_shutdown_lock.release()
1145 if restart_reason:
1146 threading.Thread(target = self._maybeRestart,
1147 args = (restart_reason,)).start()
1151 # Restart functions - handling whether we come back up on shutdown.
1154 def _clearRestart(self):
1155 self._removeDom("xend/shutdown_start_time")
1158 def _maybeRestart(self, reason):
1159 # Dispatch to the correct method based upon the configured on_{reason}
1160 # behaviour.
1161 actions = {"destroy" : self.destroy,
1162 "restart" : self._restart,
1163 "preserve" : self._preserve,
1164 "rename-restart" : self._renameRestart}
1166 action_conf = {
1167 'poweroff': 'actions_after_shutdown',
1168 'reboot': 'actions_after_reboot',
1169 'crash': 'actions_after_crash',
1172 action_target = self.info.get(action_conf.get(reason))
1173 func = actions.get(action_target, None)
1174 if func and callable(func):
1175 func()
1176 else:
1177 self.destroy() # default to destroy
1179 def _renameRestart(self):
1180 self._restart(True)
1182 def _restart(self, rename = False):
1183 """Restart the domain after it has exited.
1185 @param rename True if the old domain is to be renamed and preserved,
1186 False if it is to be destroyed.
1187 """
1188 from xen.xend import XendDomain
1190 if self._readVm(RESTART_IN_PROGRESS):
1191 log.error('Xend failed during restart of domain %s. '
1192 'Refusing to restart to avoid loops.',
1193 str(self.domid))
1194 self.destroy()
1195 return
1197 old_domid = self.domid
1198 self._writeVm(RESTART_IN_PROGRESS, 'True')
1200 now = time.time()
1201 rst = self._readVm('xend/previous_restart_time')
1202 if rst:
1203 rst = float(rst)
1204 timeout = now - rst
1205 if timeout < MINIMUM_RESTART_TIME:
1206 log.error(
1207 'VM %s restarting too fast (%f seconds since the last '
1208 'restart). Refusing to restart to avoid loops.',
1209 self.info['name_label'], timeout)
1210 self.destroy()
1211 return
1213 self._writeVm('xend/previous_restart_time', str(now))
1215 try:
1216 if rename:
1217 self._preserveForRestart()
1218 else:
1219 self._unwatchVm()
1220 self.destroyDomain()
1222 # new_dom's VM will be the same as this domain's VM, except where
1223 # the rename flag has instructed us to call preserveForRestart.
1224 # In that case, it is important that we remove the
1225 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1226 # once the new one is available.
1228 new_dom = None
1229 try:
1230 new_dom = XendDomain.instance().domain_create_from_dict(
1231 self.info)
1232 new_dom.unpause()
1233 rst_cnt = self._readVm('xend/restart_count')
1234 rst_cnt = int(rst_cnt) + 1
1235 self._writeVm('xend/restart_count', str(rst_cnt))
1236 new_dom._removeVm(RESTART_IN_PROGRESS)
1237 except:
1238 if new_dom:
1239 new_dom._removeVm(RESTART_IN_PROGRESS)
1240 new_dom.destroy()
1241 else:
1242 self._removeVm(RESTART_IN_PROGRESS)
1243 raise
1244 except:
1245 log.exception('Failed to restart domain %s.', str(old_domid))
1247 def _preserveForRestart(self):
1248 """Preserve a domain that has been shut down, by giving it a new UUID,
1249 cloning the VM details, and giving it a new name. This allows us to
1250 keep this domain for debugging, but restart a new one in its place
1251 preserving the restart semantics (name and UUID preserved).
1252 """
1254 new_uuid = uuid.createString()
1255 new_name = 'Domain-%s' % new_uuid
1256 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1257 self.info['name_label'], self.domid, self.info['uuid'],
1258 new_name, new_uuid)
1259 self._unwatchVm()
1260 self._releaseDevices()
1261 self.info['name_label'] = new_name
1262 self.info['uuid'] = new_uuid
1263 self.vmpath = XS_VMROOT + new_uuid
1264 self._storeVmDetails()
1265 self._preserve()
1268 def _preserve(self):
1269 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1270 self.domid)
1271 self._unwatchVm()
1272 self.storeDom('xend/shutdown_completed', 'True')
1273 self._stateSet(DOM_STATE_HALTED)
1276 # Debugging ..
1279 def dumpCore(self, corefile = None):
1280 """Create a core dump for this domain.
1282 @raise: XendError if core dumping failed.
1283 """
1285 try:
1286 if not corefile:
1287 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1288 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1289 self.info['name_label'], self.domid)
1291 if os.path.isdir(corefile):
1292 raise XendError("Cannot dump core in a directory: %s" %
1293 corefile)
1295 xc.domain_dumpcore(self.domid, corefile)
1296 except RuntimeError, ex:
1297 corefile_incomp = corefile+'-incomplete'
1298 os.rename(corefile, corefile_incomp)
1299 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1300 self.domid, self.info['name_label'])
1301 raise XendError("Failed to dump core: %s" % str(ex))
1304 # Device creation/deletion functions
1307 def _createDevice(self, deviceClass, devConfig):
1308 return self.getDeviceController(deviceClass).createDevice(devConfig)
1310 def _waitForDevice(self, deviceClass, devid):
1311 return self.getDeviceController(deviceClass).waitForDevice(devid)
1313 def _waitForDeviceUUID(self, dev_uuid):
1314 deviceClass, config = self.info['devices'].get(dev_uuid)
1315 self._waitForDevice(deviceClass, config['devid'])
1317 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1318 return self.getDeviceController(deviceClass).reconfigureDevice(
1319 devid, devconfig)
1321 def _createDevices(self):
1322 """Create the devices for a vm.
1324 @raise: VmError for invalid devices
1325 """
1326 ordered_refs = self.info.ordered_device_refs()
1327 for dev_uuid in ordered_refs:
1328 devclass, config = self.info['devices'][dev_uuid]
1329 if devclass in XendDevices.valid_devices():
1330 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1331 dev_uuid = config.get('uuid')
1332 devid = self._createDevice(devclass, config)
1334 # store devid in XendConfig for caching reasons
1335 if dev_uuid in self.info['devices']:
1336 self.info['devices'][dev_uuid][1]['devid'] = devid
1338 if self.image:
1339 self.image.createDeviceModel()
1341 def _releaseDevices(self, suspend = False):
1342 """Release all domain's devices. Nothrow guarantee."""
1343 if suspend and self.image:
1344 self.image.destroy(suspend)
1345 return
1347 t = xstransact("%s/device" % self.dompath)
1348 for devclass in XendDevices.valid_devices():
1349 for dev in t.list(devclass):
1350 try:
1351 log.debug("Removing %s", dev);
1352 self.destroyDevice(devclass, dev, False);
1353 except:
1354 # Log and swallow any exceptions in removal --
1355 # there's nothing more we can do.
1356 log.exception("Device release failed: %s; %s; %s",
1357 self.info['name_label'], devclass, dev)
1361 def getDeviceController(self, name):
1362 """Get the device controller for this domain, and if it
1363 doesn't exist, create it.
1365 @param name: device class name
1366 @type name: string
1367 @rtype: subclass of DevController
1368 """
1369 if name not in self._deviceControllers:
1370 devController = XendDevices.make_controller(name, self)
1371 if not devController:
1372 raise XendError("Unknown device type: %s" % name)
1373 self._deviceControllers[name] = devController
1375 return self._deviceControllers[name]
1378 # Migration functions (public)
1381 def testMigrateDevices(self, network, dst):
1382 """ Notify all device about intention of migration
1383 @raise: XendError for a device that cannot be migrated
1384 """
1385 for (n, c) in self.info.all_devices_sxpr():
1386 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1387 if rc != 0:
1388 raise XendError("Device of type '%s' refuses migration." % n)
1390 def migrateDevices(self, network, dst, step, domName=''):
1391 """Notify the devices about migration
1392 """
1393 ctr = 0
1394 try:
1395 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1396 self.migrateDevice(dev_type, dev_conf, network, dst,
1397 step, domName)
1398 ctr = ctr + 1
1399 except:
1400 for dev_type, dev_conf in self.info.all_devices_sxpr():
1401 if ctr == 0:
1402 step = step - 1
1403 ctr = ctr - 1
1404 self._recoverMigrateDevice(dev_type, dev_conf, network,
1405 dst, step, domName)
1406 raise
1408 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1409 step, domName=''):
1410 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1411 network, dst, step, domName)
1413 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1414 dst, step, domName=''):
1415 return self.getDeviceController(deviceClass).recover_migrate(
1416 deviceConfig, network, dst, step, domName)
1419 ## private:
1421 def _constructDomain(self):
1422 """Construct the domain.
1424 @raise: VmError on error
1425 """
1427 log.debug('XendDomainInfo.constructDomain')
1429 self.shutdownStartTime = None
1431 hvm = self.info.is_hvm()
1432 if hvm:
1433 info = xc.xeninfo()
1434 if 'hvm' not in info['xen_caps']:
1435 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1436 "supported by your CPU and enabled in your "
1437 "BIOS?")
1439 # Hack to pre-reserve some memory for initial domain creation.
1440 # There is an implicit memory overhead for any domain creation. This
1441 # overhead is greater for some types of domain than others. For
1442 # example, an x86 HVM domain will have a default shadow-pagetable
1443 # allocation of 1MB. We free up 2MB here to be on the safe side.
1444 balloon.free(2*1024) # 2MB should be plenty
1446 self.domid = xc.domain_create(
1447 domid = 0,
1448 ssidref = security.get_security_info(self.info, 'ssidref'),
1449 handle = uuid.fromString(self.info['uuid']),
1450 hvm = int(hvm))
1452 if self.domid < 0:
1453 raise VmError('Creating domain failed: name=%s' %
1454 self.info['name_label'])
1456 self.dompath = GetDomainPath(self.domid)
1458 self._recreateDom()
1460 # Set maximum number of vcpus in domain
1461 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1463 # register the domain in the list
1464 from xen.xend import XendDomain
1465 XendDomain.instance().add_domain(self)
1467 def _introduceDomain(self):
1468 assert self.domid is not None
1469 assert self.store_mfn is not None
1470 assert self.store_port is not None
1472 try:
1473 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1474 except RuntimeError, exn:
1475 raise XendError(str(exn))
1478 def _initDomain(self):
1479 log.debug('XendDomainInfo.initDomain: %s %s',
1480 self.domid,
1481 self.info['cpu_weight'])
1483 self._configureBootloader()
1485 try:
1486 self.image = image.create(self, self.info)
1488 if self.info['platform'].get('localtime', 0):
1489 xc.domain_set_time_offset(self.domid)
1491 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1493 # repin domain vcpus if a restricted cpus list is provided
1494 # this is done prior to memory allocation to aide in memory
1495 # distribution for NUMA systems.
1496 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1497 for v in range(0, self.info['VCPUs_max']):
1498 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1500 # Use architecture- and image-specific calculations to determine
1501 # the various headrooms necessary, given the raw configured
1502 # values. maxmem, memory, and shadow are all in KiB.
1503 # but memory_static_max etc are all stored in bytes now.
1504 memory = self.image.getRequiredAvailableMemory(
1505 self.info['memory_dynamic_max'] / 1024)
1506 maxmem = self.image.getRequiredAvailableMemory(
1507 self.info['memory_static_max'] / 1024)
1508 shadow = self.image.getRequiredShadowMemory(
1509 self.info['shadow_memory'] / 1024,
1510 self.info['memory_static_max'] / 1024)
1512 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'],)
1513 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1514 # takes MiB and we must not round down and end up under-providing.
1515 shadow = ((shadow + 1023) / 1024) * 1024
1517 # set memory limit
1518 xc.domain_setmaxmem(self.domid, maxmem)
1520 # Make sure there's enough RAM available for the domain
1521 balloon.free(memory + shadow)
1523 # Set up the shadow memory
1524 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1525 self.info['shadow_memory'] = shadow_cur
1527 self._createChannels()
1529 channel_details = self.image.createImage()
1531 self.store_mfn = channel_details['store_mfn']
1532 if 'console_mfn' in channel_details:
1533 self.console_mfn = channel_details['console_mfn']
1534 if 'notes' in channel_details:
1535 self.info.set_notes(channel_details['notes'])
1536 if 'native_protocol' in channel_details:
1537 self.native_protocol = channel_details['native_protocol'];
1539 self._introduceDomain()
1541 self._createDevices()
1543 self.image.cleanupBootloading()
1545 self.info['start_time'] = time.time()
1547 self._stateSet(DOM_STATE_RUNNING)
1548 except VmError, exn:
1549 log.exception("XendDomainInfo.initDomain: exception occurred")
1550 if self.image:
1551 self.image.cleanupBootloading()
1552 raise exn
1553 except RuntimeError, exn:
1554 log.exception("XendDomainInfo.initDomain: exception occurred")
1555 if self.image:
1556 self.image.cleanupBootloading()
1557 raise VmError(str(exn))
1560 def cleanupDomain(self):
1561 """Cleanup domain resources; release devices. Idempotent. Nothrow
1562 guarantee."""
1564 self.refresh_shutdown_lock.acquire()
1565 try:
1566 self.unwatchShutdown()
1567 self._releaseDevices()
1568 bootloader_tidy(self)
1570 if self.image:
1571 try:
1572 self.image.destroy()
1573 except:
1574 log.exception(
1575 "XendDomainInfo.cleanup: image.destroy() failed.")
1576 self.image = None
1578 try:
1579 self._removeDom()
1580 except:
1581 log.exception("Removing domain path failed.")
1583 self._stateSet(DOM_STATE_HALTED)
1584 finally:
1585 self.refresh_shutdown_lock.release()
1588 def unwatchShutdown(self):
1589 """Remove the watch on the domain's control/shutdown node, if any.
1590 Idempotent. Nothrow guarantee. Expects to be protected by the
1591 refresh_shutdown_lock."""
1593 try:
1594 try:
1595 if self.shutdownWatch:
1596 self.shutdownWatch.unwatch()
1597 finally:
1598 self.shutdownWatch = None
1599 except:
1600 log.exception("Unwatching control/shutdown failed.")
1602 def waitForShutdown(self):
1603 self.state_updated.acquire()
1604 try:
1605 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1606 self.state_updated.wait()
1607 finally:
1608 self.state_updated.release()
1611 # TODO: recategorise - called from XendCheckpoint
1614 def completeRestore(self, store_mfn, console_mfn):
1616 log.debug("XendDomainInfo.completeRestore")
1618 self.store_mfn = store_mfn
1619 self.console_mfn = console_mfn
1621 self._introduceDomain()
1622 if self.info.is_hvm():
1623 self.image = image.create(self, self.info)
1624 if self.image:
1625 self.image.createDeviceModel(True)
1626 self._storeDomDetails()
1627 self._registerWatches()
1628 self.refreshShutdown()
1630 log.debug("XendDomainInfo.completeRestore done")
1633 def _endRestore(self):
1634 self.setResume(False)
1637 # VM Destroy
1640 def _prepare_phantom_paths(self):
1641 # get associated devices to destroy
1642 # build list of phantom devices to be removed after normal devices
1643 plist = []
1644 if self.domid is not None:
1645 from xen.xend.xenstore.xstransact import xstransact
1646 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1647 for dev in t.list():
1648 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1649 % (self.dompath, dev))
1650 if backend_phantom_vbd is not None:
1651 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1652 % backend_phantom_vbd)
1653 plist.append(backend_phantom_vbd)
1654 plist.append(frontend_phantom_vbd)
1655 return plist
1657 def _cleanup_phantom_devs(self, plist):
1658 # remove phantom devices
1659 if not plist == []:
1660 time.sleep(2)
1661 for paths in plist:
1662 if paths.find('backend') != -1:
1663 from xen.xend.server import DevController
1664 # Modify online status /before/ updating state (latter is watched by
1665 # drivers, so this ordering avoids a race).
1666 xstransact.Write(paths, 'online', "0")
1667 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1668 # force
1669 xstransact.Remove(paths)
1671 def destroy(self):
1672 """Cleanup VM and destroy domain. Nothrow guarantee."""
1674 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1676 paths = self._prepare_phantom_paths()
1678 self._cleanupVm()
1679 if self.dompath is not None:
1680 xc.domain_destroy_hook(self.domid)
1681 self.destroyDomain()
1683 self._cleanup_phantom_devs(paths)
1685 if "transient" in self.info["other_config"] \
1686 and bool(self.info["other_config"]["transient"]):
1687 from xen.xend import XendDomain
1688 XendDomain.instance().domain_delete_by_dominfo(self)
1691 def destroyDomain(self):
1692 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1694 paths = self._prepare_phantom_paths()
1696 try:
1697 if self.domid is not None:
1698 xc.domain_destroy(self.domid)
1699 for state in DOM_STATES_OLD:
1700 self.info[state] = 0
1701 self._stateSet(DOM_STATE_HALTED)
1702 except:
1703 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1705 from xen.xend import XendDomain
1706 XendDomain.instance().remove_domain(self)
1708 self.cleanupDomain()
1709 self._cleanup_phantom_devs(paths)
1712 def resumeDomain(self):
1713 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1715 if self.domid is None:
1716 return
1717 try:
1718 # could also fetch a parsed note from xenstore
1719 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1720 if not fast:
1721 self._releaseDevices()
1722 self.testDeviceComplete()
1723 self.testvifsComplete()
1724 log.debug("XendDomainInfo.resumeDomain: devices released")
1726 self._resetChannels()
1728 self._removeDom('control/shutdown')
1729 self._removeDom('device-misc/vif/nextDeviceID')
1731 self._createChannels()
1732 self._introduceDomain()
1733 self._storeDomDetails()
1735 self._createDevices()
1736 log.debug("XendDomainInfo.resumeDomain: devices created")
1738 xc.domain_resume(self.domid, fast)
1739 ResumeDomain(self.domid)
1740 except:
1741 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1744 # Channels for xenstore and console
1747 def _createChannels(self):
1748 """Create the channels to the domain.
1749 """
1750 self.store_port = self._createChannel()
1751 self.console_port = self._createChannel()
1754 def _createChannel(self):
1755 """Create an event channel to the domain.
1756 """
1757 try:
1758 if self.domid != None:
1759 return xc.evtchn_alloc_unbound(domid = self.domid,
1760 remote_dom = 0)
1761 except:
1762 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1763 raise
1765 def _resetChannels(self):
1766 """Reset all event channels in the domain.
1767 """
1768 try:
1769 if self.domid != None:
1770 return xc.evtchn_reset(dom = self.domid)
1771 except:
1772 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1773 raise
1777 # Bootloader configuration
1780 def _configureBootloader(self):
1781 """Run the bootloader if we're configured to do so."""
1783 blexec = self.info['PV_bootloader']
1784 bootloader_args = self.info['PV_bootloader_args']
1785 kernel = self.info['PV_kernel']
1786 ramdisk = self.info['PV_ramdisk']
1787 args = self.info['PV_args']
1788 boot = self.info['HVM_boot_policy']
1790 if boot:
1791 # HVM booting.
1792 pass
1793 elif not blexec and kernel:
1794 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1795 # will be picked up by image.py.
1796 pass
1797 else:
1798 # Boot using bootloader
1799 if not blexec or blexec == 'pygrub':
1800 blexec = osdep.pygrub_path
1802 blcfg = None
1803 disks = [x for x in self.info['vbd_refs']
1804 if self.info['devices'][x][1]['bootable']]
1806 if not disks:
1807 msg = "Had a bootloader specified, but no disks are bootable"
1808 log.error(msg)
1809 raise VmError(msg)
1811 devinfo = self.info['devices'][disks[0]]
1812 devtype = devinfo[0]
1813 disk = devinfo[1]['uname']
1815 fn = blkdev_uname_to_file(disk)
1816 taptype = blkdev_uname_to_taptype(disk)
1817 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1818 if mounted:
1819 # This is a file, not a device. pygrub can cope with a
1820 # file if it's raw, but if it's QCOW or other such formats
1821 # used through blktap, then we need to mount it first.
1823 log.info("Mounting %s on %s." %
1824 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1826 vbd = {
1827 'mode': 'RO',
1828 'device': BOOTLOADER_LOOPBACK_DEVICE,
1831 from xen.xend import XendDomain
1832 dom0 = XendDomain.instance().privilegedDomain()
1833 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1834 fn = BOOTLOADER_LOOPBACK_DEVICE
1836 try:
1837 blcfg = bootloader(blexec, fn, self, False,
1838 bootloader_args, kernel, ramdisk, args)
1839 finally:
1840 if mounted:
1841 log.info("Unmounting %s from %s." %
1842 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1844 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1846 if blcfg is None:
1847 msg = "Had a bootloader specified, but can't find disk"
1848 log.error(msg)
1849 raise VmError(msg)
1851 self.info.update_with_image_sxp(blcfg, True)
1855 # VM Functions
1858 def _readVMDetails(self, params):
1859 """Read the specified parameters from the store.
1860 """
1861 try:
1862 return self._gatherVm(*params)
1863 except ValueError:
1864 # One of the int/float entries in params has a corresponding store
1865 # entry that is invalid. We recover, because older versions of
1866 # Xend may have put the entry there (memory/target, for example),
1867 # but this is in general a bad situation to have reached.
1868 log.exception(
1869 "Store corrupted at %s! Domain %d's configuration may be "
1870 "affected.", self.vmpath, self.domid)
1871 return []
1873 def _cleanupVm(self):
1874 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1876 self._unwatchVm()
1878 try:
1879 self._removeVm()
1880 except:
1881 log.exception("Removing VM path failed.")
1884 def checkLiveMigrateMemory(self):
1885 """ Make sure there's enough memory to migrate this domain """
1886 overhead_kb = 0
1887 if arch.type == "x86":
1888 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1889 # the minimum that Xen would allocate if no value were given.
1890 overhead_kb = self.info['VCPUs_max'] * 1024 + \
1891 (self.info['memory_static_max'] / 1024 / 1024) * 4
1892 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1893 # The domain might already have some shadow memory
1894 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1895 if overhead_kb > 0:
1896 balloon.free(overhead_kb)
1898 def _unwatchVm(self):
1899 """Remove the watch on the VM path, if any. Idempotent. Nothrow
1900 guarantee."""
1901 try:
1902 try:
1903 if self.vmWatch:
1904 self.vmWatch.unwatch()
1905 finally:
1906 self.vmWatch = None
1907 except:
1908 log.exception("Unwatching VM path failed.")
1910 def testDeviceComplete(self):
1911 """ For Block IO migration safety we must ensure that
1912 the device has shutdown correctly, i.e. all blocks are
1913 flushed to disk
1914 """
1915 start = time.time()
1916 while True:
1917 test = 0
1918 diff = time.time() - start
1919 for i in self.getDeviceController('vbd').deviceIDs():
1920 test = 1
1921 log.info("Dev %s still active, looping...", i)
1922 time.sleep(0.1)
1924 if test == 0:
1925 break
1926 if diff >= MIGRATE_TIMEOUT:
1927 log.info("Dev still active but hit max loop timeout")
1928 break
1930 def testvifsComplete(self):
1931 """ In case vifs are released and then created for the same
1932 domain, we need to wait the device shut down.
1933 """
1934 start = time.time()
1935 while True:
1936 test = 0
1937 diff = time.time() - start
1938 for i in self.getDeviceController('vif').deviceIDs():
1939 test = 1
1940 log.info("Dev %s still active, looping...", i)
1941 time.sleep(0.1)
1943 if test == 0:
1944 break
1945 if diff >= MIGRATE_TIMEOUT:
1946 log.info("Dev still active but hit max loop timeout")
1947 break
1949 def _storeVmDetails(self):
1950 to_store = {}
1952 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1953 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1954 if self._infoIsSet(info_key):
1955 to_store[key] = str(self.info[info_key])
1957 if self._infoIsSet("static_memory_min"):
1958 to_store["memory"] = str(self.info["static_memory_min"])
1959 if self._infoIsSet("static_memory_max"):
1960 to_store["maxmem"] = str(self.info["static_memory_max"])
1962 image_sxpr = self.info.image_sxpr()
1963 if image_sxpr:
1964 to_store['image'] = sxp.to_string(image_sxpr)
1966 if self._infoIsSet('security'):
1967 secinfo = self.info['security']
1968 to_store['security'] = sxp.to_string(secinfo)
1969 for idx in range(0, len(secinfo)):
1970 if secinfo[idx][0] == 'access_control':
1971 to_store['security/access_control'] = sxp.to_string(
1972 [secinfo[idx][1], secinfo[idx][2]])
1973 for aidx in range(1, len(secinfo[idx])):
1974 if secinfo[idx][aidx][0] == 'label':
1975 to_store['security/access_control/label'] = \
1976 secinfo[idx][aidx][1]
1977 if secinfo[idx][aidx][0] == 'policy':
1978 to_store['security/access_control/policy'] = \
1979 secinfo[idx][aidx][1]
1980 if secinfo[idx][0] == 'ssidref':
1981 to_store['security/ssidref'] = str(secinfo[idx][1])
1984 if not self._readVm('xend/restart_count'):
1985 to_store['xend/restart_count'] = str(0)
1987 log.debug("Storing VM details: %s", scrub_password(to_store))
1989 self._writeVm(to_store)
1990 self._setVmPermissions()
1993 def _setVmPermissions(self):
1994 """Allow the guest domain to read its UUID. We don't allow it to
1995 access any other entry, for security."""
1996 xstransact.SetPermissions('%s/uuid' % self.vmpath,
1997 { 'dom' : self.domid,
1998 'read' : True,
1999 'write' : False })
2002 # Utility functions
2005 def __getattr__(self, name):
2006 if name == "state":
2007 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2008 log.warn("".join(traceback.format_stack()))
2009 return self._stateGet()
2010 else:
2011 raise AttributeError()
2013 def __setattr__(self, name, value):
2014 if name == "state":
2015 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2016 log.warn("".join(traceback.format_stack()))
2017 self._stateSet(value)
2018 else:
2019 self.__dict__[name] = value
2021 def _stateSet(self, state):
2022 self.state_updated.acquire()
2023 try:
2024 # TODO Not sure this is correct...
2025 # _stateGet is live now. Why not fire event
2026 # even when it hasn't changed?
2027 if self._stateGet() != state:
2028 self.state_updated.notifyAll()
2029 import XendAPI
2030 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2031 'power_state')
2032 finally:
2033 self.state_updated.release()
2035 def _stateGet(self):
2036 # Lets try and reconsitute the state from xc
2037 # first lets try and get the domain info
2038 # from xc - this will tell us if the domain
2039 # exists
2040 info = dom_get(self.getDomid())
2041 if info is None or info['shutdown']:
2042 # We are either HALTED or SUSPENDED
2043 # check saved image exists
2044 from xen.xend import XendDomain
2045 managed_config_path = \
2046 XendDomain.instance()._managed_check_point_path( \
2047 self.get_uuid())
2048 if os.path.exists(managed_config_path):
2049 return XEN_API_VM_POWER_STATE_SUSPENDED
2050 else:
2051 return XEN_API_VM_POWER_STATE_HALTED
2052 else:
2053 # We are either RUNNING or PAUSED
2054 if info['paused']:
2055 return XEN_API_VM_POWER_STATE_PAUSED
2056 else:
2057 return XEN_API_VM_POWER_STATE_RUNNING
2059 def _infoIsSet(self, name):
2060 return name in self.info and self.info[name] is not None
2062 def _checkName(self, name):
2063 """Check if a vm name is valid. Valid names contain alphabetic
2064 characters, digits, or characters in '_-.:/+'.
2065 The same name cannot be used for more than one vm at the same time.
2067 @param name: name
2068 @raise: VmError if invalid
2069 """
2070 from xen.xend import XendDomain
2072 if name is None or name == '':
2073 raise VmError('Missing VM Name')
2075 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2076 raise VmError('Invalid VM Name')
2078 dom = XendDomain.instance().domain_lookup_nr(name)
2079 if dom and dom.domid != self.domid:
2080 raise VmError("VM name '%s' already exists%s" %
2081 (name,
2082 dom.domid is not None and
2083 (" as domain %s" % str(dom.domid)) or ""))
2086 def update(self, info = None, refresh = True):
2087 """Update with info from xc.domain_getinfo().
2088 """
2089 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2090 str(self.domid))
2092 if not info:
2093 info = dom_get(self.domid)
2094 if not info:
2095 return
2097 if info["maxmem_kb"] < 0:
2098 info["maxmem_kb"] = XendNode.instance() \
2099 .physinfo_dict()['total_memory'] * 1024
2101 #manually update ssidref / security fields
2102 if security.on() and info.has_key('ssidref'):
2103 if (info['ssidref'] != 0) and self.info.has_key('security'):
2104 security_field = self.info['security']
2105 if not security_field:
2106 #create new security element
2107 self.info.update({'security':
2108 [['ssidref', str(info['ssidref'])]]})
2110 #ssidref field not used any longer
2111 if 'ssidref' in info:
2112 info.pop('ssidref')
2114 # make sure state is reset for info
2115 # TODO: we should eventually get rid of old_dom_states
2117 self.info.update_config(info)
2118 self._update_consoles()
2120 if refresh:
2121 self.refreshShutdown(info)
2123 log.trace("XendDomainInfo.update done on domain %s: %s",
2124 str(self.domid), self.info)
2126 def sxpr(self, ignore_store = False, legacy_only = True):
2127 result = self.info.to_sxp(domain = self,
2128 ignore_devices = ignore_store,
2129 legacy_only = legacy_only)
2131 #if not ignore_store and self.dompath:
2132 # vnc_port = self.readDom('console/vnc-port')
2133 # if vnc_port is not None:
2134 # result.append(['device',
2135 # ['console', ['vnc-port', str(vnc_port)]]])
2137 return result
2139 # Xen API
2140 # ----------------------------------------------------------------
2142 def get_uuid(self):
2143 dom_uuid = self.info.get('uuid')
2144 if not dom_uuid: # if it doesn't exist, make one up
2145 dom_uuid = uuid.createString()
2146 self.info['uuid'] = dom_uuid
2147 return dom_uuid
2149 def get_memory_static_max(self):
2150 return self.info.get('memory_static_max', 0)
2151 def get_memory_static_min(self):
2152 return self.info.get('memory_static_min', 0)
2153 def get_memory_dynamic_max(self):
2154 return self.info.get('memory_dynamic_max', 0)
2155 def get_memory_dynamic_min(self):
2156 return self.info.get('memory_dynamic_min', 0)
2158 # only update memory-related config values if they maintain sanity
2159 def _safe_set_memory(self, key, newval):
2160 oldval = self.info.get(key, 0)
2161 try:
2162 self.info[key] = newval
2163 self.info._memory_sanity_check()
2164 except Exception, ex:
2165 self.info[key] = oldval
2166 raise
2168 def set_memory_static_max(self, val):
2169 self._safe_set_memory('memory_static_max', val)
2170 def set_memory_static_min(self, val):
2171 self._safe_set_memory('memory_static_min', val)
2172 def set_memory_dynamic_max(self, val):
2173 self._safe_set_memory('memory_dynamic_max', val)
2174 def set_memory_dynamic_min(self, val):
2175 self._safe_set_memory('memory_dynamic_min', val)
2177 def get_vcpus_params(self):
2178 if self.getDomid() is None:
2179 return self.info['vcpus_params']
2181 retval = xc.sched_credit_domain_get(self.getDomid())
2182 return retval
2183 def get_power_state(self):
2184 return XEN_API_VM_POWER_STATE[self._stateGet()]
2185 def get_platform(self):
2186 return self.info.get('platform', {})
2187 def get_pci_bus(self):
2188 return self.info.get('pci_bus', '')
2189 def get_tools_version(self):
2190 return self.info.get('tools_version', {})
2191 def get_metrics(self):
2192 return self.metrics.get_uuid();
2194 def get_on_shutdown(self):
2195 after_shutdown = self.info.get('actions_after_shutdown')
2196 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2197 return XEN_API_ON_NORMAL_EXIT[-1]
2198 return after_shutdown
2200 def get_on_reboot(self):
2201 after_reboot = self.info.get('actions_after_reboot')
2202 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2203 return XEN_API_ON_NORMAL_EXIT[-1]
2204 return after_reboot
2206 def get_on_suspend(self):
2207 # TODO: not supported
2208 after_suspend = self.info.get('actions_after_suspend')
2209 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2210 return XEN_API_ON_NORMAL_EXIT[-1]
2211 return after_suspend
2213 def get_on_crash(self):
2214 after_crash = self.info.get('actions_after_crash')
2215 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2216 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2217 return after_crash
2219 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2220 """ Get's a device configuration either from XendConfig or
2221 from the DevController.
2223 @param dev_class: device class, either, 'vbd' or 'vif'
2224 @param dev_uuid: device UUID
2226 @rtype: dictionary
2227 """
2228 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2230 # shortcut if the domain isn't started because
2231 # the devcontrollers will have no better information
2232 # than XendConfig.
2233 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2234 if dev_config:
2235 return copy.deepcopy(dev_config)
2236 return None
2238 # instead of using dev_class, we use the dev_type
2239 # that is from XendConfig.
2240 controller = self.getDeviceController(dev_type)
2241 if not controller:
2242 return None
2244 all_configs = controller.getAllDeviceConfigurations()
2245 if not all_configs:
2246 return None
2248 updated_dev_config = copy.deepcopy(dev_config)
2249 for _devid, _devcfg in all_configs.items():
2250 if _devcfg.get('uuid') == dev_uuid:
2251 updated_dev_config.update(_devcfg)
2252 updated_dev_config['id'] = _devid
2253 return updated_dev_config
2255 return updated_dev_config
2257 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2258 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2259 if not config:
2260 return {}
2262 config['VM'] = self.get_uuid()
2264 if dev_class == 'vif':
2265 if not config.has_key('name'):
2266 config['name'] = config.get('vifname', '')
2267 if not config.has_key('MAC'):
2268 config['MAC'] = config.get('mac', '')
2269 if not config.has_key('type'):
2270 config['type'] = 'paravirtualised'
2271 if not config.has_key('device'):
2272 devid = config.get('id')
2273 if devid != None:
2274 config['device'] = 'eth%d' % devid
2275 else:
2276 config['device'] = ''
2278 if not config.has_key('network'):
2279 try:
2280 bridge = config.get('bridge', None)
2281 if bridge is None:
2282 from xen.util import Brctl
2283 if_to_br = dict([(i,b)
2284 for (b,ifs) in Brctl.get_state().items()
2285 for i in ifs])
2286 vifname = "vif%s.%s" % (self.getDomid(),
2287 config.get('id'))
2288 bridge = if_to_br.get(vifname, None)
2289 config['network'] = \
2290 XendNode.instance().bridge_to_network(
2291 config.get('bridge')).get_uuid()
2292 except Exception:
2293 log.exception('bridge_to_network')
2294 # Ignore this for now -- it may happen if the device
2295 # has been specified using the legacy methods, but at
2296 # some point we're going to have to figure out how to
2297 # handle that properly.
2299 config['MTU'] = 1500 # TODO
2301 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2302 xennode = XendNode.instance()
2303 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2304 config['io_read_kbs'] = rx_bps/1024
2305 config['io_write_kbs'] = tx_bps/1024
2306 else:
2307 config['io_read_kbs'] = 0.0
2308 config['io_write_kbs'] = 0.0
2310 if dev_class == 'vbd':
2312 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2313 controller = self.getDeviceController(dev_class)
2314 devid, _1, _2 = controller.getDeviceDetails(config)
2315 xennode = XendNode.instance()
2316 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2317 config['io_read_kbs'] = rd_blkps
2318 config['io_write_kbs'] = wr_blkps
2319 else:
2320 config['io_read_kbs'] = 0.0
2321 config['io_write_kbs'] = 0.0
2323 config['VDI'] = config.get('VDI', '')
2324 config['device'] = config.get('dev', '')
2325 if ':' in config['device']:
2326 vbd_name, vbd_type = config['device'].split(':', 1)
2327 config['device'] = vbd_name
2328 if vbd_type == 'cdrom':
2329 config['type'] = XEN_API_VBD_TYPE[0]
2330 else:
2331 config['type'] = XEN_API_VBD_TYPE[1]
2333 config['driver'] = 'paravirtualised' # TODO
2334 config['image'] = config.get('uname', '')
2336 if config.get('mode', 'r') == 'r':
2337 config['mode'] = 'RO'
2338 else:
2339 config['mode'] = 'RW'
2341 if dev_class == 'vtpm':
2342 if not config.has_key('type'):
2343 config['type'] = 'paravirtualised' # TODO
2344 if not config.has_key('backend'):
2345 config['backend'] = "00000000-0000-0000-0000-000000000000"
2347 return config
2349 def get_dev_property(self, dev_class, dev_uuid, field):
2350 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2351 try:
2352 return config[field]
2353 except KeyError:
2354 raise XendError('Invalid property for device: %s' % field)
2356 def set_dev_property(self, dev_class, dev_uuid, field, value):
2357 self.info['devices'][dev_uuid][1][field] = value
2359 def get_vcpus_util(self):
2360 vcpu_util = {}
2361 xennode = XendNode.instance()
2362 if 'VCPUs_max' in self.info and self.domid != None:
2363 for i in range(0, self.info['VCPUs_max']):
2364 util = xennode.get_vcpu_util(self.domid, i)
2365 vcpu_util[str(i)] = util
2367 return vcpu_util
2369 def get_consoles(self):
2370 return self.info.get('console_refs', [])
2372 def get_vifs(self):
2373 return self.info.get('vif_refs', [])
2375 def get_vbds(self):
2376 return self.info.get('vbd_refs', [])
2378 def get_vtpms(self):
2379 return self.info.get('vtpm_refs', [])
2381 def create_vbd(self, xenapi_vbd, vdi_image_path):
2382 """Create a VBD using a VDI from XendStorageRepository.
2384 @param xenapi_vbd: vbd struct from the Xen API
2385 @param vdi_image_path: VDI UUID
2386 @rtype: string
2387 @return: uuid of the device
2388 """
2389 xenapi_vbd['image'] = vdi_image_path
2390 if vdi_image_path.startswith('tap'):
2391 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2392 else:
2393 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2395 if not dev_uuid:
2396 raise XendError('Failed to create device')
2398 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2399 XEN_API_VM_POWER_STATE_PAUSED):
2400 _, config = self.info['devices'][dev_uuid]
2402 if vdi_image_path.startswith('tap'):
2403 dev_control = self.getDeviceController('tap')
2404 else:
2405 dev_control = self.getDeviceController('vbd')
2407 try:
2408 devid = dev_control.createDevice(config)
2409 dev_control.waitForDevice(devid)
2410 self.info.device_update(dev_uuid,
2411 cfg_xenapi = {'devid': devid})
2412 except Exception, exn:
2413 log.exception(exn)
2414 del self.info['devices'][dev_uuid]
2415 self.info['vbd_refs'].remove(dev_uuid)
2416 raise
2418 return dev_uuid
2420 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2421 """Create a VBD using a VDI from XendStorageRepository.
2423 @param xenapi_vbd: vbd struct from the Xen API
2424 @param vdi_image_path: VDI UUID
2425 @rtype: string
2426 @return: uuid of the device
2427 """
2428 xenapi_vbd['image'] = vdi_image_path
2429 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2430 if not dev_uuid:
2431 raise XendError('Failed to create device')
2433 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2434 _, config = self.info['devices'][dev_uuid]
2435 config['devid'] = self.getDeviceController('tap').createDevice(config)
2437 return config['devid']
2439 def create_vif(self, xenapi_vif):
2440 """Create VIF device from the passed struct in Xen API format.
2442 @param xenapi_vif: Xen API VIF Struct.
2443 @rtype: string
2444 @return: UUID
2445 """
2446 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2447 if not dev_uuid:
2448 raise XendError('Failed to create device')
2450 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2451 XEN_API_VM_POWER_STATE_PAUSED):
2453 _, config = self.info['devices'][dev_uuid]
2454 dev_control = self.getDeviceController('vif')
2456 try:
2457 devid = dev_control.createDevice(config)
2458 dev_control.waitForDevice(devid)
2459 self.info.device_update(dev_uuid,
2460 cfg_xenapi = {'devid': devid})
2461 except Exception, exn:
2462 log.exception(exn)
2463 del self.info['devices'][dev_uuid]
2464 self.info['vif_refs'].remove(dev_uuid)
2465 raise
2467 return dev_uuid
2469 def create_vtpm(self, xenapi_vtpm):
2470 """Create a VTPM device from the passed struct in Xen API format.
2472 @return: uuid of the device
2473 @rtype: string
2474 """
2476 if self._stateGet() not in (DOM_STATE_HALTED,):
2477 raise VmError("Can only add vTPM to a halted domain.")
2478 if self.get_vtpms() != []:
2479 raise VmError('Domain already has a vTPM.')
2480 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2481 if not dev_uuid:
2482 raise XendError('Failed to create device')
2484 return dev_uuid
2486 def create_console(self, xenapi_console):
2487 """ Create a console device from a Xen API struct.
2489 @return: uuid of device
2490 @rtype: string
2491 """
2492 if self._stateGet() not in (DOM_STATE_HALTED,):
2493 raise VmError("Can only add console to a halted domain.")
2495 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2496 if not dev_uuid:
2497 raise XendError('Failed to create device')
2499 return dev_uuid
2501 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2502 if dev_uuid not in self.info['devices']:
2503 raise XendError('Device does not exist')
2505 try:
2506 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2507 XEN_API_VM_POWER_STATE_PAUSED):
2508 _, config = self.info['devices'][dev_uuid]
2509 devid = config.get('devid')
2510 if devid != None:
2511 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2512 else:
2513 raise XendError('Unable to get devid for device: %s:%s' %
2514 (dev_type, dev_uuid))
2515 finally:
2516 del self.info['devices'][dev_uuid]
2517 self.info['%s_refs' % dev_type].remove(dev_uuid)
2519 def destroy_vbd(self, dev_uuid):
2520 self.destroy_device_by_uuid('vbd', dev_uuid)
2522 def destroy_vif(self, dev_uuid):
2523 self.destroy_device_by_uuid('vif', dev_uuid)
2525 def destroy_vtpm(self, dev_uuid):
2526 self.destroy_device_by_uuid('vtpm', dev_uuid)
2528 def has_device(self, dev_class, dev_uuid):
2529 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2531 def __str__(self):
2532 return '<domain id=%s name=%s memory=%s state=%s>' % \
2533 (str(self.domid), self.info['name_label'],
2534 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2536 __repr__ = __str__