ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 15873:7dfc9a7a0d4e

Xend: "shadow_memory" setting in xm config is in MiB, not bytes.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Tue Sep 11 12:30:39 2007 +0100 (2007-09-11)
parents 0c14d0bf369e
children 9f4f70233041
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 import xen.util.xsm.xsm as 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 xc.domain_destroy_hook(self.domid)
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 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
493 raise XendError("Domain '%s' is not started" % self.info['name_label'])
495 asserts.isCharConvertible(key)
496 self.storeDom("control/sysrq", '%c' % key)
498 def device_create(self, dev_config):
499 """Create a new device.
501 @param dev_config: device configuration
502 @type dev_config: SXP object (parsed config)
503 """
504 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
505 dev_type = sxp.name(dev_config)
506 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
507 dev_config_dict = self.info['devices'][dev_uuid][1]
508 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
510 if self.domid is not None:
511 try:
512 dev_config_dict['devid'] = devid = \
513 self._createDevice(dev_type, dev_config_dict)
514 self._waitForDevice(dev_type, devid)
515 except VmError, ex:
516 raise ex
517 else:
518 devid = None
520 xen.xend.XendDomain.instance().managed_config_save(self)
521 return self.getDeviceController(dev_type).sxpr(devid)
523 def device_configure(self, dev_sxp, devid = None):
524 """Configure an existing device.
526 @param dev_config: device configuration
527 @type dev_config: SXP object (parsed config)
528 @param devid: device id
529 @type devid: int
530 @return: Returns True if successfully updated device
531 @rtype: boolean
532 """
534 # convert device sxp to a dict
535 dev_class = sxp.name(dev_sxp)
536 dev_config = {}
537 for opt_val in dev_sxp[1:]:
538 try:
539 dev_config[opt_val[0]] = opt_val[1]
540 except IndexError:
541 pass
543 # use DevController.reconfigureDevice to change device config
544 dev_control = self.getDeviceController(dev_class)
545 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
547 # update XendConfig with new device info
548 if dev_uuid:
549 self.info.device_update(dev_uuid, dev_sxp)
551 return True
553 def waitForDevices(self):
554 """Wait for this domain's configured devices to connect.
556 @raise VmError: if any device fails to initialise.
557 """
558 for devclass in XendDevices.valid_devices():
559 self.getDeviceController(devclass).waitForDevices()
561 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
562 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
563 deviceClass, devid)
565 if rm_cfg:
566 # Convert devid to device number. A device number is
567 # needed to remove its configuration.
568 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
570 # Save current sxprs. A device number and a backend
571 # path are needed to remove its configuration but sxprs
572 # do not have those after calling destroyDevice.
573 sxprs = self.getDeviceSxprs(deviceClass)
575 rc = None
576 if self.domid is not None:
577 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
578 if not force and rm_cfg:
579 # The backend path, other than the device itself,
580 # has to be passed because its accompanied frontend
581 # path may be void until its removal is actually
582 # issued. It is probable because destroyDevice is
583 # issued first.
584 for dev_num, dev_info in sxprs:
585 dev_num = int(dev_num)
586 if dev_num == dev:
587 for x in dev_info:
588 if x[0] == 'backend':
589 backend = x[1]
590 break
591 break
592 self._waitForDevice_destroy(deviceClass, devid, backend)
594 if rm_cfg:
595 if deviceClass == 'vif':
596 if self.domid is not None:
597 for dev_num, dev_info in sxprs:
598 dev_num = int(dev_num)
599 if dev_num == dev:
600 for x in dev_info:
601 if x[0] == 'mac':
602 mac = x[1]
603 break
604 break
605 dev_info = self._getDeviceInfo_vif(mac)
606 else:
607 _, dev_info = sxprs[dev]
608 else: # 'vbd' or 'tap'
609 dev_info = self._getDeviceInfo_vbd(dev)
610 # To remove the UUID of the device from refs,
611 # deviceClass must be always 'vbd'.
612 deviceClass = 'vbd'
613 if dev_info is None:
614 raise XendError("Device %s is not defined" % devid)
616 dev_uuid = sxp.child_value(dev_info, 'uuid')
617 del self.info['devices'][dev_uuid]
618 self.info['%s_refs' % deviceClass].remove(dev_uuid)
619 xen.xend.XendDomain.instance().managed_config_save(self)
621 return rc
623 def getDeviceSxprs(self, deviceClass):
624 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
625 return self.getDeviceController(deviceClass).sxprs()
626 else:
627 sxprs = []
628 dev_num = 0
629 for dev_type, dev_info in self.info.all_devices_sxpr():
630 if dev_type == deviceClass:
631 sxprs.append([dev_num, dev_info])
632 dev_num += 1
633 return sxprs
635 def getBlockDeviceClass(self, devid):
636 # To get a device number from the devid,
637 # we temporarily use the device controller of VBD.
638 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
639 dev_info = self._getDeviceInfo_vbd(dev)
640 if dev_info:
641 return dev_info[0]
643 def _getDeviceInfo_vif(self, mac):
644 for dev_type, dev_info in self.info.all_devices_sxpr():
645 if dev_type != 'vif':
646 continue
647 if mac == sxp.child_value(dev_info, 'mac'):
648 return dev_info
650 def _getDeviceInfo_vbd(self, devid):
651 for dev_type, dev_info in self.info.all_devices_sxpr():
652 if dev_type != 'vbd' and dev_type != 'tap':
653 continue
654 dev = sxp.child_value(dev_info, 'dev')
655 dev = dev.split(':')[0]
656 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
657 if devid == dev:
658 return dev_info
661 def setMemoryTarget(self, target):
662 """Set the memory target of this domain.
663 @param target: In MiB.
664 """
665 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
666 self.info['name_label'], str(self.domid), target)
668 MiB = 1024 * 1024
669 self._safe_set_memory('memory_dynamic_min', target * MiB)
670 self._safe_set_memory('memory_dynamic_max', target * MiB)
672 if self.domid >= 0:
673 self.storeVm("memory", target)
674 self.storeDom("memory/target", target << 10)
675 xen.xend.XendDomain.instance().managed_config_save(self)
677 def setMemoryMaximum(self, limit):
678 """Set the maximum memory limit of this domain
679 @param limit: In MiB.
680 """
681 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
682 self.info['name_label'], str(self.domid), limit)
684 if limit <= 0:
685 raise XendError('Invalid memory size')
687 MiB = 1024 * 1024
688 self._safe_set_memory('memory_static_max', limit * MiB)
690 if self.domid >= 0:
691 maxmem = int(limit) * 1024
692 try:
693 return xc.domain_setmaxmem(self.domid, maxmem)
694 except Exception, ex:
695 raise XendError(str(ex))
696 xen.xend.XendDomain.instance().managed_config_save(self)
699 def getVCPUInfo(self):
700 try:
701 # We include the domain name and ID, to help xm.
702 sxpr = ['domain',
703 ['domid', self.domid],
704 ['name', self.info['name_label']],
705 ['vcpu_count', self.info['VCPUs_max']]]
707 for i in range(0, self.info['VCPUs_max']):
708 if self.domid is not None:
709 info = xc.vcpu_getinfo(self.domid, i)
711 sxpr.append(['vcpu',
712 ['number', i],
713 ['online', info['online']],
714 ['blocked', info['blocked']],
715 ['running', info['running']],
716 ['cpu_time', info['cpu_time'] / 1e9],
717 ['cpu', info['cpu']],
718 ['cpumap', info['cpumap']]])
719 else:
720 sxpr.append(['vcpu',
721 ['number', i],
722 ['online', 0],
723 ['blocked', 0],
724 ['running', 0],
725 ['cpu_time', 0.0],
726 ['cpu', -1],
727 ['cpumap', self.info['cpus'] and \
728 self.info['cpus'] or range(64)]])
730 return sxpr
732 except RuntimeError, exn:
733 raise XendError(str(exn))
736 def getDomInfo(self):
737 return dom_get(self.domid)
739 #
740 # internal functions ... TODO: re-categorised
741 #
743 def _augmentInfo(self, priv):
744 """Augment self.info, as given to us through L{recreate}, with
745 values taken from the store. This recovers those values known
746 to xend but not to the hypervisor.
747 """
748 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
749 if priv:
750 augment_entries.remove('memory')
751 augment_entries.remove('maxmem')
752 augment_entries.remove('vcpus')
753 augment_entries.remove('vcpu_avail')
755 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
756 for k in augment_entries])
758 # make returned lists into a dictionary
759 vm_config = dict(zip(augment_entries, vm_config))
761 for arg in augment_entries:
762 val = vm_config[arg]
763 if val != None:
764 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
765 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
766 self.info[xapiarg] = val
767 elif arg == "memory":
768 self.info["static_memory_min"] = val
769 elif arg == "maxmem":
770 self.info["static_memory_max"] = val
771 else:
772 self.info[arg] = val
774 # For dom0, we ignore any stored value for the vcpus fields, and
775 # read the current value from Xen instead. This allows boot-time
776 # settings to take precedence over any entries in the store.
777 if priv:
778 xeninfo = dom_get(self.domid)
779 self.info['VCPUs_max'] = xeninfo['online_vcpus']
780 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
782 # read image value
783 image_sxp = self._readVm('image')
784 if image_sxp:
785 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
787 # read devices
788 devices = []
789 for devclass in XendDevices.valid_devices():
790 devconfig = self.getDeviceController(devclass).configurations()
791 if devconfig:
792 devices.extend(devconfig)
794 if not self.info['devices'] and devices is not None:
795 for device in devices:
796 self.info.device_add(device[0], cfg_sxp = device)
798 self._update_consoles()
800 def _update_consoles(self):
801 if self.domid == None or self.domid == 0:
802 return
804 # Update VT100 port if it exists
805 self.console_port = self.readDom('console/port')
806 if self.console_port is not None:
807 serial_consoles = self.info.console_get_all('vt100')
808 if not serial_consoles:
809 cfg = self.info.console_add('vt100', self.console_port)
810 self._createDevice('console', cfg)
811 else:
812 console_uuid = serial_consoles[0].get('uuid')
813 self.info.console_update(console_uuid, 'location',
814 self.console_port)
817 # Update VNC port if it exists and write to xenstore
818 vnc_port = self.readDom('console/vnc-port')
819 if vnc_port is not None:
820 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
821 if dev_type == 'vfb':
822 old_location = dev_info.get('location')
823 listen_host = dev_info.get('vnclisten', 'localhost')
824 new_location = '%s:%s' % (listen_host, str(vnc_port))
825 if old_location == new_location:
826 break
828 dev_info['location'] = new_location
829 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
830 vfb_ctrl = self.getDeviceController('vfb')
831 vfb_ctrl.reconfigureDevice(0, dev_info)
832 break
834 #
835 # Function to update xenstore /vm/*
836 #
838 def _readVm(self, *args):
839 return xstransact.Read(self.vmpath, *args)
841 def _writeVm(self, *args):
842 return xstransact.Write(self.vmpath, *args)
844 def _removeVm(self, *args):
845 return xstransact.Remove(self.vmpath, *args)
847 def _gatherVm(self, *args):
848 return xstransact.Gather(self.vmpath, *args)
850 def storeVm(self, *args):
851 return xstransact.Store(self.vmpath, *args)
853 #
854 # Function to update xenstore /dom/*
855 #
857 def readDom(self, *args):
858 return xstransact.Read(self.dompath, *args)
860 def gatherDom(self, *args):
861 return xstransact.Gather(self.dompath, *args)
863 def _writeDom(self, *args):
864 return xstransact.Write(self.dompath, *args)
866 def _removeDom(self, *args):
867 return xstransact.Remove(self.dompath, *args)
869 def storeDom(self, *args):
870 return xstransact.Store(self.dompath, *args)
872 def _recreateDom(self):
873 complete(self.dompath, lambda t: self._recreateDomFunc(t))
875 def _recreateDomFunc(self, t):
876 t.remove()
877 t.mkdir()
878 t.set_permissions({'dom' : self.domid})
879 t.write('vm', self.vmpath)
881 def _storeDomDetails(self):
882 to_store = {
883 'domid': str(self.domid),
884 'vm': self.vmpath,
885 'name': self.info['name_label'],
886 'console/limit': str(xoptions.get_console_limit() * 1024),
887 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
888 }
890 def f(n, v):
891 if v is not None:
892 if type(v) == bool:
893 to_store[n] = v and "1" or "0"
894 else:
895 to_store[n] = str(v)
897 f('console/port', self.console_port)
898 f('console/ring-ref', self.console_mfn)
899 f('store/port', self.store_port)
900 f('store/ring-ref', self.store_mfn)
902 if arch.type == "x86":
903 f('control/platform-feature-multiprocessor-suspend', True)
905 # elfnotes
906 for n, v in self.info.get_notes().iteritems():
907 n = n.lower().replace('_', '-')
908 if n == 'features':
909 for v in v.split('|'):
910 v = v.replace('_', '-')
911 if v.startswith('!'):
912 f('image/%s/%s' % (n, v[1:]), False)
913 else:
914 f('image/%s/%s' % (n, v), True)
915 else:
916 f('image/%s' % n, v)
918 if self.info.has_key('security_label'):
919 f('security_label', self.info['security_label'])
921 to_store.update(self._vcpuDomDetails())
923 log.debug("Storing domain details: %s", scrub_password(to_store))
925 self._writeDom(to_store)
927 def _vcpuDomDetails(self):
928 def availability(n):
929 if self.info['vcpu_avail'] & (1 << n):
930 return 'online'
931 else:
932 return 'offline'
934 result = {}
935 for v in range(0, self.info['VCPUs_max']):
936 result["cpu/%d/availability" % v] = availability(v)
937 return result
939 #
940 # xenstore watches
941 #
943 def _registerWatches(self):
944 """Register a watch on this VM's entries in the store, and the
945 domain's control/shutdown node, so that when they are changed
946 externally, we keep up to date. This should only be called by {@link
947 #create}, {@link #recreate}, or {@link #restore}, once the domain's
948 details have been written, but before the new instance is returned."""
949 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
950 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
951 self._handleShutdownWatch)
953 def _storeChanged(self, _):
954 log.trace("XendDomainInfo.storeChanged");
956 changed = False
958 # Check whether values in the configuration have
959 # changed in Xenstore.
961 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
962 'rtc/timeoffset']
964 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
965 for k in cfg_vm])
967 # convert two lists into a python dictionary
968 vm_details = dict(zip(cfg_vm, vm_details))
970 if vm_details['rtc/timeoffset'] == None:
971 vm_details['rtc/timeoffset'] = "0"
973 for arg, val in vm_details.items():
974 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
975 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
976 if val != None and val != self.info[xapiarg]:
977 self.info[xapiarg] = val
978 changed = True
979 elif arg == "memory":
980 if val != None and val != self.info["static_memory_min"]:
981 self.info["static_memory_min"] = val
982 changed = True
983 elif arg == "maxmem":
984 if val != None and val != self.info["static_memory_max"]:
985 self.info["static_memory_max"] = val
986 changed = True
988 # Check whether image definition has been updated
989 image_sxp = self._readVm('image')
990 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
991 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
992 changed = True
994 # Check if the rtc offset has changes
995 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
996 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
997 changed = True
999 if changed:
1000 # Update the domain section of the store, as this contains some
1001 # parameters derived from the VM configuration.
1002 self._storeDomDetails()
1004 return 1
1006 def _handleShutdownWatch(self, _):
1007 log.debug('XendDomainInfo.handleShutdownWatch')
1009 reason = self.readDom('control/shutdown')
1011 if reason and reason != 'suspend':
1012 sst = self.readDom('xend/shutdown_start_time')
1013 now = time.time()
1014 if sst:
1015 self.shutdownStartTime = float(sst)
1016 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1017 else:
1018 self.shutdownStartTime = now
1019 self.storeDom('xend/shutdown_start_time', now)
1020 timeout = SHUTDOWN_TIMEOUT
1022 log.trace(
1023 "Scheduling refreshShutdown on domain %d in %ds.",
1024 self.domid, timeout)
1025 threading.Timer(timeout, self.refreshShutdown).start()
1027 return True
1031 # Public Attributes for the VM
1035 def getDomid(self):
1036 return self.domid
1038 def setName(self, name):
1039 self._checkName(name)
1040 self.info['name_label'] = name
1041 self.storeVm("name", name)
1043 def getName(self):
1044 return self.info['name_label']
1046 def getDomainPath(self):
1047 return self.dompath
1049 def getShutdownReason(self):
1050 return self.readDom('control/shutdown')
1052 def getStorePort(self):
1053 """For use only by image.py and XendCheckpoint.py."""
1054 return self.store_port
1056 def getConsolePort(self):
1057 """For use only by image.py and XendCheckpoint.py"""
1058 return self.console_port
1060 def getFeatures(self):
1061 """For use only by image.py."""
1062 return self.info['features']
1064 def getVCpuCount(self):
1065 return self.info['VCPUs_max']
1067 def setVCpuCount(self, vcpus):
1068 if vcpus <= 0:
1069 raise XendError('Invalid VCPUs')
1071 self.info['vcpu_avail'] = (1 << vcpus) - 1
1072 if self.domid >= 0:
1073 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1074 # update dom differently depending on whether we are adjusting
1075 # vcpu number up or down, otherwise _vcpuDomDetails does not
1076 # disable the vcpus
1077 if self.info['VCPUs_max'] > vcpus:
1078 # decreasing
1079 self._writeDom(self._vcpuDomDetails())
1080 self.info['VCPUs_live'] = vcpus
1081 else:
1082 # same or increasing
1083 self.info['VCPUs_live'] = vcpus
1084 self._writeDom(self._vcpuDomDetails())
1085 else:
1086 self.info['VCPUs_max'] = vcpus
1087 xen.xend.XendDomain.instance().managed_config_save(self)
1088 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1089 vcpus)
1091 def getMemoryTarget(self):
1092 """Get this domain's target memory size, in KB."""
1093 return self.info['memory_dynamic_max'] / 1024
1095 def getMemoryMaximum(self):
1096 """Get this domain's maximum memory size, in KB."""
1097 # remember, info now stores memory in bytes
1098 return self.info['memory_static_max'] / 1024
1100 def getResume(self):
1101 return str(self._resume)
1103 def getCap(self):
1104 return self.info.get('cpu_cap', 0)
1106 def setCap(self, cpu_cap):
1107 self.info['cpu_cap'] = cpu_cap
1109 def getWeight(self):
1110 return self.info.get('cpu_weight', 256)
1112 def setWeight(self, cpu_weight):
1113 self.info['cpu_weight'] = cpu_weight
1115 def setResume(self, state):
1116 self._resume = state
1118 def getRestartCount(self):
1119 return self._readVm('xend/restart_count')
1121 def refreshShutdown(self, xeninfo = None):
1122 """ Checks the domain for whether a shutdown is required.
1124 Called from XendDomainInfo and also image.py for HVM images.
1125 """
1127 # If set at the end of this method, a restart is required, with the
1128 # given reason. This restart has to be done out of the scope of
1129 # refresh_shutdown_lock.
1130 restart_reason = None
1132 self.refresh_shutdown_lock.acquire()
1133 try:
1134 if xeninfo is None:
1135 xeninfo = dom_get(self.domid)
1136 if xeninfo is None:
1137 # The domain no longer exists. This will occur if we have
1138 # scheduled a timer to check for shutdown timeouts and the
1139 # shutdown succeeded. It will also occur if someone
1140 # destroys a domain beneath us. We clean up the domain,
1141 # just in case, but we can't clean up the VM, because that
1142 # VM may have migrated to a different domain on this
1143 # machine.
1144 self.cleanupDomain()
1145 self._stateSet(DOM_STATE_HALTED)
1146 return
1148 if xeninfo['dying']:
1149 # Dying means that a domain has been destroyed, but has not
1150 # yet been cleaned up by Xen. This state could persist
1151 # indefinitely if, for example, another domain has some of its
1152 # pages mapped. We might like to diagnose this problem in the
1153 # future, but for now all we do is make sure that it's not us
1154 # holding the pages, by calling cleanupDomain. We can't
1155 # clean up the VM, as above.
1156 self.cleanupDomain()
1157 self._stateSet(DOM_STATE_SHUTDOWN)
1158 return
1160 elif xeninfo['crashed']:
1161 if self.readDom('xend/shutdown_completed'):
1162 # We've seen this shutdown already, but we are preserving
1163 # the domain for debugging. Leave it alone.
1164 return
1166 log.warn('Domain has crashed: name=%s id=%d.',
1167 self.info['name_label'], self.domid)
1168 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1170 if xoptions.get_enable_dump():
1171 try:
1172 self.dumpCore()
1173 except XendError:
1174 # This error has been logged -- there's nothing more
1175 # we can do in this context.
1176 pass
1178 restart_reason = 'crash'
1179 self._stateSet(DOM_STATE_HALTED)
1181 elif xeninfo['shutdown']:
1182 self._stateSet(DOM_STATE_SHUTDOWN)
1183 if self.readDom('xend/shutdown_completed'):
1184 # We've seen this shutdown already, but we are preserving
1185 # the domain for debugging. Leave it alone.
1186 return
1188 else:
1189 reason = shutdown_reason(xeninfo['shutdown_reason'])
1191 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1192 self.info['name_label'], self.domid, reason)
1193 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1195 self._clearRestart()
1197 if reason == 'suspend':
1198 self._stateSet(DOM_STATE_SUSPENDED)
1199 # Don't destroy the domain. XendCheckpoint will do
1200 # this once it has finished. However, stop watching
1201 # the VM path now, otherwise we will end up with one
1202 # watch for the old domain, and one for the new.
1203 self._unwatchVm()
1204 elif reason in ('poweroff', 'reboot'):
1205 restart_reason = reason
1206 else:
1207 self.destroy()
1209 elif self.dompath is None:
1210 # We have yet to manage to call introduceDomain on this
1211 # domain. This can happen if a restore is in progress, or has
1212 # failed. Ignore this domain.
1213 pass
1214 else:
1215 # Domain is alive. If we are shutting it down, log a message
1216 # if it seems unresponsive.
1217 if xeninfo['paused']:
1218 self._stateSet(DOM_STATE_PAUSED)
1219 else:
1220 self._stateSet(DOM_STATE_RUNNING)
1222 if self.shutdownStartTime:
1223 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1224 self.shutdownStartTime)
1225 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1226 log.info(
1227 "Domain shutdown timeout expired: name=%s id=%s",
1228 self.info['name_label'], self.domid)
1229 self.storeDom('xend/unresponsive', 'True')
1230 finally:
1231 self.refresh_shutdown_lock.release()
1233 if restart_reason:
1234 threading.Thread(target = self._maybeRestart,
1235 args = (restart_reason,)).start()
1239 # Restart functions - handling whether we come back up on shutdown.
1242 def _clearRestart(self):
1243 self._removeDom("xend/shutdown_start_time")
1246 def _maybeRestart(self, reason):
1247 # Dispatch to the correct method based upon the configured on_{reason}
1248 # behaviour.
1249 actions = {"destroy" : self.destroy,
1250 "restart" : self._restart,
1251 "preserve" : self._preserve,
1252 "rename-restart" : self._renameRestart}
1254 action_conf = {
1255 'poweroff': 'actions_after_shutdown',
1256 'reboot': 'actions_after_reboot',
1257 'crash': 'actions_after_crash',
1260 action_target = self.info.get(action_conf.get(reason))
1261 func = actions.get(action_target, None)
1262 if func and callable(func):
1263 func()
1264 else:
1265 self.destroy() # default to destroy
1267 def _renameRestart(self):
1268 self._restart(True)
1270 def _restart(self, rename = False):
1271 """Restart the domain after it has exited.
1273 @param rename True if the old domain is to be renamed and preserved,
1274 False if it is to be destroyed.
1275 """
1276 from xen.xend import XendDomain
1278 if self._readVm(RESTART_IN_PROGRESS):
1279 log.error('Xend failed during restart of domain %s. '
1280 'Refusing to restart to avoid loops.',
1281 str(self.domid))
1282 self.destroy()
1283 return
1285 old_domid = self.domid
1286 self._writeVm(RESTART_IN_PROGRESS, 'True')
1288 now = time.time()
1289 rst = self._readVm('xend/previous_restart_time')
1290 if rst:
1291 rst = float(rst)
1292 timeout = now - rst
1293 if timeout < MINIMUM_RESTART_TIME:
1294 log.error(
1295 'VM %s restarting too fast (%f seconds since the last '
1296 'restart). Refusing to restart to avoid loops.',
1297 self.info['name_label'], timeout)
1298 self.destroy()
1299 return
1301 self._writeVm('xend/previous_restart_time', str(now))
1303 try:
1304 if rename:
1305 self._preserveForRestart()
1306 else:
1307 self._unwatchVm()
1308 self.destroyDomain()
1310 # new_dom's VM will be the same as this domain's VM, except where
1311 # the rename flag has instructed us to call preserveForRestart.
1312 # In that case, it is important that we remove the
1313 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1314 # once the new one is available.
1316 new_dom = None
1317 try:
1318 new_dom = XendDomain.instance().domain_create_from_dict(
1319 self.info)
1320 new_dom.waitForDevices()
1321 new_dom.unpause()
1322 rst_cnt = self._readVm('xend/restart_count')
1323 rst_cnt = int(rst_cnt) + 1
1324 self._writeVm('xend/restart_count', str(rst_cnt))
1325 new_dom._removeVm(RESTART_IN_PROGRESS)
1326 except:
1327 if new_dom:
1328 new_dom._removeVm(RESTART_IN_PROGRESS)
1329 new_dom.destroy()
1330 else:
1331 self._removeVm(RESTART_IN_PROGRESS)
1332 raise
1333 except:
1334 log.exception('Failed to restart domain %s.', str(old_domid))
1336 def _preserveForRestart(self):
1337 """Preserve a domain that has been shut down, by giving it a new UUID,
1338 cloning the VM details, and giving it a new name. This allows us to
1339 keep this domain for debugging, but restart a new one in its place
1340 preserving the restart semantics (name and UUID preserved).
1341 """
1343 new_uuid = uuid.createString()
1344 new_name = 'Domain-%s' % new_uuid
1345 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1346 self.info['name_label'], self.domid, self.info['uuid'],
1347 new_name, new_uuid)
1348 self._unwatchVm()
1349 self._releaseDevices()
1350 self.info['name_label'] = new_name
1351 self.info['uuid'] = new_uuid
1352 self.vmpath = XS_VMROOT + new_uuid
1353 self._storeVmDetails()
1354 self._preserve()
1357 def _preserve(self):
1358 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1359 self.domid)
1360 self._unwatchVm()
1361 self.storeDom('xend/shutdown_completed', 'True')
1362 self._stateSet(DOM_STATE_HALTED)
1365 # Debugging ..
1368 def dumpCore(self, corefile = None):
1369 """Create a core dump for this domain.
1371 @raise: XendError if core dumping failed.
1372 """
1374 try:
1375 if not corefile:
1376 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1377 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1378 self.info['name_label'], self.domid)
1380 if os.path.isdir(corefile):
1381 raise XendError("Cannot dump core in a directory: %s" %
1382 corefile)
1384 xc.domain_dumpcore(self.domid, corefile)
1385 except RuntimeError, ex:
1386 corefile_incomp = corefile+'-incomplete'
1387 os.rename(corefile, corefile_incomp)
1388 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1389 self.domid, self.info['name_label'])
1390 raise XendError("Failed to dump core: %s" % str(ex))
1393 # Device creation/deletion functions
1396 def _createDevice(self, deviceClass, devConfig):
1397 return self.getDeviceController(deviceClass).createDevice(devConfig)
1399 def _waitForDevice(self, deviceClass, devid):
1400 return self.getDeviceController(deviceClass).waitForDevice(devid)
1402 def _waitForDeviceUUID(self, dev_uuid):
1403 deviceClass, config = self.info['devices'].get(dev_uuid)
1404 self._waitForDevice(deviceClass, config['devid'])
1406 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1407 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1408 devid, backpath)
1410 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1411 return self.getDeviceController(deviceClass).reconfigureDevice(
1412 devid, devconfig)
1414 def _createDevices(self):
1415 """Create the devices for a vm.
1417 @raise: VmError for invalid devices
1418 """
1419 ordered_refs = self.info.ordered_device_refs()
1420 for dev_uuid in ordered_refs:
1421 devclass, config = self.info['devices'][dev_uuid]
1422 if devclass in XendDevices.valid_devices():
1423 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1424 dev_uuid = config.get('uuid')
1425 devid = self._createDevice(devclass, config)
1427 # store devid in XendConfig for caching reasons
1428 if dev_uuid in self.info['devices']:
1429 self.info['devices'][dev_uuid][1]['devid'] = devid
1431 if self.image:
1432 self.image.createDeviceModel()
1434 def _releaseDevices(self, suspend = False):
1435 """Release all domain's devices. Nothrow guarantee."""
1436 if suspend and self.image:
1437 self.image.destroy(suspend)
1438 return
1440 t = xstransact("%s/device" % self.dompath)
1441 for devclass in XendDevices.valid_devices():
1442 for dev in t.list(devclass):
1443 try:
1444 log.debug("Removing %s", dev);
1445 self.destroyDevice(devclass, dev, False);
1446 except:
1447 # Log and swallow any exceptions in removal --
1448 # there's nothing more we can do.
1449 log.exception("Device release failed: %s; %s; %s",
1450 self.info['name_label'], devclass, dev)
1454 def getDeviceController(self, name):
1455 """Get the device controller for this domain, and if it
1456 doesn't exist, create it.
1458 @param name: device class name
1459 @type name: string
1460 @rtype: subclass of DevController
1461 """
1462 if name not in self._deviceControllers:
1463 devController = XendDevices.make_controller(name, self)
1464 if not devController:
1465 raise XendError("Unknown device type: %s" % name)
1466 self._deviceControllers[name] = devController
1468 return self._deviceControllers[name]
1471 # Migration functions (public)
1474 def testMigrateDevices(self, network, dst):
1475 """ Notify all device about intention of migration
1476 @raise: XendError for a device that cannot be migrated
1477 """
1478 for (n, c) in self.info.all_devices_sxpr():
1479 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1480 if rc != 0:
1481 raise XendError("Device of type '%s' refuses migration." % n)
1483 def migrateDevices(self, network, dst, step, domName=''):
1484 """Notify the devices about migration
1485 """
1486 ctr = 0
1487 try:
1488 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1489 self.migrateDevice(dev_type, dev_conf, network, dst,
1490 step, domName)
1491 ctr = ctr + 1
1492 except:
1493 for dev_type, dev_conf in self.info.all_devices_sxpr():
1494 if ctr == 0:
1495 step = step - 1
1496 ctr = ctr - 1
1497 self._recoverMigrateDevice(dev_type, dev_conf, network,
1498 dst, step, domName)
1499 raise
1501 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1502 step, domName=''):
1503 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1504 network, dst, step, domName)
1506 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1507 dst, step, domName=''):
1508 return self.getDeviceController(deviceClass).recover_migrate(
1509 deviceConfig, network, dst, step, domName)
1512 ## private:
1514 def _constructDomain(self):
1515 """Construct the domain.
1517 @raise: VmError on error
1518 """
1520 log.debug('XendDomainInfo.constructDomain')
1522 self.shutdownStartTime = None
1524 hvm = self.info.is_hvm()
1525 if hvm:
1526 info = xc.xeninfo()
1527 if 'hvm' not in info['xen_caps']:
1528 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1529 "supported by your CPU and enabled in your "
1530 "BIOS?")
1532 # Hack to pre-reserve some memory for initial domain creation.
1533 # There is an implicit memory overhead for any domain creation. This
1534 # overhead is greater for some types of domain than others. For
1535 # example, an x86 HVM domain will have a default shadow-pagetable
1536 # allocation of 1MB. We free up 2MB here to be on the safe side.
1537 balloon.free(2*1024) # 2MB should be plenty
1539 ssidref = 0
1540 if security.on():
1541 ssidref = security.calc_dom_ssidref_from_info(self.info)
1542 if security.has_authorization(ssidref) == False:
1543 raise VmError("VM is not authorized to run.")
1545 try:
1546 self.domid = xc.domain_create(
1547 domid = 0,
1548 ssidref = ssidref,
1549 handle = uuid.fromString(self.info['uuid']),
1550 hvm = int(hvm))
1551 except Exception, e:
1552 # may get here if due to ACM the operation is not permitted
1553 if security.on():
1554 raise VmError('Domain in conflict set with running domain?')
1556 if self.domid < 0:
1557 raise VmError('Creating domain failed: name=%s' %
1558 self.info['name_label'])
1560 self.dompath = GetDomainPath(self.domid)
1562 self._recreateDom()
1564 # Set maximum number of vcpus in domain
1565 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1567 # register the domain in the list
1568 from xen.xend import XendDomain
1569 XendDomain.instance().add_domain(self)
1571 def _introduceDomain(self):
1572 assert self.domid is not None
1573 assert self.store_mfn is not None
1574 assert self.store_port is not None
1576 try:
1577 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1578 except RuntimeError, exn:
1579 raise XendError(str(exn))
1582 def _initDomain(self):
1583 log.debug('XendDomainInfo.initDomain: %s %s',
1584 self.domid,
1585 self.info['cpu_weight'])
1587 self._configureBootloader()
1589 try:
1590 self.image = image.create(self, self.info)
1592 if self.info['platform'].get('localtime', 0):
1593 xc.domain_set_time_offset(self.domid)
1595 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1597 # repin domain vcpus if a restricted cpus list is provided
1598 # this is done prior to memory allocation to aide in memory
1599 # distribution for NUMA systems.
1600 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1601 for v in range(0, self.info['VCPUs_max']):
1602 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1604 # Use architecture- and image-specific calculations to determine
1605 # the various headrooms necessary, given the raw configured
1606 # values. maxmem, memory, and shadow are all in KiB.
1607 # but memory_static_max etc are all stored in bytes now.
1608 memory = self.image.getRequiredAvailableMemory(
1609 self.info['memory_dynamic_max'] / 1024)
1610 maxmem = self.image.getRequiredAvailableMemory(
1611 self.info['memory_static_max'] / 1024)
1612 shadow = self.image.getRequiredShadowMemory(
1613 self.info['shadow_memory'] * 1024,
1614 self.info['memory_static_max'] / 1024)
1616 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'],)
1617 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1618 # takes MiB and we must not round down and end up under-providing.
1619 shadow = ((shadow + 1023) / 1024) * 1024
1621 # set memory limit
1622 xc.domain_setmaxmem(self.domid, maxmem)
1624 # Make sure there's enough RAM available for the domain
1625 balloon.free(memory + shadow)
1627 # Set up the shadow memory
1628 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1629 self.info['shadow_memory'] = shadow_cur
1631 self._createChannels()
1633 channel_details = self.image.createImage()
1635 self.store_mfn = channel_details['store_mfn']
1636 if 'console_mfn' in channel_details:
1637 self.console_mfn = channel_details['console_mfn']
1638 if 'notes' in channel_details:
1639 self.info.set_notes(channel_details['notes'])
1640 if 'native_protocol' in channel_details:
1641 self.native_protocol = channel_details['native_protocol'];
1643 self._introduceDomain()
1645 self._createDevices()
1647 self.image.cleanupBootloading()
1649 self.info['start_time'] = time.time()
1651 self._stateSet(DOM_STATE_RUNNING)
1652 except VmError, exn:
1653 log.exception("XendDomainInfo.initDomain: exception occurred")
1654 if self.image:
1655 self.image.cleanupBootloading()
1656 raise exn
1657 except RuntimeError, exn:
1658 log.exception("XendDomainInfo.initDomain: exception occurred")
1659 if self.image:
1660 self.image.cleanupBootloading()
1661 raise VmError(str(exn))
1664 def cleanupDomain(self):
1665 """Cleanup domain resources; release devices. Idempotent. Nothrow
1666 guarantee."""
1668 self.refresh_shutdown_lock.acquire()
1669 try:
1670 self.unwatchShutdown()
1671 self._releaseDevices()
1672 bootloader_tidy(self)
1674 if self.image:
1675 try:
1676 self.image.destroy()
1677 except:
1678 log.exception(
1679 "XendDomainInfo.cleanup: image.destroy() failed.")
1680 self.image = None
1682 try:
1683 self._removeDom()
1684 except:
1685 log.exception("Removing domain path failed.")
1687 self._stateSet(DOM_STATE_HALTED)
1688 self.domid = None # Do not push into _stateSet()!
1689 finally:
1690 self.refresh_shutdown_lock.release()
1693 def unwatchShutdown(self):
1694 """Remove the watch on the domain's control/shutdown node, if any.
1695 Idempotent. Nothrow guarantee. Expects to be protected by the
1696 refresh_shutdown_lock."""
1698 try:
1699 try:
1700 if self.shutdownWatch:
1701 self.shutdownWatch.unwatch()
1702 finally:
1703 self.shutdownWatch = None
1704 except:
1705 log.exception("Unwatching control/shutdown failed.")
1707 def waitForShutdown(self):
1708 self.state_updated.acquire()
1709 try:
1710 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1711 self.state_updated.wait()
1712 finally:
1713 self.state_updated.release()
1716 # TODO: recategorise - called from XendCheckpoint
1719 def completeRestore(self, store_mfn, console_mfn):
1721 log.debug("XendDomainInfo.completeRestore")
1723 self.store_mfn = store_mfn
1724 self.console_mfn = console_mfn
1726 self._introduceDomain()
1727 if self.info.is_hvm():
1728 self.image = image.create(self, self.info)
1729 if self.image:
1730 self.image.createDeviceModel(True)
1731 self._storeDomDetails()
1732 self._registerWatches()
1733 self.refreshShutdown()
1735 log.debug("XendDomainInfo.completeRestore done")
1738 def _endRestore(self):
1739 self.setResume(False)
1742 # VM Destroy
1745 def _prepare_phantom_paths(self):
1746 # get associated devices to destroy
1747 # build list of phantom devices to be removed after normal devices
1748 plist = []
1749 if self.domid is not None:
1750 from xen.xend.xenstore.xstransact import xstransact
1751 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1752 for dev in t.list():
1753 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1754 % (self.dompath, dev))
1755 if backend_phantom_vbd is not None:
1756 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1757 % backend_phantom_vbd)
1758 plist.append(backend_phantom_vbd)
1759 plist.append(frontend_phantom_vbd)
1760 return plist
1762 def _cleanup_phantom_devs(self, plist):
1763 # remove phantom devices
1764 if not plist == []:
1765 time.sleep(2)
1766 for paths in plist:
1767 if paths.find('backend') != -1:
1768 from xen.xend.server import DevController
1769 # Modify online status /before/ updating state (latter is watched by
1770 # drivers, so this ordering avoids a race).
1771 xstransact.Write(paths, 'online', "0")
1772 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1773 # force
1774 xstransact.Remove(paths)
1776 def destroy(self):
1777 """Cleanup VM and destroy domain. Nothrow guarantee."""
1779 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1781 paths = self._prepare_phantom_paths()
1783 self._cleanupVm()
1784 if self.dompath is not None:
1785 if self.domid is not None:
1786 xc.domain_destroy_hook(self.domid)
1787 self.destroyDomain()
1789 self._cleanup_phantom_devs(paths)
1791 if "transient" in self.info["other_config"] \
1792 and bool(self.info["other_config"]["transient"]):
1793 from xen.xend import XendDomain
1794 XendDomain.instance().domain_delete_by_dominfo(self)
1797 def destroyDomain(self):
1798 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1800 paths = self._prepare_phantom_paths()
1802 try:
1803 if self.domid is not None:
1804 xc.domain_destroy(self.domid)
1805 for state in DOM_STATES_OLD:
1806 self.info[state] = 0
1807 self._stateSet(DOM_STATE_HALTED)
1808 except:
1809 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1811 from xen.xend import XendDomain
1812 XendDomain.instance().remove_domain(self)
1814 self.cleanupDomain()
1815 self._cleanup_phantom_devs(paths)
1818 def resumeDomain(self):
1819 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1821 if self.domid is None:
1822 return
1823 try:
1824 # could also fetch a parsed note from xenstore
1825 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1826 if not fast:
1827 self._releaseDevices()
1828 self.testDeviceComplete()
1829 self.testvifsComplete()
1830 log.debug("XendDomainInfo.resumeDomain: devices released")
1832 self._resetChannels()
1834 self._removeDom('control/shutdown')
1835 self._removeDom('device-misc/vif/nextDeviceID')
1837 self._createChannels()
1838 self._introduceDomain()
1839 self._storeDomDetails()
1841 self._createDevices()
1842 log.debug("XendDomainInfo.resumeDomain: devices created")
1844 xc.domain_resume(self.domid, fast)
1845 ResumeDomain(self.domid)
1846 except:
1847 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1848 if self.is_hvm():
1849 self.image.resumeDeviceModel()
1853 # Channels for xenstore and console
1856 def _createChannels(self):
1857 """Create the channels to the domain.
1858 """
1859 self.store_port = self._createChannel()
1860 self.console_port = self._createChannel()
1863 def _createChannel(self):
1864 """Create an event channel to the domain.
1865 """
1866 try:
1867 if self.domid != None:
1868 return xc.evtchn_alloc_unbound(domid = self.domid,
1869 remote_dom = 0)
1870 except:
1871 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1872 raise
1874 def _resetChannels(self):
1875 """Reset all event channels in the domain.
1876 """
1877 try:
1878 if self.domid != None:
1879 return xc.evtchn_reset(dom = self.domid)
1880 except:
1881 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1882 raise
1886 # Bootloader configuration
1889 def _configureBootloader(self):
1890 """Run the bootloader if we're configured to do so."""
1892 blexec = self.info['PV_bootloader']
1893 bootloader_args = self.info['PV_bootloader_args']
1894 kernel = self.info['PV_kernel']
1895 ramdisk = self.info['PV_ramdisk']
1896 args = self.info['PV_args']
1897 boot = self.info['HVM_boot_policy']
1899 if boot:
1900 # HVM booting.
1901 pass
1902 elif not blexec and kernel:
1903 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1904 # will be picked up by image.py.
1905 pass
1906 else:
1907 # Boot using bootloader
1908 if not blexec or blexec == 'pygrub':
1909 blexec = osdep.pygrub_path
1911 blcfg = None
1912 disks = [x for x in self.info['vbd_refs']
1913 if self.info['devices'][x][1]['bootable']]
1915 if not disks:
1916 msg = "Had a bootloader specified, but no disks are bootable"
1917 log.error(msg)
1918 raise VmError(msg)
1920 devinfo = self.info['devices'][disks[0]]
1921 devtype = devinfo[0]
1922 disk = devinfo[1]['uname']
1924 fn = blkdev_uname_to_file(disk)
1925 taptype = blkdev_uname_to_taptype(disk)
1926 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1927 if mounted:
1928 # This is a file, not a device. pygrub can cope with a
1929 # file if it's raw, but if it's QCOW or other such formats
1930 # used through blktap, then we need to mount it first.
1932 log.info("Mounting %s on %s." %
1933 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1935 vbd = {
1936 'mode': 'RO',
1937 'device': BOOTLOADER_LOOPBACK_DEVICE,
1940 from xen.xend import XendDomain
1941 dom0 = XendDomain.instance().privilegedDomain()
1942 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1943 fn = BOOTLOADER_LOOPBACK_DEVICE
1945 try:
1946 blcfg = bootloader(blexec, fn, self, False,
1947 bootloader_args, kernel, ramdisk, args)
1948 finally:
1949 if mounted:
1950 log.info("Unmounting %s from %s." %
1951 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1953 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1955 if blcfg is None:
1956 msg = "Had a bootloader specified, but can't find disk"
1957 log.error(msg)
1958 raise VmError(msg)
1960 self.info.update_with_image_sxp(blcfg, True)
1964 # VM Functions
1967 def _readVMDetails(self, params):
1968 """Read the specified parameters from the store.
1969 """
1970 try:
1971 return self._gatherVm(*params)
1972 except ValueError:
1973 # One of the int/float entries in params has a corresponding store
1974 # entry that is invalid. We recover, because older versions of
1975 # Xend may have put the entry there (memory/target, for example),
1976 # but this is in general a bad situation to have reached.
1977 log.exception(
1978 "Store corrupted at %s! Domain %d's configuration may be "
1979 "affected.", self.vmpath, self.domid)
1980 return []
1982 def _cleanupVm(self):
1983 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
1985 self._unwatchVm()
1987 try:
1988 self._removeVm()
1989 except:
1990 log.exception("Removing VM path failed.")
1993 def checkLiveMigrateMemory(self):
1994 """ Make sure there's enough memory to migrate this domain """
1995 overhead_kb = 0
1996 if arch.type == "x86":
1997 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
1998 # the minimum that Xen would allocate if no value were given.
1999 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2000 (self.info['memory_static_max'] / 1024 / 1024) * 4
2001 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2002 # The domain might already have some shadow memory
2003 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2004 if overhead_kb > 0:
2005 balloon.free(overhead_kb)
2007 def _unwatchVm(self):
2008 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2009 guarantee."""
2010 try:
2011 try:
2012 if self.vmWatch:
2013 self.vmWatch.unwatch()
2014 finally:
2015 self.vmWatch = None
2016 except:
2017 log.exception("Unwatching VM path failed.")
2019 def testDeviceComplete(self):
2020 """ For Block IO migration safety we must ensure that
2021 the device has shutdown correctly, i.e. all blocks are
2022 flushed to disk
2023 """
2024 start = time.time()
2025 while True:
2026 test = 0
2027 diff = time.time() - start
2028 for i in self.getDeviceController('vbd').deviceIDs():
2029 test = 1
2030 log.info("Dev %s still active, looping...", i)
2031 time.sleep(0.1)
2033 if test == 0:
2034 break
2035 if diff >= MIGRATE_TIMEOUT:
2036 log.info("Dev still active but hit max loop timeout")
2037 break
2039 def testvifsComplete(self):
2040 """ In case vifs are released and then created for the same
2041 domain, we need to wait the device shut down.
2042 """
2043 start = time.time()
2044 while True:
2045 test = 0
2046 diff = time.time() - start
2047 for i in self.getDeviceController('vif').deviceIDs():
2048 test = 1
2049 log.info("Dev %s still active, looping...", i)
2050 time.sleep(0.1)
2052 if test == 0:
2053 break
2054 if diff >= MIGRATE_TIMEOUT:
2055 log.info("Dev still active but hit max loop timeout")
2056 break
2058 def _storeVmDetails(self):
2059 to_store = {}
2061 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2062 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2063 if self._infoIsSet(info_key):
2064 to_store[key] = str(self.info[info_key])
2066 if self._infoIsSet("static_memory_min"):
2067 to_store["memory"] = str(self.info["static_memory_min"])
2068 if self._infoIsSet("static_memory_max"):
2069 to_store["maxmem"] = str(self.info["static_memory_max"])
2071 image_sxpr = self.info.image_sxpr()
2072 if image_sxpr:
2073 to_store['image'] = sxp.to_string(image_sxpr)
2075 if not self._readVm('xend/restart_count'):
2076 to_store['xend/restart_count'] = str(0)
2078 log.debug("Storing VM details: %s", scrub_password(to_store))
2080 self._writeVm(to_store)
2081 self._setVmPermissions()
2084 def _setVmPermissions(self):
2085 """Allow the guest domain to read its UUID. We don't allow it to
2086 access any other entry, for security."""
2087 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2088 { 'dom' : self.domid,
2089 'read' : True,
2090 'write' : False })
2093 # Utility functions
2096 def __getattr__(self, name):
2097 if name == "state":
2098 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2099 log.warn("".join(traceback.format_stack()))
2100 return self._stateGet()
2101 else:
2102 raise AttributeError()
2104 def __setattr__(self, name, value):
2105 if name == "state":
2106 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2107 log.warn("".join(traceback.format_stack()))
2108 self._stateSet(value)
2109 else:
2110 self.__dict__[name] = value
2112 def _stateSet(self, state):
2113 self.state_updated.acquire()
2114 try:
2115 # TODO Not sure this is correct...
2116 # _stateGet is live now. Why not fire event
2117 # even when it hasn't changed?
2118 if self._stateGet() != state:
2119 self.state_updated.notifyAll()
2120 import XendAPI
2121 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2122 'power_state')
2123 finally:
2124 self.state_updated.release()
2126 def _stateGet(self):
2127 # Lets try and reconsitute the state from xc
2128 # first lets try and get the domain info
2129 # from xc - this will tell us if the domain
2130 # exists
2131 info = dom_get(self.getDomid())
2132 if info is None or info['shutdown']:
2133 # We are either HALTED or SUSPENDED
2134 # check saved image exists
2135 from xen.xend import XendDomain
2136 managed_config_path = \
2137 XendDomain.instance()._managed_check_point_path( \
2138 self.get_uuid())
2139 if os.path.exists(managed_config_path):
2140 return XEN_API_VM_POWER_STATE_SUSPENDED
2141 else:
2142 return XEN_API_VM_POWER_STATE_HALTED
2143 else:
2144 # We are either RUNNING or PAUSED
2145 if info['paused']:
2146 return XEN_API_VM_POWER_STATE_PAUSED
2147 else:
2148 return XEN_API_VM_POWER_STATE_RUNNING
2150 def _infoIsSet(self, name):
2151 return name in self.info and self.info[name] is not None
2153 def _checkName(self, name):
2154 """Check if a vm name is valid. Valid names contain alphabetic
2155 characters, digits, or characters in '_-.:/+'.
2156 The same name cannot be used for more than one vm at the same time.
2158 @param name: name
2159 @raise: VmError if invalid
2160 """
2161 from xen.xend import XendDomain
2163 if name is None or name == '':
2164 raise VmError('Missing VM Name')
2166 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2167 raise VmError('Invalid VM Name')
2169 dom = XendDomain.instance().domain_lookup_nr(name)
2170 if dom and dom.domid != self.domid:
2171 raise VmError("VM name '%s' already exists%s" %
2172 (name,
2173 dom.domid is not None and
2174 (" as domain %s" % str(dom.domid)) or ""))
2177 def update(self, info = None, refresh = True):
2178 """Update with info from xc.domain_getinfo().
2179 """
2180 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2181 str(self.domid))
2183 if not info:
2184 info = dom_get(self.domid)
2185 if not info:
2186 return
2188 if info["maxmem_kb"] < 0:
2189 info["maxmem_kb"] = XendNode.instance() \
2190 .physinfo_dict()['total_memory'] * 1024
2192 #ssidref field not used any longer
2193 if 'ssidref' in info:
2194 info.pop('ssidref')
2196 # make sure state is reset for info
2197 # TODO: we should eventually get rid of old_dom_states
2199 self.info.update_config(info)
2200 self._update_consoles()
2202 if refresh:
2203 self.refreshShutdown(info)
2205 log.trace("XendDomainInfo.update done on domain %s: %s",
2206 str(self.domid), self.info)
2208 def sxpr(self, ignore_store = False, legacy_only = True):
2209 result = self.info.to_sxp(domain = self,
2210 ignore_devices = ignore_store,
2211 legacy_only = legacy_only)
2213 #if not ignore_store and self.dompath:
2214 # vnc_port = self.readDom('console/vnc-port')
2215 # if vnc_port is not None:
2216 # result.append(['device',
2217 # ['console', ['vnc-port', str(vnc_port)]]])
2219 return result
2221 # Xen API
2222 # ----------------------------------------------------------------
2224 def get_uuid(self):
2225 dom_uuid = self.info.get('uuid')
2226 if not dom_uuid: # if it doesn't exist, make one up
2227 dom_uuid = uuid.createString()
2228 self.info['uuid'] = dom_uuid
2229 return dom_uuid
2231 def get_memory_static_max(self):
2232 return self.info.get('memory_static_max', 0)
2233 def get_memory_static_min(self):
2234 return self.info.get('memory_static_min', 0)
2235 def get_memory_dynamic_max(self):
2236 return self.info.get('memory_dynamic_max', 0)
2237 def get_memory_dynamic_min(self):
2238 return self.info.get('memory_dynamic_min', 0)
2240 # only update memory-related config values if they maintain sanity
2241 def _safe_set_memory(self, key, newval):
2242 oldval = self.info.get(key, 0)
2243 try:
2244 self.info[key] = newval
2245 self.info._memory_sanity_check()
2246 except Exception, ex:
2247 self.info[key] = oldval
2248 raise
2250 def set_memory_static_max(self, val):
2251 self._safe_set_memory('memory_static_max', val)
2252 def set_memory_static_min(self, val):
2253 self._safe_set_memory('memory_static_min', val)
2254 def set_memory_dynamic_max(self, val):
2255 self._safe_set_memory('memory_dynamic_max', val)
2256 def set_memory_dynamic_min(self, val):
2257 self._safe_set_memory('memory_dynamic_min', val)
2259 def get_vcpus_params(self):
2260 if self.getDomid() is None:
2261 return self.info['vcpus_params']
2263 retval = xc.sched_credit_domain_get(self.getDomid())
2264 return retval
2265 def get_power_state(self):
2266 return XEN_API_VM_POWER_STATE[self._stateGet()]
2267 def get_platform(self):
2268 return self.info.get('platform', {})
2269 def get_pci_bus(self):
2270 return self.info.get('pci_bus', '')
2271 def get_tools_version(self):
2272 return self.info.get('tools_version', {})
2273 def get_metrics(self):
2274 return self.metrics.get_uuid();
2277 def get_security_label(self, xspol=None):
2278 """
2279 Get the security label of a domain
2280 @param xspol The policy to use when converting the ssid into
2281 a label; only to be passed during the updating
2282 of the policy
2283 """
2284 domid = self.getDomid()
2286 if not xspol:
2287 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2288 xspol = XSPolicyAdminInstance().get_loaded_policy()
2290 if domid == 0:
2291 if xspol:
2292 label = xspol.policy_get_domain_label_formatted(domid)
2293 else:
2294 label = ""
2295 else:
2296 label = self.info.get('security_label', '')
2297 return label
2299 def set_security_label(self, seclab, old_seclab, xspol=None,
2300 xspol_old=None):
2301 """
2302 Set the security label of a domain from its old to
2303 a new value.
2304 @param seclab New security label formatted in the form
2305 <policy type>:<policy name>:<vm label>
2306 @param old_seclab The current security label that the
2307 VM must have.
2308 @param xspol An optional policy under which this
2309 update should be done. If not given,
2310 then the current active policy is used.
2311 @param xspol_old The old policy; only to be passed during
2312 the updating of a policy
2313 @return Returns return code, a string with errors from
2314 the hypervisor's operation, old label of the
2315 domain
2316 """
2317 rc = 0
2318 errors = ""
2319 old_label = ""
2320 new_ssidref = 0
2321 domid = self.getDomid()
2322 res_labels = None
2323 is_policy_update = (xspol_old != None)
2325 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2326 from xen.util import xsconstants
2328 state = self._stateGet()
2329 # Relabel only HALTED or RUNNING or PAUSED domains
2330 if domid != 0 and \
2331 state not in \
2332 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2333 DOM_STATE_SUSPENDED ]:
2334 log.warn("Relabeling domain not possible in state '%s'" %
2335 DOM_STATES[state])
2336 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2338 # Remove security label. Works only for halted domains
2339 if not seclab or seclab == "":
2340 if state not in [ DOM_STATE_HALTED ]:
2341 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2343 if self.info.has_key('security_label'):
2344 old_label = self.info['security_label']
2345 # Check label against expected one.
2346 if old_label != old_seclab:
2347 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2348 del self.info['security_label']
2349 xen.xend.XendDomain.instance().managed_config_save(self)
2350 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2352 tmp = seclab.split(":")
2353 if len(tmp) != 3:
2354 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2355 typ, policy, label = tmp
2357 poladmin = XSPolicyAdminInstance()
2358 if not xspol:
2359 xspol = poladmin.get_policy_by_name(policy)
2361 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2362 #if domain is running or paused try to relabel in hypervisor
2363 if not xspol:
2364 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2366 if typ != xspol.get_type_name() or \
2367 policy != xspol.get_name():
2368 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2370 if typ == xsconstants.ACM_POLICY_ID:
2371 new_ssidref = xspol.vmlabel_to_ssidref(label)
2372 if new_ssidref == xsconstants.INVALID_SSIDREF:
2373 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2375 # Check that all used resources are accessible under the
2376 # new label
2377 if not is_policy_update and \
2378 not security.resources_compatible_with_vmlabel(xspol,
2379 self, label):
2380 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2382 #Check label against expected one.
2383 old_label = self.get_security_label(xspol_old)
2384 if old_label != old_seclab:
2385 log.info("old_label != old_seclab: %s != %s" %
2386 (old_label, old_seclab))
2387 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2389 # relabel domain in the hypervisor
2390 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2391 log.info("rc from relabeling in HV: %d" % rc)
2392 else:
2393 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2395 if rc == 0:
2396 # HALTED, RUNNING or PAUSED
2397 if domid == 0:
2398 if xspol:
2399 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2400 else:
2401 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2402 else:
2403 if self.info.has_key('security_label'):
2404 old_label = self.info['security_label']
2405 # Check label against expected one, unless wildcard
2406 if old_label != old_seclab:
2407 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2409 self.info['security_label'] = seclab
2410 try:
2411 xen.xend.XendDomain.instance().managed_config_save(self)
2412 except:
2413 pass
2414 return (rc, errors, old_label, new_ssidref)
2416 def get_on_shutdown(self):
2417 after_shutdown = self.info.get('actions_after_shutdown')
2418 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2419 return XEN_API_ON_NORMAL_EXIT[-1]
2420 return after_shutdown
2422 def get_on_reboot(self):
2423 after_reboot = self.info.get('actions_after_reboot')
2424 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2425 return XEN_API_ON_NORMAL_EXIT[-1]
2426 return after_reboot
2428 def get_on_suspend(self):
2429 # TODO: not supported
2430 after_suspend = self.info.get('actions_after_suspend')
2431 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2432 return XEN_API_ON_NORMAL_EXIT[-1]
2433 return after_suspend
2435 def get_on_crash(self):
2436 after_crash = self.info.get('actions_after_crash')
2437 if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2438 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2439 return after_crash
2441 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2442 """ Get's a device configuration either from XendConfig or
2443 from the DevController.
2445 @param dev_class: device class, either, 'vbd' or 'vif'
2446 @param dev_uuid: device UUID
2448 @rtype: dictionary
2449 """
2450 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2452 # shortcut if the domain isn't started because
2453 # the devcontrollers will have no better information
2454 # than XendConfig.
2455 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2456 if dev_config:
2457 return copy.deepcopy(dev_config)
2458 return None
2460 # instead of using dev_class, we use the dev_type
2461 # that is from XendConfig.
2462 controller = self.getDeviceController(dev_type)
2463 if not controller:
2464 return None
2466 all_configs = controller.getAllDeviceConfigurations()
2467 if not all_configs:
2468 return None
2470 updated_dev_config = copy.deepcopy(dev_config)
2471 for _devid, _devcfg in all_configs.items():
2472 if _devcfg.get('uuid') == dev_uuid:
2473 updated_dev_config.update(_devcfg)
2474 updated_dev_config['id'] = _devid
2475 return updated_dev_config
2477 return updated_dev_config
2479 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2480 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2481 if not config:
2482 return {}
2484 config['VM'] = self.get_uuid()
2486 if dev_class == 'vif':
2487 if not config.has_key('name'):
2488 config['name'] = config.get('vifname', '')
2489 if not config.has_key('MAC'):
2490 config['MAC'] = config.get('mac', '')
2491 if not config.has_key('type'):
2492 config['type'] = 'paravirtualised'
2493 if not config.has_key('device'):
2494 devid = config.get('id')
2495 if devid != None:
2496 config['device'] = 'eth%d' % devid
2497 else:
2498 config['device'] = ''
2500 if not config.has_key('network'):
2501 try:
2502 bridge = config.get('bridge', None)
2503 if bridge is None:
2504 from xen.util import Brctl
2505 if_to_br = dict([(i,b)
2506 for (b,ifs) in Brctl.get_state().items()
2507 for i in ifs])
2508 vifname = "vif%s.%s" % (self.getDomid(),
2509 config.get('id'))
2510 bridge = if_to_br.get(vifname, None)
2511 config['network'] = \
2512 XendNode.instance().bridge_to_network(
2513 config.get('bridge')).get_uuid()
2514 except Exception:
2515 log.exception('bridge_to_network')
2516 # Ignore this for now -- it may happen if the device
2517 # has been specified using the legacy methods, but at
2518 # some point we're going to have to figure out how to
2519 # handle that properly.
2521 config['MTU'] = 1500 # TODO
2523 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2524 xennode = XendNode.instance()
2525 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2526 config['io_read_kbs'] = rx_bps/1024
2527 config['io_write_kbs'] = tx_bps/1024
2528 else:
2529 config['io_read_kbs'] = 0.0
2530 config['io_write_kbs'] = 0.0
2532 config['security_label'] = config.get('security_label', '')
2534 if dev_class == 'vbd':
2536 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2537 controller = self.getDeviceController(dev_class)
2538 devid, _1, _2 = controller.getDeviceDetails(config)
2539 xennode = XendNode.instance()
2540 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2541 config['io_read_kbs'] = rd_blkps
2542 config['io_write_kbs'] = wr_blkps
2543 else:
2544 config['io_read_kbs'] = 0.0
2545 config['io_write_kbs'] = 0.0
2547 config['VDI'] = config.get('VDI', '')
2548 config['device'] = config.get('dev', '')
2549 if ':' in config['device']:
2550 vbd_name, vbd_type = config['device'].split(':', 1)
2551 config['device'] = vbd_name
2552 if vbd_type == 'cdrom':
2553 config['type'] = XEN_API_VBD_TYPE[0]
2554 else:
2555 config['type'] = XEN_API_VBD_TYPE[1]
2557 config['driver'] = 'paravirtualised' # TODO
2558 config['image'] = config.get('uname', '')
2560 if config.get('mode', 'r') == 'r':
2561 config['mode'] = 'RO'
2562 else:
2563 config['mode'] = 'RW'
2565 if dev_class == 'vtpm':
2566 if not config.has_key('type'):
2567 config['type'] = 'paravirtualised' # TODO
2568 if not config.has_key('backend'):
2569 config['backend'] = "00000000-0000-0000-0000-000000000000"
2571 return config
2573 def get_dev_property(self, dev_class, dev_uuid, field):
2574 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2575 try:
2576 return config[field]
2577 except KeyError:
2578 raise XendError('Invalid property for device: %s' % field)
2580 def set_dev_property(self, dev_class, dev_uuid, field, value):
2581 self.info['devices'][dev_uuid][1][field] = value
2583 def get_vcpus_util(self):
2584 vcpu_util = {}
2585 xennode = XendNode.instance()
2586 if 'VCPUs_max' in self.info and self.domid != None:
2587 for i in range(0, self.info['VCPUs_max']):
2588 util = xennode.get_vcpu_util(self.domid, i)
2589 vcpu_util[str(i)] = util
2591 return vcpu_util
2593 def get_consoles(self):
2594 return self.info.get('console_refs', [])
2596 def get_vifs(self):
2597 return self.info.get('vif_refs', [])
2599 def get_vbds(self):
2600 return self.info.get('vbd_refs', [])
2602 def get_vtpms(self):
2603 return self.info.get('vtpm_refs', [])
2605 def create_vbd(self, xenapi_vbd, vdi_image_path):
2606 """Create a VBD using a VDI from XendStorageRepository.
2608 @param xenapi_vbd: vbd struct from the Xen API
2609 @param vdi_image_path: VDI UUID
2610 @rtype: string
2611 @return: uuid of the device
2612 """
2613 xenapi_vbd['image'] = vdi_image_path
2614 if vdi_image_path.startswith('tap'):
2615 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2616 else:
2617 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2619 if not dev_uuid:
2620 raise XendError('Failed to create device')
2622 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2623 XEN_API_VM_POWER_STATE_PAUSED):
2624 _, config = self.info['devices'][dev_uuid]
2626 if vdi_image_path.startswith('tap'):
2627 dev_control = self.getDeviceController('tap')
2628 else:
2629 dev_control = self.getDeviceController('vbd')
2631 try:
2632 devid = dev_control.createDevice(config)
2633 dev_control.waitForDevice(devid)
2634 self.info.device_update(dev_uuid,
2635 cfg_xenapi = {'devid': devid})
2636 except Exception, exn:
2637 log.exception(exn)
2638 del self.info['devices'][dev_uuid]
2639 self.info['vbd_refs'].remove(dev_uuid)
2640 raise
2642 return dev_uuid
2644 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2645 """Create a VBD using a VDI from XendStorageRepository.
2647 @param xenapi_vbd: vbd struct from the Xen API
2648 @param vdi_image_path: VDI UUID
2649 @rtype: string
2650 @return: uuid of the device
2651 """
2652 xenapi_vbd['image'] = vdi_image_path
2653 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2654 if not dev_uuid:
2655 raise XendError('Failed to create device')
2657 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2658 _, config = self.info['devices'][dev_uuid]
2659 config['devid'] = self.getDeviceController('tap').createDevice(config)
2661 return config['devid']
2663 def create_vif(self, xenapi_vif):
2664 """Create VIF device from the passed struct in Xen API format.
2666 @param xenapi_vif: Xen API VIF Struct.
2667 @rtype: string
2668 @return: UUID
2669 """
2670 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2671 if not dev_uuid:
2672 raise XendError('Failed to create device')
2674 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2675 XEN_API_VM_POWER_STATE_PAUSED):
2677 _, config = self.info['devices'][dev_uuid]
2678 dev_control = self.getDeviceController('vif')
2680 try:
2681 devid = dev_control.createDevice(config)
2682 dev_control.waitForDevice(devid)
2683 self.info.device_update(dev_uuid,
2684 cfg_xenapi = {'devid': devid})
2685 except Exception, exn:
2686 log.exception(exn)
2687 del self.info['devices'][dev_uuid]
2688 self.info['vif_refs'].remove(dev_uuid)
2689 raise
2691 return dev_uuid
2693 def create_vtpm(self, xenapi_vtpm):
2694 """Create a VTPM device from the passed struct in Xen API format.
2696 @return: uuid of the device
2697 @rtype: string
2698 """
2700 if self._stateGet() not in (DOM_STATE_HALTED,):
2701 raise VmError("Can only add vTPM to a halted domain.")
2702 if self.get_vtpms() != []:
2703 raise VmError('Domain already has a vTPM.')
2704 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2705 if not dev_uuid:
2706 raise XendError('Failed to create device')
2708 return dev_uuid
2710 def create_console(self, xenapi_console):
2711 """ Create a console device from a Xen API struct.
2713 @return: uuid of device
2714 @rtype: string
2715 """
2716 if self._stateGet() not in (DOM_STATE_HALTED,):
2717 raise VmError("Can only add console to a halted domain.")
2719 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2720 if not dev_uuid:
2721 raise XendError('Failed to create device')
2723 return dev_uuid
2725 def set_console_other_config(self, console_uuid, other_config):
2726 self.info.console_update(console_uuid, 'other_config', other_config)
2728 def destroy_device_by_uuid(self, dev_type, dev_uuid):
2729 if dev_uuid not in self.info['devices']:
2730 raise XendError('Device does not exist')
2732 try:
2733 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2734 XEN_API_VM_POWER_STATE_PAUSED):
2735 _, config = self.info['devices'][dev_uuid]
2736 devid = config.get('devid')
2737 if devid != None:
2738 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2739 else:
2740 raise XendError('Unable to get devid for device: %s:%s' %
2741 (dev_type, dev_uuid))
2742 finally:
2743 del self.info['devices'][dev_uuid]
2744 self.info['%s_refs' % dev_type].remove(dev_uuid)
2746 def destroy_vbd(self, dev_uuid):
2747 self.destroy_device_by_uuid('vbd', dev_uuid)
2749 def destroy_vif(self, dev_uuid):
2750 self.destroy_device_by_uuid('vif', dev_uuid)
2752 def destroy_vtpm(self, dev_uuid):
2753 self.destroy_device_by_uuid('vtpm', dev_uuid)
2755 def has_device(self, dev_class, dev_uuid):
2756 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2758 def __str__(self):
2759 return '<domain id=%s name=%s memory=%s state=%s>' % \
2760 (str(self.domid), self.info['name_label'],
2761 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2763 __repr__ = __str__