ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 15991:0b04a48f65cc

xend: Slightly finesse allocation of vmpath in xenstore.
Signed-off-by: Keir Fraser <keir@xensource.com>
author Keir Fraser <keir@xensource.com>
date Wed Sep 26 17:11:07 2007 +0100 (2007-09-26)
parents 69879c7bf4b5
children 8817a53c030f
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,
178 vmpath = vmpath)
180 if needs_reinitialising:
181 vm._recreateDom()
182 vm._removeVm()
183 vm._storeVmDetails()
184 vm._storeDomDetails()
186 vm.image = image.create(vm, vm.info)
187 vm.image.recreate()
189 vm._registerWatches()
190 vm.refreshShutdown(xeninfo)
192 # register the domain in the list
193 from xen.xend import XendDomain
194 XendDomain.instance().add_domain(vm)
196 return vm
199 def restore(config):
200 """Create a domain and a VM object to do a restore.
202 @param config: Domain SXP configuration
203 @type config: list of lists. (see C{create})
205 @rtype: XendDomainInfo
206 @return: A up and running XendDomainInfo instance
207 @raise VmError: Invalid configuration or failure to start.
208 @raise XendError: Errors with configuration.
209 """
211 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
212 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
213 resume = True)
214 try:
215 vm.resume()
216 return vm
217 except:
218 vm.destroy()
219 raise
221 def createDormant(domconfig):
222 """Create a dormant/inactive XenDomainInfo without creating VM.
223 This is for creating instances of persistent domains that are not
224 yet start.
226 @param domconfig: Parsed configuration
227 @type domconfig: XendConfig object
229 @rtype: XendDomainInfo
230 @return: A up and running XendDomainInfo instance
231 @raise XendError: Errors with configuration.
232 """
234 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
236 # domid does not make sense for non-running domains.
237 domconfig.pop('domid', None)
238 vm = XendDomainInfo(domconfig)
239 return vm
241 def domain_by_name(name):
242 """Get domain by name
244 @params name: Name of the domain
245 @type name: string
246 @return: XendDomainInfo or None
247 """
248 from xen.xend import XendDomain
249 return XendDomain.instance().domain_lookup_by_name_nr(name)
252 def shutdown_reason(code):
253 """Get a shutdown reason from a code.
255 @param code: shutdown code
256 @type code: int
257 @return: shutdown reason
258 @rtype: string
259 """
260 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
262 def dom_get(dom):
263 """Get info from xen for an existing domain.
265 @param dom: domain id
266 @type dom: int
267 @return: info or None
268 @rtype: dictionary
269 """
270 try:
271 domlist = xc.domain_getinfo(dom, 1)
272 if domlist and dom == domlist[0]['domid']:
273 return domlist[0]
274 except Exception, err:
275 # ignore missing domain
276 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
277 return None
280 class XendDomainInfo:
281 """An object represents a domain.
283 @TODO: try to unify dom and domid, they mean the same thing, but
284 xc refers to it as dom, and everywhere else, including
285 xenstore it is domid. The best way is to change xc's
286 python interface.
288 @ivar info: Parsed configuration
289 @type info: dictionary
290 @ivar domid: Domain ID (if VM has started)
291 @type domid: int or None
292 @ivar vmpath: XenStore path to this VM.
293 @type vmpath: string
294 @ivar dompath: XenStore path to this Domain.
295 @type dompath: string
296 @ivar image: Reference to the VM Image.
297 @type image: xen.xend.image.ImageHandler
298 @ivar store_port: event channel to xenstored
299 @type store_port: int
300 @ivar console_port: event channel to xenconsoled
301 @type console_port: int
302 @ivar store_mfn: xenstored mfn
303 @type store_mfn: int
304 @ivar console_mfn: xenconsoled mfn
305 @type console_mfn: int
306 @ivar notes: OS image notes
307 @type notes: dictionary
308 @ivar vmWatch: reference to a watch on the xenstored vmpath
309 @type vmWatch: xen.xend.xenstore.xswatch
310 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
311 @type shutdownWatch: xen.xend.xenstore.xswatch
312 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
313 @type shutdownStartTime: float or None
314 # @ivar state: Domain state
315 # @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
316 @ivar state_updated: lock for self.state
317 @type state_updated: threading.Condition
318 @ivar refresh_shutdown_lock: lock for polling shutdown state
319 @type refresh_shutdown_lock: threading.Condition
320 @ivar _deviceControllers: device controller cache for this domain
321 @type _deviceControllers: dict 'string' to DevControllers
322 """
324 def __init__(self, info, domid = None, dompath = None, augment = False,
325 priv = False, resume = False, vmpath = None):
326 """Constructor for a domain
328 @param info: parsed configuration
329 @type info: dictionary
330 @keyword domid: Set initial domain id (if any)
331 @type domid: int
332 @keyword dompath: Set initial dompath (if any)
333 @type dompath: string
334 @keyword augment: Augment given info with xenstored VM info
335 @type augment: bool
336 @keyword priv: Is a privileged domain (Dom 0)
337 @type priv: bool
338 @keyword resume: Is this domain being resumed?
339 @type resume: bool
340 """
342 self.info = info
343 if domid == None:
344 self.domid = self.info.get('domid')
345 else:
346 self.domid = domid
348 #REMOVE: uuid is now generated in XendConfig
349 #if not self._infoIsSet('uuid'):
350 # self.info['uuid'] = uuid.toString(uuid.create())
352 # Find a unique /vm/<uuid>/<integer> path if not specified.
353 # This avoids conflict between pre-/post-migrate domains when doing
354 # localhost relocation.
355 self.vmpath = vmpath
356 i = 0
357 while self.vmpath == None:
358 self.vmpath = XS_VMROOT + self.info['uuid']
359 if i != 0:
360 self.vmpath = self.vmpath + '-' + str(i)
361 try:
362 if self._readVm("uuid"):
363 self.vmpath = None
364 i = i + 1
365 except:
366 pass
368 self.dompath = dompath
370 self.image = None
371 self.store_port = None
372 self.store_mfn = None
373 self.console_port = None
374 self.console_mfn = None
376 self.native_protocol = None
378 self.vmWatch = None
379 self.shutdownWatch = None
380 self.shutdownStartTime = None
381 self._resume = resume
383 self.state_updated = threading.Condition()
384 self.refresh_shutdown_lock = threading.Condition()
385 self._stateSet(DOM_STATE_HALTED)
387 self._deviceControllers = {}
389 for state in DOM_STATES_OLD:
390 self.info[state] = 0
392 if augment:
393 self._augmentInfo(priv)
395 self._checkName(self.info['name_label'])
397 self.metrics = XendVMMetrics(uuid.createString(), self)
400 #
401 # Public functions available through XMLRPC
402 #
405 def start(self, is_managed = False):
406 """Attempts to start the VM by do the appropriate
407 initialisation if it not started.
408 """
409 from xen.xend import XendDomain
411 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED):
412 try:
413 XendTask.log_progress(0, 30, self._constructDomain)
414 XendTask.log_progress(31, 60, self._initDomain)
416 XendTask.log_progress(61, 70, self._storeVmDetails)
417 XendTask.log_progress(71, 80, self._storeDomDetails)
418 XendTask.log_progress(81, 90, self._registerWatches)
419 XendTask.log_progress(91, 100, self.refreshShutdown)
421 xendomains = XendDomain.instance()
422 xennode = XendNode.instance()
424 # save running configuration if XendDomains believe domain is
425 # persistent
426 if is_managed:
427 xendomains.managed_config_save(self)
429 if xennode.xenschedinfo() == 'credit':
430 xendomains.domain_sched_credit_set(self.getDomid(),
431 self.getWeight(),
432 self.getCap())
433 except:
434 log.exception('VM start failed')
435 self.destroy()
436 raise
437 else:
438 raise XendError('VM already running')
440 def resume(self):
441 """Resumes a domain that has come back from suspension."""
442 state = self._stateGet()
443 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
444 try:
445 self._constructDomain()
446 self._storeVmDetails()
447 self._createDevices()
448 self._createChannels()
449 self._storeDomDetails()
450 self._endRestore()
451 except:
452 log.exception('VM resume failed')
453 self.destroy()
454 raise
455 else:
456 raise XendError('VM is not susupened; it is %s'
457 % XEN_API_VM_POWER_STATE[state])
459 def shutdown(self, reason):
460 """Shutdown a domain by signalling this via xenstored."""
461 log.debug('XendDomainInfo.shutdown(%s)', reason)
462 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
463 raise XendError('Domain cannot be shutdown')
465 if self.domid == 0:
466 raise XendError('Domain 0 cannot be shutdown')
468 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
469 raise XendError('Invalid reason: %s' % reason)
470 self._removeVm('xend/previous_restart_time')
471 self.storeDom("control/shutdown", reason)
473 # HVM domain shuts itself down only if it has PV drivers
474 if self.info.is_hvm():
475 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
476 if not hvm_pvdrv:
477 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
478 xc.domain_destroy_hook(self.domid)
479 log.info("HVM save:remote shutdown dom %d!", self.domid)
480 xc.domain_shutdown(self.domid, code)
482 def pause(self):
483 """Pause domain
485 @raise XendError: Failed pausing a domain
486 """
487 try:
488 xc.domain_pause(self.domid)
489 self._stateSet(DOM_STATE_PAUSED)
490 except Exception, ex:
491 log.exception(ex)
492 raise XendError("Domain unable to be paused: %s" % str(ex))
494 def unpause(self):
495 """Unpause domain
497 @raise XendError: Failed unpausing a domain
498 """
499 try:
500 xc.domain_unpause(self.domid)
501 self._stateSet(DOM_STATE_RUNNING)
502 except Exception, ex:
503 log.exception(ex)
504 raise XendError("Domain unable to be unpaused: %s" % str(ex))
506 def send_sysrq(self, key):
507 """ Send a Sysrq equivalent key via xenstored."""
508 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
509 raise XendError("Domain '%s' is not started" % self.info['name_label'])
511 asserts.isCharConvertible(key)
512 self.storeDom("control/sysrq", '%c' % key)
514 def device_create(self, dev_config):
515 """Create a new device.
517 @param dev_config: device configuration
518 @type dev_config: SXP object (parsed config)
519 """
520 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
521 dev_type = sxp.name(dev_config)
522 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
523 dev_config_dict = self.info['devices'][dev_uuid][1]
524 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
526 if self.domid is not None:
527 try:
528 dev_config_dict['devid'] = devid = \
529 self._createDevice(dev_type, dev_config_dict)
530 self._waitForDevice(dev_type, devid)
531 except VmError, ex:
532 raise ex
533 else:
534 devid = None
536 xen.xend.XendDomain.instance().managed_config_save(self)
537 return self.getDeviceController(dev_type).sxpr(devid)
539 def device_configure(self, dev_sxp, devid = None):
540 """Configure an existing device.
542 @param dev_config: device configuration
543 @type dev_config: SXP object (parsed config)
544 @param devid: device id
545 @type devid: int
546 @return: Returns True if successfully updated device
547 @rtype: boolean
548 """
550 # convert device sxp to a dict
551 dev_class = sxp.name(dev_sxp)
552 dev_config = {}
553 for opt_val in dev_sxp[1:]:
554 try:
555 dev_config[opt_val[0]] = opt_val[1]
556 except IndexError:
557 pass
559 # use DevController.reconfigureDevice to change device config
560 dev_control = self.getDeviceController(dev_class)
561 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
563 # update XendConfig with new device info
564 if dev_uuid:
565 self.info.device_update(dev_uuid, dev_sxp)
567 return True
569 def waitForDevices(self):
570 """Wait for this domain's configured devices to connect.
572 @raise VmError: if any device fails to initialise.
573 """
574 for devclass in XendDevices.valid_devices():
575 self.getDeviceController(devclass).waitForDevices()
577 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
578 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
579 deviceClass, devid)
581 if rm_cfg:
582 # Convert devid to device number. A device number is
583 # needed to remove its configuration.
584 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
586 # Save current sxprs. A device number and a backend
587 # path are needed to remove its configuration but sxprs
588 # do not have those after calling destroyDevice.
589 sxprs = self.getDeviceSxprs(deviceClass)
591 rc = None
592 if self.domid is not None:
593 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
594 if not force and rm_cfg:
595 # The backend path, other than the device itself,
596 # has to be passed because its accompanied frontend
597 # path may be void until its removal is actually
598 # issued. It is probable because destroyDevice is
599 # issued first.
600 for dev_num, dev_info in sxprs:
601 dev_num = int(dev_num)
602 if dev_num == dev:
603 for x in dev_info:
604 if x[0] == 'backend':
605 backend = x[1]
606 break
607 break
608 self._waitForDevice_destroy(deviceClass, devid, backend)
610 if rm_cfg:
611 if deviceClass == 'vif':
612 if self.domid is not None:
613 for dev_num, dev_info in sxprs:
614 dev_num = int(dev_num)
615 if dev_num == dev:
616 for x in dev_info:
617 if x[0] == 'mac':
618 mac = x[1]
619 break
620 break
621 dev_info = self._getDeviceInfo_vif(mac)
622 else:
623 _, dev_info = sxprs[dev]
624 else: # 'vbd' or 'tap'
625 dev_info = self._getDeviceInfo_vbd(dev)
626 # To remove the UUID of the device from refs,
627 # deviceClass must be always 'vbd'.
628 deviceClass = 'vbd'
629 if dev_info is None:
630 raise XendError("Device %s is not defined" % devid)
632 dev_uuid = sxp.child_value(dev_info, 'uuid')
633 del self.info['devices'][dev_uuid]
634 self.info['%s_refs' % deviceClass].remove(dev_uuid)
635 xen.xend.XendDomain.instance().managed_config_save(self)
637 return rc
639 def getDeviceSxprs(self, deviceClass):
640 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
641 return self.getDeviceController(deviceClass).sxprs()
642 else:
643 sxprs = []
644 dev_num = 0
645 for dev_type, dev_info in self.info.all_devices_sxpr():
646 if dev_type == deviceClass:
647 sxprs.append([dev_num, dev_info])
648 dev_num += 1
649 return sxprs
651 def getBlockDeviceClass(self, devid):
652 # To get a device number from the devid,
653 # we temporarily use the device controller of VBD.
654 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
655 dev_info = self._getDeviceInfo_vbd(dev)
656 if dev_info:
657 return dev_info[0]
659 def _getDeviceInfo_vif(self, mac):
660 for dev_type, dev_info in self.info.all_devices_sxpr():
661 if dev_type != 'vif':
662 continue
663 if mac == sxp.child_value(dev_info, 'mac'):
664 return dev_info
666 def _getDeviceInfo_vbd(self, devid):
667 for dev_type, dev_info in self.info.all_devices_sxpr():
668 if dev_type != 'vbd' and dev_type != 'tap':
669 continue
670 dev = sxp.child_value(dev_info, 'dev')
671 dev = dev.split(':')[0]
672 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
673 if devid == dev:
674 return dev_info
677 def setMemoryTarget(self, target):
678 """Set the memory target of this domain.
679 @param target: In MiB.
680 """
681 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
682 self.info['name_label'], str(self.domid), target)
684 MiB = 1024 * 1024
685 self._safe_set_memory('memory_dynamic_min', target * MiB)
686 self._safe_set_memory('memory_dynamic_max', target * MiB)
688 if self.domid >= 0:
689 self.storeVm("memory", target)
690 self.storeDom("memory/target", target << 10)
691 xen.xend.XendDomain.instance().managed_config_save(self)
693 def setMemoryMaximum(self, limit):
694 """Set the maximum memory limit of this domain
695 @param limit: In MiB.
696 """
697 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
698 self.info['name_label'], str(self.domid), limit)
700 if limit <= 0:
701 raise XendError('Invalid memory size')
703 MiB = 1024 * 1024
704 self._safe_set_memory('memory_static_max', limit * MiB)
706 if self.domid >= 0:
707 maxmem = int(limit) * 1024
708 try:
709 return xc.domain_setmaxmem(self.domid, maxmem)
710 except Exception, ex:
711 raise XendError(str(ex))
712 xen.xend.XendDomain.instance().managed_config_save(self)
715 def getVCPUInfo(self):
716 try:
717 # We include the domain name and ID, to help xm.
718 sxpr = ['domain',
719 ['domid', self.domid],
720 ['name', self.info['name_label']],
721 ['vcpu_count', self.info['VCPUs_max']]]
723 for i in range(0, self.info['VCPUs_max']):
724 if self.domid is not None:
725 info = xc.vcpu_getinfo(self.domid, i)
727 sxpr.append(['vcpu',
728 ['number', i],
729 ['online', info['online']],
730 ['blocked', info['blocked']],
731 ['running', info['running']],
732 ['cpu_time', info['cpu_time'] / 1e9],
733 ['cpu', info['cpu']],
734 ['cpumap', info['cpumap']]])
735 else:
736 sxpr.append(['vcpu',
737 ['number', i],
738 ['online', 0],
739 ['blocked', 0],
740 ['running', 0],
741 ['cpu_time', 0.0],
742 ['cpu', -1],
743 ['cpumap', self.info['cpus'] and \
744 self.info['cpus'] or range(64)]])
746 return sxpr
748 except RuntimeError, exn:
749 raise XendError(str(exn))
752 def getDomInfo(self):
753 return dom_get(self.domid)
755 #
756 # internal functions ... TODO: re-categorised
757 #
759 def _augmentInfo(self, priv):
760 """Augment self.info, as given to us through L{recreate}, with
761 values taken from the store. This recovers those values known
762 to xend but not to the hypervisor.
763 """
764 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
765 if priv:
766 augment_entries.remove('memory')
767 augment_entries.remove('maxmem')
768 augment_entries.remove('vcpus')
769 augment_entries.remove('vcpu_avail')
771 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
772 for k in augment_entries])
774 # make returned lists into a dictionary
775 vm_config = dict(zip(augment_entries, vm_config))
777 for arg in augment_entries:
778 val = vm_config[arg]
779 if val != None:
780 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
781 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
782 self.info[xapiarg] = val
783 elif arg == "memory":
784 self.info["static_memory_min"] = val
785 elif arg == "maxmem":
786 self.info["static_memory_max"] = val
787 else:
788 self.info[arg] = val
790 # For dom0, we ignore any stored value for the vcpus fields, and
791 # read the current value from Xen instead. This allows boot-time
792 # settings to take precedence over any entries in the store.
793 if priv:
794 xeninfo = dom_get(self.domid)
795 self.info['VCPUs_max'] = xeninfo['online_vcpus']
796 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
798 # read image value
799 image_sxp = self._readVm('image')
800 if image_sxp:
801 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
803 # read devices
804 devices = []
805 for devclass in XendDevices.valid_devices():
806 devconfig = self.getDeviceController(devclass).configurations()
807 if devconfig:
808 devices.extend(devconfig)
810 if not self.info['devices'] and devices is not None:
811 for device in devices:
812 self.info.device_add(device[0], cfg_sxp = device)
814 self._update_consoles()
816 def _update_consoles(self):
817 if self.domid == None or self.domid == 0:
818 return
820 # Update VT100 port if it exists
821 self.console_port = self.readDom('console/port')
822 if self.console_port is not None:
823 serial_consoles = self.info.console_get_all('vt100')
824 if not serial_consoles:
825 cfg = self.info.console_add('vt100', self.console_port)
826 self._createDevice('console', cfg)
827 else:
828 console_uuid = serial_consoles[0].get('uuid')
829 self.info.console_update(console_uuid, 'location',
830 self.console_port)
833 # Update VNC port if it exists and write to xenstore
834 vnc_port = self.readDom('console/vnc-port')
835 if vnc_port is not None:
836 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
837 if dev_type == 'vfb':
838 old_location = dev_info.get('location')
839 listen_host = dev_info.get('vnclisten', 'localhost')
840 new_location = '%s:%s' % (listen_host, str(vnc_port))
841 if old_location == new_location:
842 break
844 dev_info['location'] = new_location
845 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
846 vfb_ctrl = self.getDeviceController('vfb')
847 vfb_ctrl.reconfigureDevice(0, dev_info)
848 break
850 #
851 # Function to update xenstore /vm/*
852 #
854 def _readVm(self, *args):
855 return xstransact.Read(self.vmpath, *args)
857 def _writeVm(self, *args):
858 return xstransact.Write(self.vmpath, *args)
860 def _removeVm(self, *args):
861 return xstransact.Remove(self.vmpath, *args)
863 def _gatherVm(self, *args):
864 return xstransact.Gather(self.vmpath, *args)
866 def storeVm(self, *args):
867 return xstransact.Store(self.vmpath, *args)
869 #
870 # Function to update xenstore /dom/*
871 #
873 def readDom(self, *args):
874 return xstransact.Read(self.dompath, *args)
876 def gatherDom(self, *args):
877 return xstransact.Gather(self.dompath, *args)
879 def _writeDom(self, *args):
880 return xstransact.Write(self.dompath, *args)
882 def _removeDom(self, *args):
883 return xstransact.Remove(self.dompath, *args)
885 def storeDom(self, *args):
886 return xstransact.Store(self.dompath, *args)
888 def _recreateDom(self):
889 complete(self.dompath, lambda t: self._recreateDomFunc(t))
891 def _recreateDomFunc(self, t):
892 t.remove()
893 t.mkdir()
894 t.set_permissions({'dom' : self.domid})
895 t.write('vm', self.vmpath)
897 def _storeDomDetails(self):
898 to_store = {
899 'domid': str(self.domid),
900 'vm': self.vmpath,
901 'name': self.info['name_label'],
902 'console/limit': str(xoptions.get_console_limit() * 1024),
903 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
904 }
906 def f(n, v):
907 if v is not None:
908 if type(v) == bool:
909 to_store[n] = v and "1" or "0"
910 else:
911 to_store[n] = str(v)
913 f('console/port', self.console_port)
914 f('console/ring-ref', self.console_mfn)
915 f('store/port', self.store_port)
916 f('store/ring-ref', self.store_mfn)
918 if arch.type == "x86":
919 f('control/platform-feature-multiprocessor-suspend', True)
921 # elfnotes
922 for n, v in self.info.get_notes().iteritems():
923 n = n.lower().replace('_', '-')
924 if n == 'features':
925 for v in v.split('|'):
926 v = v.replace('_', '-')
927 if v.startswith('!'):
928 f('image/%s/%s' % (n, v[1:]), False)
929 else:
930 f('image/%s/%s' % (n, v), True)
931 else:
932 f('image/%s' % n, v)
934 if self.info.has_key('security_label'):
935 f('security_label', self.info['security_label'])
937 to_store.update(self._vcpuDomDetails())
939 log.debug("Storing domain details: %s", scrub_password(to_store))
941 self._writeDom(to_store)
943 def _vcpuDomDetails(self):
944 def availability(n):
945 if self.info['vcpu_avail'] & (1 << n):
946 return 'online'
947 else:
948 return 'offline'
950 result = {}
951 for v in range(0, self.info['VCPUs_max']):
952 result["cpu/%d/availability" % v] = availability(v)
953 return result
955 #
956 # xenstore watches
957 #
959 def _registerWatches(self):
960 """Register a watch on this VM's entries in the store, and the
961 domain's control/shutdown node, so that when they are changed
962 externally, we keep up to date. This should only be called by {@link
963 #create}, {@link #recreate}, or {@link #restore}, once the domain's
964 details have been written, but before the new instance is returned."""
965 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
966 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
967 self._handleShutdownWatch)
969 def _storeChanged(self, _):
970 log.trace("XendDomainInfo.storeChanged");
972 changed = False
974 # Check whether values in the configuration have
975 # changed in Xenstore.
977 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
978 'rtc/timeoffset']
980 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
981 for k in cfg_vm])
983 # convert two lists into a python dictionary
984 vm_details = dict(zip(cfg_vm, vm_details))
986 if vm_details['rtc/timeoffset'] == None:
987 vm_details['rtc/timeoffset'] = "0"
989 for arg, val in vm_details.items():
990 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
991 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
992 if val != None and val != self.info[xapiarg]:
993 self.info[xapiarg] = val
994 changed = True
995 elif arg == "memory":
996 if val != None and val != self.info["static_memory_min"]:
997 self.info["static_memory_min"] = val
998 changed = True
999 elif arg == "maxmem":
1000 if val != None and val != self.info["static_memory_max"]:
1001 self.info["static_memory_max"] = val
1002 changed = True
1004 # Check whether image definition has been updated
1005 image_sxp = self._readVm('image')
1006 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1007 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1008 changed = True
1010 # Check if the rtc offset has changes
1011 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1012 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1013 changed = True
1015 if changed:
1016 # Update the domain section of the store, as this contains some
1017 # parameters derived from the VM configuration.
1018 self._storeDomDetails()
1020 return 1
1022 def _handleShutdownWatch(self, _):
1023 log.debug('XendDomainInfo.handleShutdownWatch')
1025 reason = self.readDom('control/shutdown')
1027 if reason and reason != 'suspend':
1028 sst = self.readDom('xend/shutdown_start_time')
1029 now = time.time()
1030 if sst:
1031 self.shutdownStartTime = float(sst)
1032 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1033 else:
1034 self.shutdownStartTime = now
1035 self.storeDom('xend/shutdown_start_time', now)
1036 timeout = SHUTDOWN_TIMEOUT
1038 log.trace(
1039 "Scheduling refreshShutdown on domain %d in %ds.",
1040 self.domid, timeout)
1041 threading.Timer(timeout, self.refreshShutdown).start()
1043 return True
1047 # Public Attributes for the VM
1051 def getDomid(self):
1052 return self.domid
1054 def setName(self, name):
1055 self._checkName(name)
1056 self.info['name_label'] = name
1057 self.storeVm("name", name)
1059 def getName(self):
1060 return self.info['name_label']
1062 def getDomainPath(self):
1063 return self.dompath
1065 def getShutdownReason(self):
1066 return self.readDom('control/shutdown')
1068 def getStorePort(self):
1069 """For use only by image.py and XendCheckpoint.py."""
1070 return self.store_port
1072 def getConsolePort(self):
1073 """For use only by image.py and XendCheckpoint.py"""
1074 return self.console_port
1076 def getFeatures(self):
1077 """For use only by image.py."""
1078 return self.info['features']
1080 def getVCpuCount(self):
1081 return self.info['VCPUs_max']
1083 def setVCpuCount(self, vcpus):
1084 if vcpus <= 0:
1085 raise XendError('Invalid VCPUs')
1087 self.info['vcpu_avail'] = (1 << vcpus) - 1
1088 if self.domid >= 0:
1089 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1090 # update dom differently depending on whether we are adjusting
1091 # vcpu number up or down, otherwise _vcpuDomDetails does not
1092 # disable the vcpus
1093 if self.info['VCPUs_max'] > vcpus:
1094 # decreasing
1095 self._writeDom(self._vcpuDomDetails())
1096 self.info['VCPUs_live'] = vcpus
1097 else:
1098 # same or increasing
1099 self.info['VCPUs_live'] = vcpus
1100 self._writeDom(self._vcpuDomDetails())
1101 else:
1102 self.info['VCPUs_max'] = vcpus
1103 xen.xend.XendDomain.instance().managed_config_save(self)
1104 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1105 vcpus)
1107 def getMemoryTarget(self):
1108 """Get this domain's target memory size, in KB."""
1109 return self.info['memory_dynamic_max'] / 1024
1111 def getMemoryMaximum(self):
1112 """Get this domain's maximum memory size, in KB."""
1113 # remember, info now stores memory in bytes
1114 return self.info['memory_static_max'] / 1024
1116 def getResume(self):
1117 return str(self._resume)
1119 def getCap(self):
1120 return self.info['vcpus_params']['cap']
1122 def setCap(self, cpu_cap):
1123 self.info['vcpus_params']['cap'] = cpu_cap
1125 def getWeight(self):
1126 return self.info['vcpus_params']['weight']
1128 def setWeight(self, cpu_weight):
1129 self.info['vcpus_params']['weight'] = cpu_weight
1131 def setResume(self, state):
1132 self._resume = state
1134 def getRestartCount(self):
1135 return self._readVm('xend/restart_count')
1137 def refreshShutdown(self, xeninfo = None):
1138 """ Checks the domain for whether a shutdown is required.
1140 Called from XendDomainInfo and also image.py for HVM images.
1141 """
1143 # If set at the end of this method, a restart is required, with the
1144 # given reason. This restart has to be done out of the scope of
1145 # refresh_shutdown_lock.
1146 restart_reason = None
1148 self.refresh_shutdown_lock.acquire()
1149 try:
1150 if xeninfo is None:
1151 xeninfo = dom_get(self.domid)
1152 if xeninfo is None:
1153 # The domain no longer exists. This will occur if we have
1154 # scheduled a timer to check for shutdown timeouts and the
1155 # shutdown succeeded. It will also occur if someone
1156 # destroys a domain beneath us. We clean up the domain,
1157 # just in case, but we can't clean up the VM, because that
1158 # VM may have migrated to a different domain on this
1159 # machine.
1160 self.cleanupDomain()
1161 self._stateSet(DOM_STATE_HALTED)
1162 return
1164 if xeninfo['dying']:
1165 # Dying means that a domain has been destroyed, but has not
1166 # yet been cleaned up by Xen. This state could persist
1167 # indefinitely if, for example, another domain has some of its
1168 # pages mapped. We might like to diagnose this problem in the
1169 # future, but for now all we do is make sure that it's not us
1170 # holding the pages, by calling cleanupDomain. We can't
1171 # clean up the VM, as above.
1172 self.cleanupDomain()
1173 self._stateSet(DOM_STATE_SHUTDOWN)
1174 return
1176 elif xeninfo['crashed']:
1177 if self.readDom('xend/shutdown_completed'):
1178 # We've seen this shutdown already, but we are preserving
1179 # the domain for debugging. Leave it alone.
1180 return
1182 log.warn('Domain has crashed: name=%s id=%d.',
1183 self.info['name_label'], self.domid)
1184 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1186 if xoptions.get_enable_dump():
1187 try:
1188 self.dumpCore()
1189 except XendError:
1190 # This error has been logged -- there's nothing more
1191 # we can do in this context.
1192 pass
1194 restart_reason = 'crash'
1195 self._stateSet(DOM_STATE_HALTED)
1197 elif xeninfo['shutdown']:
1198 self._stateSet(DOM_STATE_SHUTDOWN)
1199 if self.readDom('xend/shutdown_completed'):
1200 # We've seen this shutdown already, but we are preserving
1201 # the domain for debugging. Leave it alone.
1202 return
1204 else:
1205 reason = shutdown_reason(xeninfo['shutdown_reason'])
1207 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1208 self.info['name_label'], self.domid, reason)
1209 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1211 self._clearRestart()
1213 if reason == 'suspend':
1214 self._stateSet(DOM_STATE_SUSPENDED)
1215 # Don't destroy the domain. XendCheckpoint will do
1216 # this once it has finished. However, stop watching
1217 # the VM path now, otherwise we will end up with one
1218 # watch for the old domain, and one for the new.
1219 self._unwatchVm()
1220 elif reason in ('poweroff', 'reboot'):
1221 restart_reason = reason
1222 else:
1223 self.destroy()
1225 elif self.dompath is None:
1226 # We have yet to manage to call introduceDomain on this
1227 # domain. This can happen if a restore is in progress, or has
1228 # failed. Ignore this domain.
1229 pass
1230 else:
1231 # Domain is alive. If we are shutting it down, log a message
1232 # if it seems unresponsive.
1233 if xeninfo['paused']:
1234 self._stateSet(DOM_STATE_PAUSED)
1235 else:
1236 self._stateSet(DOM_STATE_RUNNING)
1238 if self.shutdownStartTime:
1239 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1240 self.shutdownStartTime)
1241 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1242 log.info(
1243 "Domain shutdown timeout expired: name=%s id=%s",
1244 self.info['name_label'], self.domid)
1245 self.storeDom('xend/unresponsive', 'True')
1246 finally:
1247 self.refresh_shutdown_lock.release()
1249 if restart_reason:
1250 threading.Thread(target = self._maybeRestart,
1251 args = (restart_reason,)).start()
1255 # Restart functions - handling whether we come back up on shutdown.
1258 def _clearRestart(self):
1259 self._removeDom("xend/shutdown_start_time")
1262 def _maybeRestart(self, reason):
1263 # Dispatch to the correct method based upon the configured on_{reason}
1264 # behaviour.
1265 actions = {"destroy" : self.destroy,
1266 "restart" : self._restart,
1267 "preserve" : self._preserve,
1268 "rename-restart" : self._renameRestart}
1270 action_conf = {
1271 'poweroff': 'actions_after_shutdown',
1272 'reboot': 'actions_after_reboot',
1273 'crash': 'actions_after_crash',
1276 action_target = self.info.get(action_conf.get(reason))
1277 func = actions.get(action_target, None)
1278 if func and callable(func):
1279 func()
1280 else:
1281 self.destroy() # default to destroy
1283 def _renameRestart(self):
1284 self._restart(True)
1286 def _restart(self, rename = False):
1287 """Restart the domain after it has exited.
1289 @param rename True if the old domain is to be renamed and preserved,
1290 False if it is to be destroyed.
1291 """
1292 from xen.xend import XendDomain
1294 if self._readVm(RESTART_IN_PROGRESS):
1295 log.error('Xend failed during restart of domain %s. '
1296 'Refusing to restart to avoid loops.',
1297 str(self.domid))
1298 self.destroy()
1299 return
1301 old_domid = self.domid
1302 self._writeVm(RESTART_IN_PROGRESS, 'True')
1304 now = time.time()
1305 rst = self._readVm('xend/previous_restart_time')
1306 if rst:
1307 rst = float(rst)
1308 timeout = now - rst
1309 if timeout < MINIMUM_RESTART_TIME:
1310 log.error(
1311 'VM %s restarting too fast (%f seconds since the last '
1312 'restart). Refusing to restart to avoid loops.',
1313 self.info['name_label'], timeout)
1314 self.destroy()
1315 return
1317 self._writeVm('xend/previous_restart_time', str(now))
1319 try:
1320 if rename:
1321 self._preserveForRestart()
1322 else:
1323 self._unwatchVm()
1324 self.destroyDomain()
1326 # new_dom's VM will be the same as this domain's VM, except where
1327 # the rename flag has instructed us to call preserveForRestart.
1328 # In that case, it is important that we remove the
1329 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1330 # once the new one is available.
1332 new_dom = None
1333 try:
1334 new_dom = XendDomain.instance().domain_create_from_dict(
1335 self.info)
1336 new_dom.waitForDevices()
1337 new_dom.unpause()
1338 rst_cnt = self._readVm('xend/restart_count')
1339 rst_cnt = int(rst_cnt) + 1
1340 self._writeVm('xend/restart_count', str(rst_cnt))
1341 new_dom._removeVm(RESTART_IN_PROGRESS)
1342 except:
1343 if new_dom:
1344 new_dom._removeVm(RESTART_IN_PROGRESS)
1345 new_dom.destroy()
1346 else:
1347 self._removeVm(RESTART_IN_PROGRESS)
1348 raise
1349 except:
1350 log.exception('Failed to restart domain %s.', str(old_domid))
1352 def _preserveForRestart(self):
1353 """Preserve a domain that has been shut down, by giving it a new UUID,
1354 cloning the VM details, and giving it a new name. This allows us to
1355 keep this domain for debugging, but restart a new one in its place
1356 preserving the restart semantics (name and UUID preserved).
1357 """
1359 new_uuid = uuid.createString()
1360 new_name = 'Domain-%s' % new_uuid
1361 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1362 self.info['name_label'], self.domid, self.info['uuid'],
1363 new_name, new_uuid)
1364 self._unwatchVm()
1365 self._releaseDevices()
1366 self.info['name_label'] = new_name
1367 self.info['uuid'] = new_uuid
1368 self.vmpath = XS_VMROOT + new_uuid
1369 self._storeVmDetails()
1370 self._preserve()
1373 def _preserve(self):
1374 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1375 self.domid)
1376 self._unwatchVm()
1377 self.storeDom('xend/shutdown_completed', 'True')
1378 self._stateSet(DOM_STATE_HALTED)
1381 # Debugging ..
1384 def dumpCore(self, corefile = None):
1385 """Create a core dump for this domain.
1387 @raise: XendError if core dumping failed.
1388 """
1390 try:
1391 if not corefile:
1392 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1393 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1394 self.info['name_label'], self.domid)
1396 if os.path.isdir(corefile):
1397 raise XendError("Cannot dump core in a directory: %s" %
1398 corefile)
1400 xc.domain_dumpcore(self.domid, corefile)
1401 except RuntimeError, ex:
1402 corefile_incomp = corefile+'-incomplete'
1403 os.rename(corefile, corefile_incomp)
1404 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1405 self.domid, self.info['name_label'])
1406 raise XendError("Failed to dump core: %s" % str(ex))
1409 # Device creation/deletion functions
1412 def _createDevice(self, deviceClass, devConfig):
1413 return self.getDeviceController(deviceClass).createDevice(devConfig)
1415 def _waitForDevice(self, deviceClass, devid):
1416 return self.getDeviceController(deviceClass).waitForDevice(devid)
1418 def _waitForDeviceUUID(self, dev_uuid):
1419 deviceClass, config = self.info['devices'].get(dev_uuid)
1420 self._waitForDevice(deviceClass, config['devid'])
1422 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1423 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1424 devid, backpath)
1426 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1427 return self.getDeviceController(deviceClass).reconfigureDevice(
1428 devid, devconfig)
1430 def _createDevices(self):
1431 """Create the devices for a vm.
1433 @raise: VmError for invalid devices
1434 """
1435 ordered_refs = self.info.ordered_device_refs()
1436 for dev_uuid in ordered_refs:
1437 devclass, config = self.info['devices'][dev_uuid]
1438 if devclass in XendDevices.valid_devices():
1439 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1440 dev_uuid = config.get('uuid')
1441 devid = self._createDevice(devclass, config)
1443 # store devid in XendConfig for caching reasons
1444 if dev_uuid in self.info['devices']:
1445 self.info['devices'][dev_uuid][1]['devid'] = devid
1447 if self.image:
1448 self.image.createDeviceModel()
1450 def _releaseDevices(self, suspend = False):
1451 """Release all domain's devices. Nothrow guarantee."""
1452 if suspend and self.image:
1453 self.image.destroy(suspend)
1454 return
1456 t = xstransact("%s/device" % self.dompath)
1457 for devclass in XendDevices.valid_devices():
1458 for dev in t.list(devclass):
1459 try:
1460 log.debug("Removing %s", dev);
1461 self.destroyDevice(devclass, dev, False);
1462 except:
1463 # Log and swallow any exceptions in removal --
1464 # there's nothing more we can do.
1465 log.exception("Device release failed: %s; %s; %s",
1466 self.info['name_label'], devclass, dev)
1470 def getDeviceController(self, name):
1471 """Get the device controller for this domain, and if it
1472 doesn't exist, create it.
1474 @param name: device class name
1475 @type name: string
1476 @rtype: subclass of DevController
1477 """
1478 if name not in self._deviceControllers:
1479 devController = XendDevices.make_controller(name, self)
1480 if not devController:
1481 raise XendError("Unknown device type: %s" % name)
1482 self._deviceControllers[name] = devController
1484 return self._deviceControllers[name]
1487 # Migration functions (public)
1490 def testMigrateDevices(self, network, dst):
1491 """ Notify all device about intention of migration
1492 @raise: XendError for a device that cannot be migrated
1493 """
1494 for (n, c) in self.info.all_devices_sxpr():
1495 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1496 if rc != 0:
1497 raise XendError("Device of type '%s' refuses migration." % n)
1499 def migrateDevices(self, network, dst, step, domName=''):
1500 """Notify the devices about migration
1501 """
1502 ctr = 0
1503 try:
1504 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1505 self.migrateDevice(dev_type, dev_conf, network, dst,
1506 step, domName)
1507 ctr = ctr + 1
1508 except:
1509 for dev_type, dev_conf in self.info.all_devices_sxpr():
1510 if ctr == 0:
1511 step = step - 1
1512 ctr = ctr - 1
1513 self._recoverMigrateDevice(dev_type, dev_conf, network,
1514 dst, step, domName)
1515 raise
1517 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1518 step, domName=''):
1519 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1520 network, dst, step, domName)
1522 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1523 dst, step, domName=''):
1524 return self.getDeviceController(deviceClass).recover_migrate(
1525 deviceConfig, network, dst, step, domName)
1528 ## private:
1530 def _constructDomain(self):
1531 """Construct the domain.
1533 @raise: VmError on error
1534 """
1536 log.debug('XendDomainInfo.constructDomain')
1538 self.shutdownStartTime = None
1540 hvm = self.info.is_hvm()
1541 if hvm:
1542 info = xc.xeninfo()
1543 if 'hvm' not in info['xen_caps']:
1544 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1545 "supported by your CPU and enabled in your "
1546 "BIOS?")
1548 # Hack to pre-reserve some memory for initial domain creation.
1549 # There is an implicit memory overhead for any domain creation. This
1550 # overhead is greater for some types of domain than others. For
1551 # example, an x86 HVM domain will have a default shadow-pagetable
1552 # allocation of 1MB. We free up 2MB here to be on the safe side.
1553 balloon.free(2*1024) # 2MB should be plenty
1555 ssidref = 0
1556 if security.on():
1557 ssidref = security.calc_dom_ssidref_from_info(self.info)
1558 if security.has_authorization(ssidref) == False:
1559 raise VmError("VM is not authorized to run.")
1561 try:
1562 self.domid = xc.domain_create(
1563 domid = 0,
1564 ssidref = ssidref,
1565 handle = uuid.fromString(self.info['uuid']),
1566 hvm = int(hvm))
1567 except Exception, e:
1568 # may get here if due to ACM the operation is not permitted
1569 if security.on():
1570 raise VmError('Domain in conflict set with running domain?')
1572 if self.domid < 0:
1573 raise VmError('Creating domain failed: name=%s' %
1574 self.info['name_label'])
1576 self.dompath = GetDomainPath(self.domid)
1578 self._recreateDom()
1580 # Set maximum number of vcpus in domain
1581 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1583 # register the domain in the list
1584 from xen.xend import XendDomain
1585 XendDomain.instance().add_domain(self)
1587 def _introduceDomain(self):
1588 assert self.domid is not None
1589 assert self.store_mfn is not None
1590 assert self.store_port is not None
1592 try:
1593 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1594 except RuntimeError, exn:
1595 raise XendError(str(exn))
1598 def _initDomain(self):
1599 log.debug('XendDomainInfo.initDomain: %s %s',
1600 self.domid,
1601 self.info['vcpus_params']['weight'])
1603 self._configureBootloader()
1605 try:
1606 self.image = image.create(self, self.info)
1608 if self.info['platform'].get('localtime', 0):
1609 xc.domain_set_time_offset(self.domid)
1611 xc.domain_setcpuweight(self.domid, \
1612 self.info['vcpus_params']['weight'])
1614 # repin domain vcpus if a restricted cpus list is provided
1615 # this is done prior to memory allocation to aide in memory
1616 # distribution for NUMA systems.
1617 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1618 for v in range(0, self.info['VCPUs_max']):
1619 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1621 # Use architecture- and image-specific calculations to determine
1622 # the various headrooms necessary, given the raw configured
1623 # values. maxmem, memory, and shadow are all in KiB.
1624 # but memory_static_max etc are all stored in bytes now.
1625 memory = self.image.getRequiredAvailableMemory(
1626 self.info['memory_dynamic_max'] / 1024)
1627 maxmem = self.image.getRequiredAvailableMemory(
1628 self.info['memory_static_max'] / 1024)
1629 shadow = self.image.getRequiredShadowMemory(
1630 self.info['shadow_memory'] * 1024,
1631 self.info['memory_static_max'] / 1024)
1633 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'],)
1634 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1635 # takes MiB and we must not round down and end up under-providing.
1636 shadow = ((shadow + 1023) / 1024) * 1024
1638 # set memory limit
1639 xc.domain_setmaxmem(self.domid, maxmem)
1641 # Make sure there's enough RAM available for the domain
1642 balloon.free(memory + shadow)
1644 # Set up the shadow memory
1645 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1646 self.info['shadow_memory'] = shadow_cur
1648 self._createChannels()
1650 channel_details = self.image.createImage()
1652 self.store_mfn = channel_details['store_mfn']
1653 if 'console_mfn' in channel_details:
1654 self.console_mfn = channel_details['console_mfn']
1655 if 'notes' in channel_details:
1656 self.info.set_notes(channel_details['notes'])
1657 if 'native_protocol' in channel_details:
1658 self.native_protocol = channel_details['native_protocol'];
1660 self._introduceDomain()
1662 self._createDevices()
1664 self.image.cleanupBootloading()
1666 self.info['start_time'] = time.time()
1668 self._stateSet(DOM_STATE_RUNNING)
1669 except VmError, exn:
1670 log.exception("XendDomainInfo.initDomain: exception occurred")
1671 if self.image:
1672 self.image.cleanupBootloading()
1673 raise exn
1674 except RuntimeError, exn:
1675 log.exception("XendDomainInfo.initDomain: exception occurred")
1676 if self.image:
1677 self.image.cleanupBootloading()
1678 raise VmError(str(exn))
1681 def cleanupDomain(self):
1682 """Cleanup domain resources; release devices. Idempotent. Nothrow
1683 guarantee."""
1685 self.refresh_shutdown_lock.acquire()
1686 try:
1687 self.unwatchShutdown()
1688 self._releaseDevices()
1689 bootloader_tidy(self)
1691 if self.image:
1692 try:
1693 self.image.destroy()
1694 except:
1695 log.exception(
1696 "XendDomainInfo.cleanup: image.destroy() failed.")
1697 self.image = None
1699 try:
1700 self._removeDom()
1701 except:
1702 log.exception("Removing domain path failed.")
1704 self._stateSet(DOM_STATE_HALTED)
1705 self.domid = None # Do not push into _stateSet()!
1706 finally:
1707 self.refresh_shutdown_lock.release()
1710 def unwatchShutdown(self):
1711 """Remove the watch on the domain's control/shutdown node, if any.
1712 Idempotent. Nothrow guarantee. Expects to be protected by the
1713 refresh_shutdown_lock."""
1715 try:
1716 try:
1717 if self.shutdownWatch:
1718 self.shutdownWatch.unwatch()
1719 finally:
1720 self.shutdownWatch = None
1721 except:
1722 log.exception("Unwatching control/shutdown failed.")
1724 def waitForShutdown(self):
1725 self.state_updated.acquire()
1726 try:
1727 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1728 self.state_updated.wait()
1729 finally:
1730 self.state_updated.release()
1733 # TODO: recategorise - called from XendCheckpoint
1736 def completeRestore(self, store_mfn, console_mfn):
1738 log.debug("XendDomainInfo.completeRestore")
1740 self.store_mfn = store_mfn
1741 self.console_mfn = console_mfn
1743 self._introduceDomain()
1744 if self.info.is_hvm():
1745 self.image = image.create(self, self.info)
1746 if self.image:
1747 self.image.createDeviceModel(True)
1748 self._storeDomDetails()
1749 self._registerWatches()
1750 self.refreshShutdown()
1752 log.debug("XendDomainInfo.completeRestore done")
1755 def _endRestore(self):
1756 self.setResume(False)
1759 # VM Destroy
1762 def _prepare_phantom_paths(self):
1763 # get associated devices to destroy
1764 # build list of phantom devices to be removed after normal devices
1765 plist = []
1766 if self.domid is not None:
1767 from xen.xend.xenstore.xstransact import xstransact
1768 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1769 for dev in t.list():
1770 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1771 % (self.dompath, dev))
1772 if backend_phantom_vbd is not None:
1773 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
1774 % backend_phantom_vbd)
1775 plist.append(backend_phantom_vbd)
1776 plist.append(frontend_phantom_vbd)
1777 return plist
1779 def _cleanup_phantom_devs(self, plist):
1780 # remove phantom devices
1781 if not plist == []:
1782 time.sleep(2)
1783 for paths in plist:
1784 if paths.find('backend') != -1:
1785 from xen.xend.server import DevController
1786 # Modify online status /before/ updating state (latter is watched by
1787 # drivers, so this ordering avoids a race).
1788 xstransact.Write(paths, 'online', "0")
1789 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1790 # force
1791 xstransact.Remove(paths)
1793 def destroy(self):
1794 """Cleanup VM and destroy domain. Nothrow guarantee."""
1796 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1798 paths = self._prepare_phantom_paths()
1800 self._cleanupVm()
1801 if self.dompath is not None:
1802 if self.domid is not None:
1803 xc.domain_destroy_hook(self.domid)
1804 self.destroyDomain()
1806 self._cleanup_phantom_devs(paths)
1808 if "transient" in self.info["other_config"] \
1809 and bool(self.info["other_config"]["transient"]):
1810 from xen.xend import XendDomain
1811 XendDomain.instance().domain_delete_by_dominfo(self)
1814 def destroyDomain(self):
1815 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1817 paths = self._prepare_phantom_paths()
1819 try:
1820 if self.domid is not None:
1821 xc.domain_destroy(self.domid)
1822 for state in DOM_STATES_OLD:
1823 self.info[state] = 0
1824 self._stateSet(DOM_STATE_HALTED)
1825 except:
1826 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1828 from xen.xend import XendDomain
1829 XendDomain.instance().remove_domain(self)
1831 self.cleanupDomain()
1832 self._cleanup_phantom_devs(paths)
1835 def resumeDomain(self):
1836 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1838 if self.domid is None:
1839 return
1840 try:
1841 # could also fetch a parsed note from xenstore
1842 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1843 if not fast:
1844 self._releaseDevices()
1845 self.testDeviceComplete()
1846 self.testvifsComplete()
1847 log.debug("XendDomainInfo.resumeDomain: devices released")
1849 self._resetChannels()
1851 self._removeDom('control/shutdown')
1852 self._removeDom('device-misc/vif/nextDeviceID')
1854 self._createChannels()
1855 self._introduceDomain()
1856 self._storeDomDetails()
1858 self._createDevices()
1859 log.debug("XendDomainInfo.resumeDomain: devices created")
1861 xc.domain_resume(self.domid, fast)
1862 ResumeDomain(self.domid)
1863 except:
1864 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1865 if self.is_hvm():
1866 self.image.resumeDeviceModel()
1870 # Channels for xenstore and console
1873 def _createChannels(self):
1874 """Create the channels to the domain.
1875 """
1876 self.store_port = self._createChannel()
1877 self.console_port = self._createChannel()
1880 def _createChannel(self):
1881 """Create an event channel to the domain.
1882 """
1883 try:
1884 if self.domid != None:
1885 return xc.evtchn_alloc_unbound(domid = self.domid,
1886 remote_dom = 0)
1887 except:
1888 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1889 raise
1891 def _resetChannels(self):
1892 """Reset all event channels in the domain.
1893 """
1894 try:
1895 if self.domid != None:
1896 return xc.evtchn_reset(dom = self.domid)
1897 except:
1898 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1899 raise
1903 # Bootloader configuration
1906 def _configureBootloader(self):
1907 """Run the bootloader if we're configured to do so."""
1909 blexec = self.info['PV_bootloader']
1910 bootloader_args = self.info['PV_bootloader_args']
1911 kernel = self.info['PV_kernel']
1912 ramdisk = self.info['PV_ramdisk']
1913 args = self.info['PV_args']
1914 boot = self.info['HVM_boot_policy']
1916 if boot:
1917 # HVM booting.
1918 pass
1919 elif not blexec and kernel:
1920 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
1921 # will be picked up by image.py.
1922 pass
1923 else:
1924 # Boot using bootloader
1925 if not blexec or blexec == 'pygrub':
1926 blexec = osdep.pygrub_path
1928 blcfg = None
1929 disks = [x for x in self.info['vbd_refs']
1930 if self.info['devices'][x][1]['bootable']]
1932 if not disks:
1933 msg = "Had a bootloader specified, but no disks are bootable"
1934 log.error(msg)
1935 raise VmError(msg)
1937 devinfo = self.info['devices'][disks[0]]
1938 devtype = devinfo[0]
1939 disk = devinfo[1]['uname']
1941 fn = blkdev_uname_to_file(disk)
1942 taptype = blkdev_uname_to_taptype(disk)
1943 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1944 if mounted:
1945 # This is a file, not a device. pygrub can cope with a
1946 # file if it's raw, but if it's QCOW or other such formats
1947 # used through blktap, then we need to mount it first.
1949 log.info("Mounting %s on %s." %
1950 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1952 vbd = {
1953 'mode': 'RO',
1954 'device': BOOTLOADER_LOOPBACK_DEVICE,
1957 from xen.xend import XendDomain
1958 dom0 = XendDomain.instance().privilegedDomain()
1959 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1960 fn = BOOTLOADER_LOOPBACK_DEVICE
1962 try:
1963 blcfg = bootloader(blexec, fn, self, False,
1964 bootloader_args, kernel, ramdisk, args)
1965 finally:
1966 if mounted:
1967 log.info("Unmounting %s from %s." %
1968 (fn, BOOTLOADER_LOOPBACK_DEVICE))
1970 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1972 if blcfg is None:
1973 msg = "Had a bootloader specified, but can't find disk"
1974 log.error(msg)
1975 raise VmError(msg)
1977 self.info.update_with_image_sxp(blcfg, True)
1981 # VM Functions
1984 def _readVMDetails(self, params):
1985 """Read the specified parameters from the store.
1986 """
1987 try:
1988 return self._gatherVm(*params)
1989 except ValueError:
1990 # One of the int/float entries in params has a corresponding store
1991 # entry that is invalid. We recover, because older versions of
1992 # Xend may have put the entry there (memory/target, for example),
1993 # but this is in general a bad situation to have reached.
1994 log.exception(
1995 "Store corrupted at %s! Domain %d's configuration may be "
1996 "affected.", self.vmpath, self.domid)
1997 return []
1999 def _cleanupVm(self):
2000 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2002 self._unwatchVm()
2004 try:
2005 self._removeVm()
2006 except:
2007 log.exception("Removing VM path failed.")
2010 def checkLiveMigrateMemory(self):
2011 """ Make sure there's enough memory to migrate this domain """
2012 overhead_kb = 0
2013 if arch.type == "x86":
2014 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2015 # the minimum that Xen would allocate if no value were given.
2016 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2017 (self.info['memory_static_max'] / 1024 / 1024) * 4
2018 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2019 # The domain might already have some shadow memory
2020 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2021 if overhead_kb > 0:
2022 balloon.free(overhead_kb)
2024 def _unwatchVm(self):
2025 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2026 guarantee."""
2027 try:
2028 try:
2029 if self.vmWatch:
2030 self.vmWatch.unwatch()
2031 finally:
2032 self.vmWatch = None
2033 except:
2034 log.exception("Unwatching VM path failed.")
2036 def testDeviceComplete(self):
2037 """ For Block IO migration safety we must ensure that
2038 the device has shutdown correctly, i.e. all blocks are
2039 flushed to disk
2040 """
2041 start = time.time()
2042 while True:
2043 test = 0
2044 diff = time.time() - start
2045 for i in self.getDeviceController('vbd').deviceIDs():
2046 test = 1
2047 log.info("Dev %s still active, looping...", i)
2048 time.sleep(0.1)
2050 if test == 0:
2051 break
2052 if diff >= MIGRATE_TIMEOUT:
2053 log.info("Dev still active but hit max loop timeout")
2054 break
2056 def testvifsComplete(self):
2057 """ In case vifs are released and then created for the same
2058 domain, we need to wait the device shut down.
2059 """
2060 start = time.time()
2061 while True:
2062 test = 0
2063 diff = time.time() - start
2064 for i in self.getDeviceController('vif').deviceIDs():
2065 test = 1
2066 log.info("Dev %s still active, looping...", i)
2067 time.sleep(0.1)
2069 if test == 0:
2070 break
2071 if diff >= MIGRATE_TIMEOUT:
2072 log.info("Dev still active but hit max loop timeout")
2073 break
2075 def _storeVmDetails(self):
2076 to_store = {}
2078 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2079 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2080 if self._infoIsSet(info_key):
2081 to_store[key] = str(self.info[info_key])
2083 if self._infoIsSet("static_memory_min"):
2084 to_store["memory"] = str(self.info["static_memory_min"])
2085 if self._infoIsSet("static_memory_max"):
2086 to_store["maxmem"] = str(self.info["static_memory_max"])
2088 image_sxpr = self.info.image_sxpr()
2089 if image_sxpr:
2090 to_store['image'] = sxp.to_string(image_sxpr)
2092 if not self._readVm('xend/restart_count'):
2093 to_store['xend/restart_count'] = str(0)
2095 log.debug("Storing VM details: %s", scrub_password(to_store))
2097 self._writeVm(to_store)
2098 self._setVmPermissions()
2101 def _setVmPermissions(self):
2102 """Allow the guest domain to read its UUID. We don't allow it to
2103 access any other entry, for security."""
2104 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2105 { 'dom' : self.domid,
2106 'read' : True,
2107 'write' : False })
2110 # Utility functions
2113 def __getattr__(self, name):
2114 if name == "state":
2115 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2116 log.warn("".join(traceback.format_stack()))
2117 return self._stateGet()
2118 else:
2119 raise AttributeError()
2121 def __setattr__(self, name, value):
2122 if name == "state":
2123 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2124 log.warn("".join(traceback.format_stack()))
2125 self._stateSet(value)
2126 else:
2127 self.__dict__[name] = value
2129 def _stateSet(self, state):
2130 self.state_updated.acquire()
2131 try:
2132 # TODO Not sure this is correct...
2133 # _stateGet is live now. Why not fire event
2134 # even when it hasn't changed?
2135 if self._stateGet() != state:
2136 self.state_updated.notifyAll()
2137 import XendAPI
2138 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2139 'power_state')
2140 finally:
2141 self.state_updated.release()
2143 def _stateGet(self):
2144 # Lets try and reconsitute the state from xc
2145 # first lets try and get the domain info
2146 # from xc - this will tell us if the domain
2147 # exists
2148 info = dom_get(self.getDomid())
2149 if info is None or info['shutdown']:
2150 # We are either HALTED or SUSPENDED
2151 # check saved image exists
2152 from xen.xend import XendDomain
2153 managed_config_path = \
2154 XendDomain.instance()._managed_check_point_path( \
2155 self.get_uuid())
2156 if os.path.exists(managed_config_path):
2157 return XEN_API_VM_POWER_STATE_SUSPENDED
2158 else:
2159 return XEN_API_VM_POWER_STATE_HALTED
2160 else:
2161 # We are either RUNNING or PAUSED
2162 if info['paused']:
2163 return XEN_API_VM_POWER_STATE_PAUSED
2164 else:
2165 return XEN_API_VM_POWER_STATE_RUNNING
2167 def _infoIsSet(self, name):
2168 return name in self.info and self.info[name] is not None
2170 def _checkName(self, name):
2171 """Check if a vm name is valid. Valid names contain alphabetic
2172 characters, digits, or characters in '_-.:/+'.
2173 The same name cannot be used for more than one vm at the same time.
2175 @param name: name
2176 @raise: VmError if invalid
2177 """
2178 from xen.xend import XendDomain
2180 if name is None or name == '':
2181 raise VmError('Missing VM Name')
2183 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2184 raise VmError('Invalid VM Name')
2186 dom = XendDomain.instance().domain_lookup_nr(name)
2187 if dom and dom.domid and dom.domid != self.domid:
2188 raise VmError("VM name '%s' already exists%s" %
2189 (name,
2190 dom.domid is not None and
2191 (" as domain %s" % str(dom.domid)) or ""))
2194 def update(self, info = None, refresh = True):
2195 """Update with info from xc.domain_getinfo().
2196 """
2197 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2198 str(self.domid))
2200 if not info:
2201 info = dom_get(self.domid)
2202 if not info:
2203 return
2205 if info["maxmem_kb"] < 0:
2206 info["maxmem_kb"] = XendNode.instance() \
2207 .physinfo_dict()['total_memory'] * 1024
2209 #ssidref field not used any longer
2210 if 'ssidref' in info:
2211 info.pop('ssidref')
2213 # make sure state is reset for info
2214 # TODO: we should eventually get rid of old_dom_states
2216 self.info.update_config(info)
2217 self._update_consoles()
2219 if refresh:
2220 self.refreshShutdown(info)
2222 log.trace("XendDomainInfo.update done on domain %s: %s",
2223 str(self.domid), self.info)
2225 def sxpr(self, ignore_store = False, legacy_only = True):
2226 result = self.info.to_sxp(domain = self,
2227 ignore_devices = ignore_store,
2228 legacy_only = legacy_only)
2230 #if not ignore_store and self.dompath:
2231 # vnc_port = self.readDom('console/vnc-port')
2232 # if vnc_port is not None:
2233 # result.append(['device',
2234 # ['console', ['vnc-port', str(vnc_port)]]])
2236 return result
2238 # Xen API
2239 # ----------------------------------------------------------------
2241 def get_uuid(self):
2242 dom_uuid = self.info.get('uuid')
2243 if not dom_uuid: # if it doesn't exist, make one up
2244 dom_uuid = uuid.createString()
2245 self.info['uuid'] = dom_uuid
2246 return dom_uuid
2248 def get_memory_static_max(self):
2249 return self.info.get('memory_static_max', 0)
2250 def get_memory_static_min(self):
2251 return self.info.get('memory_static_min', 0)
2252 def get_memory_dynamic_max(self):
2253 return self.info.get('memory_dynamic_max', 0)
2254 def get_memory_dynamic_min(self):
2255 return self.info.get('memory_dynamic_min', 0)
2257 # only update memory-related config values if they maintain sanity
2258 def _safe_set_memory(self, key, newval):
2259 oldval = self.info.get(key, 0)
2260 try:
2261 self.info[key] = newval
2262 self.info._memory_sanity_check()
2263 except Exception, ex:
2264 self.info[key] = oldval
2265 raise
2267 def set_memory_static_max(self, val):
2268 self._safe_set_memory('memory_static_max', val)
2269 def set_memory_static_min(self, val):
2270 self._safe_set_memory('memory_static_min', val)
2271 def set_memory_dynamic_max(self, val):
2272 self._safe_set_memory('memory_dynamic_max', val)
2273 def set_memory_dynamic_min(self, val):
2274 self._safe_set_memory('memory_dynamic_min', val)
2276 def get_vcpus_params(self):
2277 if self.getDomid() is None:
2278 return self.info['vcpus_params']
2280 retval = xc.sched_credit_domain_get(self.getDomid())
2281 return retval
2282 def get_power_state(self):
2283 return XEN_API_VM_POWER_STATE[self._stateGet()]
2284 def get_platform(self):
2285 return self.info.get('platform', {})
2286 def get_pci_bus(self):
2287 return self.info.get('pci_bus', '')
2288 def get_tools_version(self):
2289 return self.info.get('tools_version', {})
2290 def get_metrics(self):
2291 return self.metrics.get_uuid();
2294 def get_security_label(self, xspol=None):
2295 import xen.util.xsm.xsm as security
2296 label = security.get_security_label(self, xspol)
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__