ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 17400:e8f058b99171

xend: Don't allow dom0 memory to be ballooned below dom0-min-mem
configuration parameter.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Sat Apr 05 22:25:30 2008 +0100 (2008-04-05)
parents da35b52c4fd7
children 65802c51edb5
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005-2007 XenSource Ltd
17 #============================================================================
19 """Representation of a single domain.
20 Includes support for domain construction, using
21 open-ended configurations.
23 Author: Mike Wray <mike.wray@hp.com>
25 """
27 import logging
28 import time
29 import threading
30 import re
31 import copy
32 import os
33 import traceback
34 from types import StringTypes
36 import xen.lowlevel.xc
37 from xen.util import asserts
38 from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype
39 import xen.util.xsm.xsm as security
41 from xen.xend import balloon, sxp, uuid, image, arch, osdep
42 from xen.xend import XendOptions, XendNode, XendConfig
44 from xen.xend.XendConfig import scrub_password
45 from xen.xend.XendBootloader import bootloader, bootloader_tidy
46 from xen.xend.XendError import XendError, VmError
47 from xen.xend.XendDevices import XendDevices
48 from xen.xend.XendTask import XendTask
49 from xen.xend.xenstore.xstransact import xstransact, complete
50 from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, SetTarget, ResumeDomain
51 from xen.xend.xenstore.xswatch import xswatch
52 from xen.xend.XendConstants import *
53 from xen.xend.XendAPIConstants import *
55 from xen.xend.XendVMMetrics import XendVMMetrics
57 MIGRATE_TIMEOUT = 30.0
58 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
60 xc = xen.lowlevel.xc.xc()
61 xoptions = XendOptions.instance()
63 log = logging.getLogger("xend.XendDomainInfo")
64 #log.setLevel(logging.TRACE)
67 def create(config):
68 """Creates and start a VM using the supplied configuration.
70 @param config: A configuration object involving lists of tuples.
71 @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
73 @rtype: XendDomainInfo
74 @return: An up and running XendDomainInfo instance
75 @raise VmError: Invalid configuration or failure to start.
76 """
77 from xen.xend import XendDomain
78 domconfig = XendConfig.XendConfig(sxp_obj = config)
79 othervm = XendDomain.instance().domain_lookup_nr(domconfig["name_label"])
80 if othervm is None or othervm.domid is None:
81 othervm = XendDomain.instance().domain_lookup_nr(domconfig["uuid"])
82 if othervm is not None and othervm.domid is not None:
83 raise VmError("Domain '%s' already exists with ID '%d'" % (domconfig["name_label"], othervm.domid))
84 log.debug("XendDomainInfo.create(%s)", scrub_password(config))
85 vm = XendDomainInfo(domconfig)
86 try:
87 vm.start()
88 except:
89 log.exception('Domain construction failed')
90 vm.destroy()
91 raise
93 return vm
95 def create_from_dict(config_dict):
96 """Creates and start a VM using the supplied configuration.
98 @param config_dict: An configuration dictionary.
100 @rtype: XendDomainInfo
101 @return: An up and running XendDomainInfo instance
102 @raise VmError: Invalid configuration or failure to start.
103 """
105 log.debug("XendDomainInfo.create_from_dict(%s)",
106 scrub_password(config_dict))
107 vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
108 try:
109 vm.start()
110 except:
111 log.exception('Domain construction failed')
112 vm.destroy()
113 raise
114 return vm
116 def recreate(info, priv):
117 """Create the VM object for an existing domain. The domain must not
118 be dying, as the paths in the store should already have been removed,
119 and asking us to recreate them causes problems.
121 @param xeninfo: Parsed configuration
122 @type xeninfo: Dictionary
123 @param priv: Is a privileged domain (Dom 0)
124 @type priv: bool
126 @rtype: XendDomainInfo
127 @return: A up and running XendDomainInfo instance
128 @raise VmError: Invalid configuration.
129 @raise XendError: Errors with configuration.
130 """
132 log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
134 assert not info['dying']
136 xeninfo = XendConfig.XendConfig(dominfo = info)
137 xeninfo['is_control_domain'] = priv
138 xeninfo['is_a_template'] = False
139 domid = xeninfo['domid']
140 uuid1 = uuid.fromString(xeninfo['uuid'])
141 needs_reinitialising = False
143 dompath = GetDomainPath(domid)
144 if not dompath:
145 raise XendError('No domain path in store for existing '
146 'domain %d' % domid)
148 log.info("Recreating domain %d, UUID %s. at %s" %
149 (domid, xeninfo['uuid'], dompath))
151 # need to verify the path and uuid if not Domain-0
152 # if the required uuid and vm aren't set, then that means
153 # we need to recreate the dom with our own values
154 #
155 # NOTE: this is probably not desirable, really we should just
156 # abort or ignore, but there may be cases where xenstore's
157 # entry disappears (eg. xenstore-rm /)
158 #
159 try:
160 vmpath = xstransact.Read(dompath, "vm")
161 if not vmpath:
162 if not priv:
163 log.warn('/local/domain/%d/vm is missing. recreate is '
164 'confused, trying our best to recover' % domid)
165 needs_reinitialising = True
166 raise XendError('reinit')
168 uuid2_str = xstransact.Read(vmpath, "uuid")
169 if not uuid2_str:
170 log.warn('%s/uuid/ is missing. recreate is confused, '
171 'trying our best to recover' % vmpath)
172 needs_reinitialising = True
173 raise XendError('reinit')
175 uuid2 = uuid.fromString(uuid2_str)
176 if uuid1 != uuid2:
177 log.warn('UUID in /vm does not match the UUID in /dom/%d.'
178 'Trying out best to recover' % domid)
179 needs_reinitialising = True
180 except XendError:
181 pass # our best shot at 'goto' in python :)
183 vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv,
184 vmpath = vmpath)
186 if needs_reinitialising:
187 vm._recreateDom()
188 vm._removeVm()
189 vm._storeVmDetails()
190 vm._storeDomDetails()
192 vm.image = image.create(vm, vm.info)
193 vm.image.recreate()
195 vm._registerWatches()
196 vm.refreshShutdown(xeninfo)
198 # register the domain in the list
199 from xen.xend import XendDomain
200 XendDomain.instance().add_domain(vm)
202 return vm
205 def restore(config):
206 """Create a domain and a VM object to do a restore.
208 @param config: Domain SXP configuration
209 @type config: list of lists. (see C{create})
211 @rtype: XendDomainInfo
212 @return: A up and running XendDomainInfo instance
213 @raise VmError: Invalid configuration or failure to start.
214 @raise XendError: Errors with configuration.
215 """
217 log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
218 vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
219 resume = True)
220 try:
221 vm.resume()
222 return vm
223 except:
224 vm.destroy()
225 raise
227 def createDormant(domconfig):
228 """Create a dormant/inactive XenDomainInfo without creating VM.
229 This is for creating instances of persistent domains that are not
230 yet start.
232 @param domconfig: Parsed configuration
233 @type domconfig: XendConfig object
235 @rtype: XendDomainInfo
236 @return: A up and running XendDomainInfo instance
237 @raise XendError: Errors with configuration.
238 """
240 log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
242 # domid does not make sense for non-running domains.
243 domconfig.pop('domid', None)
244 vm = XendDomainInfo(domconfig)
245 return vm
247 def domain_by_name(name):
248 """Get domain by name
250 @params name: Name of the domain
251 @type name: string
252 @return: XendDomainInfo or None
253 """
254 from xen.xend import XendDomain
255 return XendDomain.instance().domain_lookup_by_name_nr(name)
258 def shutdown_reason(code):
259 """Get a shutdown reason from a code.
261 @param code: shutdown code
262 @type code: int
263 @return: shutdown reason
264 @rtype: string
265 """
266 return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
268 def dom_get(dom):
269 """Get info from xen for an existing domain.
271 @param dom: domain id
272 @type dom: int
273 @return: info or None
274 @rtype: dictionary
275 """
276 try:
277 domlist = xc.domain_getinfo(dom, 1)
278 if domlist and dom == domlist[0]['domid']:
279 return domlist[0]
280 except Exception, err:
281 # ignore missing domain
282 log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
283 return None
286 class XendDomainInfo:
287 """An object represents a domain.
289 @TODO: try to unify dom and domid, they mean the same thing, but
290 xc refers to it as dom, and everywhere else, including
291 xenstore it is domid. The best way is to change xc's
292 python interface.
294 @ivar info: Parsed configuration
295 @type info: dictionary
296 @ivar domid: Domain ID (if VM has started)
297 @type domid: int or None
298 @ivar vmpath: XenStore path to this VM.
299 @type vmpath: string
300 @ivar dompath: XenStore path to this Domain.
301 @type dompath: string
302 @ivar image: Reference to the VM Image.
303 @type image: xen.xend.image.ImageHandler
304 @ivar store_port: event channel to xenstored
305 @type store_port: int
306 @ivar console_port: event channel to xenconsoled
307 @type console_port: int
308 @ivar store_mfn: xenstored mfn
309 @type store_mfn: int
310 @ivar console_mfn: xenconsoled mfn
311 @type console_mfn: int
312 @ivar notes: OS image notes
313 @type notes: dictionary
314 @ivar vmWatch: reference to a watch on the xenstored vmpath
315 @type vmWatch: xen.xend.xenstore.xswatch
316 @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
317 @type shutdownWatch: xen.xend.xenstore.xswatch
318 @ivar shutdownStartTime: UNIX Time when domain started shutting down.
319 @type shutdownStartTime: float or None
320 # @ivar state: Domain state
321 # @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
322 @ivar state_updated: lock for self.state
323 @type state_updated: threading.Condition
324 @ivar refresh_shutdown_lock: lock for polling shutdown state
325 @type refresh_shutdown_lock: threading.Condition
326 @ivar _deviceControllers: device controller cache for this domain
327 @type _deviceControllers: dict 'string' to DevControllers
328 """
330 def __init__(self, info, domid = None, dompath = None, augment = False,
331 priv = False, resume = False, vmpath = None):
332 """Constructor for a domain
334 @param info: parsed configuration
335 @type info: dictionary
336 @keyword domid: Set initial domain id (if any)
337 @type domid: int
338 @keyword dompath: Set initial dompath (if any)
339 @type dompath: string
340 @keyword augment: Augment given info with xenstored VM info
341 @type augment: bool
342 @keyword priv: Is a privileged domain (Dom 0)
343 @type priv: bool
344 @keyword resume: Is this domain being resumed?
345 @type resume: bool
346 """
348 self.info = info
349 if domid == None:
350 self.domid = self.info.get('domid')
351 else:
352 self.domid = domid
354 #REMOVE: uuid is now generated in XendConfig
355 #if not self._infoIsSet('uuid'):
356 # self.info['uuid'] = uuid.toString(uuid.create())
358 # Find a unique /vm/<uuid>/<integer> path if not specified.
359 # This avoids conflict between pre-/post-migrate domains when doing
360 # localhost relocation.
361 self.vmpath = vmpath
362 i = 0
363 while self.vmpath == None:
364 self.vmpath = XS_VMROOT + self.info['uuid']
365 if i != 0:
366 self.vmpath = self.vmpath + '-' + str(i)
367 try:
368 if self._readVm("uuid"):
369 self.vmpath = None
370 i = i + 1
371 except:
372 pass
374 self.dompath = dompath
376 self.image = None
377 self.store_port = None
378 self.store_mfn = None
379 self.console_port = None
380 self.console_mfn = None
382 self.native_protocol = None
384 self.vmWatch = None
385 self.shutdownWatch = None
386 self.shutdownStartTime = None
387 self._resume = resume
389 self.state_updated = threading.Condition()
390 self.refresh_shutdown_lock = threading.Condition()
391 self._stateSet(DOM_STATE_HALTED)
393 self._deviceControllers = {}
395 for state in DOM_STATES_OLD:
396 self.info[state] = 0
398 if augment:
399 self._augmentInfo(priv)
401 self._checkName(self.info['name_label'])
403 self.metrics = XendVMMetrics(uuid.createString(), self)
406 #
407 # Public functions available through XMLRPC
408 #
411 def start(self, is_managed = False):
412 """Attempts to start the VM by do the appropriate
413 initialisation if it not started.
414 """
415 from xen.xend import XendDomain
417 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED, XEN_API_VM_POWER_STATE_CRASHED):
418 try:
419 XendTask.log_progress(0, 30, self._constructDomain)
420 XendTask.log_progress(31, 60, self._initDomain)
422 XendTask.log_progress(61, 70, self._storeVmDetails)
423 XendTask.log_progress(71, 80, self._storeDomDetails)
424 XendTask.log_progress(81, 90, self._registerWatches)
425 XendTask.log_progress(91, 100, self.refreshShutdown)
427 xendomains = XendDomain.instance()
428 xennode = XendNode.instance()
430 # save running configuration if XendDomains believe domain is
431 # persistent
432 if is_managed:
433 xendomains.managed_config_save(self)
435 if xennode.xenschedinfo() == 'credit':
436 xendomains.domain_sched_credit_set(self.getDomid(),
437 self.getWeight(),
438 self.getCap())
439 except:
440 log.exception('VM start failed')
441 self.destroy()
442 raise
443 else:
444 raise XendError('VM already running')
446 def resume(self):
447 """Resumes a domain that has come back from suspension."""
448 state = self._stateGet()
449 if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
450 try:
451 self._constructDomain()
452 self._storeVmDetails()
453 self._createDevices()
454 self._createChannels()
455 self._storeDomDetails()
456 self._endRestore()
457 except:
458 log.exception('VM resume failed')
459 self.destroy()
460 raise
461 else:
462 raise XendError('VM is not suspended; it is %s'
463 % XEN_API_VM_POWER_STATE[state])
465 def shutdown(self, reason):
466 """Shutdown a domain by signalling this via xenstored."""
467 log.debug('XendDomainInfo.shutdown(%s)', reason)
468 if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
469 raise XendError('Domain cannot be shutdown')
471 if self.domid == 0:
472 raise XendError('Domain 0 cannot be shutdown')
474 if reason not in DOMAIN_SHUTDOWN_REASONS.values():
475 raise XendError('Invalid reason: %s' % reason)
476 self._removeVm('xend/previous_restart_time')
477 self.storeDom("control/shutdown", reason)
479 # HVM domain shuts itself down only if it has PV drivers
480 if self.info.is_hvm():
481 hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
482 if not hvm_pvdrv:
483 code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
484 log.info("HVM save:remote shutdown dom %d!", self.domid)
485 xc.domain_shutdown(self.domid, code)
487 def pause(self):
488 """Pause domain
490 @raise XendError: Failed pausing a domain
491 """
492 try:
493 xc.domain_pause(self.domid)
494 self._stateSet(DOM_STATE_PAUSED)
495 except Exception, ex:
496 log.exception(ex)
497 raise XendError("Domain unable to be paused: %s" % str(ex))
499 def unpause(self):
500 """Unpause domain
502 @raise XendError: Failed unpausing a domain
503 """
504 try:
505 xc.domain_unpause(self.domid)
506 self._stateSet(DOM_STATE_RUNNING)
507 except Exception, ex:
508 log.exception(ex)
509 raise XendError("Domain unable to be unpaused: %s" % str(ex))
511 def send_sysrq(self, key):
512 """ Send a Sysrq equivalent key via xenstored."""
513 if self._stateGet() not in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
514 raise XendError("Domain '%s' is not started" % self.info['name_label'])
516 asserts.isCharConvertible(key)
517 self.storeDom("control/sysrq", '%c' % key)
519 def sync_pcidev_info(self):
521 if not self.info.is_hvm():
522 return
524 devid = '0'
525 dev_info = self._getDeviceInfo_pci(devid)
526 if dev_info is None:
527 return
529 # get the virtual slot info from xenstore
530 dev_uuid = sxp.child_value(dev_info, 'uuid')
531 pci_conf = self.info['devices'][dev_uuid][1]
532 pci_devs = pci_conf['devs']
534 count = 0
535 vslots = None
536 while vslots is None and count < 20:
537 vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots"
538 % (self.getDomid(), devid))
539 time.sleep(0.1)
540 count += 1
541 if vslots is None:
542 log.error("Device model didn't tell the vslots for PCI device")
543 return
545 #delete last delim
546 if vslots[-1] == ";":
547 vslots = vslots[:-1]
549 slot_list = vslots.split(';')
550 if len(slot_list) != len(pci_devs):
551 log.error("Device model's pci dev num dismatch")
552 return
554 #update the vslot info
555 count = 0;
556 for x in pci_devs:
557 x['vslt'] = slot_list[count]
558 count += 1
561 def hvm_pci_device_create(self, dev_config):
562 log.debug("XendDomainInfo.hvm_pci_device_create: %s"
563 % scrub_password(dev_config))
565 if not self.info.is_hvm():
566 raise VmError("hvm_pci_device_create called on non-HVM guest")
568 #all the PCI devs share one conf node
569 devid = '0'
571 new_dev = dev_config['devs'][0]
572 dev_info = self._getDeviceInfo_pci(devid)#from self.info['devices']
574 #check conflict before trigger hotplug event
575 if dev_info is not None:
576 dev_uuid = sxp.child_value(dev_info, 'uuid')
577 pci_conf = self.info['devices'][dev_uuid][1]
578 pci_devs = pci_conf['devs']
579 for x in pci_devs:
580 if (int(x['vslt'], 16) == int(new_dev['vslt'], 16) and
581 int(x['vslt'], 16) != 0 ):
582 raise VmError("vslot %s already have a device." % (new_dev['vslt']))
584 if (int(x['domain'], 16) == int(new_dev['domain'], 16) and
585 int(x['bus'], 16) == int(new_dev['bus'], 16) and
586 int(x['slot'], 16) == int(new_dev['slot'], 16) and
587 int(x['func'], 16) == int(new_dev['func'], 16) ):
588 raise VmError("device is already inserted")
590 # Test whether the devices can be assigned with VT-d
591 pci_str = "%s, %s, %s, %s" % (new_dev['domain'],
592 new_dev['bus'],
593 new_dev['slot'],
594 new_dev['func'])
595 bdf = xc.test_assign_device(self.domid, pci_str)
596 if bdf != 0:
597 bus = (bdf >> 16) & 0xff
598 devfn = (bdf >> 8) & 0xff
599 dev = (devfn >> 3) & 0x1f
600 func = devfn & 0x7
601 raise VmError("Fail to hot insert device(%x:%x.%x): maybe VT-d is "
602 "not enabled, or the device is not exist, or it "
603 "has already been assigned to other domain"
604 % (bus, dev, func))
606 bdf_str = "%s:%s:%s.%s@%s" % (new_dev['domain'],
607 new_dev['bus'],
608 new_dev['slot'],
609 new_dev['func'],
610 new_dev['vslt'])
611 self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str)
614 def device_create(self, dev_config):
615 """Create a new device.
617 @param dev_config: device configuration
618 @type dev_config: SXP object (parsed config)
619 """
620 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
621 dev_type = sxp.name(dev_config)
622 dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
623 dev_config_dict = self.info['devices'][dev_uuid][1]
624 log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
626 if self.domid is not None:
627 try:
628 dev_config_dict['devid'] = devid = \
629 self._createDevice(dev_type, dev_config_dict)
630 self._waitForDevice(dev_type, devid)
631 except VmError, ex:
632 del self.info['devices'][dev_uuid]
633 if dev_type == 'tap':
634 self.info['vbd_refs'].remove(dev_uuid)
635 else:
636 self.info['%s_refs' % dev_type].remove(dev_uuid)
637 raise ex
638 else:
639 devid = None
641 xen.xend.XendDomain.instance().managed_config_save(self)
642 return self.getDeviceController(dev_type).sxpr(devid)
644 def pci_convert_sxp_to_dict(self, dev_sxp):
645 """Convert pci device sxp to dict
646 @param dev_sxp: device configuration
647 @type dev_sxp: SXP object (parsed config)
648 @return: dev_config
649 @rtype: dictionary
650 """
651 # In reconfigure phase, config of PCI device looks like below:
652 #
653 # sxp:
654 # [device, [pci, [dev, [domain, '0x0'], [bus, '0x0'], [slot, '0x0'],
655 # [func, '0x0'], [vslt, '0x0']],
656 # [state, 'Initialising']]]
657 #
658 # dict:
659 # {devs: [{domain: '0x0', bus: '0x0', slot: '0x0', func: '0x0',
660 # vslt: '0x0'}],
661 # states: ['Initialising']}
662 #
663 # state 'Initialising' means the device is being attached.
664 # state 'Closing' means the device is being detached.
666 dev_config = {}
667 pci_devs = []
668 for pci_dev in sxp.children(dev_sxp, 'dev'):
669 pci_dev_info = {}
670 for opt_val in pci_dev[1:]:
671 try:
672 opt, val = opt_val
673 pci_dev_info[opt] = val
674 except TypeError:
675 pass
676 pci_devs.append(pci_dev_info)
677 dev_config['devs'] = pci_devs
678 pci_states = []
679 for pci_state in sxp.children(dev_sxp, 'state'):
680 try:
681 pci_states.append(pci_state[1])
682 except IndexError:
683 raise XendError("Error reading state while parsing pci sxp")
684 dev_config['states'] = pci_states
686 return dev_config
688 def pci_device_configure(self, dev_sxp, devid = 0):
689 """Configure an existing pci device.
691 @param dev_sxp: device configuration
692 @type dev_sxp: SXP object (parsed config)
693 @param devid: device id
694 @type devid: int
695 @return: Returns True if successfully updated device
696 @rtype: boolean
697 """
698 log.debug("XendDomainInfo.pci_device_configure: %s"
699 % scrub_password(dev_sxp))
701 dev_class = sxp.name(dev_sxp)
703 if dev_class != 'pci':
704 return False
706 pci_state = sxp.child_value(dev_sxp, 'state')
707 existing_dev_info = self._getDeviceInfo_pci(devid)
709 if existing_dev_info is None and pci_state != 'Initialising':
710 raise XendError("Cannot detach when pci platform does not exist")
712 pci_dev = sxp.children(dev_sxp, 'dev')[0]
713 dev_config = self.pci_convert_sxp_to_dict(dev_sxp)
714 dev = dev_config['devs'][0]
716 # Do HVM specific processing
717 if self.info.is_hvm():
718 if pci_state == 'Initialising':
719 # HVM PCI device attachment
720 self.hvm_pci_device_create(dev_config)
721 # Update vslt
722 vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
723 % self.getDomid())
724 dev['vslt'] = vslt
725 for n in sxp.children(pci_dev):
726 if(n[0] == 'vslt'):
727 n[1] = vslt
728 else:
729 # HVM PCI device detachment
730 existing_dev_uuid = sxp.child_value(existing_dev_info, 'uuid')
731 existing_pci_conf = self.info['devices'][existing_dev_uuid][1]
732 existing_pci_devs = existing_pci_conf['devs']
733 vslt = '0x0'
734 for x in existing_pci_devs:
735 if ( int(x['domain'], 16) == int(dev['domain'], 16) and
736 int(x['bus'], 16) == int(dev['bus'], 16) and
737 int(x['slot'], 16) == int(dev['slot'], 16) and
738 int(x['func'], 16) == int(dev['func'], 16) ):
739 vslt = x['vslt']
740 break
741 if vslt == '0x0':
742 raise VmError("Device %04x:%02x:%02x.%02x is not connected"
743 % (int(dev['domain'],16), int(dev['bus'],16),
744 int(dev['slot'],16), int(dev['func'],16)))
745 self.hvm_destroyPCIDevice(int(vslt, 16))
746 # Update vslt
747 dev['vslt'] = vslt
748 for n in sxp.children(pci_dev):
749 if(n[0] == 'vslt'):
750 n[1] = vslt
752 # If pci platform does not exist, create and exit.
753 if existing_dev_info is None:
754 self.device_create(dev_sxp)
755 return True
757 # use DevController.reconfigureDevice to change device config
758 dev_control = self.getDeviceController(dev_class)
759 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
760 if not self.info.is_hvm():
761 # in PV case, wait until backend state becomes connected.
762 dev_control.waitForDevice_reconfigure(devid)
763 num_devs = dev_control.cleanupDevice(devid)
765 # update XendConfig with new device info
766 if dev_uuid:
767 new_dev_sxp = dev_control.configuration(devid)
768 self.info.device_update(dev_uuid, new_dev_sxp)
770 # If there is no device left, destroy pci and remove config.
771 if num_devs == 0:
772 if self.info.is_hvm():
773 self.destroyDevice('pci', devid, True)
774 del self.info['devices'][dev_uuid]
775 platform = self.info['platform']
776 orig_dev_num = len(platform['pci'])
777 # TODO: can use this to keep some info to ask high level
778 # management tools to hot insert a new passthrough dev
779 # after migration
780 if orig_dev_num != 0:
781 #platform['pci'] = ["%dDEVs" % orig_dev_num]
782 platform['pci'] = []
783 else:
784 self.destroyDevice('pci', devid)
785 del self.info['devices'][dev_uuid]
787 return True
789 def device_configure(self, dev_sxp, devid = None):
790 """Configure an existing device.
792 @param dev_config: device configuration
793 @type dev_config: SXP object (parsed config)
794 @param devid: device id
795 @type devid: int
796 @return: Returns True if successfully updated device
797 @rtype: boolean
798 """
800 # convert device sxp to a dict
801 dev_class = sxp.name(dev_sxp)
802 dev_config = {}
804 if dev_class == 'pci':
805 return self.pci_device_configure(dev_sxp)
807 for opt_val in dev_sxp[1:]:
808 try:
809 dev_config[opt_val[0]] = opt_val[1]
810 except IndexError:
811 pass
813 # use DevController.reconfigureDevice to change device config
814 dev_control = self.getDeviceController(dev_class)
815 dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
817 # update XendConfig with new device info
818 if dev_uuid:
819 self.info.device_update(dev_uuid, dev_sxp)
821 return True
823 def waitForDevices(self):
824 """Wait for this domain's configured devices to connect.
826 @raise VmError: if any device fails to initialise.
827 """
828 for devclass in XendDevices.valid_devices():
829 self.getDeviceController(devclass).waitForDevices()
831 def hvm_destroyPCIDevice(self, vslot):
832 log.debug("hvm_destroyPCIDevice called %s", vslot)
834 if not self.info.is_hvm():
835 raise VmError("hvm_destroyPCIDevice called on non-HVM guest")
837 #all the PCI devs share one conf node
838 devid = '0'
839 vslot = int(vslot)
840 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
841 dev_uuid = sxp.child_value(dev_info, 'uuid')
843 #delete the pci bdf config under the pci device
844 pci_conf = self.info['devices'][dev_uuid][1]
845 pci_len = len(pci_conf['devs'])
847 #find the pass-through device with the virtual slot
848 devnum = 0
849 for x in pci_conf['devs']:
850 if int(x['vslt'], 16) == vslot:
851 break
852 devnum += 1
854 if devnum >= pci_len:
855 raise VmError("Device @ vslot 0x%x doesn't exist." % (vslot))
857 if vslot == 0:
858 raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
860 bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
861 log.info("hvm_destroyPCIDevice:%s:%s!", x, bdf_str)
863 self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str)
865 return 0
867 def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
868 log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
869 deviceClass, devid)
871 if rm_cfg:
872 # Convert devid to device number. A device number is
873 # needed to remove its configuration.
874 dev = self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
876 # Save current sxprs. A device number and a backend
877 # path are needed to remove its configuration but sxprs
878 # do not have those after calling destroyDevice.
879 sxprs = self.getDeviceSxprs(deviceClass)
881 rc = None
882 if self.domid is not None:
883 rc = self.getDeviceController(deviceClass).destroyDevice(devid, force)
884 if not force and rm_cfg:
885 # The backend path, other than the device itself,
886 # has to be passed because its accompanied frontend
887 # path may be void until its removal is actually
888 # issued. It is probable because destroyDevice is
889 # issued first.
890 for dev_num, dev_info in sxprs:
891 dev_num = int(dev_num)
892 if dev_num == dev:
893 for x in dev_info:
894 if x[0] == 'backend':
895 backend = x[1]
896 break
897 break
898 self._waitForDevice_destroy(deviceClass, devid, backend)
900 if rm_cfg:
901 if deviceClass == 'vif':
902 if self.domid is not None:
903 for dev_num, dev_info in sxprs:
904 dev_num = int(dev_num)
905 if dev_num == dev:
906 for x in dev_info:
907 if x[0] == 'mac':
908 mac = x[1]
909 break
910 break
911 dev_info = self._getDeviceInfo_vif(mac)
912 else:
913 _, dev_info = sxprs[dev]
914 else: # 'vbd' or 'tap'
915 dev_info = self._getDeviceInfo_vbd(dev)
916 # To remove the UUID of the device from refs,
917 # deviceClass must be always 'vbd'.
918 deviceClass = 'vbd'
919 if dev_info is None:
920 raise XendError("Device %s is not defined" % devid)
922 dev_uuid = sxp.child_value(dev_info, 'uuid')
923 del self.info['devices'][dev_uuid]
924 self.info['%s_refs' % deviceClass].remove(dev_uuid)
925 xen.xend.XendDomain.instance().managed_config_save(self)
927 return rc
929 def getDeviceSxprs(self, deviceClass):
930 if deviceClass == 'pci':
931 dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
932 if dev_info is None:
933 return []
934 dev_uuid = sxp.child_value(dev_info, 'uuid')
935 pci_devs = self.info['devices'][dev_uuid][1]['devs']
936 pci_len = len(pci_devs)
937 return pci_devs
938 if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
939 return self.getDeviceController(deviceClass).sxprs()
940 else:
941 sxprs = []
942 dev_num = 0
943 for dev_type, dev_info in self.info.all_devices_sxpr():
944 if dev_type == deviceClass:
945 sxprs.append([dev_num, dev_info])
946 dev_num += 1
947 return sxprs
949 def getBlockDeviceClass(self, devid):
950 # To get a device number from the devid,
951 # we temporarily use the device controller of VBD.
952 dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
953 dev_info = self._getDeviceInfo_vbd(dev)
954 if dev_info:
955 return dev_info[0]
957 def _getDeviceInfo_vif(self, mac):
958 for dev_type, dev_info in self.info.all_devices_sxpr():
959 if dev_type != 'vif':
960 continue
961 if mac == sxp.child_value(dev_info, 'mac'):
962 return dev_info
964 def _getDeviceInfo_vbd(self, devid):
965 for dev_type, dev_info in self.info.all_devices_sxpr():
966 if dev_type != 'vbd' and dev_type != 'tap':
967 continue
968 dev = sxp.child_value(dev_info, 'dev')
969 dev = dev.split(':')[0]
970 dev = self.getDeviceController(dev_type).convertToDeviceNumber(dev)
971 if devid == dev:
972 return dev_info
974 def _getDeviceInfo_pci(self, devid):
975 for dev_type, dev_info in self.info.all_devices_sxpr():
976 if dev_type != 'pci':
977 continue
978 return dev_info
979 return None
981 def setMemoryTarget(self, target):
982 """Set the memory target of this domain.
983 @param target: In MiB.
984 """
985 log.debug("Setting memory target of domain %s (%s) to %d MiB.",
986 self.info['name_label'], str(self.domid), target)
988 MiB = 1024 * 1024
990 if self.domid == 0:
991 dom0_min_mem = xoptions.get_dom0_min_mem()
992 memory_cur = self.get_memory_dynamic_max() / MiB
993 if target < memory_cur and dom0_min_mem > target:
994 raise XendError("memory_dynamic_max too small")
996 self._safe_set_memory('memory_dynamic_min', target * MiB)
997 self._safe_set_memory('memory_dynamic_max', target * MiB)
999 if self.domid >= 0:
1000 self.storeVm("memory", target)
1001 self.storeDom("memory/target", target << 10)
1002 xen.xend.XendDomain.instance().managed_config_save(self)
1004 def setMemoryMaximum(self, limit):
1005 """Set the maximum memory limit of this domain
1006 @param limit: In MiB.
1007 """
1008 log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
1009 self.info['name_label'], str(self.domid), limit)
1011 maxmem_cur = self.get_memory_static_max()
1012 MiB = 1024 * 1024
1013 self._safe_set_memory('memory_static_max', limit * MiB)
1015 if self.domid >= 0:
1016 maxmem = int(limit) * 1024
1017 try:
1018 return xc.domain_setmaxmem(self.domid, maxmem)
1019 except Exception, ex:
1020 self._safe_set_memory('memory_static_max', maxmem_cur)
1021 raise XendError(str(ex))
1022 xen.xend.XendDomain.instance().managed_config_save(self)
1025 def getVCPUInfo(self):
1026 try:
1027 # We include the domain name and ID, to help xm.
1028 sxpr = ['domain',
1029 ['domid', self.domid],
1030 ['name', self.info['name_label']],
1031 ['vcpu_count', self.info['VCPUs_max']]]
1033 for i in range(0, self.info['VCPUs_max']):
1034 if self.domid is not None:
1035 info = xc.vcpu_getinfo(self.domid, i)
1037 sxpr.append(['vcpu',
1038 ['number', i],
1039 ['online', info['online']],
1040 ['blocked', info['blocked']],
1041 ['running', info['running']],
1042 ['cpu_time', info['cpu_time'] / 1e9],
1043 ['cpu', info['cpu']],
1044 ['cpumap', info['cpumap']]])
1045 else:
1046 sxpr.append(['vcpu',
1047 ['number', i],
1048 ['online', 0],
1049 ['blocked', 0],
1050 ['running', 0],
1051 ['cpu_time', 0.0],
1052 ['cpu', -1],
1053 ['cpumap', self.info['cpus'] and \
1054 self.info['cpus'] or range(64)]])
1056 return sxpr
1058 except RuntimeError, exn:
1059 raise XendError(str(exn))
1062 def getDomInfo(self):
1063 return dom_get(self.domid)
1066 # internal functions ... TODO: re-categorised
1069 def _augmentInfo(self, priv):
1070 """Augment self.info, as given to us through L{recreate}, with
1071 values taken from the store. This recovers those values known
1072 to xend but not to the hypervisor.
1073 """
1074 augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
1075 if priv:
1076 augment_entries.remove('memory')
1077 augment_entries.remove('maxmem')
1078 augment_entries.remove('vcpus')
1079 augment_entries.remove('vcpu_avail')
1081 vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
1082 for k in augment_entries])
1084 # make returned lists into a dictionary
1085 vm_config = dict(zip(augment_entries, vm_config))
1087 for arg in augment_entries:
1088 val = vm_config[arg]
1089 if val != None:
1090 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1091 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1092 self.info[xapiarg] = val
1093 elif arg == "memory":
1094 self.info["static_memory_min"] = val
1095 elif arg == "maxmem":
1096 self.info["static_memory_max"] = val
1097 else:
1098 self.info[arg] = val
1100 # For dom0, we ignore any stored value for the vcpus fields, and
1101 # read the current value from Xen instead. This allows boot-time
1102 # settings to take precedence over any entries in the store.
1103 if priv:
1104 xeninfo = dom_get(self.domid)
1105 self.info['VCPUs_max'] = xeninfo['online_vcpus']
1106 self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
1108 # read image value
1109 image_sxp = self._readVm('image')
1110 if image_sxp:
1111 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1113 # read devices
1114 devices = []
1115 for devclass in XendDevices.valid_devices():
1116 devconfig = self.getDeviceController(devclass).configurations()
1117 if devconfig:
1118 devices.extend(devconfig)
1120 if not self.info['devices'] and devices is not None:
1121 for device in devices:
1122 self.info.device_add(device[0], cfg_sxp = device)
1124 self._update_consoles()
1126 def _update_consoles(self, transaction = None):
1127 if self.domid == None or self.domid == 0:
1128 return
1130 # Update VT100 port if it exists
1131 if transaction is None:
1132 self.console_port = self.readDom('console/port')
1133 else:
1134 self.console_port = self.readDomTxn(transaction, 'console/port')
1135 if self.console_port is not None:
1136 serial_consoles = self.info.console_get_all('vt100')
1137 if not serial_consoles:
1138 cfg = self.info.console_add('vt100', self.console_port)
1139 self._createDevice('console', cfg)
1140 else:
1141 console_uuid = serial_consoles[0].get('uuid')
1142 self.info.console_update(console_uuid, 'location',
1143 self.console_port)
1146 # Update VNC port if it exists and write to xenstore
1147 if transaction is None:
1148 vnc_port = self.readDom('console/vnc-port')
1149 else:
1150 vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
1151 if vnc_port is not None:
1152 for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
1153 if dev_type == 'vfb':
1154 old_location = dev_info.get('location')
1155 listen_host = dev_info.get('vnclisten', 'localhost')
1156 new_location = '%s:%s' % (listen_host, str(vnc_port))
1157 if old_location == new_location:
1158 break
1160 dev_info['location'] = new_location
1161 self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
1162 vfb_ctrl = self.getDeviceController('vfb')
1163 vfb_ctrl.reconfigureDevice(0, dev_info)
1164 break
1167 # Function to update xenstore /vm/*
1170 def _readVm(self, *args):
1171 return xstransact.Read(self.vmpath, *args)
1173 def _writeVm(self, *args):
1174 return xstransact.Write(self.vmpath, *args)
1176 def _removeVm(self, *args):
1177 return xstransact.Remove(self.vmpath, *args)
1179 def _gatherVm(self, *args):
1180 return xstransact.Gather(self.vmpath, *args)
1182 def _listRecursiveVm(self, *args):
1183 return xstransact.ListRecursive(self.vmpath, *args)
1185 def storeVm(self, *args):
1186 return xstransact.Store(self.vmpath, *args)
1188 def permissionsVm(self, *args):
1189 return xstransact.SetPermissions(self.vmpath, *args)
1192 def _readVmTxn(self, transaction, *args):
1193 paths = map(lambda x: self.vmpath + "/" + x, args)
1194 return transaction.read(*paths)
1196 def _writeVmTxn(self, transaction, *args):
1197 paths = map(lambda x: self.vmpath + "/" + x, args)
1198 return transaction.write(*paths)
1200 def _removeVmTxn(self, transaction, *args):
1201 paths = map(lambda x: self.vmpath + "/" + x, args)
1202 return transaction.remove(*paths)
1204 def _gatherVmTxn(self, transaction, *args):
1205 paths = map(lambda x: self.vmpath + "/" + x, args)
1206 return transaction.gather(paths)
1208 def storeVmTxn(self, transaction, *args):
1209 paths = map(lambda x: self.vmpath + "/" + x, args)
1210 return transaction.store(*paths)
1212 def permissionsVmTxn(self, transaction, *args):
1213 paths = map(lambda x: self.vmpath + "/" + x, args)
1214 return transaction.set_permissions(*paths)
1217 # Function to update xenstore /dom/*
1220 def readDom(self, *args):
1221 return xstransact.Read(self.dompath, *args)
1223 def gatherDom(self, *args):
1224 return xstransact.Gather(self.dompath, *args)
1226 def _writeDom(self, *args):
1227 return xstransact.Write(self.dompath, *args)
1229 def _removeDom(self, *args):
1230 return xstransact.Remove(self.dompath, *args)
1232 def storeDom(self, *args):
1233 return xstransact.Store(self.dompath, *args)
1236 def readDomTxn(self, transaction, *args):
1237 paths = map(lambda x: self.dompath + "/" + x, args)
1238 return transaction.read(*paths)
1240 def gatherDomTxn(self, transaction, *args):
1241 paths = map(lambda x: self.dompath + "/" + x, args)
1242 return transaction.gather(*paths)
1244 def _writeDomTxn(self, transaction, *args):
1245 paths = map(lambda x: self.dompath + "/" + x, args)
1246 return transaction.write(*paths)
1248 def _removeDomTxn(self, transaction, *args):
1249 paths = map(lambda x: self.dompath + "/" + x, args)
1250 return transaction.remove(*paths)
1252 def storeDomTxn(self, transaction, *args):
1253 paths = map(lambda x: self.dompath + "/" + x, args)
1254 return transaction.store(*paths)
1257 def _recreateDom(self):
1258 complete(self.dompath, lambda t: self._recreateDomFunc(t))
1260 def _recreateDomFunc(self, t):
1261 t.remove()
1262 t.mkdir()
1263 t.set_permissions({'dom' : self.domid})
1264 t.write('vm', self.vmpath)
1266 def _storeDomDetails(self):
1267 to_store = {
1268 'domid': str(self.domid),
1269 'vm': self.vmpath,
1270 'name': self.info['name_label'],
1271 'console/limit': str(xoptions.get_console_limit() * 1024),
1272 'memory/target': str(self.info['memory_dynamic_max'] / 1024),
1275 def f(n, v):
1276 if v is not None:
1277 if type(v) == bool:
1278 to_store[n] = v and "1" or "0"
1279 else:
1280 to_store[n] = str(v)
1282 # Figure out if we need to tell xenconsoled to ignore this guest's
1283 # console - device model will handle console if it is running
1284 constype = "ioemu"
1285 if 'device_model' not in self.info['platform']:
1286 constype = "xenconsoled"
1288 f('console/port', self.console_port)
1289 f('console/ring-ref', self.console_mfn)
1290 f('console/type', constype)
1291 f('store/port', self.store_port)
1292 f('store/ring-ref', self.store_mfn)
1294 if arch.type == "x86":
1295 f('control/platform-feature-multiprocessor-suspend', True)
1297 # elfnotes
1298 for n, v in self.info.get_notes().iteritems():
1299 n = n.lower().replace('_', '-')
1300 if n == 'features':
1301 for v in v.split('|'):
1302 v = v.replace('_', '-')
1303 if v.startswith('!'):
1304 f('image/%s/%s' % (n, v[1:]), False)
1305 else:
1306 f('image/%s/%s' % (n, v), True)
1307 else:
1308 f('image/%s' % n, v)
1310 if self.info.has_key('security_label'):
1311 f('security_label', self.info['security_label'])
1313 to_store.update(self._vcpuDomDetails())
1315 log.debug("Storing domain details: %s", scrub_password(to_store))
1317 self._writeDom(to_store)
1319 def _vcpuDomDetails(self):
1320 def availability(n):
1321 if self.info['vcpu_avail'] & (1 << n):
1322 return 'online'
1323 else:
1324 return 'offline'
1326 result = {}
1327 for v in range(0, self.info['VCPUs_max']):
1328 result["cpu/%d/availability" % v] = availability(v)
1329 return result
1332 # xenstore watches
1335 def _registerWatches(self):
1336 """Register a watch on this VM's entries in the store, and the
1337 domain's control/shutdown node, so that when they are changed
1338 externally, we keep up to date. This should only be called by {@link
1339 #create}, {@link #recreate}, or {@link #restore}, once the domain's
1340 details have been written, but before the new instance is returned."""
1341 self.vmWatch = xswatch(self.vmpath, self._storeChanged)
1342 self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
1343 self._handleShutdownWatch)
1345 def _storeChanged(self, _):
1346 log.trace("XendDomainInfo.storeChanged");
1348 changed = False
1350 # Check whether values in the configuration have
1351 # changed in Xenstore.
1353 cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
1354 'rtc/timeoffset']
1356 vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
1357 for k in cfg_vm])
1359 # convert two lists into a python dictionary
1360 vm_details = dict(zip(cfg_vm, vm_details))
1362 if vm_details['rtc/timeoffset'] == None:
1363 vm_details['rtc/timeoffset'] = "0"
1365 for arg, val in vm_details.items():
1366 if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
1367 xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
1368 if val != None and val != self.info[xapiarg]:
1369 self.info[xapiarg] = val
1370 changed = True
1371 elif arg == "memory":
1372 if val != None and val != self.info["static_memory_min"]:
1373 self.info["static_memory_min"] = val
1374 changed = True
1375 elif arg == "maxmem":
1376 if val != None and val != self.info["static_memory_max"]:
1377 self.info["static_memory_max"] = val
1378 changed = True
1380 # Check whether image definition has been updated
1381 image_sxp = self._readVm('image')
1382 if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
1383 self.info.update_with_image_sxp(sxp.from_string(image_sxp))
1384 changed = True
1386 # Check if the rtc offset has changes
1387 if vm_details.get("rtc/timeoffset", "0") != self.info["platform"].get("rtc_timeoffset", "0"):
1388 self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
1389 changed = True
1391 if changed:
1392 # Update the domain section of the store, as this contains some
1393 # parameters derived from the VM configuration.
1394 self._storeDomDetails()
1396 return 1
1398 def _handleShutdownWatch(self, _):
1399 log.debug('XendDomainInfo.handleShutdownWatch')
1401 reason = self.readDom('control/shutdown')
1403 if reason and reason != 'suspend':
1404 sst = self.readDom('xend/shutdown_start_time')
1405 now = time.time()
1406 if sst:
1407 self.shutdownStartTime = float(sst)
1408 timeout = float(sst) + SHUTDOWN_TIMEOUT - now
1409 else:
1410 self.shutdownStartTime = now
1411 self.storeDom('xend/shutdown_start_time', now)
1412 timeout = SHUTDOWN_TIMEOUT
1414 log.trace(
1415 "Scheduling refreshShutdown on domain %d in %ds.",
1416 self.domid, timeout)
1417 threading.Timer(timeout, self.refreshShutdown).start()
1419 return True
1423 # Public Attributes for the VM
1427 def getDomid(self):
1428 return self.domid
1430 def setName(self, name, to_store = True):
1431 self._checkName(name)
1432 self.info['name_label'] = name
1433 if to_store:
1434 self.storeVm("name", name)
1436 def getName(self):
1437 return self.info['name_label']
1439 def getDomainPath(self):
1440 return self.dompath
1442 def getShutdownReason(self):
1443 return self.readDom('control/shutdown')
1445 def getStorePort(self):
1446 """For use only by image.py and XendCheckpoint.py."""
1447 return self.store_port
1449 def getConsolePort(self):
1450 """For use only by image.py and XendCheckpoint.py"""
1451 return self.console_port
1453 def getFeatures(self):
1454 """For use only by image.py."""
1455 return self.info['features']
1457 def getVCpuCount(self):
1458 return self.info['VCPUs_max']
1460 def setVCpuCount(self, vcpus):
1461 if vcpus <= 0:
1462 raise XendError('Invalid VCPUs')
1464 self.info['vcpu_avail'] = (1 << vcpus) - 1
1465 if self.domid >= 0:
1466 self.storeVm('vcpu_avail', self.info['vcpu_avail'])
1467 # update dom differently depending on whether we are adjusting
1468 # vcpu number up or down, otherwise _vcpuDomDetails does not
1469 # disable the vcpus
1470 if self.info['VCPUs_max'] > vcpus:
1471 # decreasing
1472 self._writeDom(self._vcpuDomDetails())
1473 self.info['VCPUs_live'] = vcpus
1474 else:
1475 # same or increasing
1476 self.info['VCPUs_live'] = vcpus
1477 self._writeDom(self._vcpuDomDetails())
1478 else:
1479 self.info['VCPUs_max'] = vcpus
1480 xen.xend.XendDomain.instance().managed_config_save(self)
1481 log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
1482 vcpus)
1484 def getMemoryTarget(self):
1485 """Get this domain's target memory size, in KB."""
1486 return self.info['memory_dynamic_max'] / 1024
1488 def getMemoryMaximum(self):
1489 """Get this domain's maximum memory size, in KB."""
1490 # remember, info now stores memory in bytes
1491 return self.info['memory_static_max'] / 1024
1493 def getResume(self):
1494 return str(self._resume)
1496 def setResume(self, isresume):
1497 self._resume = isresume
1499 def getCpus(self):
1500 return self.info['cpus']
1502 def setCpus(self, cpumap):
1503 self.info['cpus'] = cpumap
1505 def getCap(self):
1506 return self.info['vcpus_params']['cap']
1508 def setCap(self, cpu_cap):
1509 self.info['vcpus_params']['cap'] = cpu_cap
1511 def getWeight(self):
1512 return self.info['vcpus_params']['weight']
1514 def setWeight(self, cpu_weight):
1515 self.info['vcpus_params']['weight'] = cpu_weight
1517 def getRestartCount(self):
1518 return self._readVm('xend/restart_count')
1520 def refreshShutdown(self, xeninfo = None):
1521 """ Checks the domain for whether a shutdown is required.
1523 Called from XendDomainInfo and also image.py for HVM images.
1524 """
1526 # If set at the end of this method, a restart is required, with the
1527 # given reason. This restart has to be done out of the scope of
1528 # refresh_shutdown_lock.
1529 restart_reason = None
1531 self.refresh_shutdown_lock.acquire()
1532 try:
1533 if xeninfo is None:
1534 xeninfo = dom_get(self.domid)
1535 if xeninfo is None:
1536 # The domain no longer exists. This will occur if we have
1537 # scheduled a timer to check for shutdown timeouts and the
1538 # shutdown succeeded. It will also occur if someone
1539 # destroys a domain beneath us. We clean up the domain,
1540 # just in case, but we can't clean up the VM, because that
1541 # VM may have migrated to a different domain on this
1542 # machine.
1543 self.cleanupDomain()
1544 self._stateSet(DOM_STATE_HALTED)
1545 return
1547 if xeninfo['dying']:
1548 # Dying means that a domain has been destroyed, but has not
1549 # yet been cleaned up by Xen. This state could persist
1550 # indefinitely if, for example, another domain has some of its
1551 # pages mapped. We might like to diagnose this problem in the
1552 # future, but for now all we do is make sure that it's not us
1553 # holding the pages, by calling cleanupDomain. We can't
1554 # clean up the VM, as above.
1555 self.cleanupDomain()
1556 self._stateSet(DOM_STATE_SHUTDOWN)
1557 return
1559 elif xeninfo['crashed']:
1560 if self.readDom('xend/shutdown_completed'):
1561 # We've seen this shutdown already, but we are preserving
1562 # the domain for debugging. Leave it alone.
1563 return
1565 log.warn('Domain has crashed: name=%s id=%d.',
1566 self.info['name_label'], self.domid)
1567 self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1569 restart_reason = 'crash'
1570 self._stateSet(DOM_STATE_HALTED)
1572 elif xeninfo['shutdown']:
1573 self._stateSet(DOM_STATE_SHUTDOWN)
1574 if self.readDom('xend/shutdown_completed'):
1575 # We've seen this shutdown already, but we are preserving
1576 # the domain for debugging. Leave it alone.
1577 return
1579 else:
1580 reason = shutdown_reason(xeninfo['shutdown_reason'])
1582 log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1583 self.info['name_label'], self.domid, reason)
1584 self._writeVm(LAST_SHUTDOWN_REASON, reason)
1586 self._clearRestart()
1588 if reason == 'suspend':
1589 self._stateSet(DOM_STATE_SUSPENDED)
1590 # Don't destroy the domain. XendCheckpoint will do
1591 # this once it has finished. However, stop watching
1592 # the VM path now, otherwise we will end up with one
1593 # watch for the old domain, and one for the new.
1594 self._unwatchVm()
1595 elif reason in ('poweroff', 'reboot'):
1596 restart_reason = reason
1597 else:
1598 self.destroy()
1600 elif self.dompath is None:
1601 # We have yet to manage to call introduceDomain on this
1602 # domain. This can happen if a restore is in progress, or has
1603 # failed. Ignore this domain.
1604 pass
1605 else:
1606 # Domain is alive. If we are shutting it down, log a message
1607 # if it seems unresponsive.
1608 if xeninfo['paused']:
1609 self._stateSet(DOM_STATE_PAUSED)
1610 else:
1611 self._stateSet(DOM_STATE_RUNNING)
1613 if self.shutdownStartTime:
1614 timeout = (SHUTDOWN_TIMEOUT - time.time() +
1615 self.shutdownStartTime)
1616 if (timeout < 0 and not self.readDom('xend/unresponsive')):
1617 log.info(
1618 "Domain shutdown timeout expired: name=%s id=%s",
1619 self.info['name_label'], self.domid)
1620 self.storeDom('xend/unresponsive', 'True')
1621 finally:
1622 self.refresh_shutdown_lock.release()
1624 if restart_reason:
1625 threading.Thread(target = self._maybeRestart,
1626 args = (restart_reason,)).start()
1630 # Restart functions - handling whether we come back up on shutdown.
1633 def _clearRestart(self):
1634 self._removeDom("xend/shutdown_start_time")
1636 def _maybeDumpCore(self, reason):
1637 if reason == 'crash':
1638 if xoptions.get_enable_dump() or self.get_on_crash() \
1639 in ['coredump_and_destroy', 'coredump_and_restart']:
1640 try:
1641 self.dumpCore()
1642 except XendError:
1643 # This error has been logged -- there's nothing more
1644 # we can do in this context.
1645 pass
1647 def _maybeRestart(self, reason):
1648 # Before taking configured action, dump core if configured to do so.
1650 self._maybeDumpCore(reason)
1652 # Dispatch to the correct method based upon the configured on_{reason}
1653 # behaviour.
1654 actions = {"destroy" : self.destroy,
1655 "restart" : self._restart,
1656 "preserve" : self._preserve,
1657 "rename-restart" : self._renameRestart,
1658 "coredump-destroy" : self.destroy,
1659 "coredump-restart" : self._restart}
1661 action_conf = {
1662 'poweroff': 'actions_after_shutdown',
1663 'reboot': 'actions_after_reboot',
1664 'crash': 'actions_after_crash',
1667 action_target = self.info.get(action_conf.get(reason))
1668 func = actions.get(action_target, None)
1669 if func and callable(func):
1670 func()
1671 else:
1672 self.destroy() # default to destroy
1674 def _renameRestart(self):
1675 self._restart(True)
1677 def _restart(self, rename = False):
1678 """Restart the domain after it has exited.
1680 @param rename True if the old domain is to be renamed and preserved,
1681 False if it is to be destroyed.
1682 """
1683 from xen.xend import XendDomain
1685 if self._readVm(RESTART_IN_PROGRESS):
1686 log.error('Xend failed during restart of domain %s. '
1687 'Refusing to restart to avoid loops.',
1688 str(self.domid))
1689 self.destroy()
1690 return
1692 old_domid = self.domid
1693 self._writeVm(RESTART_IN_PROGRESS, 'True')
1695 now = time.time()
1696 rst = self._readVm('xend/previous_restart_time')
1697 if rst:
1698 rst = float(rst)
1699 timeout = now - rst
1700 if timeout < MINIMUM_RESTART_TIME:
1701 log.error(
1702 'VM %s restarting too fast (%f seconds since the last '
1703 'restart). Refusing to restart to avoid loops.',
1704 self.info['name_label'], timeout)
1705 self.destroy()
1706 return
1708 self._writeVm('xend/previous_restart_time', str(now))
1710 prev_vm_xend = self._listRecursiveVm('xend')
1711 new_dom_info = self.info
1712 try:
1713 if rename:
1714 new_dom_info = self._preserveForRestart()
1715 else:
1716 self._unwatchVm()
1717 self.destroy()
1719 # new_dom's VM will be the same as this domain's VM, except where
1720 # the rename flag has instructed us to call preserveForRestart.
1721 # In that case, it is important that we remove the
1722 # RESTART_IN_PROGRESS node from the new domain, not the old one,
1723 # once the new one is available.
1725 new_dom = None
1726 try:
1727 new_dom = XendDomain.instance().domain_create_from_dict(
1728 new_dom_info)
1729 for x in prev_vm_xend[0][1]:
1730 new_dom._writeVm('xend/%s' % x[0], x[1])
1731 new_dom.waitForDevices()
1732 new_dom.unpause()
1733 rst_cnt = new_dom._readVm('xend/restart_count')
1734 rst_cnt = int(rst_cnt) + 1
1735 new_dom._writeVm('xend/restart_count', str(rst_cnt))
1736 new_dom._removeVm(RESTART_IN_PROGRESS)
1737 except:
1738 if new_dom:
1739 new_dom._removeVm(RESTART_IN_PROGRESS)
1740 new_dom.destroy()
1741 else:
1742 self._removeVm(RESTART_IN_PROGRESS)
1743 raise
1744 except:
1745 log.exception('Failed to restart domain %s.', str(old_domid))
1747 def _preserveForRestart(self):
1748 """Preserve a domain that has been shut down, by giving it a new UUID,
1749 cloning the VM details, and giving it a new name. This allows us to
1750 keep this domain for debugging, but restart a new one in its place
1751 preserving the restart semantics (name and UUID preserved).
1752 """
1754 new_uuid = uuid.createString()
1755 new_name = 'Domain-%s' % new_uuid
1756 log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1757 self.info['name_label'], self.domid, self.info['uuid'],
1758 new_name, new_uuid)
1759 self._unwatchVm()
1760 self._releaseDevices()
1761 # Remove existing vm node in xenstore
1762 self._removeVm()
1763 new_dom_info = self.info.copy()
1764 new_dom_info['name_label'] = self.info['name_label']
1765 new_dom_info['uuid'] = self.info['uuid']
1766 self.info['name_label'] = new_name
1767 self.info['uuid'] = new_uuid
1768 self.vmpath = XS_VMROOT + new_uuid
1769 # Write out new vm node to xenstore
1770 self._storeVmDetails()
1771 self._preserve()
1772 return new_dom_info
1775 def _preserve(self):
1776 log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1777 self.domid)
1778 self._unwatchVm()
1779 self.storeDom('xend/shutdown_completed', 'True')
1780 self._stateSet(DOM_STATE_HALTED)
1783 # Debugging ..
1786 def dumpCore(self, corefile = None):
1787 """Create a core dump for this domain.
1789 @raise: XendError if core dumping failed.
1790 """
1792 try:
1793 if not corefile:
1794 this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1795 corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1796 self.info['name_label'], self.domid)
1798 if os.path.isdir(corefile):
1799 raise XendError("Cannot dump core in a directory: %s" %
1800 corefile)
1802 self._writeVm(DUMPCORE_IN_PROGRESS, 'True')
1803 xc.domain_dumpcore(self.domid, corefile)
1804 self._removeVm(DUMPCORE_IN_PROGRESS)
1805 except RuntimeError, ex:
1806 corefile_incomp = corefile+'-incomplete'
1807 os.rename(corefile, corefile_incomp)
1808 self._removeVm(DUMPCORE_IN_PROGRESS)
1809 log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1810 self.domid, self.info['name_label'])
1811 raise XendError("Failed to dump core: %s" % str(ex))
1814 # Device creation/deletion functions
1817 def _createDevice(self, deviceClass, devConfig):
1818 return self.getDeviceController(deviceClass).createDevice(devConfig)
1820 def _waitForDevice(self, deviceClass, devid):
1821 return self.getDeviceController(deviceClass).waitForDevice(devid)
1823 def _waitForDeviceUUID(self, dev_uuid):
1824 deviceClass, config = self.info['devices'].get(dev_uuid)
1825 self._waitForDevice(deviceClass, config['devid'])
1827 def _waitForDevice_destroy(self, deviceClass, devid, backpath):
1828 return self.getDeviceController(deviceClass).waitForDevice_destroy(
1829 devid, backpath)
1831 def _reconfigureDevice(self, deviceClass, devid, devconfig):
1832 return self.getDeviceController(deviceClass).reconfigureDevice(
1833 devid, devconfig)
1835 def _createDevices(self):
1836 """Create the devices for a vm.
1838 @raise: VmError for invalid devices
1839 """
1840 ordered_refs = self.info.ordered_device_refs()
1841 for dev_uuid in ordered_refs:
1842 devclass, config = self.info['devices'][dev_uuid]
1843 if devclass in XendDevices.valid_devices():
1844 log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1845 dev_uuid = config.get('uuid')
1846 devid = self._createDevice(devclass, config)
1848 # store devid in XendConfig for caching reasons
1849 if dev_uuid in self.info['devices']:
1850 self.info['devices'][dev_uuid][1]['devid'] = devid
1852 if self.image:
1853 self.image.createDeviceModel()
1855 #if have pass-through devs, need the virtual pci slots info from qemu
1856 self.sync_pcidev_info()
1858 def _releaseDevices(self, suspend = False):
1859 """Release all domain's devices. Nothrow guarantee."""
1860 if self.image:
1861 try:
1862 log.debug("Destroying device model")
1863 self.image.destroyDeviceModel()
1864 except Exception, e:
1865 log.exception("Device model destroy failed %s" % str(e))
1866 else:
1867 log.debug("No device model")
1869 log.debug("Releasing devices")
1870 t = xstransact("%s/device" % self.dompath)
1871 try:
1872 for devclass in XendDevices.valid_devices():
1873 for dev in t.list(devclass):
1874 try:
1875 log.debug("Removing %s", dev);
1876 self.destroyDevice(devclass, dev, False);
1877 except:
1878 # Log and swallow any exceptions in removal --
1879 # there's nothing more we can do.
1880 log.exception("Device release failed: %s; %s; %s",
1881 self.info['name_label'], devclass, dev)
1882 finally:
1883 t.abort()
1885 def getDeviceController(self, name):
1886 """Get the device controller for this domain, and if it
1887 doesn't exist, create it.
1889 @param name: device class name
1890 @type name: string
1891 @rtype: subclass of DevController
1892 """
1893 if name not in self._deviceControllers:
1894 devController = XendDevices.make_controller(name, self)
1895 if not devController:
1896 raise XendError("Unknown device type: %s" % name)
1897 self._deviceControllers[name] = devController
1899 return self._deviceControllers[name]
1902 # Migration functions (public)
1905 def testMigrateDevices(self, network, dst):
1906 """ Notify all device about intention of migration
1907 @raise: XendError for a device that cannot be migrated
1908 """
1909 for (n, c) in self.info.all_devices_sxpr():
1910 rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST, self.getName())
1911 if rc != 0:
1912 raise XendError("Device of type '%s' refuses migration." % n)
1914 def migrateDevices(self, network, dst, step, domName=''):
1915 """Notify the devices about migration
1916 """
1917 ctr = 0
1918 try:
1919 for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1920 self.migrateDevice(dev_type, dev_conf, network, dst,
1921 step, domName)
1922 ctr = ctr + 1
1923 except:
1924 for dev_type, dev_conf in self.info.all_devices_sxpr():
1925 if ctr == 0:
1926 step = step - 1
1927 ctr = ctr - 1
1928 self._recoverMigrateDevice(dev_type, dev_conf, network,
1929 dst, step, domName)
1930 raise
1932 def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1933 step, domName=''):
1934 return self.getDeviceController(deviceClass).migrate(deviceConfig,
1935 network, dst, step, domName)
1937 def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1938 dst, step, domName=''):
1939 return self.getDeviceController(deviceClass).recover_migrate(
1940 deviceConfig, network, dst, step, domName)
1943 ## private:
1945 def _constructDomain(self):
1946 """Construct the domain.
1948 @raise: VmError on error
1949 """
1951 log.debug('XendDomainInfo.constructDomain')
1953 self.shutdownStartTime = None
1955 hap = 0
1956 hvm = self.info.is_hvm()
1957 if hvm:
1958 hap = self.info.is_hap()
1959 info = xc.xeninfo()
1960 if 'hvm' not in info['xen_caps']:
1961 raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1962 "supported by your CPU and enabled in your "
1963 "BIOS?")
1965 # Hack to pre-reserve some memory for initial domain creation.
1966 # There is an implicit memory overhead for any domain creation. This
1967 # overhead is greater for some types of domain than others. For
1968 # example, an x86 HVM domain will have a default shadow-pagetable
1969 # allocation of 1MB. We free up 2MB here to be on the safe side.
1970 balloon.free(2*1024) # 2MB should be plenty
1972 ssidref = 0
1973 if security.on():
1974 ssidref = security.calc_dom_ssidref_from_info(self.info)
1975 if security.has_authorization(ssidref) == False:
1976 raise VmError("VM is not authorized to run.")
1978 try:
1979 self.domid = xc.domain_create(
1980 domid = 0,
1981 ssidref = ssidref,
1982 handle = uuid.fromString(self.info['uuid']),
1983 flags = (int(hvm) << 0) | (int(hap) << 1),
1984 target = self.info.target())
1985 except Exception, e:
1986 # may get here if due to ACM the operation is not permitted
1987 if security.on():
1988 raise VmError('Domain in conflict set with running domain?')
1990 if self.domid < 0:
1991 raise VmError('Creating domain failed: name=%s' %
1992 self.info['name_label'])
1994 self.dompath = GetDomainPath(self.domid)
1996 self._recreateDom()
1998 # Set timer configration of domain
1999 timer_mode = self.info["platform"].get("timer_mode")
2000 if hvm and timer_mode is not None:
2001 xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
2002 long(timer_mode))
2004 # Optionally enable virtual HPET
2005 hpet = self.info["platform"].get("hpet")
2006 if hvm and hpet is not None:
2007 xc.hvm_set_param(self.domid, HVM_PARAM_HPET_ENABLED,
2008 long(hpet))
2010 # Set maximum number of vcpus in domain
2011 xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
2013 # Test whether the devices can be assigned with VT-d
2014 pci_str = str(self.info["platform"].get("pci"))
2015 if hvm and pci_str:
2016 bdf = xc.test_assign_device(self.domid, pci_str)
2017 if bdf != 0:
2018 bus = (bdf >> 16) & 0xff
2019 devfn = (bdf >> 8) & 0xff
2020 dev = (devfn >> 3) & 0x1f
2021 func = devfn & 0x7
2022 raise VmError("Fail to assign device(%x:%x.%x): maybe VT-d is "
2023 "not enabled, or the device is not exist, or it "
2024 "has already been assigned to other domain"
2025 % (bus, dev, func))
2027 # register the domain in the list
2028 from xen.xend import XendDomain
2029 XendDomain.instance().add_domain(self)
2031 def _introduceDomain(self):
2032 assert self.domid is not None
2033 assert self.store_mfn is not None
2034 assert self.store_port is not None
2036 try:
2037 IntroduceDomain(self.domid, self.store_mfn, self.store_port)
2038 except RuntimeError, exn:
2039 raise XendError(str(exn))
2041 def _setTarget(self, target):
2042 assert self.domid is not None
2044 try:
2045 SetTarget(self.domid, target)
2046 self.storeDom('target', target)
2047 except RuntimeError, exn:
2048 raise XendError(str(exn))
2051 def _initDomain(self):
2052 log.debug('XendDomainInfo.initDomain: %s %s',
2053 self.domid,
2054 self.info['vcpus_params']['weight'])
2056 self._configureBootloader()
2058 try:
2059 if self.info['platform'].get('localtime', 0):
2060 if time.localtime(time.time())[8]:
2061 self.info['platform']['rtc_timeoffset'] = -time.altzone
2062 else:
2063 self.info['platform']['rtc_timeoffset'] = -time.timezone
2065 self.image = image.create(self, self.info)
2067 # repin domain vcpus if a restricted cpus list is provided
2068 # this is done prior to memory allocation to aide in memory
2069 # distribution for NUMA systems.
2070 if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
2071 for v in range(0, self.info['VCPUs_max']):
2072 xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
2073 else:
2074 def find_relaxed_node(node_list):
2075 import sys
2076 if node_list is None:
2077 node_list = range(0, info['nr_nodes'])
2078 nodeload = [0]
2079 nodeload = nodeload * info['nr_nodes']
2080 from xen.xend import XendDomain
2081 doms = XendDomain.instance().list('all')
2082 for dom in doms:
2083 cpuinfo = dom.getVCPUInfo()
2084 for vcpu in sxp.children(cpuinfo, 'vcpu'):
2085 def vinfo(n, t):
2086 return t(sxp.child_value(vcpu, n))
2087 cpumap = vinfo('cpumap', list)
2088 for i in node_list:
2089 node_cpumask = info['node_to_cpu'][i]
2090 for j in node_cpumask:
2091 if j in cpumap:
2092 nodeload[i] += 1
2093 break
2094 for i in node_list:
2095 if len(info['node_to_cpu'][i]) > 0:
2096 nodeload[i] = int(nodeload[i] / len(info['node_to_cpu'][i]))
2097 else:
2098 nodeload[i] = sys.maxint
2099 index = nodeload.index( min(nodeload) )
2100 return index
2102 info = xc.physinfo()
2103 if info['nr_nodes'] > 1:
2104 node_memory_list = info['node_to_memory']
2105 needmem = self.image.getRequiredAvailableMemory(self.info['memory_dynamic_max']) / 1024
2106 candidate_node_list = []
2107 for i in range(0, info['nr_nodes']):
2108 if node_memory_list[i] >= needmem and len(info['node_to_cpu'][i]) > 0:
2109 candidate_node_list.append(i)
2110 index = find_relaxed_node(candidate_node_list)
2111 cpumask = info['node_to_cpu'][index]
2112 for v in range(0, self.info['VCPUs_max']):
2113 xc.vcpu_setaffinity(self.domid, v, cpumask)
2115 # Use architecture- and image-specific calculations to determine
2116 # the various headrooms necessary, given the raw configured
2117 # values. maxmem, memory, and shadow are all in KiB.
2118 # but memory_static_max etc are all stored in bytes now.
2119 memory = self.image.getRequiredAvailableMemory(
2120 self.info['memory_dynamic_max'] / 1024)
2121 maxmem = self.image.getRequiredAvailableMemory(
2122 self.info['memory_static_max'] / 1024)
2123 shadow = self.image.getRequiredShadowMemory(
2124 self.info['shadow_memory'] * 1024,
2125 self.info['memory_static_max'] / 1024)
2127 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'],)
2128 # Round shadow up to a multiple of a MiB, as shadow_mem_control
2129 # takes MiB and we must not round down and end up under-providing.
2130 shadow = ((shadow + 1023) / 1024) * 1024
2132 # set memory limit
2133 xc.domain_setmaxmem(self.domid, maxmem)
2135 # Make sure there's enough RAM available for the domain
2136 balloon.free(memory + shadow)
2138 # Set up the shadow memory
2139 shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
2140 self.info['shadow_memory'] = shadow_cur
2142 self._createChannels()
2144 channel_details = self.image.createImage()
2146 self.store_mfn = channel_details['store_mfn']
2147 if 'console_mfn' in channel_details:
2148 self.console_mfn = channel_details['console_mfn']
2149 if 'notes' in channel_details:
2150 self.info.set_notes(channel_details['notes'])
2151 if 'native_protocol' in channel_details:
2152 self.native_protocol = channel_details['native_protocol'];
2154 self._introduceDomain()
2155 if self.info.target():
2156 self._setTarget(self.info.target())
2158 self._createDevices()
2160 self.image.cleanupBootloading()
2162 self.info['start_time'] = time.time()
2164 self._stateSet(DOM_STATE_RUNNING)
2165 except VmError, exn:
2166 log.exception("XendDomainInfo.initDomain: exception occurred")
2167 if self.image:
2168 self.image.cleanupBootloading()
2169 raise exn
2170 except RuntimeError, exn:
2171 log.exception("XendDomainInfo.initDomain: exception occurred")
2172 if self.image:
2173 self.image.cleanupBootloading()
2174 raise VmError(str(exn))
2177 def cleanupDomain(self):
2178 """Cleanup domain resources; release devices. Idempotent. Nothrow
2179 guarantee."""
2181 self.refresh_shutdown_lock.acquire()
2182 try:
2183 self.unwatchShutdown()
2184 self._releaseDevices()
2185 bootloader_tidy(self)
2187 if self.image:
2188 self.image = None
2190 try:
2191 self._removeDom()
2192 except:
2193 log.exception("Removing domain path failed.")
2195 self._stateSet(DOM_STATE_HALTED)
2196 self.domid = None # Do not push into _stateSet()!
2197 finally:
2198 self.refresh_shutdown_lock.release()
2201 def unwatchShutdown(self):
2202 """Remove the watch on the domain's control/shutdown node, if any.
2203 Idempotent. Nothrow guarantee. Expects to be protected by the
2204 refresh_shutdown_lock."""
2206 try:
2207 try:
2208 if self.shutdownWatch:
2209 self.shutdownWatch.unwatch()
2210 finally:
2211 self.shutdownWatch = None
2212 except:
2213 log.exception("Unwatching control/shutdown failed.")
2215 def waitForShutdown(self):
2216 self.state_updated.acquire()
2217 try:
2218 while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
2219 self.state_updated.wait(timeout=1.0)
2220 finally:
2221 self.state_updated.release()
2224 # TODO: recategorise - called from XendCheckpoint
2227 def completeRestore(self, store_mfn, console_mfn):
2229 log.debug("XendDomainInfo.completeRestore")
2231 self.store_mfn = store_mfn
2232 self.console_mfn = console_mfn
2234 self._introduceDomain()
2235 self.image = image.create(self, self.info)
2236 if self.image:
2237 self.image.createDeviceModel(True)
2238 self._storeDomDetails()
2239 self._registerWatches()
2240 self.refreshShutdown()
2242 log.debug("XendDomainInfo.completeRestore done")
2245 def _endRestore(self):
2246 self.setResume(False)
2249 # VM Destroy
2252 def _prepare_phantom_paths(self):
2253 # get associated devices to destroy
2254 # build list of phantom devices to be removed after normal devices
2255 plist = []
2256 if self.domid is not None:
2257 t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
2258 try:
2259 for dev in t.list():
2260 backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
2261 % (self.dompath, dev))
2262 if backend_phantom_vbd is not None:
2263 frontend_phantom_vbd = xstransact.Read("%s/frontend" \
2264 % backend_phantom_vbd)
2265 plist.append(backend_phantom_vbd)
2266 plist.append(frontend_phantom_vbd)
2267 finally:
2268 t.abort()
2269 return plist
2271 def _cleanup_phantom_devs(self, plist):
2272 # remove phantom devices
2273 if not plist == []:
2274 time.sleep(2)
2275 for paths in plist:
2276 if paths.find('backend') != -1:
2277 from xen.xend.server import DevController
2278 # Modify online status /before/ updating state (latter is watched by
2279 # drivers, so this ordering avoids a race).
2280 xstransact.Write(paths, 'online', "0")
2281 xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
2282 # force
2283 xstransact.Remove(paths)
2285 def destroy(self):
2286 """Cleanup VM and destroy domain. Nothrow guarantee."""
2288 log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
2290 paths = self._prepare_phantom_paths()
2292 self._cleanupVm()
2293 if self.dompath is not None:
2294 self.destroyDomain()
2296 self._cleanup_phantom_devs(paths)
2298 if "transient" in self.info["other_config"] \
2299 and bool(self.info["other_config"]["transient"]):
2300 from xen.xend import XendDomain
2301 XendDomain.instance().domain_delete_by_dominfo(self)
2304 def destroyDomain(self):
2305 log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
2307 paths = self._prepare_phantom_paths()
2309 try:
2310 if self.domid is not None:
2311 xc.domain_destroy_hook(self.domid)
2312 xc.domain_destroy(self.domid)
2313 for state in DOM_STATES_OLD:
2314 self.info[state] = 0
2315 self._stateSet(DOM_STATE_HALTED)
2316 except:
2317 log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
2319 from xen.xend import XendDomain
2320 XendDomain.instance().remove_domain(self)
2322 self.cleanupDomain()
2323 self._cleanup_phantom_devs(paths)
2326 def resumeDomain(self):
2327 log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
2329 if self.domid is None:
2330 return
2331 try:
2332 # could also fetch a parsed note from xenstore
2333 fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
2334 if not fast:
2335 self._releaseDevices()
2336 self.testDeviceComplete()
2337 self.testvifsComplete()
2338 log.debug("XendDomainInfo.resumeDomain: devices released")
2340 self._resetChannels()
2342 self._removeDom('control/shutdown')
2343 self._removeDom('device-misc/vif/nextDeviceID')
2345 self._createChannels()
2346 self._introduceDomain()
2347 self._storeDomDetails()
2349 self._createDevices()
2350 log.debug("XendDomainInfo.resumeDomain: devices created")
2352 xc.domain_resume(self.domid, fast)
2353 ResumeDomain(self.domid)
2354 except:
2355 log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
2356 self.image.resumeDeviceModel()
2357 log.debug("XendDomainInfo.resumeDomain: completed")
2361 # Channels for xenstore and console
2364 def _createChannels(self):
2365 """Create the channels to the domain.
2366 """
2367 self.store_port = self._createChannel()
2368 self.console_port = self._createChannel()
2371 def _createChannel(self):
2372 """Create an event channel to the domain.
2373 """
2374 try:
2375 if self.domid != None:
2376 return xc.evtchn_alloc_unbound(domid = self.domid,
2377 remote_dom = 0)
2378 except:
2379 log.exception("Exception in alloc_unbound(%s)", str(self.domid))
2380 raise
2382 def _resetChannels(self):
2383 """Reset all event channels in the domain.
2384 """
2385 try:
2386 if self.domid != None:
2387 return xc.evtchn_reset(dom = self.domid)
2388 except:
2389 log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
2390 raise
2394 # Bootloader configuration
2397 def _configureBootloader(self):
2398 """Run the bootloader if we're configured to do so."""
2400 blexec = self.info['PV_bootloader']
2401 bootloader_args = self.info['PV_bootloader_args']
2402 kernel = self.info['PV_kernel']
2403 ramdisk = self.info['PV_ramdisk']
2404 args = self.info['PV_args']
2405 boot = self.info['HVM_boot_policy']
2407 if boot:
2408 # HVM booting.
2409 pass
2410 elif not blexec and kernel:
2411 # Boot from dom0. Nothing left to do -- the kernel and ramdisk
2412 # will be picked up by image.py.
2413 pass
2414 else:
2415 # Boot using bootloader
2416 if not blexec or blexec == 'pygrub':
2417 blexec = osdep.pygrub_path
2419 blcfg = None
2420 disks = [x for x in self.info['vbd_refs']
2421 if self.info['devices'][x][1]['bootable']]
2423 if not disks:
2424 msg = "Had a bootloader specified, but no disks are bootable"
2425 log.error(msg)
2426 raise VmError(msg)
2428 devinfo = self.info['devices'][disks[0]]
2429 devtype = devinfo[0]
2430 disk = devinfo[1]['uname']
2432 fn = blkdev_uname_to_file(disk)
2433 taptype = blkdev_uname_to_taptype(disk)
2434 mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
2435 if mounted:
2436 # This is a file, not a device. pygrub can cope with a
2437 # file if it's raw, but if it's QCOW or other such formats
2438 # used through blktap, then we need to mount it first.
2440 log.info("Mounting %s on %s." %
2441 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2443 vbd = {
2444 'mode': 'RO',
2445 'device': BOOTLOADER_LOOPBACK_DEVICE,
2448 from xen.xend import XendDomain
2449 dom0 = XendDomain.instance().privilegedDomain()
2450 dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
2451 fn = BOOTLOADER_LOOPBACK_DEVICE
2453 try:
2454 blcfg = bootloader(blexec, fn, self, False,
2455 bootloader_args, kernel, ramdisk, args)
2456 finally:
2457 if mounted:
2458 log.info("Unmounting %s from %s." %
2459 (fn, BOOTLOADER_LOOPBACK_DEVICE))
2461 dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
2463 if blcfg is None:
2464 msg = "Had a bootloader specified, but can't find disk"
2465 log.error(msg)
2466 raise VmError(msg)
2468 self.info.update_with_image_sxp(blcfg, True)
2472 # VM Functions
2475 def _readVMDetails(self, params):
2476 """Read the specified parameters from the store.
2477 """
2478 try:
2479 return self._gatherVm(*params)
2480 except ValueError:
2481 # One of the int/float entries in params has a corresponding store
2482 # entry that is invalid. We recover, because older versions of
2483 # Xend may have put the entry there (memory/target, for example),
2484 # but this is in general a bad situation to have reached.
2485 log.exception(
2486 "Store corrupted at %s! Domain %d's configuration may be "
2487 "affected.", self.vmpath, self.domid)
2488 return []
2490 def _cleanupVm(self):
2491 """Cleanup VM resources. Idempotent. Nothrow guarantee."""
2493 self._unwatchVm()
2495 try:
2496 self._removeVm()
2497 except:
2498 log.exception("Removing VM path failed.")
2501 def checkLiveMigrateMemory(self):
2502 """ Make sure there's enough memory to migrate this domain """
2503 overhead_kb = 0
2504 if arch.type == "x86":
2505 # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than
2506 # the minimum that Xen would allocate if no value were given.
2507 overhead_kb = self.info['VCPUs_max'] * 1024 + \
2508 (self.info['memory_static_max'] / 1024 / 1024) * 4
2509 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
2510 # The domain might already have some shadow memory
2511 overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
2512 if overhead_kb > 0:
2513 balloon.free(overhead_kb)
2515 def _unwatchVm(self):
2516 """Remove the watch on the VM path, if any. Idempotent. Nothrow
2517 guarantee."""
2518 try:
2519 try:
2520 if self.vmWatch:
2521 self.vmWatch.unwatch()
2522 finally:
2523 self.vmWatch = None
2524 except:
2525 log.exception("Unwatching VM path failed.")
2527 def testDeviceComplete(self):
2528 """ For Block IO migration safety we must ensure that
2529 the device has shutdown correctly, i.e. all blocks are
2530 flushed to disk
2531 """
2532 start = time.time()
2533 while True:
2534 test = 0
2535 diff = time.time() - start
2536 for i in self.getDeviceController('vbd').deviceIDs():
2537 test = 1
2538 log.info("Dev %s still active, looping...", i)
2539 time.sleep(0.1)
2541 if test == 0:
2542 break
2543 if diff >= MIGRATE_TIMEOUT:
2544 log.info("Dev still active but hit max loop timeout")
2545 break
2547 def testvifsComplete(self):
2548 """ In case vifs are released and then created for the same
2549 domain, we need to wait the device shut down.
2550 """
2551 start = time.time()
2552 while True:
2553 test = 0
2554 diff = time.time() - start
2555 for i in self.getDeviceController('vif').deviceIDs():
2556 test = 1
2557 log.info("Dev %s still active, looping...", i)
2558 time.sleep(0.1)
2560 if test == 0:
2561 break
2562 if diff >= MIGRATE_TIMEOUT:
2563 log.info("Dev still active but hit max loop timeout")
2564 break
2566 def _storeVmDetails(self):
2567 to_store = {}
2569 for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
2570 info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
2571 if self._infoIsSet(info_key):
2572 to_store[key] = str(self.info[info_key])
2574 if self._infoIsSet("static_memory_min"):
2575 to_store["memory"] = str(self.info["static_memory_min"])
2576 if self._infoIsSet("static_memory_max"):
2577 to_store["maxmem"] = str(self.info["static_memory_max"])
2579 image_sxpr = self.info.image_sxpr()
2580 if image_sxpr:
2581 to_store['image'] = sxp.to_string(image_sxpr)
2583 if not self._readVm('xend/restart_count'):
2584 to_store['xend/restart_count'] = str(0)
2586 log.debug("Storing VM details: %s", scrub_password(to_store))
2588 self._writeVm(to_store)
2589 self._setVmPermissions()
2592 def _setVmPermissions(self):
2593 """Allow the guest domain to read its UUID. We don't allow it to
2594 access any other entry, for security."""
2595 xstransact.SetPermissions('%s/uuid' % self.vmpath,
2596 { 'dom' : self.domid,
2597 'read' : True,
2598 'write' : False })
2601 # Utility functions
2604 def __getattr__(self, name):
2605 if name == "state":
2606 log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
2607 log.warn("".join(traceback.format_stack()))
2608 return self._stateGet()
2609 else:
2610 raise AttributeError()
2612 def __setattr__(self, name, value):
2613 if name == "state":
2614 log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2615 log.warn("".join(traceback.format_stack()))
2616 self._stateSet(value)
2617 else:
2618 self.__dict__[name] = value
2620 def _stateSet(self, state):
2621 self.state_updated.acquire()
2622 try:
2623 # TODO Not sure this is correct...
2624 # _stateGet is live now. Why not fire event
2625 # even when it hasn't changed?
2626 if self._stateGet() != state:
2627 self.state_updated.notifyAll()
2628 import XendAPI
2629 XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2630 'power_state')
2631 finally:
2632 self.state_updated.release()
2634 def _stateGet(self):
2635 # Lets try and reconsitute the state from xc
2636 # first lets try and get the domain info
2637 # from xc - this will tell us if the domain
2638 # exists
2639 info = dom_get(self.getDomid())
2640 if info is None or info['shutdown']:
2641 # We are either HALTED or SUSPENDED
2642 # check saved image exists
2643 from xen.xend import XendDomain
2644 managed_config_path = \
2645 XendDomain.instance()._managed_check_point_path( \
2646 self.get_uuid())
2647 if os.path.exists(managed_config_path):
2648 return XEN_API_VM_POWER_STATE_SUSPENDED
2649 else:
2650 return XEN_API_VM_POWER_STATE_HALTED
2651 elif info['crashed']:
2652 # Crashed
2653 return XEN_API_VM_POWER_STATE_CRASHED
2654 else:
2655 # We are either RUNNING or PAUSED
2656 if info['paused']:
2657 return XEN_API_VM_POWER_STATE_PAUSED
2658 else:
2659 return XEN_API_VM_POWER_STATE_RUNNING
2661 def _infoIsSet(self, name):
2662 return name in self.info and self.info[name] is not None
2664 def _checkName(self, name):
2665 """Check if a vm name is valid. Valid names contain alphabetic
2666 characters, digits, or characters in '_-.:/+'.
2667 The same name cannot be used for more than one vm at the same time.
2669 @param name: name
2670 @raise: VmError if invalid
2671 """
2672 from xen.xend import XendDomain
2674 if name is None or name == '':
2675 raise VmError('Missing VM Name')
2677 if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2678 raise VmError('Invalid VM Name')
2680 dom = XendDomain.instance().domain_lookup_nr(name)
2681 if dom and dom.info['uuid'] != self.info['uuid']:
2682 raise VmError("VM name '%s' already exists%s" %
2683 (name,
2684 dom.domid is not None and
2685 (" as domain %s" % str(dom.domid)) or ""))
2688 def update(self, info = None, refresh = True, transaction = None):
2689 """Update with info from xc.domain_getinfo().
2690 """
2691 log.trace("XendDomainInfo.update(%s) on domain %s", info,
2692 str(self.domid))
2694 if not info:
2695 info = dom_get(self.domid)
2696 if not info:
2697 return
2699 if info["maxmem_kb"] < 0:
2700 info["maxmem_kb"] = XendNode.instance() \
2701 .physinfo_dict()['total_memory'] * 1024
2703 #ssidref field not used any longer
2704 if 'ssidref' in info:
2705 info.pop('ssidref')
2707 # make sure state is reset for info
2708 # TODO: we should eventually get rid of old_dom_states
2710 self.info.update_config(info)
2711 self._update_consoles(transaction)
2713 if refresh:
2714 self.refreshShutdown(info)
2716 log.trace("XendDomainInfo.update done on domain %s: %s",
2717 str(self.domid), self.info)
2719 def sxpr(self, ignore_store = False, legacy_only = True):
2720 result = self.info.to_sxp(domain = self,
2721 ignore_devices = ignore_store,
2722 legacy_only = legacy_only)
2724 #if not ignore_store and self.dompath:
2725 # vnc_port = self.readDom('console/vnc-port')
2726 # if vnc_port is not None:
2727 # result.append(['device',
2728 # ['console', ['vnc-port', str(vnc_port)]]])
2730 return result
2732 # Xen API
2733 # ----------------------------------------------------------------
2735 def get_uuid(self):
2736 dom_uuid = self.info.get('uuid')
2737 if not dom_uuid: # if it doesn't exist, make one up
2738 dom_uuid = uuid.createString()
2739 self.info['uuid'] = dom_uuid
2740 return dom_uuid
2742 def get_memory_static_max(self):
2743 return self.info.get('memory_static_max', 0)
2744 def get_memory_static_min(self):
2745 return self.info.get('memory_static_min', 0)
2746 def get_memory_dynamic_max(self):
2747 return self.info.get('memory_dynamic_max', 0)
2748 def get_memory_dynamic_min(self):
2749 return self.info.get('memory_dynamic_min', 0)
2751 # only update memory-related config values if they maintain sanity
2752 def _safe_set_memory(self, key, newval):
2753 oldval = self.info.get(key, 0)
2754 try:
2755 self.info[key] = newval
2756 self.info._memory_sanity_check()
2757 except Exception, ex:
2758 self.info[key] = oldval
2759 raise
2761 def set_memory_static_max(self, val):
2762 self._safe_set_memory('memory_static_max', val)
2763 def set_memory_static_min(self, val):
2764 self._safe_set_memory('memory_static_min', val)
2765 def set_memory_dynamic_max(self, val):
2766 self._safe_set_memory('memory_dynamic_max', val)
2767 def set_memory_dynamic_min(self, val):
2768 self._safe_set_memory('memory_dynamic_min', val)
2770 def get_vcpus_params(self):
2771 if self.getDomid() is None:
2772 return self.info['vcpus_params']
2774 retval = xc.sched_credit_domain_get(self.getDomid())
2775 return retval
2776 def get_power_state(self):
2777 return XEN_API_VM_POWER_STATE[self._stateGet()]
2778 def get_platform(self):
2779 return self.info.get('platform', {})
2780 def get_pci_bus(self):
2781 return self.info.get('pci_bus', '')
2782 def get_tools_version(self):
2783 return self.info.get('tools_version', {})
2784 def get_metrics(self):
2785 return self.metrics.get_uuid();
2788 def get_security_label(self, xspol=None):
2789 import xen.util.xsm.xsm as security
2790 label = security.get_security_label(self, xspol)
2791 return label
2793 def set_security_label(self, seclab, old_seclab, xspol=None,
2794 xspol_old=None):
2795 """
2796 Set the security label of a domain from its old to
2797 a new value.
2798 @param seclab New security label formatted in the form
2799 <policy type>:<policy name>:<vm label>
2800 @param old_seclab The current security label that the
2801 VM must have.
2802 @param xspol An optional policy under which this
2803 update should be done. If not given,
2804 then the current active policy is used.
2805 @param xspol_old The old policy; only to be passed during
2806 the updating of a policy
2807 @return Returns return code, a string with errors from
2808 the hypervisor's operation, old label of the
2809 domain
2810 """
2811 rc = 0
2812 errors = ""
2813 old_label = ""
2814 new_ssidref = 0
2815 domid = self.getDomid()
2816 res_labels = None
2817 is_policy_update = (xspol_old != None)
2819 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
2820 from xen.util import xsconstants
2822 state = self._stateGet()
2823 # Relabel only HALTED or RUNNING or PAUSED domains
2824 if domid != 0 and \
2825 state not in \
2826 [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
2827 DOM_STATE_SUSPENDED ]:
2828 log.warn("Relabeling domain not possible in state '%s'" %
2829 DOM_STATES[state])
2830 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2832 # Remove security label. Works only for halted domains
2833 if not seclab or seclab == "":
2834 if state not in [ DOM_STATE_HALTED ]:
2835 return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
2837 if self.info.has_key('security_label'):
2838 old_label = self.info['security_label']
2839 # Check label against expected one.
2840 if old_label != old_seclab:
2841 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2842 del self.info['security_label']
2843 xen.xend.XendDomain.instance().managed_config_save(self)
2844 return (xsconstants.XSERR_SUCCESS, "", "", 0)
2846 tmp = seclab.split(":")
2847 if len(tmp) != 3:
2848 return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
2849 typ, policy, label = tmp
2851 poladmin = XSPolicyAdminInstance()
2852 if not xspol:
2853 xspol = poladmin.get_policy_by_name(policy)
2855 if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
2856 #if domain is running or paused try to relabel in hypervisor
2857 if not xspol:
2858 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2860 if typ != xspol.get_type_name() or \
2861 policy != xspol.get_name():
2862 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2864 if typ == xsconstants.ACM_POLICY_ID:
2865 new_ssidref = xspol.vmlabel_to_ssidref(label)
2866 if new_ssidref == xsconstants.INVALID_SSIDREF:
2867 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2869 # Check that all used resources are accessible under the
2870 # new label
2871 if not is_policy_update and \
2872 not security.resources_compatible_with_vmlabel(xspol,
2873 self, label):
2874 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2876 #Check label against expected one. Can only do this
2877 # if the policy hasn't changed underneath in the meantime
2878 if xspol_old == None:
2879 old_label = self.get_security_label()
2880 if old_label != old_seclab:
2881 log.info("old_label != old_seclab: %s != %s" %
2882 (old_label, old_seclab))
2883 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2885 # relabel domain in the hypervisor
2886 rc, errors = security.relabel_domains([[domid, new_ssidref]])
2887 log.info("rc from relabeling in HV: %d" % rc)
2888 else:
2889 return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
2891 if rc == 0:
2892 # HALTED, RUNNING or PAUSED
2893 if domid == 0:
2894 if xspol:
2895 self.info['security_label'] = seclab
2896 ssidref = poladmin.set_domain0_bootlabel(xspol, label)
2897 else:
2898 return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
2899 else:
2900 if self.info.has_key('security_label'):
2901 old_label = self.info['security_label']
2902 # Check label against expected one, unless wildcard
2903 if old_label != old_seclab:
2904 return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
2906 self.info['security_label'] = seclab
2908 try:
2909 xen.xend.XendDomain.instance().managed_config_save(self)
2910 except:
2911 pass
2912 return (rc, errors, old_label, new_ssidref)
2914 def get_on_shutdown(self):
2915 after_shutdown = self.info.get('actions_after_shutdown')
2916 if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2917 return XEN_API_ON_NORMAL_EXIT[-1]
2918 return after_shutdown
2920 def get_on_reboot(self):
2921 after_reboot = self.info.get('actions_after_reboot')
2922 if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2923 return XEN_API_ON_NORMAL_EXIT[-1]
2924 return after_reboot
2926 def get_on_suspend(self):
2927 # TODO: not supported
2928 after_suspend = self.info.get('actions_after_suspend')
2929 if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2930 return XEN_API_ON_NORMAL_EXIT[-1]
2931 return after_suspend
2933 def get_on_crash(self):
2934 after_crash = self.info.get('actions_after_crash')
2935 if not after_crash or after_crash not in \
2936 XEN_API_ON_CRASH_BEHAVIOUR + restart_modes:
2937 return XEN_API_ON_CRASH_BEHAVIOUR[0]
2938 return XEN_API_ON_CRASH_BEHAVIOUR_FILTER[after_crash]
2940 def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2941 """ Get's a device configuration either from XendConfig or
2942 from the DevController.
2944 @param dev_class: device class, either, 'vbd' or 'vif'
2945 @param dev_uuid: device UUID
2947 @rtype: dictionary
2948 """
2949 dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2951 # shortcut if the domain isn't started because
2952 # the devcontrollers will have no better information
2953 # than XendConfig.
2954 if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2955 if dev_config:
2956 return copy.deepcopy(dev_config)
2957 return None
2959 # instead of using dev_class, we use the dev_type
2960 # that is from XendConfig.
2961 controller = self.getDeviceController(dev_type)
2962 if not controller:
2963 return None
2965 all_configs = controller.getAllDeviceConfigurations()
2966 if not all_configs:
2967 return None
2969 updated_dev_config = copy.deepcopy(dev_config)
2970 for _devid, _devcfg in all_configs.items():
2971 if _devcfg.get('uuid') == dev_uuid:
2972 updated_dev_config.update(_devcfg)
2973 updated_dev_config['id'] = _devid
2974 return updated_dev_config
2976 return updated_dev_config
2978 def get_dev_xenapi_config(self, dev_class, dev_uuid):
2979 config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2980 if not config:
2981 return {}
2983 config['VM'] = self.get_uuid()
2985 if dev_class == 'vif':
2986 if not config.has_key('name'):
2987 config['name'] = config.get('vifname', '')
2988 if not config.has_key('MAC'):
2989 config['MAC'] = config.get('mac', '')
2990 if not config.has_key('type'):
2991 config['type'] = 'paravirtualised'
2992 if not config.has_key('device'):
2993 devid = config.get('id')
2994 if devid != None:
2995 config['device'] = 'eth%d' % devid
2996 else:
2997 config['device'] = ''
2999 if not config.has_key('network'):
3000 try:
3001 bridge = config.get('bridge', None)
3002 if bridge is None:
3003 from xen.util import Brctl
3004 if_to_br = dict([(i,b)
3005 for (b,ifs) in Brctl.get_state().items()
3006 for i in ifs])
3007 vifname = "vif%s.%s" % (self.getDomid(),
3008 config.get('id'))
3009 bridge = if_to_br.get(vifname, None)
3010 config['network'] = \
3011 XendNode.instance().bridge_to_network(
3012 config.get('bridge')).get_uuid()
3013 except Exception:
3014 log.exception('bridge_to_network')
3015 # Ignore this for now -- it may happen if the device
3016 # has been specified using the legacy methods, but at
3017 # some point we're going to have to figure out how to
3018 # handle that properly.
3020 config['MTU'] = 1500 # TODO
3022 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
3023 xennode = XendNode.instance()
3024 rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
3025 config['io_read_kbs'] = rx_bps/1024
3026 config['io_write_kbs'] = tx_bps/1024
3027 rx, tx = xennode.get_vif_stat(self.domid, devid)
3028 config['io_total_read_kbs'] = rx/1024
3029 config['io_total_write_kbs'] = tx/1024
3030 else:
3031 config['io_read_kbs'] = 0.0
3032 config['io_write_kbs'] = 0.0
3033 config['io_total_read_kbs'] = 0.0
3034 config['io_total_write_kbs'] = 0.0
3036 config['security_label'] = config.get('security_label', '')
3038 if dev_class == 'vbd':
3040 if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
3041 controller = self.getDeviceController(dev_class)
3042 devid, _1, _2 = controller.getDeviceDetails(config)
3043 xennode = XendNode.instance()
3044 rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
3045 config['io_read_kbs'] = rd_blkps
3046 config['io_write_kbs'] = wr_blkps
3047 else:
3048 config['io_read_kbs'] = 0.0
3049 config['io_write_kbs'] = 0.0
3051 config['VDI'] = config.get('VDI', '')
3052 config['device'] = config.get('dev', '')
3053 if ':' in config['device']:
3054 vbd_name, vbd_type = config['device'].split(':', 1)
3055 config['device'] = vbd_name
3056 if vbd_type == 'cdrom':
3057 config['type'] = XEN_API_VBD_TYPE[0]
3058 else:
3059 config['type'] = XEN_API_VBD_TYPE[1]
3061 config['driver'] = 'paravirtualised' # TODO
3062 config['image'] = config.get('uname', '')
3064 if config.get('mode', 'r') == 'r':
3065 config['mode'] = 'RO'
3066 else:
3067 config['mode'] = 'RW'
3069 if dev_class == 'vtpm':
3070 if not config.has_key('type'):
3071 config['type'] = 'paravirtualised' # TODO
3072 if not config.has_key('backend'):
3073 config['backend'] = "00000000-0000-0000-0000-000000000000"
3075 return config
3077 def get_dev_property(self, dev_class, dev_uuid, field):
3078 config = self.get_dev_xenapi_config(dev_class, dev_uuid)
3079 try:
3080 return config[field]
3081 except KeyError:
3082 raise XendError('Invalid property for device: %s' % field)
3084 def set_dev_property(self, dev_class, dev_uuid, field, value):
3085 self.info['devices'][dev_uuid][1][field] = value
3087 def get_vcpus_util(self):
3088 vcpu_util = {}
3089 xennode = XendNode.instance()
3090 if 'VCPUs_max' in self.info and self.domid != None:
3091 for i in range(0, self.info['VCPUs_max']):
3092 util = xennode.get_vcpu_util(self.domid, i)
3093 vcpu_util[str(i)] = util
3095 return vcpu_util
3097 def get_consoles(self):
3098 return self.info.get('console_refs', [])
3100 def get_vifs(self):
3101 return self.info.get('vif_refs', [])
3103 def get_vbds(self):
3104 return self.info.get('vbd_refs', [])
3106 def get_vtpms(self):
3107 return self.info.get('vtpm_refs', [])
3109 def create_vbd(self, xenapi_vbd, vdi_image_path):
3110 """Create a VBD using a VDI from XendStorageRepository.
3112 @param xenapi_vbd: vbd struct from the Xen API
3113 @param vdi_image_path: VDI UUID
3114 @rtype: string
3115 @return: uuid of the device
3116 """
3117 xenapi_vbd['image'] = vdi_image_path
3118 if vdi_image_path.startswith('tap'):
3119 dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
3120 else:
3121 dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
3123 if not dev_uuid:
3124 raise XendError('Failed to create device')
3126 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3127 XEN_API_VM_POWER_STATE_PAUSED):
3128 _, config = self.info['devices'][dev_uuid]
3130 if vdi_image_path.startswith('tap'):
3131 dev_control = self.getDeviceController('tap')
3132 else:
3133 dev_control = self.getDeviceController('vbd')
3135 try:
3136 devid = dev_control.createDevice(config)
3137 dev_control.waitForDevice(devid)
3138 self.info.device_update(dev_uuid,
3139 cfg_xenapi = {'devid': devid})
3140 except Exception, exn:
3141 log.exception(exn)
3142 del self.info['devices'][dev_uuid]
3143 self.info['vbd_refs'].remove(dev_uuid)
3144 raise
3146 return dev_uuid
3148 def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
3149 """Create a VBD using a VDI from XendStorageRepository.
3151 @param xenapi_vbd: vbd struct from the Xen API
3152 @param vdi_image_path: VDI UUID
3153 @rtype: string
3154 @return: uuid of the device
3155 """
3156 xenapi_vbd['image'] = vdi_image_path
3157 dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
3158 if not dev_uuid:
3159 raise XendError('Failed to create device')
3161 if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
3162 _, config = self.info['devices'][dev_uuid]
3163 config['devid'] = self.getDeviceController('tap').createDevice(config)
3165 return config['devid']
3167 def create_vif(self, xenapi_vif):
3168 """Create VIF device from the passed struct in Xen API format.
3170 @param xenapi_vif: Xen API VIF Struct.
3171 @rtype: string
3172 @return: UUID
3173 """
3174 dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
3175 if not dev_uuid:
3176 raise XendError('Failed to create device')
3178 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3179 XEN_API_VM_POWER_STATE_PAUSED):
3181 _, config = self.info['devices'][dev_uuid]
3182 dev_control = self.getDeviceController('vif')
3184 try:
3185 devid = dev_control.createDevice(config)
3186 dev_control.waitForDevice(devid)
3187 self.info.device_update(dev_uuid,
3188 cfg_xenapi = {'devid': devid})
3189 except Exception, exn:
3190 log.exception(exn)
3191 del self.info['devices'][dev_uuid]
3192 self.info['vif_refs'].remove(dev_uuid)
3193 raise
3195 return dev_uuid
3197 def create_vtpm(self, xenapi_vtpm):
3198 """Create a VTPM device from the passed struct in Xen API format.
3200 @return: uuid of the device
3201 @rtype: string
3202 """
3204 if self._stateGet() not in (DOM_STATE_HALTED,):
3205 raise VmError("Can only add vTPM to a halted domain.")
3206 if self.get_vtpms() != []:
3207 raise VmError('Domain already has a vTPM.')
3208 dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
3209 if not dev_uuid:
3210 raise XendError('Failed to create device')
3212 return dev_uuid
3214 def create_console(self, xenapi_console):
3215 """ Create a console device from a Xen API struct.
3217 @return: uuid of device
3218 @rtype: string
3219 """
3220 if self._stateGet() not in (DOM_STATE_HALTED,):
3221 raise VmError("Can only add console to a halted domain.")
3223 dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
3224 if not dev_uuid:
3225 raise XendError('Failed to create device')
3227 return dev_uuid
3229 def set_console_other_config(self, console_uuid, other_config):
3230 self.info.console_update(console_uuid, 'other_config', other_config)
3232 def destroy_device_by_uuid(self, dev_type, dev_uuid):
3233 if dev_uuid not in self.info['devices']:
3234 raise XendError('Device does not exist')
3236 try:
3237 if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
3238 XEN_API_VM_POWER_STATE_PAUSED):
3239 _, config = self.info['devices'][dev_uuid]
3240 devid = config.get('devid')
3241 if devid != None:
3242 self.getDeviceController(dev_type).destroyDevice(devid, force = False)
3243 else:
3244 raise XendError('Unable to get devid for device: %s:%s' %
3245 (dev_type, dev_uuid))
3246 finally:
3247 del self.info['devices'][dev_uuid]
3248 self.info['%s_refs' % dev_type].remove(dev_uuid)
3250 def destroy_vbd(self, dev_uuid):
3251 self.destroy_device_by_uuid('vbd', dev_uuid)
3253 def destroy_vif(self, dev_uuid):
3254 self.destroy_device_by_uuid('vif', dev_uuid)
3256 def destroy_vtpm(self, dev_uuid):
3257 self.destroy_device_by_uuid('vtpm', dev_uuid)
3259 def has_device(self, dev_class, dev_uuid):
3260 return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
3262 def __str__(self):
3263 return '<domain id=%s name=%s memory=%s state=%s>' % \
3264 (str(self.domid), self.info['name_label'],
3265 str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
3267 __repr__ = __str__