ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 17578:94c6501c4ffe

xend: Refactor security.on() call

I am refactoring the security.on() call to return the actual type of
the security module that is found to be enabled rather than just
returning True or False.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 06 10:05:52 2008 +0100 (2008-05-06)
parents 2ab9f85f221f
children 5c3df1bded82
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
40 from xen.util import xsconstants
42 from xen.xend import balloon, sxp, uuid, image, arch, osdep
43 from xen.xend import XendOptions, XendNode, XendConfig
45 from xen.xend.XendConfig import scrub_password
46 from xen.xend.XendBootloader import bootloader, bootloader_tidy
47 from xen.xend.XendError import XendError, VmError
48 from xen.xend.XendDevices import XendDevices
49 from xen.xend.XendTask import XendTask
50 from xen.xend.xenstore.xstransact import xstransact, complete
51 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, SetTarget, ResumeDomain
52 from xen.xend.xenstore.xswatch import xswatch
53 from xen.xend.XendConstants import *
54 from xen.xend.XendAPIConstants import *
56 from xen.xend.XendVMMetrics import XendVMMetrics
58 MIGRATE_TIMEOUT = 30.0
59 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
61 xc = xen.lowlevel.xc.xc()
62 xoptions = XendOptions.instance()
64 log = logging.getLogger("xend.XendDomainInfo")
65 #log.setLevel(logging.TRACE)
68 def create(config):
69 """Creates and start a VM using the supplied configuration.
71 @param config: A configuration object involving lists of tuples.
72 @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
74 @rtype: XendDomainInfo
75 @return: An up and running XendDomainInfo instance
76 @raise VmError: Invalid configuration or failure to start.
77 """
78 from xen.xend import XendDomain
79 domconfig = XendConfig.XendConfig(sxp_obj = config)
80 othervm = XendDomain.instance().domain_lookup_nr(domconfig["name_label"])
81 if othervm is None or othervm.domid is None:
82 othervm = XendDomain.instance().domain_lookup_nr(domconfig["uuid"])
83 if othervm is not None and othervm.domid is not None:
84 raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid))
85 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
86 vm = XendDomainInfo(domconfig)
87 try:
88 vm.start()
89 except:
90 log.exception('Domain construction failed')
91 vm.destroy()
92 raise
94 return vm
96 def create_from_dict(config_dict):
97 """Creates and start a VM using the supplied configuration.
99 @param config_dict: An configuration dictionary.
101 @rtype: XendDomainInfo
102 @return: An up and running XendDomainInfo instance
103 @raise VmError: Invalid configuration or failure to start.
104 """
106 log.debug("XendDomainInfo.create_from_dict(%s)",
107 scrub_password(config_dict))
108 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
109 try:
110 vm.start()
111 except:
112 log.exception('Domain construction failed')
113 vm.destroy()
114 raise
115 return vm
117 def recreate(info, priv):
118 """Create the VM object for an existing domain. The domain must not
119 be dying, as the paths in the store should already have been removed,
120 and asking us to recreate them causes problems.
122 @param xeninfo: Parsed configuration
123 @type xeninfo: Dictionary
124 @param priv: Is a privileged domain (Dom 0)
125 @type priv: bool
127 @rtype: XendDomainInfo
128 @return: A up and running XendDomainInfo instance
129 @raise VmError: Invalid configuration.
130 @raise XendError: Errors with configuration.
131 """
133 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
135 assert not info['dying']
137 xeninfo = XendConfig.XendConfig(dominfo = info)
138 xeninfo['is_control_domain'] = priv
139 xeninfo['is_a_template'] = False
140 domid = xeninfo['domid']
141 uuid1 = uuid.fromString(xeninfo['uuid'])
142 needs_reinitialising = False
144 dompath = GetDomainPath(domid)
145 if not dompath:
146 raise XendError('No domain path in store for existing '
147 'domain %d' % domid)
149 log.info("Recreating domain %d, UUID %s. at %s" %
150 (domid, xeninfo['uuid'], dompath))
152 # need to verify the path and uuid if not Domain-0
153 # if the required uuid and vm aren't set, then that means
154 # we need to recreate the dom with our own values
155 #
156 # NOTE: this is probably not desirable, really we should just
157 # abort or ignore, but there may be cases where xenstore's
158 # entry disappears (eg. xenstore-rm /)
159 #
160 try:
161 vmpath = xstransact.Read(dompath, "vm")
162 if not vmpath:
163 if not priv:
164 log.warn('/local/domain/%d/vm is missing. recreate is '
165 'confused, trying our best to recover' % domid)
166 needs_reinitialising = True
167 raise XendError('reinit')
169 uuid2_str = xstransact.Read(vmpath, "uuid")
170 if not uuid2_str:
171 log.warn('%s/uuid/ is missing. recreate is confused, '
172 'trying our best to recover' % vmpath)
173 needs_reinitialising = True
174 raise XendError('reinit')
176 uuid2 = uuid.fromString(uuid2_str)
177 if uuid1 != uuid2:
178 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
179 'Trying out best to recover' % domid)
180 needs_reinitialising = True
181 except XendError:
182 pass # our best shot at 'goto' in python :)
184 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv,
185 vmpath = vmpath)
187 if needs_reinitialising:
188 vm._recreateDom()
189 vm._removeVm()
190 vm._storeVmDetails()
191 vm._storeDomDetails()
193 vm.image = image.create(vm, vm.info)
194 vm.image.recreate()
196 vm._registerWatches()
197 vm.refreshShutdown(xeninfo)
199 # register the domain in the list
200 from xen.xend import XendDomain
201 XendDomain.instance().add_domain(vm)
203 return vm
206 def restore(config):
207 """Create a domain and a VM object to do a restore.
209 @param config: Domain SXP configuration
210 @type config: list of lists. (see C{create})
212 @rtype: XendDomainInfo
213 @return: A up and running XendDomainInfo instance
214 @raise VmError: Invalid configuration or failure to start.
215 @raise XendError: Errors with configuration.
216 """
218 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
219 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
220 resume = True)
221 try:
222 vm.resume()
223 return vm
224 except:
225 vm.destroy()
226 raise
228 def createDormant(domconfig):
229 """Create a dormant/inactive XenDomainInfo without creating VM.
230 This is for creating instances of persistent domains that are not
231 yet start.
233 @param domconfig: Parsed configuration
234 @type domconfig: XendConfig object
236 @rtype: XendDomainInfo
237 @return: A up and running XendDomainInfo instance
238 @raise XendError: Errors with configuration.
239 """
241 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
243 # domid does not make sense for non-running domains.
244 domconfig.pop('domid', None)
245 vm = XendDomainInfo(domconfig)
246 return vm
248 def domain_by_name(name):
249 """Get domain by name
251 @params name: Name of the domain
252 @type name: string
253 @return: XendDomainInfo or None
254 """
255 from xen.xend import XendDomain
256 return XendDomain.instance().domain_lookup_by_name_nr(name)
259 def shutdown_reason(code):
260 """Get a shutdown reason from a code.
262 @param code: shutdown code
263 @type code: int
264 @return: shutdown reason
265 @rtype: string
266 """
267 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
269 def dom_get(dom):
270 """Get info from xen for an existing domain.
272 @param dom: domain id
273 @type dom: int
274 @return: info or None
275 @rtype: dictionary
276 """
277 try:
278 domlist = xc.domain_getinfo(dom, 1)
279 if domlist and dom == domlist[0]['domid']:
280 return domlist[0]
281 except Exception, err:
282 # ignore missing domain
283 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
284 return None
287 class XendDomainInfo:
288 """An object represents a domain.
290 @TODO: try to unify dom and domid, they mean the same thing, but
291 xc refers to it as dom, and everywhere else, including
292 xenstore it is domid. The best way is to change xc's
293 python interface.
295 @ivar info: Parsed configuration
296 @type info: dictionary
297 @ivar domid: Domain ID (if VM has started)
298 @type domid: int or None
299 @ivar vmpath: XenStore path to this VM.
300 @type vmpath: string
301 @ivar dompath: XenStore path to this Domain.
302 @type dompath: string
303 @ivar image: Reference to the VM Image.
304 @type image: xen.xend.image.ImageHandler
305 @ivar store_port: event channel to xenstored
306 @type store_port: int
307 @ivar console_port: event channel to xenconsoled
308 @type console_port: int
309 @ivar store_mfn: xenstored mfn
310 @type store_mfn: int
311 @ivar console_mfn: xenconsoled mfn
312 @type console_mfn: int
313 @ivar notes: OS image notes
314 @type notes: dictionary
315 @ivar vmWatch: reference to a watch on the xenstored vmpath
316 @type vmWatch: xen.xend.xenstore.xswatch
317 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
318 @type shutdownWatch: xen.xend.xenstore.xswatch
319 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
320 @type shutdownStartTime: float or None
321 # @ivar state: Domain state
322 # @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
323 @ivar state_updated: lock for self.state
324 @type state_updated: threading.Condition
325 @ivar refresh_shutdown_lock: lock for polling shutdown state
326 @type refresh_shutdown_lock: threading.Condition
327 @ivar _deviceControllers: device controller cache for this domain
328 @type _deviceControllers: dict 'string' to DevControllers
329 """
331 def __init__(self, info, domid = None, dompath = None, augment = False,
332 priv = False, resume = False, vmpath = None):
333 """Constructor for a domain
335 @param info: parsed configuration
336 @type info: dictionary
337 @keyword domid: Set initial domain id (if any)
338 @type domid: int
339 @keyword dompath: Set initial dompath (if any)
340 @type dompath: string
341 @keyword augment: Augment given info with xenstored VM info
342 @type augment: bool
343 @keyword priv: Is a privileged domain (Dom 0)
344 @type priv: bool
345 @keyword resume: Is this domain being resumed?
346 @type resume: bool
347 """
349 self.info = info
350 if domid == None:
351 self.domid = self.info.get('domid')
352 else:
353 self.domid = domid
355 #REMOVE: uuid is now generated in XendConfig
356 #if not self._infoIsSet('uuid'):
357 # self.info['uuid'] = uuid.toString(uuid.create())
359 # Find a unique /vm/<uuid>/<integer> path if not specified.
360 # This avoids conflict between pre-/post-migrate domains when doing
361 # localhost relocation.
362 self.vmpath = vmpath
363 i = 0
364 while self.vmpath == None:
365 self.vmpath = XS_VMROOT + self.info['uuid']
366 if i != 0:
367 self.vmpath = self.vmpath + '-' + str(i)
368 try:
369 if self._readVm("uuid"):
370 self.vmpath = None
371 i = i + 1
372 except:
373 pass
375 self.dompath = dompath
377 self.image = None
378 self.store_port = None
379 self.store_mfn = None
380 self.console_port = None
381 self.console_mfn = None
383 self.native_protocol = None
385 self.vmWatch = None
386 self.shutdownWatch = None
387 self.shutdownStartTime = None
388 self._resume = resume
390 self.state_updated = threading.Condition()
391 self.refresh_shutdown_lock = threading.Condition()
392 self._stateSet(DOM_STATE_HALTED)
394 self._deviceControllers = {}
396 for state in DOM_STATES_OLD:
397 self.info[state] = 0
399 if augment:
400 self._augmentInfo(priv)
402 self._checkName(self.info['name_label'])
404 self.metrics = XendVMMetrics(uuid.createString(), self)
407 #
408 # Public functions available through XMLRPC
409 #
412 def start(self, is_managed = False):
413 """Attempts to start the VM by do the appropriate
414 initialisation if it not started.
415 """
416 from xen.xend import XendDomain
418 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED, XEN_API_VM_POWER_STATE_CRASHED):
419 try:
420 XendTask.log_progress(0, 30, self._constructDomain)
421 XendTask.log_progress(31, 60, self._initDomain)
423 XendTask.log_progress(61, 70, self._storeVmDetails)
424 XendTask.log_progress(71, 80, self._storeDomDetails)
425 XendTask.log_progress(81, 90, self._registerWatches)
426 XendTask.log_progress(91, 100, self.refreshShutdown)
428 xendomains = XendDomain.instance()
429 xennode = XendNode.instance()
431 # save running configuration if XendDomains believe domain is
432 # persistent
433 if is_managed:
434 xendomains.managed_config_save(self)
436 if xennode.xenschedinfo() == 'credit':
437 xendomains.domain_sched_credit_set(self.getDomid(),
438 self.getWeight(),
439 self.getCap())
440 except:
441 log.exception('VM start failed')
442 self.destroy()
443 raise
444 else:
445 raise XendError('VM already running')
447 def resume(self):
448 """Resumes a domain that has come back from suspension."""
449 state = self._stateGet()
450 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
451 try:
452 self._constructDomain()
453 self._storeVmDetails()
454 self._createDevices()
455 self._createChannels()
456 self._storeDomDetails()
457 self._endRestore()
458 except:
459 log.exception('VM resume failed')
460 self.destroy()
461 raise
462 else:
463 raise XendError('VM is not suspended; it is %s'
464 % XEN_API_VM_POWER_STATE[state])
466 def shutdown(self, reason):
467 """Shutdown a domain by signalling this via xenstored."""
468 log.debug('XendDomainInfo.shutdown(%s)', reason)
469 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
470 raise XendError('Domain cannot be shutdown')
472 if self.domid == 0:
473 raise XendError('Domain 0 cannot be shutdown')
475 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
476 raise XendError('Invalid reason: %s' % reason)
477 self._removeVm('xend/previous_restart_time')
478 self.storeDom("control/shutdown", reason)
480 # HVM domain shuts itself down only if it has PV drivers
481 if self.info.is_hvm():
482 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
483 if not hvm_pvdrv:
484 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
485 log.info("HVM save:remote shutdown dom %d!", self.domid)
486 xc.domain_shutdown(self.domid, code)
488 def pause(self):
489 """Pause domain
491 @raise XendError: Failed pausing a domain
492 """
493 try:
494 xc.domain_pause(self.domid)
495 self._stateSet(DOM_STATE_PAUSED)
496 except Exception, ex:
497 log.exception(ex)
498 raise XendError("Domain unable to be paused: %s" % str(ex))
500 def unpause(self):
501 """Unpause domain
503 @raise XendError: Failed unpausing a domain
504 """
505 try:
506 xc.domain_unpause(self.domid)
507 self._stateSet(DOM_STATE_RUNNING)
508 except Exception, ex:
509 log.exception(ex)
510 raise XendError("Domain unable to be unpaused: %s" % str(ex))
512 def send_sysrq(self, key):
513 """ Send a Sysrq equivalent key via xenstored."""
514 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
515 raise XendError("Domain '%s' is not started" % self.info['name_label'])
517 asserts.isCharConvertible(key)
518 self.storeDom("control/sysrq", '%c' % key)
520 def sync_pcidev_info(self):
522 if not self.info.is_hvm():
523 return
525 devid = '0'
526 dev_info = self._getDeviceInfo_pci(devid)
527 if dev_info is None:
528 return
530 # get the virtual slot info from xenstore
531 dev_uuid = sxp.child_value(dev_info, 'uuid')
532 pci_conf = self.info['devices'][dev_uuid][1]
533 pci_devs = pci_conf['devs']
535 count = 0
536 vslots = None
537 while vslots is None and count < 20:
538 vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots"
539 % (self.getDomid(), devid))
540 time.sleep(0.1)
541 count += 1
542 if vslots is None:
543 log.error("Device model didn't tell the vslots for PCI device")
544 return
546 #delete last delim
547 if vslots[-1] == ";":
548 vslots = vslots[:-1]
550 slot_list = vslots.split(';')
551 if len(slot_list) != len(pci_devs):
552 log.error("Device model's pci dev num dismatch")
553 return
555 #update the vslot info
556 count = 0;
557 for x in pci_devs:
558 x['vslt'] = slot_list[count]
559 count += 1
562 def hvm_pci_device_create(self, dev_config):
563 log.debug("XendDomainInfo.hvm_pci_device_create: %s"
564 % scrub_password(dev_config))
566 if not self.info.is_hvm():
567 raise VmError("hvm_pci_device_create called on non-HVM guest")
569 #all the PCI devs share one conf node
570 devid = '0'
572 new_dev = dev_config['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)
615 def device_create(self, dev_config):
616 """Create a new device.
618 @param dev_config: device configuration
619 @type dev_config: SXP object (parsed config)
620 """
621 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
622 dev_type = sxp.name(dev_config)
623 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
624 dev_config_dict = self.info['devices'][dev_uuid][1]
625 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
627 if self.domid is not None:
628 try:
629 dev_config_dict['devid'] = devid = \
630 self._createDevice(dev_type, dev_config_dict)
631 self._waitForDevice(dev_type, devid)
632 except VmError, ex:
633 del self.info['devices'][dev_uuid]
634 if dev_type == 'tap':
635 self.info['vbd_refs'].remove(dev_uuid)
636 else:
637 self.info['%s_refs' % dev_type].remove(dev_uuid)
638 raise ex
639 else:
640 devid = None
642 xen.xend.XendDomain.instance().managed_config_save(self)
643 return self.getDeviceController(dev_type).sxpr(devid)
645 def pci_convert_sxp_to_dict(self, dev_sxp):
646 """Convert pci device sxp to dict
647 @param dev_sxp: device configuration
648 @type dev_sxp: SXP object (parsed config)
649 @return: dev_config
650 @rtype: dictionary
651 """
652 # In reconfigure phase, config of PCI device looks like below:
653 #
654 # sxp:
655 # [device, [pci, [dev, [domain, '0x0'], [bus, '0x0'], [slot, '0x0'],
656 # [func, '0x0'], [vslt, '0x0']],
657 # [state, 'Initialising']]]
658 #
659 # dict:
660 # {devs: [{domain: '0x0', bus: '0x0', slot: '0x0', func: '0x0',
661 # vslt: '0x0'}],
662 # states: ['Initialising']}
663 #
664 # state 'Initialising' means the device is being attached.
665 # state 'Closing' means the device is being detached.
667 dev_config = {}
668 pci_devs = []
669 for pci_dev in sxp.children(dev_sxp, 'dev'):
670 pci_dev_info = {}
671 for opt_val in pci_dev[1:]:
672 try:
673 opt, val = opt_val
674 pci_dev_info[opt] = val
675 except TypeError:
676 pass
677 pci_devs.append(pci_dev_info)
678 dev_config['devs'] = pci_devs
679 pci_states = []
680 for pci_state in sxp.children(dev_sxp, 'state'):
681 try:
682 pci_states.append(pci_state[1])
683 except IndexError:
684 raise XendError("Error reading state while parsing pci sxp")
685 dev_config['states'] = pci_states
687 return dev_config
689 def pci_device_configure(self, dev_sxp, devid = 0):
690 """Configure an existing pci device.
692 @param dev_sxp: device configuration
693 @type dev_sxp: SXP object (parsed config)
694 @param devid: device id
695 @type devid: int
696 @return: Returns True if successfully updated device
697 @rtype: boolean
698 """
699 log.debug("XendDomainInfo.pci_device_configure: %s"
700 % scrub_password(dev_sxp))
702 dev_class = sxp.name(dev_sxp)
704 if dev_class != 'pci':
705 return False
707 pci_state = sxp.child_value(dev_sxp, 'state')
708 existing_dev_info = self._getDeviceInfo_pci(devid)
710 if existing_dev_info is None and pci_state != 'Initialising':
711 raise XendError("Cannot detach when pci platform does not exist")
713 pci_dev = sxp.children(dev_sxp, 'dev')[0]
714 dev_config = self.pci_convert_sxp_to_dict(dev_sxp)
715 dev = dev_config['devs'][0]
717 # Do HVM specific processing
718 if self.info.is_hvm():
719 if pci_state == 'Initialising':
720 # HVM PCI device attachment
721 self.hvm_pci_device_create(dev_config)
722 # Update vslt
723 vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
724 % self.getDomid())
725 dev['vslt'] = vslt
726 for n in sxp.children(pci_dev):
727 if(n[0] == 'vslt'):
728 n[1] = vslt
729 else:
730 # HVM PCI device detachment
731 existing_dev_uuid = sxp.child_value(existing_dev_info, 'uuid')
732 existing_pci_conf = self.info['devices'][existing_dev_uuid][1]
733 existing_pci_devs = existing_pci_conf['devs']
734 vslt = '0x0'
735 for x in existing_pci_devs:
736 if ( int(x['domain'], 16) == int(dev['domain'], 16) and
737 int(x['bus'], 16) == int(dev['bus'], 16) and
738 int(x['slot'], 16) == int(dev['slot'], 16) and
739 int(x['func'], 16) == int(dev['func'], 16) ):
740 vslt = x['vslt']
741 break
742 if vslt == '0x0':
743 raise VmError("Device %04x:%02x:%02x.%02x is not connected"
744 % (int(dev['domain'],16), int(dev['bus'],16),
745 int(dev['slot'],16), int(dev['func'],16)))
746 self.hvm_destroyPCIDevice(int(vslt, 16))
747 # Update vslt
748 dev['vslt'] = vslt
749 for n in sxp.children(pci_dev):
750 if(n[0] == 'vslt'):
751 n[1] = vslt
753 # If pci platform does not exist, create and exit.
754 if existing_dev_info is None:
755 self.device_create(dev_sxp)
756 return True
758 # use DevController.reconfigureDevice to change device config
759 dev_control = self.getDeviceController(dev_class)
760 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
761 if not self.info.is_hvm():
762 # in PV case, wait until backend state becomes connected.
763 dev_control.waitForDevice_reconfigure(devid)
764 num_devs = dev_control.cleanupDevice(devid)
766 # update XendConfig with new device info
767 if dev_uuid:
768 new_dev_sxp = dev_control.configuration(devid)
769 self.info.device_update(dev_uuid, new_dev_sxp)
771 # If there is no device left, destroy pci and remove config.
772 if num_devs == 0:
773 if self.info.is_hvm():
774 self.destroyDevice('pci', devid, True)
775 del self.info['devices'][dev_uuid]
776 platform = self.info['platform']
777 orig_dev_num = len(platform['pci'])
778 # TODO: can use this to keep some info to ask high level
779 # management tools to hot insert a new passthrough dev
780 # after migration
781 if orig_dev_num != 0:
782 #platform['pci'] = ["%dDEVs" % orig_dev_num]
783 platform['pci'] = []
784 else:
785 self.destroyDevice('pci', devid)
786 del self.info['devices'][dev_uuid]
788 return True
790 def device_configure(self, dev_sxp, devid = None):
791 """Configure an existing device.
793 @param dev_config: device configuration
794 @type dev_config: SXP object (parsed config)
795 @param devid: device id
796 @type devid: int
797 @return: Returns True if successfully updated device
798 @rtype: boolean
799 """
801 # convert device sxp to a dict
802 dev_class = sxp.name(dev_sxp)
803 dev_config = {}
805 if dev_class == 'pci':
806 return self.pci_device_configure(dev_sxp)
808 for opt_val in dev_sxp[1:]:
809 try:
810 dev_config[opt_val[0]] = opt_val[1]
811 except IndexError:
812 pass
814 # use DevController.reconfigureDevice to change device config
815 dev_control = self.getDeviceController(dev_class)
816 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
818 # update XendConfig with new device info
819 if dev_uuid:
820 self.info.device_update(dev_uuid, dev_sxp)
822 return True
824 def waitForDevices(self):
825 """Wait for this domain's configured devices to connect.
827 @raise VmError: if any device fails to initialise.
828 """
829 for devclass in XendDevices.valid_devices():
830 self.getDeviceController(devclass).waitForDevices()
832 def hvm_destroyPCIDevice(self, vslot):
833 log.debug("hvm_destroyPCIDevice called %s", vslot)
835 if not self.info.is_hvm():
836 raise VmError("hvm_destroyPCIDevice called on non-HVM guest")
838 #all the PCI devs share one conf node
839 devid = '0'
840 vslot = int(vslot)
841 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
842 dev_uuid = sxp.child_value(dev_info, 'uuid')
844 #delete the pci bdf config under the pci device
845 pci_conf = self.info['devices'][dev_uuid][1]
846 pci_len = len(pci_conf['devs'])
848 #find the pass-through device with the virtual slot
849 devnum = 0
850 for x in pci_conf['devs']:
851 if int(x['vslt'], 16) == vslot:
852 break
853 devnum += 1
855 if devnum >= pci_len:
856 raise VmError("Device @ vslot 0x%x doesn't exist." % (vslot))
858 if vslot == 0:
859 raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
861 bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
862 log.info("hvm_destroyPCIDevice:%s:%s!", x, bdf_str)
864 self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str)
866 return 0
868 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
869 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
870 deviceClass, devid)
872 if rm_cfg:
873 # Convert devid to device number. A device number is
874 # needed to remove its configuration.
875 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
877 # Save current sxprs. A device number and a backend
878 # path are needed to remove its configuration but sxprs
879 # do not have those after calling destroyDevice.
880 sxprs = self.getDeviceSxprs(deviceClass)
882 rc = None
883 if self.domid is not None:
884 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
885 if not force and rm_cfg:
886 # The backend path, other than the device itself,
887 # has to be passed because its accompanied frontend
888 # path may be void until its removal is actually
889 # issued. It is probable because destroyDevice is
890 # issued first.
891 for dev_num, dev_info in sxprs:
892 dev_num = int(dev_num)
893 if dev_num == dev:
894 for x in dev_info:
895 if x[0] == 'backend':
896 backend = x[1]
897 break
898 break
899 self._waitForDevice_destroy(deviceClass, devid, backend)
901 if rm_cfg:
902 if deviceClass == 'vif':
903 if self.domid is not None:
904 for dev_num, dev_info in sxprs:
905 dev_num = int(dev_num)
906 if dev_num == dev:
907 for x in dev_info:
908 if x[0] == 'mac':
909 mac = x[1]
910 break
911 break
912 dev_info = self._getDeviceInfo_vif(mac)
913 else:
914 _, dev_info = sxprs[dev]
915 else: # 'vbd' or 'tap'
916 dev_info = self._getDeviceInfo_vbd(dev)
917 # To remove the UUID of the device from refs,
918 # deviceClass must be always 'vbd'.
919 deviceClass = 'vbd'
920 if dev_info is None:
921 raise XendError("Device %s is not defined" % devid)
923 dev_uuid = sxp.child_value(dev_info, 'uuid')
924 del self.info['devices'][dev_uuid]
925 self.info['%s_refs' % deviceClass].remove(dev_uuid)
926 xen.xend.XendDomain.instance().managed_config_save(self)
928 return rc
930 def getDeviceSxprs(self, deviceClass):
931 if deviceClass == 'pci':
932 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
933 if dev_info is None:
934 return []
935 dev_uuid = sxp.child_value(dev_info, 'uuid')
936 pci_devs = self.info['devices'][dev_uuid][1]['devs']
937 pci_len = len(pci_devs)
938 return pci_devs
939 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
940 return self.getDeviceController(deviceClass).sxprs()
941 else:
942 sxprs = []
943 dev_num = 0
944 for dev_type, dev_info in self.info.all_devices_sxpr():
945 if dev_type == deviceClass:
946 sxprs.append([dev_num, dev_info])
947 dev_num += 1
948 return sxprs
950 def getBlockDeviceClass(self, devid):
951 # To get a device number from the devid,
952 # we temporarily use the device controller of VBD.
953 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
954 dev_info = self._getDeviceInfo_vbd(dev)
955 if dev_info:
956 return dev_info[0]
958 def _getDeviceInfo_vif(self, mac):
959 for dev_type, dev_info in self.info.all_devices_sxpr():
960 if dev_type != 'vif':
961 continue
962 if mac == sxp.child_value(dev_info, 'mac'):
963 return dev_info
965 def _getDeviceInfo_vbd(self, devid):
966 for dev_type, dev_info in self.info.all_devices_sxpr():
967 if dev_type != 'vbd' and dev_type != 'tap':
968 continue
969 dev = sxp.child_value(dev_info, 'dev')
970 dev = dev.split(':')[0]
971 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
972 if devid == dev:
973 return dev_info
975 def _getDeviceInfo_pci(self, devid):
976 for dev_type, dev_info in self.info.all_devices_sxpr():
977 if dev_type != 'pci':
978 continue
979 return dev_info
980 return None
982 def setMemoryTarget(self, target):
983 """Set the memory target of this domain.
984 @param target: In MiB.
985 """
986 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
987 self.info['name_label'], str(self.domid), target)
989 MiB = 1024 * 1024
991 if self.domid == 0:
992 dom0_min_mem = xoptions.get_dom0_min_mem()
993 memory_cur = self.get_memory_dynamic_max() / MiB
994 if target < memory_cur and dom0_min_mem > target:
995 raise XendError("memory_dynamic_max too small")
997 self._safe_set_memory('memory_dynamic_min', target * MiB)
998 self._safe_set_memory('memory_dynamic_max', target * MiB)
1000 if self.domid >= 0:
1001 self.storeVm("memory", target)
1002 self.storeDom("memory/target", target << 10)
1003 xen.xend.XendDomain.instance().managed_config_save(self)
1005 def setMemoryMaximum(self, limit):
1006 """Set the maximum memory limit of this domain
1007 @param limit: In MiB.
1008 """
1009 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
1010 self.info['name_label'], str(self.domid), limit)
1012 maxmem_cur = self.get_memory_static_max()
1013 MiB = 1024 * 1024
1014 self._safe_set_memory('memory_static_max', limit * MiB)
1016 if self.domid >= 0:
1017 maxmem = int(limit) * 1024
1018 try:
1019 return xc.domain_setmaxmem(self.domid, maxmem)
1020 except Exception, ex:
1021 self._safe_set_memory('memory_static_max', maxmem_cur)
1022 raise XendError(str(ex))
1023 xen.xend.XendDomain.instance().managed_config_save(self)
1026 def getVCPUInfo(self):
1027 try:
1028 # We include the domain name and ID, to help xm.
1029 sxpr = ['domain',
1030 ['domid', self.domid],
1031 ['name', self.info['name_label']],
1032 ['vcpu_count', self.info['VCPUs_max']]]
1034 for i in range(0, self.info['VCPUs_max']):
1035 if self.domid is not None:
1036 info = xc.vcpu_getinfo(self.domid, i)
1038 sxpr.append(['vcpu',
1039 ['number', i],
1040 ['online', info['online']],
1041 ['blocked', info['blocked']],
1042 ['running', info['running']],
1043 ['cpu_time', info['cpu_time'] / 1e9],
1044 ['cpu', info['cpu']],
1045 ['cpumap', info['cpumap']]])
1046 else:
1047 sxpr.append(['vcpu',
1048 ['number', i],
1049 ['online', 0],
1050 ['blocked', 0],
1051 ['running', 0],
1052 ['cpu_time', 0.0],
1053 ['cpu', -1],
1054 ['cpumap', self.info['cpus'] and \
1055 self.info['cpus'] or range(64)]])
1057 return sxpr
1059 except RuntimeError, exn:
1060 raise XendError(str(exn))
1063 def getDomInfo(self):
1064 return dom_get(self.domid)
1067 # internal functions ... TODO: re-categorised
1070 def _augmentInfo(self, priv):
1071 """Augment self.info, as given to us through L{recreate}, with
1072 values taken from the store. This recovers those values known
1073 to xend but not to the hypervisor.
1074 """
1075 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
1076 if priv:
1077 augment_entries.remove('memory')
1078 augment_entries.remove('maxmem')
1079 augment_entries.remove('vcpus')
1080 augment_entries.remove('vcpu_avail')
1082 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
1083 for k in augment_entries])
1085 # make returned lists into a dictionary
1086 vm_config = dict(zip(augment_entries, vm_config))
1088 for arg in augment_entries:
1089 val = vm_config[arg]
1090 if val != None:
1091 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1092 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1093 self.info[xapiarg] = val
1094 elif arg == "memory":
1095 self.info["static_memory_min"] = val
1096 elif arg == "maxmem":
1097 self.info["static_memory_max"] = val
1098 else:
1099 self.info[arg] = val
1101 # For dom0, we ignore any stored value for the vcpus fields, and
1102 # read the current value from Xen instead. This allows boot-time
1103 # settings to take precedence over any entries in the store.
1104 if priv:
1105 xeninfo = dom_get(self.domid)
1106 self.info['VCPUs_max'] = xeninfo['online_vcpus']
1107 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
1109 # read image value
1110 image_sxp = self._readVm('image')
1111 if image_sxp:
1112 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1114 # read devices
1115 devices = []
1116 for devclass in XendDevices.valid_devices():
1117 devconfig = self.getDeviceController(devclass).configurations()
1118 if devconfig:
1119 devices.extend(devconfig)
1121 if not self.info['devices'] and devices is not None:
1122 for device in devices:
1123 self.info.device_add(device[0], cfg_sxp = device)
1125 self._update_consoles()
1127 def _update_consoles(self, transaction = None):
1128 if self.domid == None or self.domid == 0:
1129 return
1131 # Update VT100 port if it exists
1132 if transaction is None:
1133 self.console_port = self.readDom('console/port')
1134 else:
1135 self.console_port = self.readDomTxn(transaction, 'console/port')
1136 if self.console_port is not None:
1137 serial_consoles = self.info.console_get_all('vt100')
1138 if not serial_consoles:
1139 cfg = self.info.console_add('vt100', self.console_port)
1140 self._createDevice('console', cfg)
1141 else:
1142 console_uuid = serial_consoles[0].get('uuid')
1143 self.info.console_update(console_uuid, 'location',
1144 self.console_port)
1147 # Update VNC port if it exists and write to xenstore
1148 if transaction is None:
1149 vnc_port = self.readDom('console/vnc-port')
1150 else:
1151 vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
1152 if vnc_port is not None:
1153 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
1154 if dev_type == 'vfb':
1155 old_location = dev_info.get('location')
1156 listen_host = dev_info.get('vnclisten', 'localhost')
1157 new_location = '%s:%s' % (listen_host, str(vnc_port))
1158 if old_location == new_location:
1159 break
1161 dev_info['location'] = new_location
1162 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
1163 vfb_ctrl = self.getDeviceController('vfb')
1164 vfb_ctrl.reconfigureDevice(0, dev_info)
1165 break
1168 # Function to update xenstore /vm/*
1171 def _readVm(self, *args):
1172 return xstransact.Read(self.vmpath, *args)
1174 def _writeVm(self, *args):
1175 return xstransact.Write(self.vmpath, *args)
1177 def _removeVm(self, *args):
1178 return xstransact.Remove(self.vmpath, *args)
1180 def _gatherVm(self, *args):
1181 return xstransact.Gather(self.vmpath, *args)
1183 def _listRecursiveVm(self, *args):
1184 return xstransact.ListRecursive(self.vmpath, *args)
1186 def storeVm(self, *args):
1187 return xstransact.Store(self.vmpath, *args)
1189 def permissionsVm(self, *args):
1190 return xstransact.SetPermissions(self.vmpath, *args)
1193 def _readVmTxn(self, transaction, *args):
1194 paths = map(lambda x: self.vmpath + "/" + x, args)
1195 return transaction.read(*paths)
1197 def _writeVmTxn(self, transaction, *args):
1198 paths = map(lambda x: self.vmpath + "/" + x, args)
1199 return transaction.write(*paths)
1201 def _removeVmTxn(self, transaction, *args):
1202 paths = map(lambda x: self.vmpath + "/" + x, args)
1203 return transaction.remove(*paths)
1205 def _gatherVmTxn(self, transaction, *args):
1206 paths = map(lambda x: self.vmpath + "/" + x, args)
1207 return transaction.gather(paths)
1209 def storeVmTxn(self, transaction, *args):
1210 paths = map(lambda x: self.vmpath + "/" + x, args)
1211 return transaction.store(*paths)
1213 def permissionsVmTxn(self, transaction, *args):
1214 paths = map(lambda x: self.vmpath + "/" + x, args)
1215 return transaction.set_permissions(*paths)
1218 # Function to update xenstore /dom/*
1221 def readDom(self, *args):
1222 return xstransact.Read(self.dompath, *args)
1224 def gatherDom(self, *args):
1225 return xstransact.Gather(self.dompath, *args)
1227 def _writeDom(self, *args):
1228 return xstransact.Write(self.dompath, *args)
1230 def _removeDom(self, *args):
1231 return xstransact.Remove(self.dompath, *args)
1233 def storeDom(self, *args):
1234 return xstransact.Store(self.dompath, *args)
1237 def readDomTxn(self, transaction, *args):
1238 paths = map(lambda x: self.dompath + "/" + x, args)
1239 return transaction.read(*paths)
1241 def gatherDomTxn(self, transaction, *args):
1242 paths = map(lambda x: self.dompath + "/" + x, args)
1243 return transaction.gather(*paths)
1245 def _writeDomTxn(self, transaction, *args):
1246 paths = map(lambda x: self.dompath + "/" + x, args)
1247 return transaction.write(*paths)
1249 def _removeDomTxn(self, transaction, *args):
1250 paths = map(lambda x: self.dompath + "/" + x, args)
1251 return transaction.remove(*paths)
1253 def storeDomTxn(self, transaction, *args):
1254 paths = map(lambda x: self.dompath + "/" + x, args)
1255 return transaction.store(*paths)
1258 def _recreateDom(self):
1259 complete(self.dompath, lambda t: self._recreateDomFunc(t))
1261 def _recreateDomFunc(self, t):
1262 t.remove()
1263 t.mkdir()
1264 t.set_permissions({'dom' : self.domid})
1265 t.write('vm', self.vmpath)
1267 def _storeDomDetails(self):
1268 to_store = {
1269 'domid': str(self.domid),
1270 'vm': self.vmpath,
1271 'name': self.info['name_label'],
1272 'console/limit': str(xoptions.get_console_limit() * 1024),
1273 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
1276 def f(n, v):
1277 if v is not None:
1278 if type(v) == bool:
1279 to_store[n] = v and "1" or "0"
1280 else:
1281 to_store[n] = str(v)
1283 # Figure out if we need to tell xenconsoled to ignore this guest's
1284 # console - device model will handle console if it is running
1285 constype = "ioemu"
1286 if 'device_model' not in self.info['platform']:
1287 constype = "xenconsoled"
1289 f('console/port', self.console_port)
1290 f('console/ring-ref', self.console_mfn)
1291 f('console/type', constype)
1292 f('store/port', self.store_port)
1293 f('store/ring-ref', self.store_mfn)
1295 if arch.type == "x86":
1296 f('control/platform-feature-multiprocessor-suspend', True)
1298 # elfnotes
1299 for n, v in self.info.get_notes().iteritems():
1300 n = n.lower().replace('_', '-')
1301 if n == 'features':
1302 for v in v.split('|'):
1303 v = v.replace('_', '-')
1304 if v.startswith('!'):
1305 f('image/%s/%s' % (n, v[1:]), False)
1306 else:
1307 f('image/%s/%s' % (n, v), True)
1308 else:
1309 f('image/%s' % n, v)
1311 if self.info.has_key('security_label'):
1312 f('security_label', self.info['security_label'])
1314 to_store.update(self._vcpuDomDetails())
1316 log.debug("Storing domain details: %s", scrub_password(to_store))
1318 self._writeDom(to_store)
1320 def _vcpuDomDetails(self):
1321 def availability(n):
1322 if self.info['vcpu_avail'] & (1 << n):
1323 return 'online'
1324 else:
1325 return 'offline'
1327 result = {}
1328 for v in range(0, self.info['VCPUs_max']):
1329 result["cpu/%d/availability" % v] = availability(v)
1330 return result
1333 # xenstore watches
1336 def _registerWatches(self):
1337 """Register a watch on this VM's entries in the store, and the
1338 domain's control/shutdown node, so that when they are changed
1339 externally, we keep up to date. This should only be called by {@link
1340 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1341 details have been written, but before the new instance is returned."""
1342 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1343 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1344 self._handleShutdownWatch)
1346 def _storeChanged(self, _):
1347 log.trace("XendDomainInfo.storeChanged");
1349 changed = False
1351 # Check whether values in the configuration have
1352 # changed in Xenstore.
1354 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1355 'rtc/timeoffset']
1357 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1358 for k in cfg_vm])
1360 # convert two lists into a python dictionary
1361 vm_details = dict(zip(cfg_vm, vm_details))
1363 if vm_details['rtc/timeoffset'] == None:
1364 vm_details['rtc/timeoffset'] = "0"
1366 for arg, val in vm_details.items():
1367 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1368 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1369 if val != None and val != self.info[xapiarg]:
1370 self.info[xapiarg] = val
1371 changed = True
1372 elif arg == "memory":
1373 if val != None and val != self.info["static_memory_min"]:
1374 self.info["static_memory_min"] = val
1375 changed = True
1376 elif arg == "maxmem":
1377 if val != None and val != self.info["static_memory_max"]:
1378 self.info["static_memory_max"] = val
1379 changed = True
1381 # Check whether image definition has been updated
1382 image_sxp = self._readVm('image')
1383 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1384 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1385 changed = True
1387 # Check if the rtc offset has changes
1388 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1389 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1390 changed = True
1392 if changed:
1393 # Update the domain section of the store, as this contains some
1394 # parameters derived from the VM configuration.
1395 self._storeDomDetails()
1397 return 1
1399 def _handleShutdownWatch(self, _):
1400 log.debug('XendDomainInfo.handleShutdownWatch')
1402 reason = self.readDom('control/shutdown')
1404 if reason and reason != 'suspend':
1405 sst = self.readDom('xend/shutdown_start_time')
1406 now = time.time()
1407 if sst:
1408 self.shutdownStartTime = float(sst)
1409 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1410 else:
1411 self.shutdownStartTime = now
1412 self.storeDom('xend/shutdown_start_time', now)
1413 timeout = SHUTDOWN_TIMEOUT
1415 log.trace(
1416 "Scheduling refreshShutdown on domain %d in %ds.",
1417 self.domid, timeout)
1418 threading.Timer(timeout, self.refreshShutdown).start()
1420 return True
1424 # Public Attributes for the VM
1428 def getDomid(self):
1429 return self.domid
1431 def setName(self, name, to_store = True):
1432 self._checkName(name)
1433 self.info['name_label'] = name
1434 if to_store:
1435 self.storeVm("name", name)
1437 def getName(self):
1438 return self.info['name_label']
1440 def getDomainPath(self):
1441 return self.dompath
1443 def getShutdownReason(self):
1444 return self.readDom('control/shutdown')
1446 def getStorePort(self):
1447 """For use only by image.py and XendCheckpoint.py."""
1448 return self.store_port
1450 def getConsolePort(self):
1451 """For use only by image.py and XendCheckpoint.py"""
1452 return self.console_port
1454 def getFeatures(self):
1455 """For use only by image.py."""
1456 return self.info['features']
1458 def getVCpuCount(self):
1459 return self.info['VCPUs_max']
1461 def setVCpuCount(self, vcpus):
1462 if vcpus <= 0:
1463 raise XendError('Invalid VCPUs')
1465 self.info['vcpu_avail'] = (1 << vcpus) - 1
1466 if self.domid >= 0:
1467 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1468 # update dom differently depending on whether we are adjusting
1469 # vcpu number up or down, otherwise _vcpuDomDetails does not
1470 # disable the vcpus
1471 if self.info['VCPUs_max'] > vcpus:
1472 # decreasing
1473 self._writeDom(self._vcpuDomDetails())
1474 self.info['VCPUs_live'] = vcpus
1475 else:
1476 # same or increasing
1477 self.info['VCPUs_live'] = vcpus
1478 self._writeDom(self._vcpuDomDetails())
1479 else:
1480 self.info['VCPUs_max'] = vcpus
1481 xen.xend.XendDomain.instance().managed_config_save(self)
1482 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1483 vcpus)
1485 def getMemoryTarget(self):
1486 """Get this domain's target memory size, in KB."""
1487 return self.info['memory_dynamic_max'] / 1024
1489 def getMemoryMaximum(self):
1490 """Get this domain's maximum memory size, in KB."""
1491 # remember, info now stores memory in bytes
1492 return self.info['memory_static_max'] / 1024
1494 def getResume(self):
1495 return str(self._resume)
1497 def setResume(self, isresume):
1498 self._resume = isresume
1500 def getCpus(self):
1501 return self.info['cpus']
1503 def setCpus(self, cpumap):
1504 self.info['cpus'] = cpumap
1506 def getCap(self):
1507 return self.info['vcpus_params']['cap']
1509 def setCap(self, cpu_cap):
1510 self.info['vcpus_params']['cap'] = cpu_cap
1512 def getWeight(self):
1513 return self.info['vcpus_params']['weight']
1515 def setWeight(self, cpu_weight):
1516 self.info['vcpus_params']['weight'] = cpu_weight
1518 def getRestartCount(self):
1519 return self._readVm('xend/restart_count')
1521 def refreshShutdown(self, xeninfo = None):
1522 """ Checks the domain for whether a shutdown is required.
1524 Called from XendDomainInfo and also image.py for HVM images.
1525 """
1527 # If set at the end of this method, a restart is required, with the
1528 # given reason. This restart has to be done out of the scope of
1529 # refresh_shutdown_lock.
1530 restart_reason = None
1532 self.refresh_shutdown_lock.acquire()
1533 try:
1534 if xeninfo is None:
1535 xeninfo = dom_get(self.domid)
1536 if xeninfo is None:
1537 # The domain no longer exists. This will occur if we have
1538 # scheduled a timer to check for shutdown timeouts and the
1539 # shutdown succeeded. It will also occur if someone
1540 # destroys a domain beneath us. We clean up the domain,
1541 # just in case, but we can't clean up the VM, because that
1542 # VM may have migrated to a different domain on this
1543 # machine.
1544 self.cleanupDomain()
1545 self._stateSet(DOM_STATE_HALTED)
1546 return
1548 if xeninfo['dying']:
1549 # Dying means that a domain has been destroyed, but has not
1550 # yet been cleaned up by Xen. This state could persist
1551 # indefinitely if, for example, another domain has some of its
1552 # pages mapped. We might like to diagnose this problem in the
1553 # future, but for now all we do is make sure that it's not us
1554 # holding the pages, by calling cleanupDomain. We can't
1555 # clean up the VM, as above.
1556 self.cleanupDomain()
1557 self._stateSet(DOM_STATE_SHUTDOWN)
1558 return
1560 elif xeninfo['crashed']:
1561 if self.readDom('xend/shutdown_completed'):
1562 # We've seen this shutdown already, but we are preserving
1563 # the domain for debugging. Leave it alone.
1564 return
1566 log.warn('Domain has crashed: name=%s id=%d.',
1567 self.info['name_label'], self.domid)
1568 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1570 restart_reason = 'crash'
1571 self._stateSet(DOM_STATE_HALTED)
1573 elif xeninfo['shutdown']:
1574 self._stateSet(DOM_STATE_SHUTDOWN)
1575 if self.readDom('xend/shutdown_completed'):
1576 # We've seen this shutdown already, but we are preserving
1577 # the domain for debugging. Leave it alone.
1578 return
1580 else:
1581 reason = shutdown_reason(xeninfo['shutdown_reason'])
1583 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1584 self.info['name_label'], self.domid, reason)
1585 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1587 self._clearRestart()
1589 if reason == 'suspend':
1590 self._stateSet(DOM_STATE_SUSPENDED)
1591 # Don't destroy the domain. XendCheckpoint will do
1592 # this once it has finished. However, stop watching
1593 # the VM path now, otherwise we will end up with one
1594 # watch for the old domain, and one for the new.
1595 self._unwatchVm()
1596 elif reason in ('poweroff', 'reboot'):
1597 restart_reason = reason
1598 else:
1599 self.destroy()
1601 elif self.dompath is None:
1602 # We have yet to manage to call introduceDomain on this
1603 # domain. This can happen if a restore is in progress, or has
1604 # failed. Ignore this domain.
1605 pass
1606 else:
1607 # Domain is alive. If we are shutting it down, log a message
1608 # if it seems unresponsive.
1609 if xeninfo['paused']:
1610 self._stateSet(DOM_STATE_PAUSED)
1611 else:
1612 self._stateSet(DOM_STATE_RUNNING)
1614 if self.shutdownStartTime:
1615 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1616 self.shutdownStartTime)
1617 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1618 log.info(
1619 "Domain shutdown timeout expired: name=%s id=%s",
1620 self.info['name_label'], self.domid)
1621 self.storeDom('xend/unresponsive', 'True')
1622 finally:
1623 self.refresh_shutdown_lock.release()
1625 if restart_reason:
1626 threading.Thread(target = self._maybeRestart,
1627 args = (restart_reason,)).start()
1631 # Restart functions - handling whether we come back up on shutdown.
1634 def _clearRestart(self):
1635 self._removeDom("xend/shutdown_start_time")
1637 def _maybeDumpCore(self, reason):
1638 if reason == 'crash':
1639 if xoptions.get_enable_dump() or self.get_on_crash() \
1640 in ['coredump_and_destroy', 'coredump_and_restart']:
1641 try:
1642 self.dumpCore()
1643 except XendError:
1644 # This error has been logged -- there's nothing more
1645 # we can do in this context.
1646 pass
1648 def _maybeRestart(self, reason):
1649 # Before taking configured action, dump core if configured to do so.
1651 self._maybeDumpCore(reason)
1653 # Dispatch to the correct method based upon the configured on_{reason}
1654 # behaviour.
1655 actions = {"destroy" : self.destroy,
1656 "restart" : self._restart,
1657 "preserve" : self._preserve,
1658 "rename-restart" : self._renameRestart,
1659 "coredump-destroy" : self.destroy,
1660 "coredump-restart" : self._restart}
1662 action_conf = {
1663 'poweroff': 'actions_after_shutdown',
1664 'reboot': 'actions_after_reboot',
1665 'crash': 'actions_after_crash',
1668 action_target = self.info.get(action_conf.get(reason))
1669 func = actions.get(action_target, None)
1670 if func and callable(func):
1671 func()
1672 else:
1673 self.destroy() # default to destroy
1675 def _renameRestart(self):
1676 self._restart(True)
1678 def _restart(self, rename = False):
1679 """Restart the domain after it has exited.
1681 @param rename True if the old domain is to be renamed and preserved,
1682 False if it is to be destroyed.
1683 """
1684 from xen.xend import XendDomain
1686 if self._readVm(RESTART_IN_PROGRESS):
1687 log.error('Xend failed during restart of domain %s. '
1688 'Refusing to restart to avoid loops.',
1689 str(self.domid))
1690 self.destroy()
1691 return
1693 old_domid = self.domid
1694 self._writeVm(RESTART_IN_PROGRESS, 'True')
1696 now = time.time()
1697 rst = self._readVm('xend/previous_restart_time')
1698 if rst:
1699 rst = float(rst)
1700 timeout = now - rst
1701 if timeout < MINIMUM_RESTART_TIME:
1702 log.error(
1703 'VM %s restarting too fast (%f seconds since the last '
1704 'restart). Refusing to restart to avoid loops.',
1705 self.info['name_label'], timeout)
1706 self.destroy()
1707 return
1709 self._writeVm('xend/previous_restart_time', str(now))
1711 prev_vm_xend = self._listRecursiveVm('xend')
1712 new_dom_info = self.info
1713 try:
1714 if rename:
1715 new_dom_info = self._preserveForRestart()
1716 else:
1717 self._unwatchVm()
1718 self.destroy()
1720 # new_dom's VM will be the same as this domain's VM, except where
1721 # the rename flag has instructed us to call preserveForRestart.
1722 # In that case, it is important that we remove the
1723 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1724 # once the new one is available.
1726 new_dom = None
1727 try:
1728 new_dom = XendDomain.instance().domain_create_from_dict(
1729 new_dom_info)
1730 for x in prev_vm_xend[0][1]:
1731 new_dom._writeVm('xend/%s' % x[0], x[1])
1732 new_dom.waitForDevices()
1733 new_dom.unpause()
1734 rst_cnt = new_dom._readVm('xend/restart_count')
1735 rst_cnt = int(rst_cnt) + 1
1736 new_dom._writeVm('xend/restart_count', str(rst_cnt))
1737 new_dom._removeVm(RESTART_IN_PROGRESS)
1738 except:
1739 if new_dom:
1740 new_dom._removeVm(RESTART_IN_PROGRESS)
1741 new_dom.destroy()
1742 else:
1743 self._removeVm(RESTART_IN_PROGRESS)
1744 raise
1745 except:
1746 log.exception('Failed to restart domain %s.', str(old_domid))
1748 def _preserveForRestart(self):
1749 """Preserve a domain that has been shut down, by giving it a new UUID,
1750 cloning the VM details, and giving it a new name. This allows us to
1751 keep this domain for debugging, but restart a new one in its place
1752 preserving the restart semantics (name and UUID preserved).
1753 """
1755 new_uuid = uuid.createString()
1756 new_name = 'Domain-%s' % new_uuid
1757 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1758 self.info['name_label'], self.domid, self.info['uuid'],
1759 new_name, new_uuid)
1760 self._unwatchVm()
1761 self._releaseDevices()
1762 # Remove existing vm node in xenstore
1763 self._removeVm()
1764 new_dom_info = self.info.copy()
1765 new_dom_info['name_label'] = self.info['name_label']
1766 new_dom_info['uuid'] = self.info['uuid']
1767 self.info['name_label'] = new_name
1768 self.info['uuid'] = new_uuid
1769 self.vmpath = XS_VMROOT + new_uuid
1770 # Write out new vm node to xenstore
1771 self._storeVmDetails()
1772 self._preserve()
1773 return new_dom_info
1776 def _preserve(self):
1777 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1778 self.domid)
1779 self._unwatchVm()
1780 self.storeDom('xend/shutdown_completed', 'True')
1781 self._stateSet(DOM_STATE_HALTED)
1784 # Debugging ..
1787 def dumpCore(self, corefile = None):
1788 """Create a core dump for this domain.
1790 @raise: XendError if core dumping failed.
1791 """
1793 try:
1794 if not corefile:
1795 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1796 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1797 self.info['name_label'], self.domid)
1799 if os.path.isdir(corefile):
1800 raise XendError("Cannot dump core in a directory: %s" %
1801 corefile)
1803 self._writeVm(DUMPCORE_IN_PROGRESS, 'True')
1804 xc.domain_dumpcore(self.domid, corefile)
1805 self._removeVm(DUMPCORE_IN_PROGRESS)
1806 except RuntimeError, ex:
1807 corefile_incomp = corefile+'-incomplete'
1808 os.rename(corefile, corefile_incomp)
1809 self._removeVm(DUMPCORE_IN_PROGRESS)
1810 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1811 self.domid, self.info['name_label'])
1812 raise XendError("Failed to dump core: %s" % str(ex))
1815 # Device creation/deletion functions
1818 def _createDevice(self, deviceClass, devConfig):
1819 return self.getDeviceController(deviceClass).createDevice(devConfig)
1821 def _waitForDevice(self, deviceClass, devid):
1822 return self.getDeviceController(deviceClass).waitForDevice(devid)
1824 def _waitForDeviceUUID(self, dev_uuid):
1825 deviceClass, config = self.info['devices'].get(dev_uuid)
1826 self._waitForDevice(deviceClass, config['devid'])
1828 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1829 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1830 devid, backpath)
1832 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1833 return self.getDeviceController(deviceClass).reconfigureDevice(
1834 devid, devconfig)
1836 def _createDevices(self):
1837 """Create the devices for a vm.
1839 @raise: VmError for invalid devices
1840 """
1841 if self.image:
1842 self.image.prepareEnvironment()
1844 ordered_refs = self.info.ordered_device_refs()
1845 for dev_uuid in ordered_refs:
1846 devclass, config = self.info['devices'][dev_uuid]
1847 if devclass in XendDevices.valid_devices():
1848 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1849 dev_uuid = config.get('uuid')
1850 devid = self._createDevice(devclass, config)
1852 # store devid in XendConfig for caching reasons
1853 if dev_uuid in self.info['devices']:
1854 self.info['devices'][dev_uuid][1]['devid'] = devid
1856 if self.image:
1857 self.image.createDeviceModel()
1859 #if have pass-through devs, need the virtual pci slots info from qemu
1860 self.sync_pcidev_info()
1862 def _releaseDevices(self, suspend = False):
1863 """Release all domain's devices. Nothrow guarantee."""
1864 if self.image:
1865 try:
1866 log.debug("Destroying device model")
1867 self.image.destroyDeviceModel()
1868 except Exception, e:
1869 log.exception("Device model destroy failed %s" % str(e))
1870 else:
1871 log.debug("No device model")
1873 log.debug("Releasing devices")
1874 t = xstransact("%s/device" % self.dompath)
1875 try:
1876 for devclass in XendDevices.valid_devices():
1877 for dev in t.list(devclass):
1878 try:
1879 log.debug("Removing %s", dev);
1880 self.destroyDevice(devclass, dev, False);
1881 except:
1882 # Log and swallow any exceptions in removal --
1883 # there's nothing more we can do.
1884 log.exception("Device release failed: %s; %s; %s",
1885 self.info['name_label'], devclass, dev)
1886 finally:
1887 t.abort()
1889 def getDeviceController(self, name):
1890 """Get the device controller for this domain, and if it
1891 doesn't exist, create it.
1893 @param name: device class name
1894 @type name: string
1895 @rtype: subclass of DevController
1896 """
1897 if name not in self._deviceControllers:
1898 devController = XendDevices.make_controller(name, self)
1899 if not devController:
1900 raise XendError("Unknown device type: %s" % name)
1901 self._deviceControllers[name] = devController
1903 return self._deviceControllers[name]
1906 # Migration functions (public)
1909 def testMigrateDevices(self, network, dst):
1910 """ Notify all device about intention of migration
1911 @raise: XendError for a device that cannot be migrated
1912 """
1913 for (n, c) in self.info.all_devices_sxpr():
1914 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1915 if rc != 0:
1916 raise XendError("Device of type '%s' refuses migration." % n)
1918 def migrateDevices(self, network, dst, step, domName=''):
1919 """Notify the devices about migration
1920 """
1921 ctr = 0
1922 try:
1923 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1924 self.migrateDevice(dev_type, dev_conf, network, dst,
1925 step, domName)
1926 ctr = ctr + 1
1927 except:
1928 for dev_type, dev_conf in self.info.all_devices_sxpr():
1929 if ctr == 0:
1930 step = step - 1
1931 ctr = ctr - 1
1932 self._recoverMigrateDevice(dev_type, dev_conf, network,
1933 dst, step, domName)
1934 raise
1936 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1937 step, domName=''):
1938 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1939 network, dst, step, domName)
1941 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1942 dst, step, domName=''):
1943 return self.getDeviceController(deviceClass).recover_migrate(
1944 deviceConfig, network, dst, step, domName)
1947 ## private:
1949 def _constructDomain(self):
1950 """Construct the domain.
1952 @raise: VmError on error
1953 """
1955 log.debug('XendDomainInfo.constructDomain')
1957 self.shutdownStartTime = None
1959 hap = 0
1960 hvm = self.info.is_hvm()
1961 if hvm:
1962 hap = self.info.is_hap()
1963 info = xc.xeninfo()
1964 if 'hvm' not in info['xen_caps']:
1965 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1966 "supported by your CPU and enabled in your "
1967 "BIOS?")
1969 # Hack to pre-reserve some memory for initial domain creation.
1970 # There is an implicit memory overhead for any domain creation. This
1971 # overhead is greater for some types of domain than others. For
1972 # example, an x86 HVM domain will have a default shadow-pagetable
1973 # allocation of 1MB. We free up 2MB here to be on the safe side.
1974 balloon.free(2*1024) # 2MB should be plenty
1976 ssidref = 0
1977 if security.on() == xsconstants.XS_POLICY_ACM:
1978 ssidref = security.calc_dom_ssidref_from_info(self.info)
1979 if security.has_authorization(ssidref) == False:
1980 raise VmError("VM is not authorized to run.")
1982 try:
1983 self.domid = xc.domain_create(
1984 domid = 0,
1985 ssidref = ssidref,
1986 handle = uuid.fromString(self.info['uuid']),
1987 flags = (int(hvm) << 0) | (int(hap) << 1),
1988 target = self.info.target())
1989 except Exception, e:
1990 # may get here if due to ACM the operation is not permitted
1991 if security.on() == xsconstants.XS_POLICY_ACM:
1992 raise VmError('Domain in conflict set with running domain?')
1994 if self.domid < 0:
1995 raise VmError('Creating domain failed: name=%s' %
1996 self.info['name_label'])
1998 self.dompath = GetDomainPath(self.domid)
2000 self._recreateDom()
2002 # Set timer configration of domain
2003 timer_mode = self.info["platform"].get("timer_mode")
2004 if hvm and timer_mode is not None:
2005 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
2006 long(timer_mode))
2008 # Optionally enable virtual HPET
2009 hpet = self.info["platform"].get("hpet")
2010 if hvm and hpet is not None:
2011 xc.hvm_set_param(self.domid, HVM_PARAM_HPET_ENABLED,
2012 long(hpet))
2014 # Set maximum number of vcpus in domain
2015 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
2017 # Test whether the devices can be assigned with VT-d
2018 pci_str = str(self.info["platform"].get("pci"))
2019 if hvm and pci_str:
2020 bdf = xc.test_assign_device(self.domid, pci_str)
2021 if bdf != 0:
2022 bus = (bdf >> 16) & 0xff
2023 devfn = (bdf >> 8) & 0xff
2024 dev = (devfn >> 3) & 0x1f
2025 func = devfn & 0x7
2026 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
2027 "not enabled, or the device is not exist, or it "
2028 "has already been assigned to other domain"
2029 % (bus, dev, func))
2031 # register the domain in the list
2032 from xen.xend import XendDomain
2033 XendDomain.instance().add_domain(self)
2035 def _introduceDomain(self):
2036 assert self.domid is not None
2037 assert self.store_mfn is not None
2038 assert self.store_port is not None
2040 try:
2041 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
2042 except RuntimeError, exn:
2043 raise XendError(str(exn))
2045 def _setTarget(self, target):
2046 assert self.domid is not None
2048 try:
2049 SetTarget(self.domid, target)
2050 self.storeDom('target', target)
2051 except RuntimeError, exn:
2052 raise XendError(str(exn))
2055 def _initDomain(self):
2056 log.debug('XendDomainInfo.initDomain: %s %s',
2057 self.domid,
2058 self.info['vcpus_params']['weight'])
2060 self._configureBootloader()
2062 try:
2063 if self.info['platform'].get('localtime', 0):
2064 if time.localtime(time.time())[8]:
2065 self.info['platform']['rtc_timeoffset'] = -time.altzone
2066 else:
2067 self.info['platform']['rtc_timeoffset'] = -time.timezone
2069 self.image = image.create(self, self.info)
2071 # repin domain vcpus if a restricted cpus list is provided
2072 # this is done prior to memory allocation to aide in memory
2073 # distribution for NUMA systems.
2074 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
2075 for v in range(0, self.info['VCPUs_max']):
2076 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
2077 else:
2078 def find_relaxed_node(node_list):
2079 import sys
2080 if node_list is None:
2081 node_list = range(0, info['nr_nodes'])
2082 nodeload = [0]
2083 nodeload = nodeload * info['nr_nodes']
2084 from xen.xend import XendDomain
2085 doms = XendDomain.instance().list('all')
2086 for dom in doms:
2087 cpuinfo = dom.getVCPUInfo()
2088 for vcpu in sxp.children(cpuinfo, 'vcpu'):
2089 def vinfo(n, t):
2090 return t(sxp.child_value(vcpu, n))
2091 cpumap = vinfo('cpumap', list)
2092 for i in node_list:
2093 node_cpumask = info['node_to_cpu'][i]
2094 for j in node_cpumask:
2095 if j in cpumap:
2096 nodeload[i] += 1
2097 break
2098 for i in node_list:
2099 if len(info['node_to_cpu'][i]) > 0:
2100 nodeload[i] = int(nodeload[i] / len(info['node_to_cpu'][i]))
2101 else:
2102 nodeload[i] = sys.maxint
2103 index = nodeload.index( min(nodeload) )
2104 return index
2106 info = xc.physinfo()
2107 if info['nr_nodes'] > 1:
2108 node_memory_list = info['node_to_memory']
2109 needmem = self.image.getRequiredAvailableMemory(self.info['memory_dynamic_max']) / 1024
2110 candidate_node_list = []
2111 for i in range(0, info['nr_nodes']):
2112 if node_memory_list[i] >= needmem and len(info['node_to_cpu'][i]) > 0:
2113 candidate_node_list.append(i)
2114 index = find_relaxed_node(candidate_node_list)
2115 cpumask = info['node_to_cpu'][index]
2116 for v in range(0, self.info['VCPUs_max']):
2117 xc.vcpu_setaffinity(self.domid, v, cpumask)
2119 # Use architecture- and image-specific calculations to determine
2120 # the various headrooms necessary, given the raw configured
2121 # values. maxmem, memory, and shadow are all in KiB.
2122 # but memory_static_max etc are all stored in bytes now.
2123 memory = self.image.getRequiredAvailableMemory(
2124 self.info['memory_dynamic_max'] / 1024)
2125 maxmem = self.image.getRequiredAvailableMemory(
2126 self.info['memory_static_max'] / 1024)
2127 shadow = self.image.getRequiredShadowMemory(
2128 self.info['shadow_memory'] * 1024,
2129 self.info['memory_static_max'] / 1024)
2131 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'],)
2132 # Round shadow up to a multiple of a MiB, as shadow_mem_control
2133 # takes MiB and we must not round down and end up under-providing.
2134 shadow = ((shadow + 1023) / 1024) * 1024
2136 # set memory limit
2137 xc.domain_setmaxmem(self.domid, maxmem)
2139 # Reserve 1 page per MiB of RAM for separate VT-d page table.
2140 vtd_mem = 4 * (self.info['memory_static_max'] / 1024 / 1024)
2141 # Round vtd_mem up to a multiple of a MiB.
2142 vtd_mem = ((vtd_mem + 1023) / 1024) * 1024
2144 # Make sure there's enough RAM available for the domain
2145 balloon.free(memory + shadow + vtd_mem)
2147 # Set up the shadow memory
2148 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
2149 self.info['shadow_memory'] = shadow_cur
2151 self._createChannels()
2153 channel_details = self.image.createImage()
2155 self.store_mfn = channel_details['store_mfn']
2156 if 'console_mfn' in channel_details:
2157 self.console_mfn = channel_details['console_mfn']
2158 if 'notes' in channel_details:
2159 self.info.set_notes(channel_details['notes'])
2160 if 'native_protocol' in channel_details:
2161 self.native_protocol = channel_details['native_protocol'];
2163 self._introduceDomain()
2164 if self.info.target():
2165 self._setTarget(self.info.target())
2167 self._createDevices()
2169 self.image.cleanupBootloading()
2171 self.info['start_time'] = time.time()
2173 self._stateSet(DOM_STATE_RUNNING)
2174 except VmError, exn:
2175 log.exception("XendDomainInfo.initDomain: exception occurred")
2176 if self.image:
2177 self.image.cleanupBootloading()
2178 raise exn
2179 except RuntimeError, exn:
2180 log.exception("XendDomainInfo.initDomain: exception occurred")
2181 if self.image:
2182 self.image.cleanupBootloading()
2183 raise VmError(str(exn))
2186 def cleanupDomain(self):
2187 """Cleanup domain resources; release devices. Idempotent. Nothrow
2188 guarantee."""
2190 self.refresh_shutdown_lock.acquire()
2191 try:
2192 self.unwatchShutdown()
2193 self._releaseDevices()
2194 bootloader_tidy(self)
2196 if self.image:
2197 self.image = None
2199 try:
2200 self._removeDom()
2201 except:
2202 log.exception("Removing domain path failed.")
2204 self._stateSet(DOM_STATE_HALTED)
2205 self.domid = None # Do not push into _stateSet()!
2206 finally:
2207 self.refresh_shutdown_lock.release()
2210 def unwatchShutdown(self):
2211 """Remove the watch on the domain's control/shutdown node, if any.
2212 Idempotent. Nothrow guarantee. Expects to be protected by the
2213 refresh_shutdown_lock."""
2215 try:
2216 try:
2217 if self.shutdownWatch:
2218 self.shutdownWatch.unwatch()
2219 finally:
2220 self.shutdownWatch = None
2221 except:
2222 log.exception("Unwatching control/shutdown failed.")
2224 def waitForShutdown(self):
2225 self.state_updated.acquire()
2226 try:
2227 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
2228 self.state_updated.wait(timeout=1.0)
2229 finally:
2230 self.state_updated.release()
2233 # TODO: recategorise - called from XendCheckpoint
2236 def completeRestore(self, store_mfn, console_mfn):
2238 log.debug("XendDomainInfo.completeRestore")
2240 self.store_mfn = store_mfn
2241 self.console_mfn = console_mfn
2243 self._introduceDomain()
2244 self.image = image.create(self, self.info)
2245 if self.image:
2246 self.image.createDeviceModel(True)
2247 self._storeDomDetails()
2248 self._registerWatches()
2249 self.refreshShutdown()
2251 log.debug("XendDomainInfo.completeRestore done")
2254 def _endRestore(self):
2255 self.setResume(False)
2258 # VM Destroy
2261 def _prepare_phantom_paths(self):
2262 # get associated devices to destroy
2263 # build list of phantom devices to be removed after normal devices
2264 plist = []
2265 if self.domid is not None:
2266 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
2267 try:
2268 for dev in t.list():
2269 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
2270 % (self.dompath, dev))
2271 if backend_phantom_vbd is not None:
2272 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
2273 % backend_phantom_vbd)
2274 plist.append(backend_phantom_vbd)
2275 plist.append(frontend_phantom_vbd)
2276 finally:
2277 t.abort()
2278 return plist
2280 def _cleanup_phantom_devs(self, plist):
2281 # remove phantom devices
2282 if not plist == []:
2283 time.sleep(2)
2284 for paths in plist:
2285 if paths.find('backend') != -1:
2286 from xen.xend.server import DevController
2287 # Modify online status /before/ updating state (latter is watched by
2288 # drivers, so this ordering avoids a race).
2289 xstransact.Write(paths, 'online', "0")
2290 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
2291 # force
2292 xstransact.Remove(paths)
2294 def destroy(self):
2295 """Cleanup VM and destroy domain. Nothrow guarantee."""
2297 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
2299 paths = self._prepare_phantom_paths()
2301 self._cleanupVm()
2302 if self.dompath is not None:
2303 self.destroyDomain()
2305 self._cleanup_phantom_devs(paths)
2307 if "transient" in self.info["other_config"] \
2308 and bool(self.info["other_config"]["transient"]):
2309 from xen.xend import XendDomain
2310 XendDomain.instance().domain_delete_by_dominfo(self)
2313 def destroyDomain(self):
2314 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
2316 paths = self._prepare_phantom_paths()
2318 try:
2319 if self.domid is not None:
2320 xc.domain_destroy_hook(self.domid)
2321 xc.domain_destroy(self.domid)
2322 for state in DOM_STATES_OLD:
2323 self.info[state] = 0
2324 self._stateSet(DOM_STATE_HALTED)
2325 except:
2326 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
2328 from xen.xend import XendDomain
2329 XendDomain.instance().remove_domain(self)
2331 self.cleanupDomain()
2332 self._cleanup_phantom_devs(paths)
2335 def resetDomain(self):
2336 log.debug("XendDomainInfo.resetDomain(%s)", str(self.domid))
2338 old_domid = self.domid
2339 prev_vm_xend = self._listRecursiveVm('xend')
2340 new_dom_info = self.info
2341 try:
2342 self._unwatchVm()
2343 self.destroy()
2345 new_dom = None
2346 try:
2347 from xen.xend import XendDomain
2348 new_dom_info['domid'] = None
2349 new_dom = XendDomain.instance().domain_create_from_dict(
2350 new_dom_info)
2351 for x in prev_vm_xend[0][1]:
2352 new_dom._writeVm('xend/%s' % x[0], x[1])
2353 new_dom.waitForDevices()
2354 new_dom.unpause()
2355 except:
2356 if new_dom:
2357 new_dom.destroy()
2358 raise
2359 except:
2360 log.exception('Failed to reset domain %s.', str(old_domid))
2363 def resumeDomain(self):
2364 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
2366 if self.domid is None:
2367 return
2368 try:
2369 # could also fetch a parsed note from xenstore
2370 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
2371 if not fast:
2372 self._releaseDevices()
2373 self.testDeviceComplete()
2374 self.testvifsComplete()
2375 log.debug("XendDomainInfo.resumeDomain: devices released")
2377 self._resetChannels()
2379 self._removeDom('control/shutdown')
2380 self._removeDom('device-misc/vif/nextDeviceID')
2382 self._createChannels()
2383 self._introduceDomain()
2384 self._storeDomDetails()
2386 self._createDevices()
2387 log.debug("XendDomainInfo.resumeDomain: devices created")
2389 xc.domain_resume(self.domid, fast)
2390 ResumeDomain(self.domid)
2391 except:
2392 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
2393 self.image.resumeDeviceModel()
2394 log.debug("XendDomainInfo.resumeDomain: completed")
2398 # Channels for xenstore and console
2401 def _createChannels(self):
2402 """Create the channels to the domain.
2403 """
2404 self.store_port = self._createChannel()
2405 self.console_port = self._createChannel()
2408 def _createChannel(self):
2409 """Create an event channel to the domain.
2410 """
2411 try:
2412 if self.domid != None:
2413 return xc.evtchn_alloc_unbound(domid = self.domid,
2414 remote_dom = 0)
2415 except:
2416 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2417 raise
2419 def _resetChannels(self):
2420 """Reset all event channels in the domain.
2421 """
2422 try:
2423 if self.domid != None:
2424 return xc.evtchn_reset(dom = self.domid)
2425 except:
2426 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2427 raise
2431 # Bootloader configuration
2434 def _configureBootloader(self):
2435 """Run the bootloader if we're configured to do so."""
2437 blexec = self.info['PV_bootloader']
2438 bootloader_args = self.info['PV_bootloader_args']
2439 kernel = self.info['PV_kernel']
2440 ramdisk = self.info['PV_ramdisk']
2441 args = self.info['PV_args']
2442 boot = self.info['HVM_boot_policy']
2444 if boot:
2445 # HVM booting.
2446 pass
2447 elif not blexec and kernel:
2448 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2449 # will be picked up by image.py.
2450 pass
2451 else:
2452 # Boot using bootloader
2453 if not blexec or blexec == 'pygrub':
2454 blexec = osdep.pygrub_path
2456 blcfg = None
2457 disks = [x for x in self.info['vbd_refs']
2458 if self.info['devices'][x][1]['bootable']]
2460 if not disks:
2461 msg = "Had a bootloader specified, but no disks are bootable"
2462 log.error(msg)
2463 raise VmError(msg)
2465 devinfo = self.info['devices'][disks[0]]
2466 devtype = devinfo[0]
2467 disk = devinfo[1]['uname']
2469 fn = blkdev_uname_to_file(disk)
2470 taptype = blkdev_uname_to_taptype(disk)
2471 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2472 if mounted:
2473 # This is a file, not a device. pygrub can cope with a
2474 # file if it's raw, but if it's QCOW or other such formats
2475 # used through blktap, then we need to mount it first.
2477 log.info("Mounting %s on %s." %
2478 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2480 vbd = {
2481 'mode': 'RO',
2482 'device': BOOTLOADER_LOOPBACK_DEVICE,
2485 from xen.xend import XendDomain
2486 dom0 = XendDomain.instance().privilegedDomain()
2487 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2488 fn = BOOTLOADER_LOOPBACK_DEVICE
2490 try:
2491 blcfg = bootloader(blexec, fn, self, False,
2492 bootloader_args, kernel, ramdisk, args)
2493 finally:
2494 if mounted:
2495 log.info("Unmounting %s from %s." %
2496 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2498 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2500 if blcfg is None:
2501 msg = "Had a bootloader specified, but can't find disk"
2502 log.error(msg)
2503 raise VmError(msg)
2505 self.info.update_with_image_sxp(blcfg, True)
2509 # VM Functions
2512 def _readVMDetails(self, params):
2513 """Read the specified parameters from the store.
2514 """
2515 try:
2516 return self._gatherVm(*params)
2517 except ValueError:
2518 # One of the int/float entries in params has a corresponding store
2519 # entry that is invalid. We recover, because older versions of
2520 # Xend may have put the entry there (memory/target, for example),
2521 # but this is in general a bad situation to have reached.
2522 log.exception(
2523 "Store corrupted at %s! Domain %d's configuration may be "
2524 "affected.", self.vmpath, self.domid)
2525 return []
2527 def _cleanupVm(self):
2528 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2530 self._unwatchVm()
2532 try:
2533 self._removeVm()
2534 except:
2535 log.exception("Removing VM path failed.")
2538 def checkLiveMigrateMemory(self):
2539 """ Make sure there's enough memory to migrate this domain """
2540 overhead_kb = 0
2541 if arch.type == "x86":
2542 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2543 # the minimum that Xen would allocate if no value were given.
2544 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2545 (self.info['memory_static_max'] / 1024 / 1024) * 4
2546 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2547 # The domain might already have some shadow memory
2548 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2549 if overhead_kb > 0:
2550 balloon.free(overhead_kb)
2552 def _unwatchVm(self):
2553 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2554 guarantee."""
2555 try:
2556 try:
2557 if self.vmWatch:
2558 self.vmWatch.unwatch()
2559 finally:
2560 self.vmWatch = None
2561 except:
2562 log.exception("Unwatching VM path failed.")
2564 def testDeviceComplete(self):
2565 """ For Block IO migration safety we must ensure that
2566 the device has shutdown correctly, i.e. all blocks are
2567 flushed to disk
2568 """
2569 start = time.time()
2570 while True:
2571 test = 0
2572 diff = time.time() - start
2573 for i in self.getDeviceController('vbd').deviceIDs():
2574 test = 1
2575 log.info("Dev %s still active, looping...", i)
2576 time.sleep(0.1)
2578 if test == 0:
2579 break
2580 if diff >= MIGRATE_TIMEOUT:
2581 log.info("Dev still active but hit max loop timeout")
2582 break
2584 def testvifsComplete(self):
2585 """ In case vifs are released and then created for the same
2586 domain, we need to wait the device shut down.
2587 """
2588 start = time.time()
2589 while True:
2590 test = 0
2591 diff = time.time() - start
2592 for i in self.getDeviceController('vif').deviceIDs():
2593 test = 1
2594 log.info("Dev %s still active, looping...", i)
2595 time.sleep(0.1)
2597 if test == 0:
2598 break
2599 if diff >= MIGRATE_TIMEOUT:
2600 log.info("Dev still active but hit max loop timeout")
2601 break
2603 def _storeVmDetails(self):
2604 to_store = {}
2606 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2607 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2608 if self._infoIsSet(info_key):
2609 to_store[key] = str(self.info[info_key])
2611 if self._infoIsSet("static_memory_min"):
2612 to_store["memory"] = str(self.info["static_memory_min"])
2613 if self._infoIsSet("static_memory_max"):
2614 to_store["maxmem"] = str(self.info["static_memory_max"])
2616 image_sxpr = self.info.image_sxpr()
2617 if image_sxpr:
2618 to_store['image'] = sxp.to_string(image_sxpr)
2620 if not self._readVm('xend/restart_count'):
2621 to_store['xend/restart_count'] = str(0)
2623 log.debug("Storing VM details: %s", scrub_password(to_store))
2625 self._writeVm(to_store)
2626 self._setVmPermissions()
2629 def _setVmPermissions(self):
2630 """Allow the guest domain to read its UUID. We don't allow it to
2631 access any other entry, for security."""
2632 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2633 { 'dom' : self.domid,
2634 'read' : True,
2635 'write' : False })
2638 # Utility functions
2641 def __getattr__(self, name):
2642 if name == "state":
2643 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2644 log.warn("".join(traceback.format_stack()))
2645 return self._stateGet()
2646 else:
2647 raise AttributeError()
2649 def __setattr__(self, name, value):
2650 if name == "state":
2651 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2652 log.warn("".join(traceback.format_stack()))
2653 self._stateSet(value)
2654 else:
2655 self.__dict__[name] = value
2657 def _stateSet(self, state):
2658 self.state_updated.acquire()
2659 try:
2660 # TODO Not sure this is correct...
2661 # _stateGet is live now. Why not fire event
2662 # even when it hasn't changed?
2663 if self._stateGet() != state:
2664 self.state_updated.notifyAll()
2665 import XendAPI
2666 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2667 'power_state')
2668 finally:
2669 self.state_updated.release()
2671 def _stateGet(self):
2672 # Lets try and reconsitute the state from xc
2673 # first lets try and get the domain info
2674 # from xc - this will tell us if the domain
2675 # exists
2676 info = dom_get(self.getDomid())
2677 if info is None or info['shutdown']:
2678 # We are either HALTED or SUSPENDED
2679 # check saved image exists
2680 from xen.xend import XendDomain
2681 managed_config_path = \
2682 XendDomain.instance()._managed_check_point_path( \
2683 self.get_uuid())
2684 if os.path.exists(managed_config_path):
2685 return XEN_API_VM_POWER_STATE_SUSPENDED
2686 else:
2687 return XEN_API_VM_POWER_STATE_HALTED
2688 elif info['crashed']:
2689 # Crashed
2690 return XEN_API_VM_POWER_STATE_CRASHED
2691 else:
2692 # We are either RUNNING or PAUSED
2693 if info['paused']:
2694 return XEN_API_VM_POWER_STATE_PAUSED
2695 else:
2696 return XEN_API_VM_POWER_STATE_RUNNING
2698 def _infoIsSet(self, name):
2699 return name in self.info and self.info[name] is not None
2701 def _checkName(self, name):
2702 """Check if a vm name is valid. Valid names contain alphabetic
2703 characters, digits, or characters in '_-.:/+'.
2704 The same name cannot be used for more than one vm at the same time.
2706 @param name: name
2707 @raise: VmError if invalid
2708 """
2709 from xen.xend import XendDomain
2711 if name is None or name == '':
2712 raise VmError('Missing VM Name')
2714 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2715 raise VmError('Invalid VM Name')
2717 dom = XendDomain.instance().domain_lookup_nr(name)
2718 if dom and dom.info['uuid'] != self.info['uuid']:
2719 raise VmError("VM name '%s' already exists%s" %
2720 (name,
2721 dom.domid is not None and
2722 (" as domain %s" % str(dom.domid)) or ""))
2725 def update(self, info = None, refresh = True, transaction = None):
2726 """Update with info from xc.domain_getinfo().
2727 """
2728 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2729 str(self.domid))
2731 if not info:
2732 info = dom_get(self.domid)
2733 if not info:
2734 return
2736 if info["maxmem_kb"] < 0:
2737 info["maxmem_kb"] = XendNode.instance() \
2738 .physinfo_dict()['total_memory'] * 1024
2740 #ssidref field not used any longer
2741 if 'ssidref' in info:
2742 info.pop('ssidref')
2744 # make sure state is reset for info
2745 # TODO: we should eventually get rid of old_dom_states
2747 self.info.update_config(info)
2748 self._update_consoles(transaction)
2750 if refresh:
2751 self.refreshShutdown(info)
2753 log.trace("XendDomainInfo.update done on domain %s: %s",
2754 str(self.domid), self.info)
2756 def sxpr(self, ignore_store = False, legacy_only = True):
2757 result = self.info.to_sxp(domain = self,
2758 ignore_devices = ignore_store,
2759 legacy_only = legacy_only)
2761 #if not ignore_store and self.dompath:
2762 # vnc_port = self.readDom('console/vnc-port')
2763 # if vnc_port is not None:
2764 # result.append(['device',
2765 # ['console', ['vnc-port', str(vnc_port)]]])
2767 return result
2769 # Xen API
2770 # ----------------------------------------------------------------
2772 def get_uuid(self):
2773 dom_uuid = self.info.get('uuid')
2774 if not dom_uuid: # if it doesn't exist, make one up
2775 dom_uuid = uuid.createString()
2776 self.info['uuid'] = dom_uuid
2777 return dom_uuid
2779 def get_memory_static_max(self):
2780 return self.info.get('memory_static_max', 0)
2781 def get_memory_static_min(self):
2782 return self.info.get('memory_static_min', 0)
2783 def get_memory_dynamic_max(self):
2784 return self.info.get('memory_dynamic_max', 0)
2785 def get_memory_dynamic_min(self):
2786 return self.info.get('memory_dynamic_min', 0)
2788 # only update memory-related config values if they maintain sanity
2789 def _safe_set_memory(self, key, newval):
2790 oldval = self.info.get(key, 0)
2791 try:
2792 self.info[key] = newval
2793 self.info._memory_sanity_check()
2794 except Exception, ex:
2795 self.info[key] = oldval
2796 raise
2798 def set_memory_static_max(self, val):
2799 self._safe_set_memory('memory_static_max', val)
2800 def set_memory_static_min(self, val):
2801 self._safe_set_memory('memory_static_min', val)
2802 def set_memory_dynamic_max(self, val):
2803 self._safe_set_memory('memory_dynamic_max', val)
2804 def set_memory_dynamic_min(self, val):
2805 self._safe_set_memory('memory_dynamic_min', val)
2807 def get_vcpus_params(self):
2808 if self.getDomid() is None:
2809 return self.info['vcpus_params']
2811 retval = xc.sched_credit_domain_get(self.getDomid())
2812 return retval
2813 def get_power_state(self):
2814 return XEN_API_VM_POWER_STATE[self._stateGet()]
2815 def get_platform(self):
2816 return self.info.get('platform', {})
2817 def get_pci_bus(self):
2818 return self.info.get('pci_bus', '')
2819 def get_tools_version(self):
2820 return self.info.get('tools_version', {})
2821 def get_metrics(self):
2822 return self.metrics.get_uuid();
2825 def get_security_label(self, xspol=None):
2826 import xen.util.xsm.xsm as security
2827 label = security.get_security_label(self, xspol)
2828 return label
2830 def set_security_label(self, seclab, old_seclab, xspol=None,
2831 xspol_old=None):
2832 """
2833 Set the security label of a domain from its old to
2834 a new value.
2835 @param seclab New security label formatted in the form
2836 <policy type>:<policy name>:<vm label>
2837 @param old_seclab The current security label that the
2838 VM must have.
2839 @param xspol An optional policy under which this
2840 update should be done. If not given,
2841 then the current active policy is used.
2842 @param xspol_old The old policy; only to be passed during
2843 the updating of a policy
2844 @return Returns return code, a string with errors from
2845 the hypervisor's operation, old label of the
2846 domain
2847 """
2848 rc = 0
2849 errors = ""
2850 old_label = ""
2851 new_ssidref = 0
2852 domid = self.getDomid()
2853 res_labels = None
2854 is_policy_update = (xspol_old != None)
2856 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2858 state = self._stateGet()
2859 # Relabel only HALTED or RUNNING or PAUSED domains
2860 if domid != 0 and \
2861 state not in \
2862 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2863 DOM_STATE_SUSPENDED ]:
2864 log.warn("Relabeling domain not possible in state '%s'" %
2865 DOM_STATES[state])
2866 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2868 # Remove security label. Works only for halted domains
2869 if not seclab or seclab == "":
2870 if state not in [ DOM_STATE_HALTED ]:
2871 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2873 if self.info.has_key('security_label'):
2874 old_label = self.info['security_label']
2875 # Check label against expected one.
2876 if old_label != old_seclab:
2877 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2878 del self.info['security_label']
2879 xen.xend.XendDomain.instance().managed_config_save(self)
2880 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2882 tmp = seclab.split(":")
2883 if len(tmp) != 3:
2884 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2885 typ, policy, label = tmp
2887 poladmin = XSPolicyAdminInstance()
2888 if not xspol:
2889 xspol = poladmin.get_policy_by_name(policy)
2891 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2892 #if domain is running or paused try to relabel in hypervisor
2893 if not xspol:
2894 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2896 if typ != xspol.get_type_name() or \
2897 policy != xspol.get_name():
2898 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2900 if typ == xsconstants.ACM_POLICY_ID:
2901 new_ssidref = xspol.vmlabel_to_ssidref(label)
2902 if new_ssidref == xsconstants.INVALID_SSIDREF:
2903 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2905 # Check that all used resources are accessible under the
2906 # new label
2907 if not is_policy_update and \
2908 not security.resources_compatible_with_vmlabel(xspol,
2909 self, label):
2910 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2912 #Check label against expected one. Can only do this
2913 # if the policy hasn't changed underneath in the meantime
2914 if xspol_old == None:
2915 old_label = self.get_security_label()
2916 if old_label != old_seclab:
2917 log.info("old_label != old_seclab: %s != %s" %
2918 (old_label, old_seclab))
2919 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2921 # relabel domain in the hypervisor
2922 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2923 log.info("rc from relabeling in HV: %d" % rc)
2924 else:
2925 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2927 if rc == 0:
2928 # HALTED, RUNNING or PAUSED
2929 if domid == 0:
2930 if xspol:
2931 self.info['security_label'] = seclab
2932 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2933 else:
2934 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2935 else:
2936 if self.info.has_key('security_label'):
2937 old_label = self.info['security_label']
2938 # Check label against expected one, unless wildcard
2939 if old_label != old_seclab:
2940 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2942 self.info['security_label'] = seclab
2944 try:
2945 xen.xend.XendDomain.instance().managed_config_save(self)
2946 except:
2947 pass
2948 return (rc, errors, old_label, new_ssidref)
2950 def get_on_shutdown(self):
2951 after_shutdown = self.info.get('actions_after_shutdown')
2952 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2953 return XEN_API_ON_NORMAL_EXIT[-1]
2954 return after_shutdown
2956 def get_on_reboot(self):
2957 after_reboot = self.info.get('actions_after_reboot')
2958 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2959 return XEN_API_ON_NORMAL_EXIT[-1]
2960 return after_reboot
2962 def get_on_suspend(self):
2963 # TODO: not supported
2964 after_suspend = self.info.get('actions_after_suspend')
2965 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2966 return XEN_API_ON_NORMAL_EXIT[-1]
2967 return after_suspend
2969 def get_on_crash(self):
2970 after_crash = self.info.get('actions_after_crash')
2971 if not after_crash or after_crash not in \
2972 XEN_API_ON_CRASH_BEHAVIOUR + restart_modes:
2973 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2974 return XEN_API_ON_CRASH_BEHAVIOUR_FILTER[after_crash]
2976 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2977 """ Get's a device configuration either from XendConfig or
2978 from the DevController.
2980 @param dev_class: device class, either, 'vbd' or 'vif'
2981 @param dev_uuid: device UUID
2983 @rtype: dictionary
2984 """
2985 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2987 # shortcut if the domain isn't started because
2988 # the devcontrollers will have no better information
2989 # than XendConfig.
2990 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2991 if dev_config:
2992 return copy.deepcopy(dev_config)
2993 return None
2995 # instead of using dev_class, we use the dev_type
2996 # that is from XendConfig.
2997 controller = self.getDeviceController(dev_type)
2998 if not controller:
2999 return None
3001 all_configs = controller.getAllDeviceConfigurations()
3002 if not all_configs:
3003 return None
3005 updated_dev_config = copy.deepcopy(dev_config)
3006 for _devid, _devcfg in all_configs.items():
3007 if _devcfg.get('uuid') == dev_uuid:
3008 updated_dev_config.update(_devcfg)
3009 updated_dev_config['id'] = _devid
3010 return updated_dev_config
3012 return updated_dev_config
3014 def get_dev_xenapi_config(self, dev_class, dev_uuid):
3015 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
3016 if not config:
3017 return {}
3019 config['VM'] = self.get_uuid()
3021 if dev_class == 'vif':
3022 if not config.has_key('name'):
3023 config['name'] = config.get('vifname', '')
3024 if not config.has_key('MAC'):
3025 config['MAC'] = config.get('mac', '')
3026 if not config.has_key('type'):
3027 config['type'] = 'paravirtualised'
3028 if not config.has_key('device'):
3029 devid = config.get('id')
3030 if devid != None:
3031 config['device'] = 'eth%d' % devid
3032 else:
3033 config['device'] = ''
3035 if not config.has_key('network'):
3036 try:
3037 bridge = config.get('bridge', None)
3038 if bridge is None:
3039 from xen.util import Brctl
3040 if_to_br = dict([(i,b)
3041 for (b,ifs) in Brctl.get_state().items()
3042 for i in ifs])
3043 vifname = "vif%s.%s" % (self.getDomid(),
3044 config.get('id'))
3045 bridge = if_to_br.get(vifname, None)
3046 config['network'] = \
3047 XendNode.instance().bridge_to_network(
3048 config.get('bridge')).get_uuid()
3049 except Exception:
3050 log.exception('bridge_to_network')
3051 # Ignore this for now -- it may happen if the device
3052 # has been specified using the legacy methods, but at
3053 # some point we're going to have to figure out how to
3054 # handle that properly.
3056 config['MTU'] = 1500 # TODO
3058 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
3059 xennode = XendNode.instance()
3060 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
3061 config['io_read_kbs'] = rx_bps/1024
3062 config['io_write_kbs'] = tx_bps/1024
3063 rx, tx = xennode.get_vif_stat(self.domid, devid)
3064 config['io_total_read_kbs'] = rx/1024
3065 config['io_total_write_kbs'] = tx/1024
3066 else:
3067 config['io_read_kbs'] = 0.0
3068 config['io_write_kbs'] = 0.0
3069 config['io_total_read_kbs'] = 0.0
3070 config['io_total_write_kbs'] = 0.0
3072 config['security_label'] = config.get('security_label', '')
3074 if dev_class == 'vbd':
3076 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
3077 controller = self.getDeviceController(dev_class)
3078 devid, _1, _2 = controller.getDeviceDetails(config)
3079 xennode = XendNode.instance()
3080 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
3081 config['io_read_kbs'] = rd_blkps
3082 config['io_write_kbs'] = wr_blkps
3083 else:
3084 config['io_read_kbs'] = 0.0
3085 config['io_write_kbs'] = 0.0
3087 config['VDI'] = config.get('VDI', '')
3088 config['device'] = config.get('dev', '')
3089 if ':' in config['device']:
3090 vbd_name, vbd_type = config['device'].split(':', 1)
3091 config['device'] = vbd_name
3092 if vbd_type == 'cdrom':
3093 config['type'] = XEN_API_VBD_TYPE[0]
3094 else:
3095 config['type'] = XEN_API_VBD_TYPE[1]
3097 config['driver'] = 'paravirtualised' # TODO
3098 config['image'] = config.get('uname', '')
3100 if config.get('mode', 'r') == 'r':
3101 config['mode'] = 'RO'
3102 else:
3103 config['mode'] = 'RW'
3105 if dev_class == 'vtpm':
3106 if not config.has_key('type'):
3107 config['type'] = 'paravirtualised' # TODO
3108 if not config.has_key('backend'):
3109 config['backend'] = "00000000-0000-0000-0000-000000000000"
3111 return config
3113 def get_dev_property(self, dev_class, dev_uuid, field):
3114 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
3115 try:
3116 return config[field]
3117 except KeyError:
3118 raise XendError('Invalid property for device: %s' % field)
3120 def set_dev_property(self, dev_class, dev_uuid, field, value):
3121 self.info['devices'][dev_uuid][1][field] = value
3123 def get_vcpus_util(self):
3124 vcpu_util = {}
3125 xennode = XendNode.instance()
3126 if 'VCPUs_max' in self.info and self.domid != None:
3127 for i in range(0, self.info['VCPUs_max']):
3128 util = xennode.get_vcpu_util(self.domid, i)
3129 vcpu_util[str(i)] = util
3131 return vcpu_util
3133 def get_consoles(self):
3134 return self.info.get('console_refs', [])
3136 def get_vifs(self):
3137 return self.info.get('vif_refs', [])
3139 def get_vbds(self):
3140 return self.info.get('vbd_refs', [])
3142 def get_vtpms(self):
3143 return self.info.get('vtpm_refs', [])
3145 def create_vbd(self, xenapi_vbd, vdi_image_path):
3146 """Create a VBD using a VDI from XendStorageRepository.
3148 @param xenapi_vbd: vbd struct from the Xen API
3149 @param vdi_image_path: VDI UUID
3150 @rtype: string
3151 @return: uuid of the device
3152 """
3153 xenapi_vbd['image'] = vdi_image_path
3154 if vdi_image_path.startswith('tap'):
3155 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
3156 else:
3157 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
3159 if not dev_uuid:
3160 raise XendError('Failed to create device')
3162 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3163 XEN_API_VM_POWER_STATE_PAUSED):
3164 _, config = self.info['devices'][dev_uuid]
3166 if vdi_image_path.startswith('tap'):
3167 dev_control = self.getDeviceController('tap')
3168 else:
3169 dev_control = self.getDeviceController('vbd')
3171 try:
3172 devid = dev_control.createDevice(config)
3173 dev_control.waitForDevice(devid)
3174 self.info.device_update(dev_uuid,
3175 cfg_xenapi = {'devid': devid})
3176 except Exception, exn:
3177 log.exception(exn)
3178 del self.info['devices'][dev_uuid]
3179 self.info['vbd_refs'].remove(dev_uuid)
3180 raise
3182 return dev_uuid
3184 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
3185 """Create a VBD using a VDI from XendStorageRepository.
3187 @param xenapi_vbd: vbd struct from the Xen API
3188 @param vdi_image_path: VDI UUID
3189 @rtype: string
3190 @return: uuid of the device
3191 """
3192 xenapi_vbd['image'] = vdi_image_path
3193 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
3194 if not dev_uuid:
3195 raise XendError('Failed to create device')
3197 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
3198 _, config = self.info['devices'][dev_uuid]
3199 config['devid'] = self.getDeviceController('tap').createDevice(config)
3201 return config['devid']
3203 def create_vif(self, xenapi_vif):
3204 """Create VIF device from the passed struct in Xen API format.
3206 @param xenapi_vif: Xen API VIF Struct.
3207 @rtype: string
3208 @return: UUID
3209 """
3210 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
3211 if not dev_uuid:
3212 raise XendError('Failed to create device')
3214 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3215 XEN_API_VM_POWER_STATE_PAUSED):
3217 _, config = self.info['devices'][dev_uuid]
3218 dev_control = self.getDeviceController('vif')
3220 try:
3221 devid = dev_control.createDevice(config)
3222 dev_control.waitForDevice(devid)
3223 self.info.device_update(dev_uuid,
3224 cfg_xenapi = {'devid': devid})
3225 except Exception, exn:
3226 log.exception(exn)
3227 del self.info['devices'][dev_uuid]
3228 self.info['vif_refs'].remove(dev_uuid)
3229 raise
3231 return dev_uuid
3233 def create_vtpm(self, xenapi_vtpm):
3234 """Create a VTPM device from the passed struct in Xen API format.
3236 @return: uuid of the device
3237 @rtype: string
3238 """
3240 if self._stateGet() not in (DOM_STATE_HALTED,):
3241 raise VmError("Can only add vTPM to a halted domain.")
3242 if self.get_vtpms() != []:
3243 raise VmError('Domain already has a vTPM.')
3244 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
3245 if not dev_uuid:
3246 raise XendError('Failed to create device')
3248 return dev_uuid
3250 def create_console(self, xenapi_console):
3251 """ Create a console device from a Xen API struct.
3253 @return: uuid of device
3254 @rtype: string
3255 """
3256 if self._stateGet() not in (DOM_STATE_HALTED,):
3257 raise VmError("Can only add console to a halted domain.")
3259 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
3260 if not dev_uuid:
3261 raise XendError('Failed to create device')
3263 return dev_uuid
3265 def set_console_other_config(self, console_uuid, other_config):
3266 self.info.console_update(console_uuid, 'other_config', other_config)
3268 def destroy_device_by_uuid(self, dev_type, dev_uuid):
3269 if dev_uuid not in self.info['devices']:
3270 raise XendError('Device does not exist')
3272 try:
3273 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3274 XEN_API_VM_POWER_STATE_PAUSED):
3275 _, config = self.info['devices'][dev_uuid]
3276 devid = config.get('devid')
3277 if devid != None:
3278 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
3279 else:
3280 raise XendError('Unable to get devid for device: %s:%s' %
3281 (dev_type, dev_uuid))
3282 finally:
3283 del self.info['devices'][dev_uuid]
3284 self.info['%s_refs' % dev_type].remove(dev_uuid)
3286 def destroy_vbd(self, dev_uuid):
3287 self.destroy_device_by_uuid('vbd', dev_uuid)
3289 def destroy_vif(self, dev_uuid):
3290 self.destroy_device_by_uuid('vif', dev_uuid)
3292 def destroy_vtpm(self, dev_uuid):
3293 self.destroy_device_by_uuid('vtpm', dev_uuid)
3295 def has_device(self, dev_class, dev_uuid):
3296 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
3298 def __str__(self):
3299 return '<domain id=%s name=%s memory=%s state=%s>' % \
3300 (str(self.domid), self.info['name_label'],
3301 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
3303 __repr__ = __str__