ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 17129:450be98ef36b

Fix xm mem-max command to restore previous setting if xc.domain_setmaxmem fails.
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Feb 26 15:02:26 2008 +0000 (2008-02-26)
parents c6eeb71a85cf
children b58180cf8ab8
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, SetTarget, 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 """
77 from xen.xend import XendDomain
78 domconfig = XendConfig.XendConfig(sxp_obj = config)
79 othervm = XendDomain.instance().domain_lookup_nr(domconfig["name_label"])
80 if othervm is None or othervm.domid is None:
81 othervm = XendDomain.instance().domain_lookup_nr(domconfig["uuid"])
82 if othervm is not None and othervm.domid is not None:
83 raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid))
84 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
85 vm = XendDomainInfo(domconfig)
86 try:
87 vm.start()
88 except:
89 log.exception('Domain construction failed')
90 vm.destroy()
91 raise
93 return vm
95 def create_from_dict(config_dict):
96 """Creates and start a VM using the supplied configuration.
98 @param config_dict: An configuration dictionary.
100 @rtype: XendDomainInfo
101 @return: An up and running XendDomainInfo instance
102 @raise VmError: Invalid configuration or failure to start.
103 """
105 log.debug("XendDomainInfo.create_from_dict(%s)",
106 scrub_password(config_dict))
107 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
108 try:
109 vm.start()
110 except:
111 log.exception('Domain construction failed')
112 vm.destroy()
113 raise
114 return vm
116 def recreate(info, priv):
117 """Create the VM object for an existing domain. The domain must not
118 be dying, as the paths in the store should already have been removed,
119 and asking us to recreate them causes problems.
121 @param xeninfo: Parsed configuration
122 @type xeninfo: Dictionary
123 @param priv: Is a privileged domain (Dom 0)
124 @type priv: bool
126 @rtype: XendDomainInfo
127 @return: A up and running XendDomainInfo instance
128 @raise VmError: Invalid configuration.
129 @raise XendError: Errors with configuration.
130 """
132 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
134 assert not info['dying']
136 xeninfo = XendConfig.XendConfig(dominfo = info)
137 xeninfo['is_control_domain'] = priv
138 xeninfo['is_a_template'] = False
139 domid = xeninfo['domid']
140 uuid1 = uuid.fromString(xeninfo['uuid'])
141 needs_reinitialising = False
143 dompath = GetDomainPath(domid)
144 if not dompath:
145 raise XendError('No domain path in store for existing '
146 'domain %d' % domid)
148 log.info("Recreating domain %d, UUID %s. at %s" %
149 (domid, xeninfo['uuid'], dompath))
151 # need to verify the path and uuid if not Domain-0
152 # if the required uuid and vm aren't set, then that means
153 # we need to recreate the dom with our own values
154 #
155 # NOTE: this is probably not desirable, really we should just
156 # abort or ignore, but there may be cases where xenstore's
157 # entry disappears (eg. xenstore-rm /)
158 #
159 try:
160 vmpath = xstransact.Read(dompath, "vm")
161 if not vmpath:
162 if not priv:
163 log.warn('/local/domain/%d/vm is missing. recreate is '
164 'confused, trying our best to recover' % domid)
165 needs_reinitialising = True
166 raise XendError('reinit')
168 uuid2_str = xstransact.Read(vmpath, "uuid")
169 if not uuid2_str:
170 log.warn('%s/uuid/ is missing. recreate is confused, '
171 'trying our best to recover' % vmpath)
172 needs_reinitialising = True
173 raise XendError('reinit')
175 uuid2 = uuid.fromString(uuid2_str)
176 if uuid1 != uuid2:
177 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
178 'Trying out best to recover' % domid)
179 needs_reinitialising = True
180 except XendError:
181 pass # our best shot at 'goto' in python :)
183 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv,
184 vmpath = vmpath)
186 if needs_reinitialising:
187 vm._recreateDom()
188 vm._removeVm()
189 vm._storeVmDetails()
190 vm._storeDomDetails()
192 vm.image = image.create(vm, vm.info)
193 vm.image.recreate()
195 vm._registerWatches()
196 vm.refreshShutdown(xeninfo)
198 # register the domain in the list
199 from xen.xend import XendDomain
200 XendDomain.instance().add_domain(vm)
202 return vm
205 def restore(config):
206 """Create a domain and a VM object to do a restore.
208 @param config: Domain SXP configuration
209 @type config: list of lists. (see C{create})
211 @rtype: XendDomainInfo
212 @return: A up and running XendDomainInfo instance
213 @raise VmError: Invalid configuration or failure to start.
214 @raise XendError: Errors with configuration.
215 """
217 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
218 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
219 resume = True)
220 try:
221 vm.resume()
222 return vm
223 except:
224 vm.destroy()
225 raise
227 def createDormant(domconfig):
228 """Create a dormant/inactive XenDomainInfo without creating VM.
229 This is for creating instances of persistent domains that are not
230 yet start.
232 @param domconfig: Parsed configuration
233 @type domconfig: XendConfig object
235 @rtype: XendDomainInfo
236 @return: A up and running XendDomainInfo instance
237 @raise XendError: Errors with configuration.
238 """
240 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
242 # domid does not make sense for non-running domains.
243 domconfig.pop('domid', None)
244 vm = XendDomainInfo(domconfig)
245 return vm
247 def domain_by_name(name):
248 """Get domain by name
250 @params name: Name of the domain
251 @type name: string
252 @return: XendDomainInfo or None
253 """
254 from xen.xend import XendDomain
255 return XendDomain.instance().domain_lookup_by_name_nr(name)
258 def shutdown_reason(code):
259 """Get a shutdown reason from a code.
261 @param code: shutdown code
262 @type code: int
263 @return: shutdown reason
264 @rtype: string
265 """
266 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
268 def dom_get(dom):
269 """Get info from xen for an existing domain.
271 @param dom: domain id
272 @type dom: int
273 @return: info or None
274 @rtype: dictionary
275 """
276 try:
277 domlist = xc.domain_getinfo(dom, 1)
278 if domlist and dom == domlist[0]['domid']:
279 return domlist[0]
280 except Exception, err:
281 # ignore missing domain
282 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
283 return None
286 class XendDomainInfo:
287 """An object represents a domain.
289 @TODO: try to unify dom and domid, they mean the same thing, but
290 xc refers to it as dom, and everywhere else, including
291 xenstore it is domid. The best way is to change xc's
292 python interface.
294 @ivar info: Parsed configuration
295 @type info: dictionary
296 @ivar domid: Domain ID (if VM has started)
297 @type domid: int or None
298 @ivar vmpath: XenStore path to this VM.
299 @type vmpath: string
300 @ivar dompath: XenStore path to this Domain.
301 @type dompath: string
302 @ivar image: Reference to the VM Image.
303 @type image: xen.xend.image.ImageHandler
304 @ivar store_port: event channel to xenstored
305 @type store_port: int
306 @ivar console_port: event channel to xenconsoled
307 @type console_port: int
308 @ivar store_mfn: xenstored mfn
309 @type store_mfn: int
310 @ivar console_mfn: xenconsoled mfn
311 @type console_mfn: int
312 @ivar notes: OS image notes
313 @type notes: dictionary
314 @ivar vmWatch: reference to a watch on the xenstored vmpath
315 @type vmWatch: xen.xend.xenstore.xswatch
316 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
317 @type shutdownWatch: xen.xend.xenstore.xswatch
318 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
319 @type shutdownStartTime: float or None
320 # @ivar state: Domain state
321 # @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
322 @ivar state_updated: lock for self.state
323 @type state_updated: threading.Condition
324 @ivar refresh_shutdown_lock: lock for polling shutdown state
325 @type refresh_shutdown_lock: threading.Condition
326 @ivar _deviceControllers: device controller cache for this domain
327 @type _deviceControllers: dict 'string' to DevControllers
328 """
330 def __init__(self, info, domid = None, dompath = None, augment = False,
331 priv = False, resume = False, vmpath = None):
332 """Constructor for a domain
334 @param info: parsed configuration
335 @type info: dictionary
336 @keyword domid: Set initial domain id (if any)
337 @type domid: int
338 @keyword dompath: Set initial dompath (if any)
339 @type dompath: string
340 @keyword augment: Augment given info with xenstored VM info
341 @type augment: bool
342 @keyword priv: Is a privileged domain (Dom 0)
343 @type priv: bool
344 @keyword resume: Is this domain being resumed?
345 @type resume: bool
346 """
348 self.info = info
349 if domid == None:
350 self.domid = self.info.get('domid')
351 else:
352 self.domid = domid
354 #REMOVE: uuid is now generated in XendConfig
355 #if not self._infoIsSet('uuid'):
356 # self.info['uuid'] = uuid.toString(uuid.create())
358 # Find a unique /vm/<uuid>/<integer> path if not specified.
359 # This avoids conflict between pre-/post-migrate domains when doing
360 # localhost relocation.
361 self.vmpath = vmpath
362 i = 0
363 while self.vmpath == None:
364 self.vmpath = XS_VMROOT + self.info['uuid']
365 if i != 0:
366 self.vmpath = self.vmpath + '-' + str(i)
367 try:
368 if self._readVm("uuid"):
369 self.vmpath = None
370 i = i + 1
371 except:
372 pass
374 self.dompath = dompath
376 self.image = None
377 self.store_port = None
378 self.store_mfn = None
379 self.console_port = None
380 self.console_mfn = None
382 self.native_protocol = None
384 self.vmWatch = None
385 self.shutdownWatch = None
386 self.shutdownStartTime = None
387 self._resume = resume
389 self.state_updated = threading.Condition()
390 self.refresh_shutdown_lock = threading.Condition()
391 self._stateSet(DOM_STATE_HALTED)
393 self._deviceControllers = {}
395 for state in DOM_STATES_OLD:
396 self.info[state] = 0
398 if augment:
399 self._augmentInfo(priv)
401 self._checkName(self.info['name_label'])
403 self.metrics = XendVMMetrics(uuid.createString(), self)
406 #
407 # Public functions available through XMLRPC
408 #
411 def start(self, is_managed = False):
412 """Attempts to start the VM by do the appropriate
413 initialisation if it not started.
414 """
415 from xen.xend import XendDomain
417 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED, XEN_API_VM_POWER_STATE_CRASHED):
418 try:
419 XendTask.log_progress(0, 30, self._constructDomain)
420 XendTask.log_progress(31, 60, self._initDomain)
422 XendTask.log_progress(61, 70, self._storeVmDetails)
423 XendTask.log_progress(71, 80, self._storeDomDetails)
424 XendTask.log_progress(81, 90, self._registerWatches)
425 XendTask.log_progress(91, 100, self.refreshShutdown)
427 xendomains = XendDomain.instance()
428 xennode = XendNode.instance()
430 # save running configuration if XendDomains believe domain is
431 # persistent
432 if is_managed:
433 xendomains.managed_config_save(self)
435 if xennode.xenschedinfo() == 'credit':
436 xendomains.domain_sched_credit_set(self.getDomid(),
437 self.getWeight(),
438 self.getCap())
439 except:
440 log.exception('VM start failed')
441 self.destroy()
442 raise
443 else:
444 raise XendError('VM already running')
446 def resume(self):
447 """Resumes a domain that has come back from suspension."""
448 state = self._stateGet()
449 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
450 try:
451 self._constructDomain()
452 self._storeVmDetails()
453 self._createDevices()
454 self._createChannels()
455 self._storeDomDetails()
456 self._endRestore()
457 except:
458 log.exception('VM resume failed')
459 self.destroy()
460 raise
461 else:
462 raise XendError('VM is not susupened; it is %s'
463 % XEN_API_VM_POWER_STATE[state])
465 def shutdown(self, reason):
466 """Shutdown a domain by signalling this via xenstored."""
467 log.debug('XendDomainInfo.shutdown(%s)', reason)
468 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
469 raise XendError('Domain cannot be shutdown')
471 if self.domid == 0:
472 raise XendError('Domain 0 cannot be shutdown')
474 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
475 raise XendError('Invalid reason: %s' % reason)
476 self._removeVm('xend/previous_restart_time')
477 self.storeDom("control/shutdown", reason)
479 # HVM domain shuts itself down only if it has PV drivers
480 if self.info.is_hvm():
481 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
482 if not hvm_pvdrv:
483 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
484 log.info("HVM save:remote shutdown dom %d!", self.domid)
485 xc.domain_shutdown(self.domid, code)
487 def pause(self):
488 """Pause domain
490 @raise XendError: Failed pausing a domain
491 """
492 try:
493 xc.domain_pause(self.domid)
494 self._stateSet(DOM_STATE_PAUSED)
495 except Exception, ex:
496 log.exception(ex)
497 raise XendError("Domain unable to be paused: %s" % str(ex))
499 def unpause(self):
500 """Unpause domain
502 @raise XendError: Failed unpausing a domain
503 """
504 try:
505 xc.domain_unpause(self.domid)
506 self._stateSet(DOM_STATE_RUNNING)
507 except Exception, ex:
508 log.exception(ex)
509 raise XendError("Domain unable to be unpaused: %s" % str(ex))
511 def send_sysrq(self, key):
512 """ Send a Sysrq equivalent key via xenstored."""
513 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
514 raise XendError("Domain '%s' is not started" % self.info['name_label'])
516 asserts.isCharConvertible(key)
517 self.storeDom("control/sysrq", '%c' % key)
519 def sync_pcidev_info(self):
521 if not self.info.is_hvm():
522 return
524 devid = '0'
525 dev_info = self._getDeviceInfo_pci(devid)
526 if dev_info is None:
527 return
529 # get the virtual slot info from xenstore
530 dev_uuid = sxp.child_value(dev_info, 'uuid')
531 pci_conf = self.info['devices'][dev_uuid][1]
532 pci_devs = pci_conf['devs']
534 count = 0
535 vslots = None
536 while vslots is None and count < 20:
537 vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots"
538 % (self.getDomid(), devid))
539 time.sleep(0.1)
540 count += 1
541 if vslots is None:
542 log.error("Device model didn't tell the vslots for PCI device")
543 return
545 #delete last delim
546 if vslots[-1] == ";":
547 vslots = vslots[:-1]
549 slot_list = vslots.split(';')
550 if len(slot_list) != len(pci_devs):
551 log.error("Device model's pci dev num dismatch")
552 return
554 #update the vslot info
555 count = 0;
556 for x in pci_devs:
557 x['vslt'] = slot_list[count]
558 count += 1
561 def pci_device_create(self, dev_config):
562 log.debug("XendDomainInfo.pci_device_create: %s" % scrub_password(dev_config))
564 if not self.info.is_hvm():
565 raise VmError("only HVM guest support pci attach")
567 #all the PCI devs share one conf node
568 devid = '0'
570 dev_type = sxp.name(dev_config)
571 new_devs = sxp.child_value(dev_config, 'devs')
572 new_dev = new_devs[0]
573 dev_info = self._getDeviceInfo_pci(devid)#from self.info['devices']
575 #check conflict before trigger hotplug event
576 if dev_info is not None:
577 dev_uuid = sxp.child_value(dev_info, 'uuid')
578 pci_conf = self.info['devices'][dev_uuid][1]
579 pci_devs = pci_conf['devs']
580 for x in pci_devs:
581 if (int(x['vslt'], 16) == int(new_dev['vslt'], 16) and
582 int(x['vslt'], 16) != 0 ):
583 raise VmError("vslot %s already have a device." % (new_dev['vslt']))
585 if (int(x['domain'], 16) == int(new_dev['domain'], 16) and
586 int(x['bus'], 16) == int(new_dev['bus'], 16) and
587 int(x['slot'], 16) == int(new_dev['slot'], 16) and
588 int(x['func'], 16) == int(new_dev['func'], 16) ):
589 raise VmError("device is already inserted")
591 # Test whether the devices can be assigned with VT-d
592 pci_str = "%s, %s, %s, %s" % (new_dev['domain'],
593 new_dev['bus'],
594 new_dev['slot'],
595 new_dev['func'])
596 bdf = xc.test_assign_device(self.domid, pci_str)
597 if bdf != 0:
598 bus = (bdf >> 16) & 0xff
599 devfn = (bdf >> 8) & 0xff
600 dev = (devfn >> 3) & 0x1f
601 func = devfn & 0x7
602 raise VmError("Fail to hot insert device(%x:%x.%x): maybe VT-d is "
603 "not enabled, or the device is not exist, or it "
604 "has already been assigned to other domain"
605 % (bus, dev, func))
607 bdf_str = "%s:%s:%s.%s@%s" % (new_dev['domain'],
608 new_dev['bus'],
609 new_dev['slot'],
610 new_dev['func'],
611 new_dev['vslt'])
612 self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str)
614 # update the virtual pci slot
615 vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
616 % self.getDomid())
617 new_dev['vslt'] = vslt
619 if dev_info is None:
620 # create a new one from scrach
621 dev_cfg_sxp = [dev_type,
622 ['dev',
623 ['domain', new_dev['domain']],
624 ['bus', new_dev['bus']],
625 ['slot', new_dev['slot']],
626 ['func', new_dev['func']],
627 ['vslt', new_dev['vslt']]
628 ]]
629 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_cfg_sxp)
630 dev_config_dict = self.info['devices'][dev_uuid][1]
631 try:
632 dev_config_dict['devid'] = devid = \
633 self._createDevice(dev_type, dev_config_dict)
634 self._waitForDevice(dev_type, devid)
635 except VmError, ex:
636 raise ex
637 else:
638 # update the pci config to add the new dev
639 pci_devs.extend(new_devs)
640 self._reconfigureDevice('pci', devid, pci_conf)
642 return self.getDeviceController('pci').sxpr(devid)
644 def device_create(self, dev_config):
645 """Create a new device.
647 @param dev_config: device configuration
648 @type dev_config: SXP object (parsed config)
649 """
650 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
651 dev_type = sxp.name(dev_config)
653 if dev_type == 'pci':
654 rc = self.pci_device_create(dev_config)
655 return rc
657 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
658 dev_config_dict = self.info['devices'][dev_uuid][1]
659 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
661 if self.domid is not None:
662 try:
663 dev_config_dict['devid'] = devid = \
664 self._createDevice(dev_type, dev_config_dict)
665 self._waitForDevice(dev_type, devid)
666 except VmError, ex:
667 del self.info['devices'][dev_uuid]
668 if dev_type == 'tap':
669 self.info['vbd_refs'].remove(dev_uuid)
670 else:
671 self.info['%s_refs' % dev_type].remove(dev_uuid)
672 raise ex
673 else:
674 devid = None
676 xen.xend.XendDomain.instance().managed_config_save(self)
677 return self.getDeviceController(dev_type).sxpr(devid)
679 def device_configure(self, dev_sxp, devid = None):
680 """Configure an existing device.
682 @param dev_config: device configuration
683 @type dev_config: SXP object (parsed config)
684 @param devid: device id
685 @type devid: int
686 @return: Returns True if successfully updated device
687 @rtype: boolean
688 """
690 # convert device sxp to a dict
691 dev_class = sxp.name(dev_sxp)
692 dev_config = {}
693 for opt_val in dev_sxp[1:]:
694 try:
695 dev_config[opt_val[0]] = opt_val[1]
696 except IndexError:
697 pass
699 # use DevController.reconfigureDevice to change device config
700 dev_control = self.getDeviceController(dev_class)
701 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
703 # update XendConfig with new device info
704 if dev_uuid:
705 self.info.device_update(dev_uuid, dev_sxp)
707 return True
709 def waitForDevices(self):
710 """Wait for this domain's configured devices to connect.
712 @raise VmError: if any device fails to initialise.
713 """
714 for devclass in XendDevices.valid_devices():
715 self.getDeviceController(devclass).waitForDevices()
717 def destroyPCIDevice(self, vslot):
718 log.debug("destroyPCIDevice called %s", vslot)
720 if not self.info.is_hvm():
721 raise VmError("only HVM guest support pci detach")
723 #all the PCI devs share one conf node
724 devid = '0'
725 vslot = int(vslot)
726 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
727 dev_uuid = sxp.child_value(dev_info, 'uuid')
729 #delete the pci bdf config under the pci device
730 pci_conf = self.info['devices'][dev_uuid][1]
731 pci_len = len(pci_conf['devs'])
733 #find the pass-through device with the virtual slot
734 devnum = 0
735 for x in pci_conf['devs']:
736 if int(x['vslt'], 16) == vslot:
737 break
738 devnum += 1
740 if devnum >= pci_len:
741 raise VmError("Device @ vslot 0x%x doesn't exist." % (vslot))
743 if vslot == 0:
744 raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
746 bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
747 log.info("destroyPCIDevice:%s:%s!", x, bdf_str)
749 self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str)
751 if pci_len > 1:
752 del pci_conf['devs'][devnum]
753 self._reconfigureDevice('pci', devid, pci_conf)
754 else:
755 self.getDeviceController('pci').destroyDevice(devid, True)
756 del self.info['devices'][dev_uuid]
757 platform = self.info['platform']
758 orig_dev_num = len(platform['pci'])
760 #need remove the pci config
761 #TODO:can use this to keep some info to ask high level management tools to hot insert a new passthrough dev after migration
762 if orig_dev_num != 0:
763 # platform['pci'] = ["%dDEVs" % orig_dev_num]
764 platform['pci'] = []
766 return 0
768 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
769 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
770 deviceClass, devid)
772 if deviceClass == 'dpci':
773 rc = self.destroyPCIDevice(devid)
774 return rc
776 if rm_cfg:
777 # Convert devid to device number. A device number is
778 # needed to remove its configuration.
779 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
781 # Save current sxprs. A device number and a backend
782 # path are needed to remove its configuration but sxprs
783 # do not have those after calling destroyDevice.
784 sxprs = self.getDeviceSxprs(deviceClass)
786 rc = None
787 if self.domid is not None:
788 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
789 if not force and rm_cfg:
790 # The backend path, other than the device itself,
791 # has to be passed because its accompanied frontend
792 # path may be void until its removal is actually
793 # issued. It is probable because destroyDevice is
794 # issued first.
795 for dev_num, dev_info in sxprs:
796 dev_num = int(dev_num)
797 if dev_num == dev:
798 for x in dev_info:
799 if x[0] == 'backend':
800 backend = x[1]
801 break
802 break
803 self._waitForDevice_destroy(deviceClass, devid, backend)
805 if rm_cfg:
806 if deviceClass == 'vif':
807 if self.domid is not None:
808 for dev_num, dev_info in sxprs:
809 dev_num = int(dev_num)
810 if dev_num == dev:
811 for x in dev_info:
812 if x[0] == 'mac':
813 mac = x[1]
814 break
815 break
816 dev_info = self._getDeviceInfo_vif(mac)
817 else:
818 _, dev_info = sxprs[dev]
819 else: # 'vbd' or 'tap'
820 dev_info = self._getDeviceInfo_vbd(dev)
821 # To remove the UUID of the device from refs,
822 # deviceClass must be always 'vbd'.
823 deviceClass = 'vbd'
824 if dev_info is None:
825 raise XendError("Device %s is not defined" % devid)
827 dev_uuid = sxp.child_value(dev_info, 'uuid')
828 del self.info['devices'][dev_uuid]
829 self.info['%s_refs' % deviceClass].remove(dev_uuid)
830 xen.xend.XendDomain.instance().managed_config_save(self)
832 return rc
834 def getDeviceSxprs(self, deviceClass):
835 if deviceClass == 'pci':
836 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
837 if dev_info is None:
838 return []
839 dev_uuid = sxp.child_value(dev_info, 'uuid')
840 pci_devs = self.info['devices'][dev_uuid][1]['devs']
841 pci_len = len(pci_devs)
842 return pci_devs
843 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
844 return self.getDeviceController(deviceClass).sxprs()
845 else:
846 sxprs = []
847 dev_num = 0
848 for dev_type, dev_info in self.info.all_devices_sxpr():
849 if dev_type == deviceClass:
850 sxprs.append([dev_num, dev_info])
851 dev_num += 1
852 return sxprs
854 def getBlockDeviceClass(self, devid):
855 # To get a device number from the devid,
856 # we temporarily use the device controller of VBD.
857 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
858 dev_info = self._getDeviceInfo_vbd(dev)
859 if dev_info:
860 return dev_info[0]
862 def _getDeviceInfo_vif(self, mac):
863 for dev_type, dev_info in self.info.all_devices_sxpr():
864 if dev_type != 'vif':
865 continue
866 if mac == sxp.child_value(dev_info, 'mac'):
867 return dev_info
869 def _getDeviceInfo_vbd(self, devid):
870 for dev_type, dev_info in self.info.all_devices_sxpr():
871 if dev_type != 'vbd' and dev_type != 'tap':
872 continue
873 dev = sxp.child_value(dev_info, 'dev')
874 dev = dev.split(':')[0]
875 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
876 if devid == dev:
877 return dev_info
879 def _getDeviceInfo_pci(self, devid):
880 for dev_type, dev_info in self.info.all_devices_sxpr():
881 if dev_type != 'pci':
882 continue
883 return dev_info
884 return None
886 def setMemoryTarget(self, target):
887 """Set the memory target of this domain.
888 @param target: In MiB.
889 """
890 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
891 self.info['name_label'], str(self.domid), target)
893 MiB = 1024 * 1024
894 self._safe_set_memory('memory_dynamic_min', target * MiB)
895 self._safe_set_memory('memory_dynamic_max', target * MiB)
897 if self.domid >= 0:
898 self.storeVm("memory", target)
899 self.storeDom("memory/target", target << 10)
900 xen.xend.XendDomain.instance().managed_config_save(self)
902 def setMemoryMaximum(self, limit):
903 """Set the maximum memory limit of this domain
904 @param limit: In MiB.
905 """
906 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
907 self.info['name_label'], str(self.domid), limit)
909 maxmem_cur = self.get_memory_static_max()
910 MiB = 1024 * 1024
911 self._safe_set_memory('memory_static_max', limit * MiB)
913 if self.domid >= 0:
914 maxmem = int(limit) * 1024
915 try:
916 return xc.domain_setmaxmem(self.domid, maxmem)
917 except Exception, ex:
918 self._safe_set_memory('memory_static_max', maxmem_cur)
919 raise XendError(str(ex))
920 xen.xend.XendDomain.instance().managed_config_save(self)
923 def getVCPUInfo(self):
924 try:
925 # We include the domain name and ID, to help xm.
926 sxpr = ['domain',
927 ['domid', self.domid],
928 ['name', self.info['name_label']],
929 ['vcpu_count', self.info['VCPUs_max']]]
931 for i in range(0, self.info['VCPUs_max']):
932 if self.domid is not None:
933 info = xc.vcpu_getinfo(self.domid, i)
935 sxpr.append(['vcpu',
936 ['number', i],
937 ['online', info['online']],
938 ['blocked', info['blocked']],
939 ['running', info['running']],
940 ['cpu_time', info['cpu_time'] / 1e9],
941 ['cpu', info['cpu']],
942 ['cpumap', info['cpumap']]])
943 else:
944 sxpr.append(['vcpu',
945 ['number', i],
946 ['online', 0],
947 ['blocked', 0],
948 ['running', 0],
949 ['cpu_time', 0.0],
950 ['cpu', -1],
951 ['cpumap', self.info['cpus'] and \
952 self.info['cpus'] or range(64)]])
954 return sxpr
956 except RuntimeError, exn:
957 raise XendError(str(exn))
960 def getDomInfo(self):
961 return dom_get(self.domid)
963 #
964 # internal functions ... TODO: re-categorised
965 #
967 def _augmentInfo(self, priv):
968 """Augment self.info, as given to us through L{recreate}, with
969 values taken from the store. This recovers those values known
970 to xend but not to the hypervisor.
971 """
972 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
973 if priv:
974 augment_entries.remove('memory')
975 augment_entries.remove('maxmem')
976 augment_entries.remove('vcpus')
977 augment_entries.remove('vcpu_avail')
979 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
980 for k in augment_entries])
982 # make returned lists into a dictionary
983 vm_config = dict(zip(augment_entries, vm_config))
985 for arg in augment_entries:
986 val = vm_config[arg]
987 if val != None:
988 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
989 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
990 self.info[xapiarg] = val
991 elif arg == "memory":
992 self.info["static_memory_min"] = val
993 elif arg == "maxmem":
994 self.info["static_memory_max"] = val
995 else:
996 self.info[arg] = val
998 # For dom0, we ignore any stored value for the vcpus fields, and
999 # read the current value from Xen instead. This allows boot-time
1000 # settings to take precedence over any entries in the store.
1001 if priv:
1002 xeninfo = dom_get(self.domid)
1003 self.info['VCPUs_max'] = xeninfo['online_vcpus']
1004 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
1006 # read image value
1007 image_sxp = self._readVm('image')
1008 if image_sxp:
1009 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1011 # read devices
1012 devices = []
1013 for devclass in XendDevices.valid_devices():
1014 devconfig = self.getDeviceController(devclass).configurations()
1015 if devconfig:
1016 devices.extend(devconfig)
1018 if not self.info['devices'] and devices is not None:
1019 for device in devices:
1020 self.info.device_add(device[0], cfg_sxp = device)
1022 self._update_consoles()
1024 def _update_consoles(self, transaction = None):
1025 if self.domid == None or self.domid == 0:
1026 return
1028 # Update VT100 port if it exists
1029 if transaction is None:
1030 self.console_port = self.readDom('console/port')
1031 else:
1032 self.console_port = self.readDomTxn(transaction, 'console/port')
1033 if self.console_port is not None:
1034 serial_consoles = self.info.console_get_all('vt100')
1035 if not serial_consoles:
1036 cfg = self.info.console_add('vt100', self.console_port)
1037 self._createDevice('console', cfg)
1038 else:
1039 console_uuid = serial_consoles[0].get('uuid')
1040 self.info.console_update(console_uuid, 'location',
1041 self.console_port)
1044 # Update VNC port if it exists and write to xenstore
1045 if transaction is None:
1046 vnc_port = self.readDom('console/vnc-port')
1047 else:
1048 vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
1049 if vnc_port is not None:
1050 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
1051 if dev_type == 'vfb':
1052 old_location = dev_info.get('location')
1053 listen_host = dev_info.get('vnclisten', 'localhost')
1054 new_location = '%s:%s' % (listen_host, str(vnc_port))
1055 if old_location == new_location:
1056 break
1058 dev_info['location'] = new_location
1059 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
1060 vfb_ctrl = self.getDeviceController('vfb')
1061 vfb_ctrl.reconfigureDevice(0, dev_info)
1062 break
1065 # Function to update xenstore /vm/*
1068 def _readVm(self, *args):
1069 return xstransact.Read(self.vmpath, *args)
1071 def _writeVm(self, *args):
1072 return xstransact.Write(self.vmpath, *args)
1074 def _removeVm(self, *args):
1075 return xstransact.Remove(self.vmpath, *args)
1077 def _gatherVm(self, *args):
1078 return xstransact.Gather(self.vmpath, *args)
1080 def _listRecursiveVm(self, *args):
1081 return xstransact.ListRecursive(self.vmpath, *args)
1083 def storeVm(self, *args):
1084 return xstransact.Store(self.vmpath, *args)
1086 def permissionsVm(self, *args):
1087 return xstransact.SetPermissions(self.vmpath, *args)
1090 def _readVmTxn(self, transaction, *args):
1091 paths = map(lambda x: self.vmpath + "/" + x, args)
1092 return transaction.read(*paths)
1094 def _writeVmTxn(self, transaction, *args):
1095 paths = map(lambda x: self.vmpath + "/" + x, args)
1096 return transaction.write(*paths)
1098 def _removeVmTxn(self, transaction, *args):
1099 paths = map(lambda x: self.vmpath + "/" + x, args)
1100 return transaction.remove(*paths)
1102 def _gatherVmTxn(self, transaction, *args):
1103 paths = map(lambda x: self.vmpath + "/" + x, args)
1104 return transaction.gather(paths)
1106 def storeVmTxn(self, transaction, *args):
1107 paths = map(lambda x: self.vmpath + "/" + x, args)
1108 return transaction.store(*paths)
1110 def permissionsVmTxn(self, transaction, *args):
1111 paths = map(lambda x: self.vmpath + "/" + x, args)
1112 return transaction.set_permissions(*paths)
1115 # Function to update xenstore /dom/*
1118 def readDom(self, *args):
1119 return xstransact.Read(self.dompath, *args)
1121 def gatherDom(self, *args):
1122 return xstransact.Gather(self.dompath, *args)
1124 def _writeDom(self, *args):
1125 return xstransact.Write(self.dompath, *args)
1127 def _removeDom(self, *args):
1128 return xstransact.Remove(self.dompath, *args)
1130 def storeDom(self, *args):
1131 return xstransact.Store(self.dompath, *args)
1134 def readDomTxn(self, transaction, *args):
1135 paths = map(lambda x: self.dompath + "/" + x, args)
1136 return transaction.read(*paths)
1138 def gatherDomTxn(self, transaction, *args):
1139 paths = map(lambda x: self.dompath + "/" + x, args)
1140 return transaction.gather(*paths)
1142 def _writeDomTxn(self, transaction, *args):
1143 paths = map(lambda x: self.dompath + "/" + x, args)
1144 return transaction.write(*paths)
1146 def _removeDomTxn(self, transaction, *args):
1147 paths = map(lambda x: self.dompath + "/" + x, args)
1148 return transaction.remove(*paths)
1150 def storeDomTxn(self, transaction, *args):
1151 paths = map(lambda x: self.dompath + "/" + x, args)
1152 return transaction.store(*paths)
1155 def _recreateDom(self):
1156 complete(self.dompath, lambda t: self._recreateDomFunc(t))
1158 def _recreateDomFunc(self, t):
1159 t.remove()
1160 t.mkdir()
1161 t.set_permissions({'dom' : self.domid})
1162 t.write('vm', self.vmpath)
1164 def _storeDomDetails(self):
1165 to_store = {
1166 'domid': str(self.domid),
1167 'vm': self.vmpath,
1168 'name': self.info['name_label'],
1169 'console/limit': str(xoptions.get_console_limit() * 1024),
1170 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
1173 def f(n, v):
1174 if v is not None:
1175 if type(v) == bool:
1176 to_store[n] = v and "1" or "0"
1177 else:
1178 to_store[n] = str(v)
1180 # Figure out if we need to tell xenconsoled to ignore this guest's
1181 # console - device model will handle console if it is running
1182 constype = "ioemu"
1183 if 'device_model' not in self.info['platform']:
1184 constype = "xenconsoled"
1186 f('console/port', self.console_port)
1187 f('console/ring-ref', self.console_mfn)
1188 f('console/type', constype)
1189 f('store/port', self.store_port)
1190 f('store/ring-ref', self.store_mfn)
1192 if arch.type == "x86":
1193 f('control/platform-feature-multiprocessor-suspend', True)
1195 # elfnotes
1196 for n, v in self.info.get_notes().iteritems():
1197 n = n.lower().replace('_', '-')
1198 if n == 'features':
1199 for v in v.split('|'):
1200 v = v.replace('_', '-')
1201 if v.startswith('!'):
1202 f('image/%s/%s' % (n, v[1:]), False)
1203 else:
1204 f('image/%s/%s' % (n, v), True)
1205 else:
1206 f('image/%s' % n, v)
1208 if self.info.has_key('security_label'):
1209 f('security_label', self.info['security_label'])
1211 to_store.update(self._vcpuDomDetails())
1213 log.debug("Storing domain details: %s", scrub_password(to_store))
1215 self._writeDom(to_store)
1217 def _vcpuDomDetails(self):
1218 def availability(n):
1219 if self.info['vcpu_avail'] & (1 << n):
1220 return 'online'
1221 else:
1222 return 'offline'
1224 result = {}
1225 for v in range(0, self.info['VCPUs_max']):
1226 result["cpu/%d/availability" % v] = availability(v)
1227 return result
1230 # xenstore watches
1233 def _registerWatches(self):
1234 """Register a watch on this VM's entries in the store, and the
1235 domain's control/shutdown node, so that when they are changed
1236 externally, we keep up to date. This should only be called by {@link
1237 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1238 details have been written, but before the new instance is returned."""
1239 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1240 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1241 self._handleShutdownWatch)
1243 def _storeChanged(self, _):
1244 log.trace("XendDomainInfo.storeChanged");
1246 changed = False
1248 # Check whether values in the configuration have
1249 # changed in Xenstore.
1251 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1252 'rtc/timeoffset']
1254 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1255 for k in cfg_vm])
1257 # convert two lists into a python dictionary
1258 vm_details = dict(zip(cfg_vm, vm_details))
1260 if vm_details['rtc/timeoffset'] == None:
1261 vm_details['rtc/timeoffset'] = "0"
1263 for arg, val in vm_details.items():
1264 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1265 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1266 if val != None and val != self.info[xapiarg]:
1267 self.info[xapiarg] = val
1268 changed = True
1269 elif arg == "memory":
1270 if val != None and val != self.info["static_memory_min"]:
1271 self.info["static_memory_min"] = val
1272 changed = True
1273 elif arg == "maxmem":
1274 if val != None and val != self.info["static_memory_max"]:
1275 self.info["static_memory_max"] = val
1276 changed = True
1278 # Check whether image definition has been updated
1279 image_sxp = self._readVm('image')
1280 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1281 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1282 changed = True
1284 # Check if the rtc offset has changes
1285 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1286 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1287 changed = True
1289 if changed:
1290 # Update the domain section of the store, as this contains some
1291 # parameters derived from the VM configuration.
1292 self._storeDomDetails()
1294 return 1
1296 def _handleShutdownWatch(self, _):
1297 log.debug('XendDomainInfo.handleShutdownWatch')
1299 reason = self.readDom('control/shutdown')
1301 if reason and reason != 'suspend':
1302 sst = self.readDom('xend/shutdown_start_time')
1303 now = time.time()
1304 if sst:
1305 self.shutdownStartTime = float(sst)
1306 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1307 else:
1308 self.shutdownStartTime = now
1309 self.storeDom('xend/shutdown_start_time', now)
1310 timeout = SHUTDOWN_TIMEOUT
1312 log.trace(
1313 "Scheduling refreshShutdown on domain %d in %ds.",
1314 self.domid, timeout)
1315 threading.Timer(timeout, self.refreshShutdown).start()
1317 return True
1321 # Public Attributes for the VM
1325 def getDomid(self):
1326 return self.domid
1328 def setName(self, name, to_store = True):
1329 self._checkName(name)
1330 self.info['name_label'] = name
1331 if to_store:
1332 self.storeVm("name", name)
1334 def getName(self):
1335 return self.info['name_label']
1337 def getDomainPath(self):
1338 return self.dompath
1340 def getShutdownReason(self):
1341 return self.readDom('control/shutdown')
1343 def getStorePort(self):
1344 """For use only by image.py and XendCheckpoint.py."""
1345 return self.store_port
1347 def getConsolePort(self):
1348 """For use only by image.py and XendCheckpoint.py"""
1349 return self.console_port
1351 def getFeatures(self):
1352 """For use only by image.py."""
1353 return self.info['features']
1355 def getVCpuCount(self):
1356 return self.info['VCPUs_max']
1358 def setVCpuCount(self, vcpus):
1359 if vcpus <= 0:
1360 raise XendError('Invalid VCPUs')
1362 self.info['vcpu_avail'] = (1 << vcpus) - 1
1363 if self.domid >= 0:
1364 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1365 # update dom differently depending on whether we are adjusting
1366 # vcpu number up or down, otherwise _vcpuDomDetails does not
1367 # disable the vcpus
1368 if self.info['VCPUs_max'] > vcpus:
1369 # decreasing
1370 self._writeDom(self._vcpuDomDetails())
1371 self.info['VCPUs_live'] = vcpus
1372 else:
1373 # same or increasing
1374 self.info['VCPUs_live'] = vcpus
1375 self._writeDom(self._vcpuDomDetails())
1376 else:
1377 self.info['VCPUs_max'] = vcpus
1378 xen.xend.XendDomain.instance().managed_config_save(self)
1379 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1380 vcpus)
1382 def getMemoryTarget(self):
1383 """Get this domain's target memory size, in KB."""
1384 return self.info['memory_dynamic_max'] / 1024
1386 def getMemoryMaximum(self):
1387 """Get this domain's maximum memory size, in KB."""
1388 # remember, info now stores memory in bytes
1389 return self.info['memory_static_max'] / 1024
1391 def getResume(self):
1392 return str(self._resume)
1394 def setResume(self, isresume):
1395 self._resume = isresume
1397 def getCap(self):
1398 return self.info['vcpus_params']['cap']
1400 def setCap(self, cpu_cap):
1401 self.info['vcpus_params']['cap'] = cpu_cap
1403 def getWeight(self):
1404 return self.info['vcpus_params']['weight']
1406 def setWeight(self, cpu_weight):
1407 self.info['vcpus_params']['weight'] = cpu_weight
1409 def setResume(self, state):
1410 self._resume = state
1412 def getRestartCount(self):
1413 return self._readVm('xend/restart_count')
1415 def refreshShutdown(self, xeninfo = None):
1416 """ Checks the domain for whether a shutdown is required.
1418 Called from XendDomainInfo and also image.py for HVM images.
1419 """
1421 # If set at the end of this method, a restart is required, with the
1422 # given reason. This restart has to be done out of the scope of
1423 # refresh_shutdown_lock.
1424 restart_reason = None
1426 self.refresh_shutdown_lock.acquire()
1427 try:
1428 if xeninfo is None:
1429 xeninfo = dom_get(self.domid)
1430 if xeninfo is None:
1431 # The domain no longer exists. This will occur if we have
1432 # scheduled a timer to check for shutdown timeouts and the
1433 # shutdown succeeded. It will also occur if someone
1434 # destroys a domain beneath us. We clean up the domain,
1435 # just in case, but we can't clean up the VM, because that
1436 # VM may have migrated to a different domain on this
1437 # machine.
1438 self.cleanupDomain()
1439 self._stateSet(DOM_STATE_HALTED)
1440 return
1442 if xeninfo['dying']:
1443 # Dying means that a domain has been destroyed, but has not
1444 # yet been cleaned up by Xen. This state could persist
1445 # indefinitely if, for example, another domain has some of its
1446 # pages mapped. We might like to diagnose this problem in the
1447 # future, but for now all we do is make sure that it's not us
1448 # holding the pages, by calling cleanupDomain. We can't
1449 # clean up the VM, as above.
1450 self.cleanupDomain()
1451 self._stateSet(DOM_STATE_SHUTDOWN)
1452 return
1454 elif xeninfo['crashed']:
1455 if self.readDom('xend/shutdown_completed'):
1456 # We've seen this shutdown already, but we are preserving
1457 # the domain for debugging. Leave it alone.
1458 return
1460 log.warn('Domain has crashed: name=%s id=%d.',
1461 self.info['name_label'], self.domid)
1462 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1464 restart_reason = 'crash'
1465 self._stateSet(DOM_STATE_HALTED)
1467 elif xeninfo['shutdown']:
1468 self._stateSet(DOM_STATE_SHUTDOWN)
1469 if self.readDom('xend/shutdown_completed'):
1470 # We've seen this shutdown already, but we are preserving
1471 # the domain for debugging. Leave it alone.
1472 return
1474 else:
1475 reason = shutdown_reason(xeninfo['shutdown_reason'])
1477 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1478 self.info['name_label'], self.domid, reason)
1479 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1481 self._clearRestart()
1483 if reason == 'suspend':
1484 self._stateSet(DOM_STATE_SUSPENDED)
1485 # Don't destroy the domain. XendCheckpoint will do
1486 # this once it has finished. However, stop watching
1487 # the VM path now, otherwise we will end up with one
1488 # watch for the old domain, and one for the new.
1489 self._unwatchVm()
1490 elif reason in ('poweroff', 'reboot'):
1491 restart_reason = reason
1492 else:
1493 self.destroy()
1495 elif self.dompath is None:
1496 # We have yet to manage to call introduceDomain on this
1497 # domain. This can happen if a restore is in progress, or has
1498 # failed. Ignore this domain.
1499 pass
1500 else:
1501 # Domain is alive. If we are shutting it down, log a message
1502 # if it seems unresponsive.
1503 if xeninfo['paused']:
1504 self._stateSet(DOM_STATE_PAUSED)
1505 else:
1506 self._stateSet(DOM_STATE_RUNNING)
1508 if self.shutdownStartTime:
1509 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1510 self.shutdownStartTime)
1511 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1512 log.info(
1513 "Domain shutdown timeout expired: name=%s id=%s",
1514 self.info['name_label'], self.domid)
1515 self.storeDom('xend/unresponsive', 'True')
1516 finally:
1517 self.refresh_shutdown_lock.release()
1519 if restart_reason:
1520 threading.Thread(target = self._maybeRestart,
1521 args = (restart_reason,)).start()
1525 # Restart functions - handling whether we come back up on shutdown.
1528 def _clearRestart(self):
1529 self._removeDom("xend/shutdown_start_time")
1531 def _maybeDumpCore(self, reason):
1532 if reason == 'crash':
1533 if xoptions.get_enable_dump() or self.get_on_crash() \
1534 in ['coredump_and_destroy', 'coredump_and_restart']:
1535 try:
1536 self.dumpCore()
1537 except XendError:
1538 # This error has been logged -- there's nothing more
1539 # we can do in this context.
1540 pass
1542 def _maybeRestart(self, reason):
1543 # Before taking configured action, dump core if configured to do so.
1545 self._maybeDumpCore(reason)
1547 # Dispatch to the correct method based upon the configured on_{reason}
1548 # behaviour.
1549 actions = {"destroy" : self.destroy,
1550 "restart" : self._restart,
1551 "preserve" : self._preserve,
1552 "rename-restart" : self._renameRestart,
1553 "coredump-destroy" : self.destroy,
1554 "coredump-restart" : self._restart}
1556 action_conf = {
1557 'poweroff': 'actions_after_shutdown',
1558 'reboot': 'actions_after_reboot',
1559 'crash': 'actions_after_crash',
1562 action_target = self.info.get(action_conf.get(reason))
1563 func = actions.get(action_target, None)
1564 if func and callable(func):
1565 func()
1566 else:
1567 self.destroy() # default to destroy
1569 def _renameRestart(self):
1570 self._restart(True)
1572 def _restart(self, rename = False):
1573 """Restart the domain after it has exited.
1575 @param rename True if the old domain is to be renamed and preserved,
1576 False if it is to be destroyed.
1577 """
1578 from xen.xend import XendDomain
1580 if self._readVm(RESTART_IN_PROGRESS):
1581 log.error('Xend failed during restart of domain %s. '
1582 'Refusing to restart to avoid loops.',
1583 str(self.domid))
1584 self.destroy()
1585 return
1587 old_domid = self.domid
1588 self._writeVm(RESTART_IN_PROGRESS, 'True')
1590 now = time.time()
1591 rst = self._readVm('xend/previous_restart_time')
1592 if rst:
1593 rst = float(rst)
1594 timeout = now - rst
1595 if timeout < MINIMUM_RESTART_TIME:
1596 log.error(
1597 'VM %s restarting too fast (%f seconds since the last '
1598 'restart). Refusing to restart to avoid loops.',
1599 self.info['name_label'], timeout)
1600 self.destroy()
1601 return
1603 self._writeVm('xend/previous_restart_time', str(now))
1605 prev_vm_xend = self._listRecursiveVm('xend')
1606 new_dom_info = self.info
1607 try:
1608 if rename:
1609 new_dom_info = self._preserveForRestart()
1610 else:
1611 self._unwatchVm()
1612 self.destroy()
1614 # new_dom's VM will be the same as this domain's VM, except where
1615 # the rename flag has instructed us to call preserveForRestart.
1616 # In that case, it is important that we remove the
1617 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1618 # once the new one is available.
1620 new_dom = None
1621 try:
1622 new_dom = XendDomain.instance().domain_create_from_dict(
1623 new_dom_info)
1624 for x in prev_vm_xend[0][1]:
1625 new_dom._writeVm('xend/%s' % x[0], x[1])
1626 new_dom.waitForDevices()
1627 new_dom.unpause()
1628 rst_cnt = new_dom._readVm('xend/restart_count')
1629 rst_cnt = int(rst_cnt) + 1
1630 new_dom._writeVm('xend/restart_count', str(rst_cnt))
1631 new_dom._removeVm(RESTART_IN_PROGRESS)
1632 except:
1633 if new_dom:
1634 new_dom._removeVm(RESTART_IN_PROGRESS)
1635 new_dom.destroy()
1636 else:
1637 self._removeVm(RESTART_IN_PROGRESS)
1638 raise
1639 except:
1640 log.exception('Failed to restart domain %s.', str(old_domid))
1642 def _preserveForRestart(self):
1643 """Preserve a domain that has been shut down, by giving it a new UUID,
1644 cloning the VM details, and giving it a new name. This allows us to
1645 keep this domain for debugging, but restart a new one in its place
1646 preserving the restart semantics (name and UUID preserved).
1647 """
1649 new_uuid = uuid.createString()
1650 new_name = 'Domain-%s' % new_uuid
1651 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1652 self.info['name_label'], self.domid, self.info['uuid'],
1653 new_name, new_uuid)
1654 self._unwatchVm()
1655 self._releaseDevices()
1656 # Remove existing vm node in xenstore
1657 self._removeVm()
1658 new_dom_info = self.info.copy()
1659 new_dom_info['name_label'] = self.info['name_label']
1660 new_dom_info['uuid'] = self.info['uuid']
1661 self.info['name_label'] = new_name
1662 self.info['uuid'] = new_uuid
1663 self.vmpath = XS_VMROOT + new_uuid
1664 # Write out new vm node to xenstore
1665 self._storeVmDetails()
1666 self._preserve()
1667 return new_dom_info
1670 def _preserve(self):
1671 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1672 self.domid)
1673 self._unwatchVm()
1674 self.storeDom('xend/shutdown_completed', 'True')
1675 self._stateSet(DOM_STATE_HALTED)
1678 # Debugging ..
1681 def dumpCore(self, corefile = None):
1682 """Create a core dump for this domain.
1684 @raise: XendError if core dumping failed.
1685 """
1687 try:
1688 if not corefile:
1689 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1690 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1691 self.info['name_label'], self.domid)
1693 if os.path.isdir(corefile):
1694 raise XendError("Cannot dump core in a directory: %s" %
1695 corefile)
1697 xc.domain_dumpcore(self.domid, corefile)
1698 except RuntimeError, ex:
1699 corefile_incomp = corefile+'-incomplete'
1700 os.rename(corefile, corefile_incomp)
1701 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1702 self.domid, self.info['name_label'])
1703 raise XendError("Failed to dump core: %s" % str(ex))
1706 # Device creation/deletion functions
1709 def _createDevice(self, deviceClass, devConfig):
1710 return self.getDeviceController(deviceClass).createDevice(devConfig)
1712 def _waitForDevice(self, deviceClass, devid):
1713 return self.getDeviceController(deviceClass).waitForDevice(devid)
1715 def _waitForDeviceUUID(self, dev_uuid):
1716 deviceClass, config = self.info['devices'].get(dev_uuid)
1717 self._waitForDevice(deviceClass, config['devid'])
1719 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1720 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1721 devid, backpath)
1723 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1724 return self.getDeviceController(deviceClass).reconfigureDevice(
1725 devid, devconfig)
1727 def _createDevices(self):
1728 """Create the devices for a vm.
1730 @raise: VmError for invalid devices
1731 """
1732 ordered_refs = self.info.ordered_device_refs()
1733 for dev_uuid in ordered_refs:
1734 devclass, config = self.info['devices'][dev_uuid]
1735 if devclass in XendDevices.valid_devices():
1736 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1737 dev_uuid = config.get('uuid')
1738 devid = self._createDevice(devclass, config)
1740 # store devid in XendConfig for caching reasons
1741 if dev_uuid in self.info['devices']:
1742 self.info['devices'][dev_uuid][1]['devid'] = devid
1744 if self.image:
1745 self.image.createDeviceModel()
1747 #if have pass-through devs, need the virtual pci slots info from qemu
1748 self.sync_pcidev_info()
1750 def _releaseDevices(self, suspend = False):
1751 """Release all domain's devices. Nothrow guarantee."""
1752 if self.image:
1753 try:
1754 log.debug("Destroying device model")
1755 self.image.destroyDeviceModel()
1756 except Exception, e:
1757 log.exception("Device model destroy failed %s" % str(e))
1758 else:
1759 log.debug("No device model")
1761 log.debug("Releasing devices")
1762 t = xstransact("%s/device" % self.dompath)
1763 try:
1764 for devclass in XendDevices.valid_devices():
1765 for dev in t.list(devclass):
1766 try:
1767 log.debug("Removing %s", dev);
1768 self.destroyDevice(devclass, dev, False);
1769 except:
1770 # Log and swallow any exceptions in removal --
1771 # there's nothing more we can do.
1772 log.exception("Device release failed: %s; %s; %s",
1773 self.info['name_label'], devclass, dev)
1774 finally:
1775 t.abort()
1777 def getDeviceController(self, name):
1778 """Get the device controller for this domain, and if it
1779 doesn't exist, create it.
1781 @param name: device class name
1782 @type name: string
1783 @rtype: subclass of DevController
1784 """
1785 if name not in self._deviceControllers:
1786 devController = XendDevices.make_controller(name, self)
1787 if not devController:
1788 raise XendError("Unknown device type: %s" % name)
1789 self._deviceControllers[name] = devController
1791 return self._deviceControllers[name]
1794 # Migration functions (public)
1797 def testMigrateDevices(self, network, dst):
1798 """ Notify all device about intention of migration
1799 @raise: XendError for a device that cannot be migrated
1800 """
1801 for (n, c) in self.info.all_devices_sxpr():
1802 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1803 if rc != 0:
1804 raise XendError("Device of type '%s' refuses migration." % n)
1806 def migrateDevices(self, network, dst, step, domName=''):
1807 """Notify the devices about migration
1808 """
1809 ctr = 0
1810 try:
1811 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1812 self.migrateDevice(dev_type, dev_conf, network, dst,
1813 step, domName)
1814 ctr = ctr + 1
1815 except:
1816 for dev_type, dev_conf in self.info.all_devices_sxpr():
1817 if ctr == 0:
1818 step = step - 1
1819 ctr = ctr - 1
1820 self._recoverMigrateDevice(dev_type, dev_conf, network,
1821 dst, step, domName)
1822 raise
1824 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1825 step, domName=''):
1826 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1827 network, dst, step, domName)
1829 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1830 dst, step, domName=''):
1831 return self.getDeviceController(deviceClass).recover_migrate(
1832 deviceConfig, network, dst, step, domName)
1835 ## private:
1837 def _constructDomain(self):
1838 """Construct the domain.
1840 @raise: VmError on error
1841 """
1843 log.debug('XendDomainInfo.constructDomain')
1845 self.shutdownStartTime = None
1847 hap = 0
1848 hvm = self.info.is_hvm()
1849 if hvm:
1850 hap = self.info.is_hap()
1851 info = xc.xeninfo()
1852 if 'hvm' not in info['xen_caps']:
1853 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1854 "supported by your CPU and enabled in your "
1855 "BIOS?")
1857 # Hack to pre-reserve some memory for initial domain creation.
1858 # There is an implicit memory overhead for any domain creation. This
1859 # overhead is greater for some types of domain than others. For
1860 # example, an x86 HVM domain will have a default shadow-pagetable
1861 # allocation of 1MB. We free up 2MB here to be on the safe side.
1862 balloon.free(2*1024) # 2MB should be plenty
1864 ssidref = 0
1865 if security.on():
1866 ssidref = security.calc_dom_ssidref_from_info(self.info)
1867 if security.has_authorization(ssidref) == False:
1868 raise VmError("VM is not authorized to run.")
1870 try:
1871 self.domid = xc.domain_create(
1872 domid = 0,
1873 ssidref = ssidref,
1874 handle = uuid.fromString(self.info['uuid']),
1875 flags = (int(hvm) << 0) | (int(hap) << 1),
1876 target = self.info.target())
1877 except Exception, e:
1878 # may get here if due to ACM the operation is not permitted
1879 if security.on():
1880 raise VmError('Domain in conflict set with running domain?')
1882 if self.domid < 0:
1883 raise VmError('Creating domain failed: name=%s' %
1884 self.info['name_label'])
1886 self.dompath = GetDomainPath(self.domid)
1888 self._recreateDom()
1890 # Set timer configration of domain
1891 timer_mode = self.info["platform"].get("timer_mode")
1892 if hvm and timer_mode is not None:
1893 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
1894 long(timer_mode))
1896 # Optionally enable virtual HPET
1897 hpet = self.info["platform"].get("hpet")
1898 if hvm and hpet is not None:
1899 xc.hvm_set_param(self.domid, HVM_PARAM_HPET_ENABLED,
1900 long(hpet))
1902 # Set maximum number of vcpus in domain
1903 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1905 # Test whether the devices can be assigned with VT-d
1906 pci_str = str(self.info["platform"].get("pci"))
1907 if hvm and pci_str:
1908 bdf = xc.test_assign_device(self.domid, pci_str)
1909 if bdf != 0:
1910 bus = (bdf >> 16) & 0xff
1911 devfn = (bdf >> 8) & 0xff
1912 dev = (devfn >> 3) & 0x1f
1913 func = devfn & 0x7
1914 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
1915 "not enabled, or the device is not exist, or it "
1916 "has already been assigned to other domain"
1917 % (bus, dev, func))
1919 # register the domain in the list
1920 from xen.xend import XendDomain
1921 XendDomain.instance().add_domain(self)
1923 def _introduceDomain(self):
1924 assert self.domid is not None
1925 assert self.store_mfn is not None
1926 assert self.store_port is not None
1928 try:
1929 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1930 except RuntimeError, exn:
1931 raise XendError(str(exn))
1933 def _setTarget(self, target):
1934 assert self.domid is not None
1936 try:
1937 SetTarget(self.domid, target)
1938 self.storeDom('target', target)
1939 except RuntimeError, exn:
1940 raise XendError(str(exn))
1943 def _initDomain(self):
1944 log.debug('XendDomainInfo.initDomain: %s %s',
1945 self.domid,
1946 self.info['vcpus_params']['weight'])
1948 self._configureBootloader()
1950 try:
1951 if self.info['platform'].get('localtime', 0):
1952 t = time.time()
1953 loc = time.localtime(t)
1954 utc = time.gmtime(t)
1955 timeoffset = int(time.mktime(loc) - time.mktime(utc))
1956 self.info['platform']['rtc_timeoffset'] = timeoffset
1958 self.image = image.create(self, self.info)
1960 # repin domain vcpus if a restricted cpus list is provided
1961 # this is done prior to memory allocation to aide in memory
1962 # distribution for NUMA systems.
1963 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1964 for v in range(0, self.info['VCPUs_max']):
1965 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1967 # Use architecture- and image-specific calculations to determine
1968 # the various headrooms necessary, given the raw configured
1969 # values. maxmem, memory, and shadow are all in KiB.
1970 # but memory_static_max etc are all stored in bytes now.
1971 memory = self.image.getRequiredAvailableMemory(
1972 self.info['memory_dynamic_max'] / 1024)
1973 maxmem = self.image.getRequiredAvailableMemory(
1974 self.info['memory_static_max'] / 1024)
1975 shadow = self.image.getRequiredShadowMemory(
1976 self.info['shadow_memory'] * 1024,
1977 self.info['memory_static_max'] / 1024)
1979 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'],)
1980 # Round shadow up to a multiple of a MiB, as shadow_mem_control
1981 # takes MiB and we must not round down and end up under-providing.
1982 shadow = ((shadow + 1023) / 1024) * 1024
1984 # set memory limit
1985 xc.domain_setmaxmem(self.domid, maxmem)
1987 # Make sure there's enough RAM available for the domain
1988 balloon.free(memory + shadow)
1990 # Set up the shadow memory
1991 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1992 self.info['shadow_memory'] = shadow_cur
1994 self._createChannels()
1996 channel_details = self.image.createImage()
1998 self.store_mfn = channel_details['store_mfn']
1999 if 'console_mfn' in channel_details:
2000 self.console_mfn = channel_details['console_mfn']
2001 if 'notes' in channel_details:
2002 self.info.set_notes(channel_details['notes'])
2003 if 'native_protocol' in channel_details:
2004 self.native_protocol = channel_details['native_protocol'];
2006 self._introduceDomain()
2007 if self.info.target():
2008 self._setTarget(self.info.target())
2010 self._createDevices()
2012 self.image.cleanupBootloading()
2014 self.info['start_time'] = time.time()
2016 self._stateSet(DOM_STATE_RUNNING)
2017 except VmError, exn:
2018 log.exception("XendDomainInfo.initDomain: exception occurred")
2019 if self.image:
2020 self.image.cleanupBootloading()
2021 raise exn
2022 except RuntimeError, exn:
2023 log.exception("XendDomainInfo.initDomain: exception occurred")
2024 if self.image:
2025 self.image.cleanupBootloading()
2026 raise VmError(str(exn))
2029 def cleanupDomain(self):
2030 """Cleanup domain resources; release devices. Idempotent. Nothrow
2031 guarantee."""
2033 self.refresh_shutdown_lock.acquire()
2034 try:
2035 self.unwatchShutdown()
2036 self._releaseDevices()
2037 bootloader_tidy(self)
2039 if self.image:
2040 self.image = None
2042 try:
2043 self._removeDom()
2044 except:
2045 log.exception("Removing domain path failed.")
2047 self._stateSet(DOM_STATE_HALTED)
2048 self.domid = None # Do not push into _stateSet()!
2049 finally:
2050 self.refresh_shutdown_lock.release()
2053 def unwatchShutdown(self):
2054 """Remove the watch on the domain's control/shutdown node, if any.
2055 Idempotent. Nothrow guarantee. Expects to be protected by the
2056 refresh_shutdown_lock."""
2058 try:
2059 try:
2060 if self.shutdownWatch:
2061 self.shutdownWatch.unwatch()
2062 finally:
2063 self.shutdownWatch = None
2064 except:
2065 log.exception("Unwatching control/shutdown failed.")
2067 def waitForShutdown(self):
2068 self.state_updated.acquire()
2069 try:
2070 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
2071 self.state_updated.wait()
2072 finally:
2073 self.state_updated.release()
2076 # TODO: recategorise - called from XendCheckpoint
2079 def completeRestore(self, store_mfn, console_mfn):
2081 log.debug("XendDomainInfo.completeRestore")
2083 self.store_mfn = store_mfn
2084 self.console_mfn = console_mfn
2086 self._introduceDomain()
2087 self.image = image.create(self, self.info)
2088 if self.image:
2089 self.image.createDeviceModel(True)
2090 self._storeDomDetails()
2091 self._registerWatches()
2092 self.refreshShutdown()
2094 log.debug("XendDomainInfo.completeRestore done")
2097 def _endRestore(self):
2098 self.setResume(False)
2101 # VM Destroy
2104 def _prepare_phantom_paths(self):
2105 # get associated devices to destroy
2106 # build list of phantom devices to be removed after normal devices
2107 plist = []
2108 if self.domid is not None:
2109 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
2110 try:
2111 for dev in t.list():
2112 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
2113 % (self.dompath, dev))
2114 if backend_phantom_vbd is not None:
2115 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
2116 % backend_phantom_vbd)
2117 plist.append(backend_phantom_vbd)
2118 plist.append(frontend_phantom_vbd)
2119 finally:
2120 t.abort()
2121 return plist
2123 def _cleanup_phantom_devs(self, plist):
2124 # remove phantom devices
2125 if not plist == []:
2126 time.sleep(2)
2127 for paths in plist:
2128 if paths.find('backend') != -1:
2129 from xen.xend.server import DevController
2130 # Modify online status /before/ updating state (latter is watched by
2131 # drivers, so this ordering avoids a race).
2132 xstransact.Write(paths, 'online', "0")
2133 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
2134 # force
2135 xstransact.Remove(paths)
2137 def destroy(self):
2138 """Cleanup VM and destroy domain. Nothrow guarantee."""
2140 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
2142 paths = self._prepare_phantom_paths()
2144 self._cleanupVm()
2145 if self.dompath is not None:
2146 self.destroyDomain()
2148 self._cleanup_phantom_devs(paths)
2150 if "transient" in self.info["other_config"] \
2151 and bool(self.info["other_config"]["transient"]):
2152 from xen.xend import XendDomain
2153 XendDomain.instance().domain_delete_by_dominfo(self)
2156 def destroyDomain(self):
2157 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
2159 paths = self._prepare_phantom_paths()
2161 try:
2162 if self.domid is not None:
2163 xc.domain_destroy_hook(self.domid)
2164 xc.domain_destroy(self.domid)
2165 for state in DOM_STATES_OLD:
2166 self.info[state] = 0
2167 self._stateSet(DOM_STATE_HALTED)
2168 except:
2169 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
2171 from xen.xend import XendDomain
2172 XendDomain.instance().remove_domain(self)
2174 self.cleanupDomain()
2175 self._cleanup_phantom_devs(paths)
2178 def resumeDomain(self):
2179 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
2181 if self.domid is None:
2182 return
2183 try:
2184 # could also fetch a parsed note from xenstore
2185 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
2186 if not fast:
2187 self._releaseDevices()
2188 self.testDeviceComplete()
2189 self.testvifsComplete()
2190 log.debug("XendDomainInfo.resumeDomain: devices released")
2192 self._resetChannels()
2194 self._removeDom('control/shutdown')
2195 self._removeDom('device-misc/vif/nextDeviceID')
2197 self._createChannels()
2198 self._introduceDomain()
2199 self._storeDomDetails()
2201 self._createDevices()
2202 log.debug("XendDomainInfo.resumeDomain: devices created")
2204 xc.domain_resume(self.domid, fast)
2205 ResumeDomain(self.domid)
2206 except:
2207 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
2208 self.image.resumeDeviceModel()
2209 log.debug("XendDomainInfo.resumeDomain: completed")
2213 # Channels for xenstore and console
2216 def _createChannels(self):
2217 """Create the channels to the domain.
2218 """
2219 self.store_port = self._createChannel()
2220 self.console_port = self._createChannel()
2223 def _createChannel(self):
2224 """Create an event channel to the domain.
2225 """
2226 try:
2227 if self.domid != None:
2228 return xc.evtchn_alloc_unbound(domid = self.domid,
2229 remote_dom = 0)
2230 except:
2231 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2232 raise
2234 def _resetChannels(self):
2235 """Reset all event channels in the domain.
2236 """
2237 try:
2238 if self.domid != None:
2239 return xc.evtchn_reset(dom = self.domid)
2240 except:
2241 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2242 raise
2246 # Bootloader configuration
2249 def _configureBootloader(self):
2250 """Run the bootloader if we're configured to do so."""
2252 blexec = self.info['PV_bootloader']
2253 bootloader_args = self.info['PV_bootloader_args']
2254 kernel = self.info['PV_kernel']
2255 ramdisk = self.info['PV_ramdisk']
2256 args = self.info['PV_args']
2257 boot = self.info['HVM_boot_policy']
2259 if boot:
2260 # HVM booting.
2261 pass
2262 elif not blexec and kernel:
2263 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2264 # will be picked up by image.py.
2265 pass
2266 else:
2267 # Boot using bootloader
2268 if not blexec or blexec == 'pygrub':
2269 blexec = osdep.pygrub_path
2271 blcfg = None
2272 disks = [x for x in self.info['vbd_refs']
2273 if self.info['devices'][x][1]['bootable']]
2275 if not disks:
2276 msg = "Had a bootloader specified, but no disks are bootable"
2277 log.error(msg)
2278 raise VmError(msg)
2280 devinfo = self.info['devices'][disks[0]]
2281 devtype = devinfo[0]
2282 disk = devinfo[1]['uname']
2284 fn = blkdev_uname_to_file(disk)
2285 taptype = blkdev_uname_to_taptype(disk)
2286 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2287 if mounted:
2288 # This is a file, not a device. pygrub can cope with a
2289 # file if it's raw, but if it's QCOW or other such formats
2290 # used through blktap, then we need to mount it first.
2292 log.info("Mounting %s on %s." %
2293 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2295 vbd = {
2296 'mode': 'RO',
2297 'device': BOOTLOADER_LOOPBACK_DEVICE,
2300 from xen.xend import XendDomain
2301 dom0 = XendDomain.instance().privilegedDomain()
2302 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2303 fn = BOOTLOADER_LOOPBACK_DEVICE
2305 try:
2306 blcfg = bootloader(blexec, fn, self, False,
2307 bootloader_args, kernel, ramdisk, args)
2308 finally:
2309 if mounted:
2310 log.info("Unmounting %s from %s." %
2311 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2313 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2315 if blcfg is None:
2316 msg = "Had a bootloader specified, but can't find disk"
2317 log.error(msg)
2318 raise VmError(msg)
2320 self.info.update_with_image_sxp(blcfg, True)
2324 # VM Functions
2327 def _readVMDetails(self, params):
2328 """Read the specified parameters from the store.
2329 """
2330 try:
2331 return self._gatherVm(*params)
2332 except ValueError:
2333 # One of the int/float entries in params has a corresponding store
2334 # entry that is invalid. We recover, because older versions of
2335 # Xend may have put the entry there (memory/target, for example),
2336 # but this is in general a bad situation to have reached.
2337 log.exception(
2338 "Store corrupted at %s! Domain %d's configuration may be "
2339 "affected.", self.vmpath, self.domid)
2340 return []
2342 def _cleanupVm(self):
2343 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2345 self._unwatchVm()
2347 try:
2348 self._removeVm()
2349 except:
2350 log.exception("Removing VM path failed.")
2353 def checkLiveMigrateMemory(self):
2354 """ Make sure there's enough memory to migrate this domain """
2355 overhead_kb = 0
2356 if arch.type == "x86":
2357 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2358 # the minimum that Xen would allocate if no value were given.
2359 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2360 (self.info['memory_static_max'] / 1024 / 1024) * 4
2361 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2362 # The domain might already have some shadow memory
2363 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2364 if overhead_kb > 0:
2365 balloon.free(overhead_kb)
2367 def _unwatchVm(self):
2368 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2369 guarantee."""
2370 try:
2371 try:
2372 if self.vmWatch:
2373 self.vmWatch.unwatch()
2374 finally:
2375 self.vmWatch = None
2376 except:
2377 log.exception("Unwatching VM path failed.")
2379 def testDeviceComplete(self):
2380 """ For Block IO migration safety we must ensure that
2381 the device has shutdown correctly, i.e. all blocks are
2382 flushed to disk
2383 """
2384 start = time.time()
2385 while True:
2386 test = 0
2387 diff = time.time() - start
2388 for i in self.getDeviceController('vbd').deviceIDs():
2389 test = 1
2390 log.info("Dev %s still active, looping...", i)
2391 time.sleep(0.1)
2393 if test == 0:
2394 break
2395 if diff >= MIGRATE_TIMEOUT:
2396 log.info("Dev still active but hit max loop timeout")
2397 break
2399 def testvifsComplete(self):
2400 """ In case vifs are released and then created for the same
2401 domain, we need to wait the device shut down.
2402 """
2403 start = time.time()
2404 while True:
2405 test = 0
2406 diff = time.time() - start
2407 for i in self.getDeviceController('vif').deviceIDs():
2408 test = 1
2409 log.info("Dev %s still active, looping...", i)
2410 time.sleep(0.1)
2412 if test == 0:
2413 break
2414 if diff >= MIGRATE_TIMEOUT:
2415 log.info("Dev still active but hit max loop timeout")
2416 break
2418 def _storeVmDetails(self):
2419 to_store = {}
2421 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2422 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2423 if self._infoIsSet(info_key):
2424 to_store[key] = str(self.info[info_key])
2426 if self._infoIsSet("static_memory_min"):
2427 to_store["memory"] = str(self.info["static_memory_min"])
2428 if self._infoIsSet("static_memory_max"):
2429 to_store["maxmem"] = str(self.info["static_memory_max"])
2431 image_sxpr = self.info.image_sxpr()
2432 if image_sxpr:
2433 to_store['image'] = sxp.to_string(image_sxpr)
2435 if not self._readVm('xend/restart_count'):
2436 to_store['xend/restart_count'] = str(0)
2438 log.debug("Storing VM details: %s", scrub_password(to_store))
2440 self._writeVm(to_store)
2441 self._setVmPermissions()
2444 def _setVmPermissions(self):
2445 """Allow the guest domain to read its UUID. We don't allow it to
2446 access any other entry, for security."""
2447 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2448 { 'dom' : self.domid,
2449 'read' : True,
2450 'write' : False })
2453 # Utility functions
2456 def __getattr__(self, name):
2457 if name == "state":
2458 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2459 log.warn("".join(traceback.format_stack()))
2460 return self._stateGet()
2461 else:
2462 raise AttributeError()
2464 def __setattr__(self, name, value):
2465 if name == "state":
2466 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2467 log.warn("".join(traceback.format_stack()))
2468 self._stateSet(value)
2469 else:
2470 self.__dict__[name] = value
2472 def _stateSet(self, state):
2473 self.state_updated.acquire()
2474 try:
2475 # TODO Not sure this is correct...
2476 # _stateGet is live now. Why not fire event
2477 # even when it hasn't changed?
2478 if self._stateGet() != state:
2479 self.state_updated.notifyAll()
2480 import XendAPI
2481 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2482 'power_state')
2483 finally:
2484 self.state_updated.release()
2486 def _stateGet(self):
2487 # Lets try and reconsitute the state from xc
2488 # first lets try and get the domain info
2489 # from xc - this will tell us if the domain
2490 # exists
2491 info = dom_get(self.getDomid())
2492 if info is None or info['shutdown']:
2493 # We are either HALTED or SUSPENDED
2494 # check saved image exists
2495 from xen.xend import XendDomain
2496 managed_config_path = \
2497 XendDomain.instance()._managed_check_point_path( \
2498 self.get_uuid())
2499 if os.path.exists(managed_config_path):
2500 return XEN_API_VM_POWER_STATE_SUSPENDED
2501 else:
2502 return XEN_API_VM_POWER_STATE_HALTED
2503 elif info['crashed']:
2504 # Crashed
2505 return XEN_API_VM_POWER_STATE_CRASHED
2506 else:
2507 # We are either RUNNING or PAUSED
2508 if info['paused']:
2509 return XEN_API_VM_POWER_STATE_PAUSED
2510 else:
2511 return XEN_API_VM_POWER_STATE_RUNNING
2513 def _infoIsSet(self, name):
2514 return name in self.info and self.info[name] is not None
2516 def _checkName(self, name):
2517 """Check if a vm name is valid. Valid names contain alphabetic
2518 characters, digits, or characters in '_-.:/+'.
2519 The same name cannot be used for more than one vm at the same time.
2521 @param name: name
2522 @raise: VmError if invalid
2523 """
2524 from xen.xend import XendDomain
2526 if name is None or name == '':
2527 raise VmError('Missing VM Name')
2529 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2530 raise VmError('Invalid VM Name')
2532 dom = XendDomain.instance().domain_lookup_nr(name)
2533 if dom and dom.info['uuid'] != self.info['uuid']:
2534 raise VmError("VM name '%s' already exists%s" %
2535 (name,
2536 dom.domid is not None and
2537 (" as domain %s" % str(dom.domid)) or ""))
2540 def update(self, info = None, refresh = True, transaction = None):
2541 """Update with info from xc.domain_getinfo().
2542 """
2543 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2544 str(self.domid))
2546 if not info:
2547 info = dom_get(self.domid)
2548 if not info:
2549 return
2551 if info["maxmem_kb"] < 0:
2552 info["maxmem_kb"] = XendNode.instance() \
2553 .physinfo_dict()['total_memory'] * 1024
2555 #ssidref field not used any longer
2556 if 'ssidref' in info:
2557 info.pop('ssidref')
2559 # make sure state is reset for info
2560 # TODO: we should eventually get rid of old_dom_states
2562 self.info.update_config(info)
2563 self._update_consoles(transaction)
2565 if refresh:
2566 self.refreshShutdown(info)
2568 log.trace("XendDomainInfo.update done on domain %s: %s",
2569 str(self.domid), self.info)
2571 def sxpr(self, ignore_store = False, legacy_only = True):
2572 result = self.info.to_sxp(domain = self,
2573 ignore_devices = ignore_store,
2574 legacy_only = legacy_only)
2576 #if not ignore_store and self.dompath:
2577 # vnc_port = self.readDom('console/vnc-port')
2578 # if vnc_port is not None:
2579 # result.append(['device',
2580 # ['console', ['vnc-port', str(vnc_port)]]])
2582 return result
2584 # Xen API
2585 # ----------------------------------------------------------------
2587 def get_uuid(self):
2588 dom_uuid = self.info.get('uuid')
2589 if not dom_uuid: # if it doesn't exist, make one up
2590 dom_uuid = uuid.createString()
2591 self.info['uuid'] = dom_uuid
2592 return dom_uuid
2594 def get_memory_static_max(self):
2595 return self.info.get('memory_static_max', 0)
2596 def get_memory_static_min(self):
2597 return self.info.get('memory_static_min', 0)
2598 def get_memory_dynamic_max(self):
2599 return self.info.get('memory_dynamic_max', 0)
2600 def get_memory_dynamic_min(self):
2601 return self.info.get('memory_dynamic_min', 0)
2603 # only update memory-related config values if they maintain sanity
2604 def _safe_set_memory(self, key, newval):
2605 oldval = self.info.get(key, 0)
2606 try:
2607 self.info[key] = newval
2608 self.info._memory_sanity_check()
2609 except Exception, ex:
2610 self.info[key] = oldval
2611 raise
2613 def set_memory_static_max(self, val):
2614 self._safe_set_memory('memory_static_max', val)
2615 def set_memory_static_min(self, val):
2616 self._safe_set_memory('memory_static_min', val)
2617 def set_memory_dynamic_max(self, val):
2618 self._safe_set_memory('memory_dynamic_max', val)
2619 def set_memory_dynamic_min(self, val):
2620 self._safe_set_memory('memory_dynamic_min', val)
2622 def get_vcpus_params(self):
2623 if self.getDomid() is None:
2624 return self.info['vcpus_params']
2626 retval = xc.sched_credit_domain_get(self.getDomid())
2627 return retval
2628 def get_power_state(self):
2629 return XEN_API_VM_POWER_STATE[self._stateGet()]
2630 def get_platform(self):
2631 return self.info.get('platform', {})
2632 def get_pci_bus(self):
2633 return self.info.get('pci_bus', '')
2634 def get_tools_version(self):
2635 return self.info.get('tools_version', {})
2636 def get_metrics(self):
2637 return self.metrics.get_uuid();
2640 def get_security_label(self, xspol=None):
2641 import xen.util.xsm.xsm as security
2642 label = security.get_security_label(self, xspol)
2643 return label
2645 def set_security_label(self, seclab, old_seclab, xspol=None,
2646 xspol_old=None):
2647 """
2648 Set the security label of a domain from its old to
2649 a new value.
2650 @param seclab New security label formatted in the form
2651 <policy type>:<policy name>:<vm label>
2652 @param old_seclab The current security label that the
2653 VM must have.
2654 @param xspol An optional policy under which this
2655 update should be done. If not given,
2656 then the current active policy is used.
2657 @param xspol_old The old policy; only to be passed during
2658 the updating of a policy
2659 @return Returns return code, a string with errors from
2660 the hypervisor's operation, old label of the
2661 domain
2662 """
2663 rc = 0
2664 errors = ""
2665 old_label = ""
2666 new_ssidref = 0
2667 domid = self.getDomid()
2668 res_labels = None
2669 is_policy_update = (xspol_old != None)
2671 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2672 from xen.util import xsconstants
2674 state = self._stateGet()
2675 # Relabel only HALTED or RUNNING or PAUSED domains
2676 if domid != 0 and \
2677 state not in \
2678 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2679 DOM_STATE_SUSPENDED ]:
2680 log.warn("Relabeling domain not possible in state '%s'" %
2681 DOM_STATES[state])
2682 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2684 # Remove security label. Works only for halted domains
2685 if not seclab or seclab == "":
2686 if state not in [ DOM_STATE_HALTED ]:
2687 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2689 if self.info.has_key('security_label'):
2690 old_label = self.info['security_label']
2691 # Check label against expected one.
2692 if old_label != old_seclab:
2693 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2694 del self.info['security_label']
2695 xen.xend.XendDomain.instance().managed_config_save(self)
2696 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2698 tmp = seclab.split(":")
2699 if len(tmp) != 3:
2700 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2701 typ, policy, label = tmp
2703 poladmin = XSPolicyAdminInstance()
2704 if not xspol:
2705 xspol = poladmin.get_policy_by_name(policy)
2707 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2708 #if domain is running or paused try to relabel in hypervisor
2709 if not xspol:
2710 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2712 if typ != xspol.get_type_name() or \
2713 policy != xspol.get_name():
2714 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2716 if typ == xsconstants.ACM_POLICY_ID:
2717 new_ssidref = xspol.vmlabel_to_ssidref(label)
2718 if new_ssidref == xsconstants.INVALID_SSIDREF:
2719 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2721 # Check that all used resources are accessible under the
2722 # new label
2723 if not is_policy_update and \
2724 not security.resources_compatible_with_vmlabel(xspol,
2725 self, label):
2726 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2728 #Check label against expected one. Can only do this
2729 # if the policy hasn't changed underneath in the meantime
2730 if xspol_old == None:
2731 old_label = self.get_security_label()
2732 if old_label != old_seclab:
2733 log.info("old_label != old_seclab: %s != %s" %
2734 (old_label, old_seclab))
2735 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2737 # relabel domain in the hypervisor
2738 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2739 log.info("rc from relabeling in HV: %d" % rc)
2740 else:
2741 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2743 if rc == 0:
2744 # HALTED, RUNNING or PAUSED
2745 if domid == 0:
2746 if xspol:
2747 self.info['security_label'] = seclab
2748 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2749 else:
2750 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2751 else:
2752 if self.info.has_key('security_label'):
2753 old_label = self.info['security_label']
2754 # Check label against expected one, unless wildcard
2755 if old_label != old_seclab:
2756 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2758 self.info['security_label'] = seclab
2760 try:
2761 xen.xend.XendDomain.instance().managed_config_save(self)
2762 except:
2763 pass
2764 return (rc, errors, old_label, new_ssidref)
2766 def get_on_shutdown(self):
2767 after_shutdown = self.info.get('actions_after_shutdown')
2768 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2769 return XEN_API_ON_NORMAL_EXIT[-1]
2770 return after_shutdown
2772 def get_on_reboot(self):
2773 after_reboot = self.info.get('actions_after_reboot')
2774 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2775 return XEN_API_ON_NORMAL_EXIT[-1]
2776 return after_reboot
2778 def get_on_suspend(self):
2779 # TODO: not supported
2780 after_suspend = self.info.get('actions_after_suspend')
2781 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2782 return XEN_API_ON_NORMAL_EXIT[-1]
2783 return after_suspend
2785 def get_on_crash(self):
2786 after_crash = self.info.get('actions_after_crash')
2787 if not after_crash or after_crash not in \
2788 XEN_API_ON_CRASH_BEHAVIOUR + restart_modes:
2789 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2790 return XEN_API_ON_CRASH_BEHAVIOUR_FILTER[after_crash]
2792 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2793 """ Get's a device configuration either from XendConfig or
2794 from the DevController.
2796 @param dev_class: device class, either, 'vbd' or 'vif'
2797 @param dev_uuid: device UUID
2799 @rtype: dictionary
2800 """
2801 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2803 # shortcut if the domain isn't started because
2804 # the devcontrollers will have no better information
2805 # than XendConfig.
2806 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2807 if dev_config:
2808 return copy.deepcopy(dev_config)
2809 return None
2811 # instead of using dev_class, we use the dev_type
2812 # that is from XendConfig.
2813 controller = self.getDeviceController(dev_type)
2814 if not controller:
2815 return None
2817 all_configs = controller.getAllDeviceConfigurations()
2818 if not all_configs:
2819 return None
2821 updated_dev_config = copy.deepcopy(dev_config)
2822 for _devid, _devcfg in all_configs.items():
2823 if _devcfg.get('uuid') == dev_uuid:
2824 updated_dev_config.update(_devcfg)
2825 updated_dev_config['id'] = _devid
2826 return updated_dev_config
2828 return updated_dev_config
2830 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2831 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2832 if not config:
2833 return {}
2835 config['VM'] = self.get_uuid()
2837 if dev_class == 'vif':
2838 if not config.has_key('name'):
2839 config['name'] = config.get('vifname', '')
2840 if not config.has_key('MAC'):
2841 config['MAC'] = config.get('mac', '')
2842 if not config.has_key('type'):
2843 config['type'] = 'paravirtualised'
2844 if not config.has_key('device'):
2845 devid = config.get('id')
2846 if devid != None:
2847 config['device'] = 'eth%d' % devid
2848 else:
2849 config['device'] = ''
2851 if not config.has_key('network'):
2852 try:
2853 bridge = config.get('bridge', None)
2854 if bridge is None:
2855 from xen.util import Brctl
2856 if_to_br = dict([(i,b)
2857 for (b,ifs) in Brctl.get_state().items()
2858 for i in ifs])
2859 vifname = "vif%s.%s" % (self.getDomid(),
2860 config.get('id'))
2861 bridge = if_to_br.get(vifname, None)
2862 config['network'] = \
2863 XendNode.instance().bridge_to_network(
2864 config.get('bridge')).get_uuid()
2865 except Exception:
2866 log.exception('bridge_to_network')
2867 # Ignore this for now -- it may happen if the device
2868 # has been specified using the legacy methods, but at
2869 # some point we're going to have to figure out how to
2870 # handle that properly.
2872 config['MTU'] = 1500 # TODO
2874 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2875 xennode = XendNode.instance()
2876 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2877 config['io_read_kbs'] = rx_bps/1024
2878 config['io_write_kbs'] = tx_bps/1024
2879 rx, tx = xennode.get_vif_stat(self.domid, devid)
2880 config['io_total_read_kbs'] = rx/1024
2881 config['io_total_write_kbs'] = tx/1024
2882 else:
2883 config['io_read_kbs'] = 0.0
2884 config['io_write_kbs'] = 0.0
2885 config['io_total_read_kbs'] = 0.0
2886 config['io_total_write_kbs'] = 0.0
2888 config['security_label'] = config.get('security_label', '')
2890 if dev_class == 'vbd':
2892 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2893 controller = self.getDeviceController(dev_class)
2894 devid, _1, _2 = controller.getDeviceDetails(config)
2895 xennode = XendNode.instance()
2896 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2897 config['io_read_kbs'] = rd_blkps
2898 config['io_write_kbs'] = wr_blkps
2899 else:
2900 config['io_read_kbs'] = 0.0
2901 config['io_write_kbs'] = 0.0
2903 config['VDI'] = config.get('VDI', '')
2904 config['device'] = config.get('dev', '')
2905 if ':' in config['device']:
2906 vbd_name, vbd_type = config['device'].split(':', 1)
2907 config['device'] = vbd_name
2908 if vbd_type == 'cdrom':
2909 config['type'] = XEN_API_VBD_TYPE[0]
2910 else:
2911 config['type'] = XEN_API_VBD_TYPE[1]
2913 config['driver'] = 'paravirtualised' # TODO
2914 config['image'] = config.get('uname', '')
2916 if config.get('mode', 'r') == 'r':
2917 config['mode'] = 'RO'
2918 else:
2919 config['mode'] = 'RW'
2921 if dev_class == 'vtpm':
2922 if not config.has_key('type'):
2923 config['type'] = 'paravirtualised' # TODO
2924 if not config.has_key('backend'):
2925 config['backend'] = "00000000-0000-0000-0000-000000000000"
2927 return config
2929 def get_dev_property(self, dev_class, dev_uuid, field):
2930 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2931 try:
2932 return config[field]
2933 except KeyError:
2934 raise XendError('Invalid property for device: %s' % field)
2936 def set_dev_property(self, dev_class, dev_uuid, field, value):
2937 self.info['devices'][dev_uuid][1][field] = value
2939 def get_vcpus_util(self):
2940 vcpu_util = {}
2941 xennode = XendNode.instance()
2942 if 'VCPUs_max' in self.info and self.domid != None:
2943 for i in range(0, self.info['VCPUs_max']):
2944 util = xennode.get_vcpu_util(self.domid, i)
2945 vcpu_util[str(i)] = util
2947 return vcpu_util
2949 def get_consoles(self):
2950 return self.info.get('console_refs', [])
2952 def get_vifs(self):
2953 return self.info.get('vif_refs', [])
2955 def get_vbds(self):
2956 return self.info.get('vbd_refs', [])
2958 def get_vtpms(self):
2959 return self.info.get('vtpm_refs', [])
2961 def create_vbd(self, xenapi_vbd, vdi_image_path):
2962 """Create a VBD using a VDI from XendStorageRepository.
2964 @param xenapi_vbd: vbd struct from the Xen API
2965 @param vdi_image_path: VDI UUID
2966 @rtype: string
2967 @return: uuid of the device
2968 """
2969 xenapi_vbd['image'] = vdi_image_path
2970 if vdi_image_path.startswith('tap'):
2971 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2972 else:
2973 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2975 if not dev_uuid:
2976 raise XendError('Failed to create device')
2978 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2979 XEN_API_VM_POWER_STATE_PAUSED):
2980 _, config = self.info['devices'][dev_uuid]
2982 if vdi_image_path.startswith('tap'):
2983 dev_control = self.getDeviceController('tap')
2984 else:
2985 dev_control = self.getDeviceController('vbd')
2987 try:
2988 devid = dev_control.createDevice(config)
2989 dev_control.waitForDevice(devid)
2990 self.info.device_update(dev_uuid,
2991 cfg_xenapi = {'devid': devid})
2992 except Exception, exn:
2993 log.exception(exn)
2994 del self.info['devices'][dev_uuid]
2995 self.info['vbd_refs'].remove(dev_uuid)
2996 raise
2998 return dev_uuid
3000 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
3001 """Create a VBD using a VDI from XendStorageRepository.
3003 @param xenapi_vbd: vbd struct from the Xen API
3004 @param vdi_image_path: VDI UUID
3005 @rtype: string
3006 @return: uuid of the device
3007 """
3008 xenapi_vbd['image'] = vdi_image_path
3009 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
3010 if not dev_uuid:
3011 raise XendError('Failed to create device')
3013 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
3014 _, config = self.info['devices'][dev_uuid]
3015 config['devid'] = self.getDeviceController('tap').createDevice(config)
3017 return config['devid']
3019 def create_vif(self, xenapi_vif):
3020 """Create VIF device from the passed struct in Xen API format.
3022 @param xenapi_vif: Xen API VIF Struct.
3023 @rtype: string
3024 @return: UUID
3025 """
3026 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
3027 if not dev_uuid:
3028 raise XendError('Failed to create device')
3030 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3031 XEN_API_VM_POWER_STATE_PAUSED):
3033 _, config = self.info['devices'][dev_uuid]
3034 dev_control = self.getDeviceController('vif')
3036 try:
3037 devid = dev_control.createDevice(config)
3038 dev_control.waitForDevice(devid)
3039 self.info.device_update(dev_uuid,
3040 cfg_xenapi = {'devid': devid})
3041 except Exception, exn:
3042 log.exception(exn)
3043 del self.info['devices'][dev_uuid]
3044 self.info['vif_refs'].remove(dev_uuid)
3045 raise
3047 return dev_uuid
3049 def create_vtpm(self, xenapi_vtpm):
3050 """Create a VTPM device from the passed struct in Xen API format.
3052 @return: uuid of the device
3053 @rtype: string
3054 """
3056 if self._stateGet() not in (DOM_STATE_HALTED,):
3057 raise VmError("Can only add vTPM to a halted domain.")
3058 if self.get_vtpms() != []:
3059 raise VmError('Domain already has a vTPM.')
3060 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
3061 if not dev_uuid:
3062 raise XendError('Failed to create device')
3064 return dev_uuid
3066 def create_console(self, xenapi_console):
3067 """ Create a console device from a Xen API struct.
3069 @return: uuid of device
3070 @rtype: string
3071 """
3072 if self._stateGet() not in (DOM_STATE_HALTED,):
3073 raise VmError("Can only add console to a halted domain.")
3075 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
3076 if not dev_uuid:
3077 raise XendError('Failed to create device')
3079 return dev_uuid
3081 def set_console_other_config(self, console_uuid, other_config):
3082 self.info.console_update(console_uuid, 'other_config', other_config)
3084 def destroy_device_by_uuid(self, dev_type, dev_uuid):
3085 if dev_uuid not in self.info['devices']:
3086 raise XendError('Device does not exist')
3088 try:
3089 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3090 XEN_API_VM_POWER_STATE_PAUSED):
3091 _, config = self.info['devices'][dev_uuid]
3092 devid = config.get('devid')
3093 if devid != None:
3094 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
3095 else:
3096 raise XendError('Unable to get devid for device: %s:%s' %
3097 (dev_type, dev_uuid))
3098 finally:
3099 del self.info['devices'][dev_uuid]
3100 self.info['%s_refs' % dev_type].remove(dev_uuid)
3102 def destroy_vbd(self, dev_uuid):
3103 self.destroy_device_by_uuid('vbd', dev_uuid)
3105 def destroy_vif(self, dev_uuid):
3106 self.destroy_device_by_uuid('vif', dev_uuid)
3108 def destroy_vtpm(self, dev_uuid):
3109 self.destroy_device_by_uuid('vtpm', dev_uuid)
3111 def has_device(self, dev_class, dev_uuid):
3112 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
3114 def __str__(self):
3115 return '<domain id=%s name=%s memory=%s state=%s>' % \
3116 (str(self.domid), self.info['name_label'],
3117 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
3119 __repr__ = __str__