ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 7056:8874b5004ef4

Move ImageHandler registration into image.py from XendDomainInfo.py. Move
ImageHandler.create and findImageHandlerClass to the module level, and inline
addImageHandlerClass. Move ImageHandler.kernel, ramdisk, cmdline, and flags,
which seem inadvertently to have been declared as class variables, and make
them instance variables instead.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Sat Sep 24 20:36:15 2005 +0100 (2005-09-24)
parents ad0270abc9b9
children b5ab24152d56
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 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 string
28 import time
29 import threading
30 import errno
32 import xen.lowlevel.xc
33 from xen.util.blkif import blkdev_uname_to_file
35 from xen.xend.server.channel import EventChannel
37 from xen.xend import image
38 from xen.xend import sxp
39 from xen.xend.XendBootloader import bootloader
40 from xen.xend.XendLogging import log
41 from xen.xend.XendError import XendError, VmError
42 from xen.xend.XendRoot import get_component
44 from xen.xend.uuid import getUuid
45 from xen.xend.xenstore.xstransact import xstransact
46 from xen.xend.xenstore.xsutil import IntroduceDomain
48 """Shutdown code for poweroff."""
49 DOMAIN_POWEROFF = 0
51 """Shutdown code for reboot."""
52 DOMAIN_REBOOT = 1
54 """Shutdown code for suspend."""
55 DOMAIN_SUSPEND = 2
57 """Shutdown code for crash."""
58 DOMAIN_CRASH = 3
60 """Map shutdown codes to strings."""
61 shutdown_reasons = {
62 DOMAIN_POWEROFF: "poweroff",
63 DOMAIN_REBOOT : "reboot",
64 DOMAIN_SUSPEND : "suspend",
65 DOMAIN_CRASH : "crash",
66 }
68 RESTART_ALWAYS = 'always'
69 RESTART_ONREBOOT = 'onreboot'
70 RESTART_NEVER = 'never'
72 restart_modes = [
73 RESTART_ALWAYS,
74 RESTART_ONREBOOT,
75 RESTART_NEVER,
76 ]
78 STATE_RESTART_PENDING = 'pending'
79 STATE_RESTART_BOOTING = 'booting'
81 STATE_VM_OK = "ok"
82 STATE_VM_TERMINATED = "terminated"
83 STATE_VM_SUSPENDED = "suspended"
85 """Flag for a block device backend domain."""
86 SIF_BLK_BE_DOMAIN = (1<<4)
88 """Flag for a net device backend domain."""
89 SIF_NET_BE_DOMAIN = (1<<5)
91 """Flag for a TPM device backend domain."""
92 SIF_TPM_BE_DOMAIN = (1<<7)
95 xc = xen.lowlevel.xc.new()
98 def domain_exists(name):
99 # See comment in XendDomain constructor.
100 xd = get_component('xen.xend.XendDomain')
101 return xd.domain_lookup_by_name(name)
103 def shutdown_reason(code):
104 """Get a shutdown reason from a code.
106 @param code: shutdown code
107 @type code: int
108 @return: shutdown reason
109 @rtype: string
110 """
111 return shutdown_reasons.get(code, "?")
113 def dom_get(dom):
114 """Get info from xen for an existing domain.
116 @param dom: domain id
117 @return: info or None
118 """
119 try:
120 domlist = xc.domain_getinfo(dom, 1)
121 if domlist and dom == domlist[0]['dom']:
122 return domlist[0]
123 except Exception, err:
124 # ignore missing domain
125 log.exception("domain_getinfo(%d) failed, ignoring", dom)
126 return None
128 class XendDomainInfo:
129 """Virtual machine object."""
131 """Minimum time between domain restarts in seconds.
132 """
133 MINIMUM_RESTART_TIME = 20
136 def create(cls, dompath, config):
137 """Create a VM from a configuration.
139 @param dompath: The path to all domain information
140 @param config configuration
141 @raise: VmError for invalid configuration
142 """
144 log.debug("XendDomainInfo.create(%s, ...)", dompath)
146 vm = cls(getUuid(), dompath, cls.parseConfig(config))
147 vm.construct()
148 return vm
150 create = classmethod(create)
153 def recreate(cls, uuid, dompath, domid, info):
154 """Create the VM object for an existing domain.
156 @param dompath: The path to all domain information
157 @param info: domain info from xc
158 """
160 log.debug("XendDomainInfo.recreate(%s, %s, %s, %s)", uuid, dompath,
161 domid, info)
163 return cls(uuid, dompath, info, domid, True)
165 recreate = classmethod(recreate)
168 def restore(cls, dompath, config, uuid = None):
169 """Create a domain and a VM object to do a restore.
171 @param dompath: The path to all domain information
172 @param config: domain configuration
173 @param uuid: uuid to use
174 """
176 log.debug("XendDomainInfo.restore(%s, %s, %s)", dompath, config, uuid)
178 if not uuid:
179 uuid = getUuid()
181 try:
182 ssidref = int(sxp.child_value(config, 'ssidref'))
183 except TypeError, exn:
184 raise VmError('Invalid ssidref in config: %s' % exn)
186 log.debug('restoring with ssidref = %d' % ssidref)
188 vm = cls(uuid, dompath, cls.parseConfig(config),
189 xc.domain_create(ssidref = ssidref))
190 vm.clear_shutdown()
191 vm.create_channel()
192 vm.configure()
193 vm.exportToDB()
194 return vm
196 restore = classmethod(restore)
199 def parseConfig(cls, config):
200 def get_cfg(name, conv = None):
201 val = sxp.child_value(config, name)
203 if conv and not val is None:
204 try:
205 return conv(val)
206 except TypeError, exn:
207 raise VmError(
208 'Invalid setting %s = %s in configuration: %s' %
209 (name, val, str(exn)))
210 else:
211 return val
214 log.debug("parseConfig: config is %s" % str(config))
216 result = {}
217 imagecfg = "()"
219 result['name'] = get_cfg('name')
220 result['ssidref'] = get_cfg('ssidref', int)
221 result['memory'] = get_cfg('memory', int)
222 result['mem_kb'] = get_cfg('mem_kb', int)
223 result['maxmem'] = get_cfg('maxmem', int)
224 result['maxmem_kb'] = get_cfg('maxmem_kb', int)
225 result['cpu'] = get_cfg('cpu', int)
226 result['cpu_weight'] = get_cfg('cpu_weight', float)
227 result['bootloader'] = get_cfg('bootloader')
228 result['restart_mode'] = get_cfg('restart')
230 try:
231 imagecfg = get_cfg('image')
233 if imagecfg:
234 result['image'] = imagecfg
235 result['vcpus'] = int(sxp.child_value(imagecfg, 'vcpus',
236 1))
237 else:
238 result['vcpus'] = 1
239 except TypeError, exn:
240 raise VmError(
241 'Invalid configuration setting: vcpus = %s: %s' %
242 (sxp.child_value(imagecfg, 'vcpus', 1),
243 str(exn)))
245 result['backend'] = []
246 for c in sxp.children(config, 'backend'):
247 result['backend'].append(sxp.name(sxp.child0(c)))
249 result['device'] = []
250 for d in sxp.children(config, 'device'):
251 c = sxp.child0(d)
252 result['device'].append((sxp.name(c), c))
254 log.debug("parseConfig: result is %s" % str(result))
255 return result
258 parseConfig = classmethod(parseConfig)
261 def __init__(self, uuid, parentpath, info, domid = None, augment = False):
263 self.uuid = uuid
264 self.info = info
266 self.path = parentpath + "/" + uuid
268 if domid:
269 self.domid = domid
270 elif 'dom' in info:
271 self.domid = int(info['dom'])
272 else:
273 self.domid = None
275 if augment:
276 self.augmentInfo()
278 self.validateInfo()
280 self.image = None
282 self.store_channel = None
283 self.store_mfn = None
284 self.console_channel = None
285 self.console_mfn = None
287 #todo: state: running, suspended
288 self.state = STATE_VM_OK
289 self.state_updated = threading.Condition()
290 self.shutdown_pending = None
292 self.restart_state = None
293 self.restart_time = None
294 self.restart_count = 0
296 self.writeVm("uuid", self.uuid)
297 self.storeDom("vm", self.path)
300 def augmentInfo(self):
301 def useIfNeeded(name, val):
302 if not self.infoIsSet(name) and val is not None:
303 self.info[name] = val
305 params = (("name", str),
306 ("start-time", float))
308 from_store = self.gatherVm(*params)
310 map(lambda x, y: useIfNeeded(x[0], y), params, from_store)
313 def validateInfo(self):
314 """Validate and normalise the info block. This has either been parsed
315 by parseConfig, or received from xc through recreate.
316 """
317 def defaultInfo(name, val):
318 if not self.infoIsSet(name):
319 self.info[name] = val()
321 try:
322 defaultInfo('name', lambda: "Domain-%d" % self.domid)
323 defaultInfo('restart_mode', lambda: RESTART_ONREBOOT)
324 defaultInfo('cpu_weight', lambda: 1.0)
325 defaultInfo('bootloader', lambda: None)
326 defaultInfo('backend', lambda: [])
327 defaultInfo('device', lambda: [])
329 self.check_name(self.info['name'])
331 # Internally, we keep only maxmem_KiB, and not maxmem or maxmem_kb
332 # (which come from outside, and are in MiB and KiB respectively).
333 # This means that any maxmem or maxmem_kb settings here have come
334 # from outside, and maxmem_KiB must be updated to reflect them.
335 # If we have both maxmem and maxmem_kb and these are not
336 # consistent, then this is an error, as we've no way to tell which
337 # one takes precedence.
339 # Exactly the same thing applies to memory_KiB, memory, and
340 # mem_kb.
342 def discard_negatives(name):
343 if self.infoIsSet(name) and self.info[name] <= 0:
344 del self.info[name]
346 def valid_KiB_(mb_name, kb_name):
347 discard_negatives(kb_name)
348 discard_negatives(mb_name)
350 if self.infoIsSet(kb_name):
351 if self.infoIsSet(mb_name):
352 mb = self.info[mb_name]
353 kb = self.info[kb_name]
354 if mb * 1024 == kb:
355 return kb
356 else:
357 raise VmError(
358 'Inconsistent %s / %s settings: %s / %s' %
359 (mb_name, kb_name, mb, kb))
360 else:
361 return self.info[kb_name]
362 elif self.infoIsSet(mb_name):
363 return self.info[mb_name] * 1024
364 else:
365 return None
367 def valid_KiB(mb_name, kb_name):
368 result = valid_KiB_(mb_name, kb_name)
369 if result <= 0:
370 raise VmError('Invalid %s / %s: %s' %
371 (mb_name, kb_name, result))
372 else:
373 return result
375 def delIf(name):
376 if name in self.info:
377 del self.info[name]
379 self.info['memory_KiB'] = valid_KiB('memory', 'mem_kb')
380 delIf('memory')
381 delIf('mem_kb')
382 self.info['maxmem_KiB'] = valid_KiB_('maxmem', 'maxmem_kb')
383 delIf('maxmem')
384 delIf('maxmem_kb')
386 if not self.info['maxmem_KiB']:
387 self.info['maxmem_KiB'] = 1 << 30
389 if self.info['maxmem_KiB'] > self.info['memory_KiB']:
390 self.info['maxmem_KiB'] = self.info['memory_KiB']
392 # Validate the given backend names.
393 for s in self.info['backend']:
394 if s not in backendFlags:
395 raise VmError('Invalid backend type: %s' % s)
397 for (n, c) in self.info['device']:
398 if not n or not c or n not in controllerClasses:
399 raise VmError('invalid device (%s, %s)' %
400 (str(n), str(c)))
402 if self.info['restart_mode'] not in restart_modes:
403 raise VmError('invalid restart mode: ' +
404 str(self.info['restart_mode']))
406 if 'cpumap' not in self.info:
407 if [self.info['vcpus'] == 1]:
408 self.info['cpumap'] = [1];
409 else:
410 raise VmError('Cannot create CPU map')
412 except KeyError, exn:
413 log.exception(exn)
414 raise VmError('Unspecified domain detail: %s' % str(exn))
417 def readVm(self, *args):
418 return xstransact.Read(self.path, *args)
420 def writeVm(self, *args):
421 return xstransact.Write(self.path, *args)
423 def removeVm(self, *args):
424 return xstransact.Remove(self.path, *args)
426 def gatherVm(self, *args):
427 return xstransact.Gather(self.path, *args)
429 def storeVm(self, *args):
430 return xstransact.Store(self.path, *args)
432 def readDom(self, *args):
433 return xstransact.Read(self.path, *args)
435 def writeDom(self, *args):
436 return xstransact.Write(self.path, *args)
438 def removeDom(self, *args):
439 return xstransact.Remove(self.path, *args)
441 def gatherDom(self, *args):
442 return xstransact.Gather(self.path, *args)
444 def storeDom(self, *args):
445 return xstransact.Store(self.path, *args)
448 def exportToDB(self):
449 to_store = {
450 'domid': str(self.domid),
451 'uuid': self.uuid,
453 'restart_time': str(self.restart_time),
455 'xend/state': self.state,
456 'xend/restart_count': str(self.restart_count),
457 'xend/restart_mode': str(self.info['restart_mode']),
459 'memory/target': str(self.info['memory_KiB'])
460 }
462 for (k, v) in self.info.items():
463 to_store[k] = str(v)
465 log.debug("Storing %s" % str(to_store))
467 self.writeVm(to_store)
470 def setDomid(self, domid):
471 """Set the domain id.
473 @param dom: domain id
474 """
475 self.domid = domid
476 self.storeDom("domid", self.domid)
478 def getDomid(self):
479 return self.domid
481 def setName(self, name):
482 self.check_name(name)
483 self.info['name'] = name
484 self.storeVm("name", name)
486 def getName(self):
487 return self.info['name']
489 def getPath(self):
490 return self.path
492 def getUuid(self):
493 return self.uuid
495 def getVCpuCount(self):
496 return self.info['vcpus']
498 def getSsidref(self):
499 return self.info['ssidref']
501 def getMemoryTarget(self):
502 """Get this domain's target memory size, in KiB."""
503 return self.info['memory_KiB']
505 def setStoreRef(self, ref):
506 self.store_mfn = ref
507 self.storeDom("store/ring-ref", ref)
510 def getBackendFlags(self):
511 return reduce(lambda x, y: x | backendFlags[y],
512 self.info['backend'], 0)
515 def closeStoreChannel(self):
516 """Close the store channel, if any. Nothrow guarantee."""
518 try:
519 if self.store_channel:
520 try:
521 self.store_channel.close()
522 self.removeDom("store/port")
523 finally:
524 self.store_channel = None
525 except Exception, exn:
526 log.exception(exn)
529 def setConsoleRef(self, ref):
530 self.console_mfn = ref
531 self.storeDom("console/ring-ref", ref)
534 def setMemoryTarget(self, target):
535 """Set the memory target of this domain.
536 @param target In KiB.
537 """
538 self.info['memory_KiB'] = target
539 self.storeDom("memory/target", target)
542 def update(self, info = None):
543 """Update with info from xc.domain_getinfo().
544 """
546 log.debug("XendDomainInfo.update(%s) on domain %d", info, self.domid)
548 if not info:
549 info = dom_get(self.domid)
550 if not info:
551 return
553 self.info.update(info)
554 self.validateInfo()
556 log.debug("XendDomainInfo.update done on domain %d: %s", self.domid,
557 self.info)
560 def state_set(self, state):
561 self.state_updated.acquire()
562 if self.state != state:
563 self.state = state
564 self.state_updated.notifyAll()
565 self.state_updated.release()
566 self.exportToDB()
568 def state_wait(self, state):
569 self.state_updated.acquire()
570 while self.state != state:
571 self.state_updated.wait()
572 self.state_updated.release()
574 def __str__(self):
575 s = "<domain"
576 s += " id=" + str(self.domid)
577 s += " name=" + self.info['name']
578 s += " memory=" + str(self.info['memory_KiB'] / 1024)
579 s += " ssidref=" + str(self.info['ssidref'])
580 s += ">"
581 return s
583 __repr__ = __str__
586 def getDeviceController(self, name):
587 if name not in controllerClasses:
588 raise XendError("unknown device type: " + str(name))
590 return controllerClasses[name](self)
593 def createDevice(self, deviceClass, devconfig):
594 return self.getDeviceController(deviceClass).createDevice(devconfig)
597 def configureDevice(self, deviceClass, devid, devconfig):
598 return self.getDeviceController(deviceClass).configureDevice(
599 devid, devconfig)
602 def destroyDevice(self, deviceClass, devid):
603 return self.getDeviceController(deviceClass).destroyDevice(devid)
606 def sxpr(self):
607 sxpr = ['domain',
608 ['domid', self.domid],
609 ['name', self.info['name']],
610 ['memory', self.info['memory_KiB'] / 1024],
611 ['ssidref', self.info['ssidref']]]
612 if self.uuid:
613 sxpr.append(['uuid', self.uuid])
614 if self.info:
615 sxpr.append(['maxmem', self.info['maxmem_KiB'] / 1024])
617 if self.infoIsSet('device'):
618 for (_, c) in self.info['device']:
619 sxpr.append(['device', c])
621 def stateChar(name):
622 if name in self.info:
623 if self.info[name]:
624 return name[0]
625 else:
626 return '-'
627 else:
628 return '?'
630 state = reduce(
631 lambda x, y: x + y,
632 map(stateChar,
633 ['running', 'blocked', 'paused', 'shutdown', 'crashed']))
635 sxpr.append(['state', state])
636 if self.infoIsSet('shutdown'):
637 reason = shutdown_reason(self.info['shutdown_reason'])
638 sxpr.append(['shutdown_reason', reason])
639 if self.infoIsSet('cpu_time'):
640 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
641 sxpr.append(['vcpus', self.info['vcpus']])
642 sxpr.append(['cpumap', self.info['cpumap']])
643 if self.infoIsSet('vcpu_to_cpu'):
644 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
645 # build a string, using '|' to separate items, show only up
646 # to number of vcpus in domain, and trim the trailing '|'
647 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
648 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
650 if self.infoIsSet('start_time'):
651 up_time = time.time() - self.info['start_time']
652 sxpr.append(['up_time', str(up_time) ])
653 sxpr.append(['start_time', str(self.info['start_time']) ])
655 if self.store_channel:
656 sxpr.append(self.store_channel.sxpr())
657 if self.store_mfn:
658 sxpr.append(['store_mfn', self.store_mfn])
659 if self.console_channel:
660 sxpr.append(['console_channel', self.console_channel.sxpr()])
661 if self.console_mfn:
662 sxpr.append(['console_mfn', self.console_mfn])
663 if self.restart_count:
664 sxpr.append(['restart_count', self.restart_count])
665 if self.restart_state:
666 sxpr.append(['restart_state', self.restart_state])
667 if self.restart_time:
668 sxpr.append(['restart_time', str(self.restart_time)])
669 return sxpr
671 def check_name(self, name):
672 """Check if a vm name is valid. Valid names contain alphabetic characters,
673 digits, or characters in '_-.:/+'.
674 The same name cannot be used for more than one vm at the same time.
676 @param name: name
677 @raise: VmError if invalid
678 """
679 if name is None or name == '':
680 raise VmError('missing vm name')
681 for c in name:
682 if c in string.digits: continue
683 if c in '_-.:/+': continue
684 if c in string.ascii_letters: continue
685 raise VmError('invalid vm name')
686 dominfo = domain_exists(name)
687 # When creating or rebooting, a domain with my name should not exist.
688 # When restoring, a domain with my name will exist, but it should have
689 # my domain id.
690 if not dominfo:
691 return
692 if dominfo.is_terminated():
693 return
694 if self.domid is None:
695 raise VmError("VM name '%s' already in use by domain %d" %
696 (name, dominfo.domid))
697 if dominfo.domid != self.domid:
698 raise VmError("VM name '%s' is used in both domains %d and %d" %
699 (name, self.domid, dominfo.domid))
702 def construct(self):
703 """Construct the vm instance from its configuration.
705 @param config: configuration
706 @raise: VmError on error
707 """
708 # todo - add support for scheduling params?
709 try:
710 self.initDomain()
712 # Create domain devices.
713 self.construct_image()
714 self.configure()
715 self.exportToDB()
716 except Exception, ex:
717 # Catch errors, cleanup and re-raise.
718 print 'Domain construction error:', ex
719 import traceback
720 traceback.print_exc()
721 self.destroy()
722 raise
725 def initDomain(self):
726 log.debug('XendDomainInfo.initDomain: %s %s %s %s)',
727 str(self.domid),
728 str(self.info['memory_KiB']),
729 str(self.info['ssidref']),
730 str(self.info['cpu_weight']))
732 self.domid = xc.domain_create(dom = self.domid or 0,
733 ssidref = self.info['ssidref'])
735 if 'image' not in self.info:
736 raise VmError('Missing image in configuration')
738 self.image = image.create(self,
739 self.info['image'],
740 self.info['device'])
742 if self.domid <= 0:
743 raise VmError('Creating domain failed: name=%s' %
744 self.info['name'])
746 if self.info['bootloader']:
747 self.image.handleBootloading()
749 xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
750 m = self.image.getDomainMemory(self.info['memory_KiB'])
751 xc.domain_setmaxmem(self.domid, m)
752 xc.domain_memory_increase_reservation(self.domid, m, 0, 0)
754 cpu = self.info['cpu']
755 if cpu is not None and cpu != -1:
756 xc.domain_pincpu(self.domid, 0, 1 << cpu)
758 self.info['start_time'] = time.time()
760 log.debug('init_domain> Created domain=%d name=%s memory=%d',
761 self.domid, self.info['name'], self.info['memory_KiB'])
764 def configure_vcpus(self, vcpus):
765 d = {}
766 for v in range(0, vcpus):
767 d["cpu/%d/availability" % v] = "online"
768 self.writeVm(d)
770 def construct_image(self):
771 """Construct the boot image for the domain.
772 """
773 self.create_channel()
774 self.image.createImage()
775 self.exportToDB()
776 if self.store_channel and self.store_mfn >= 0:
777 IntroduceDomain(self.domid, self.store_mfn,
778 self.store_channel.port1, self.path)
779 # get the configured value of vcpus and update store
780 self.configure_vcpus(self.info['vcpus'])
783 def delete(self):
784 """Delete the vm's db.
785 """
786 try:
787 xstransact.Remove(self.path, 'domid')
788 except Exception, ex:
789 log.warning("error in domain db delete: %s", ex)
792 def destroy_domain(self):
793 """Destroy the vm's domain.
794 The domain will not finally go away unless all vm
795 devices have been released.
796 """
797 if self.domid is None:
798 return
799 try:
800 xc.domain_destroy(dom=self.domid)
801 except Exception, err:
802 log.exception("Domain destroy failed: %s", self.info['name'])
804 def cleanup(self):
805 """Cleanup vm resources: release devices.
806 """
807 self.state = STATE_VM_TERMINATED
808 self.release_devices()
809 self.closeStoreChannel()
810 if self.console_channel:
811 # notify processes using this console?
812 try:
813 self.console_channel.close()
814 self.console_channel = None
815 except:
816 pass
817 if self.image:
818 try:
819 self.image.destroy()
820 self.image = None
821 except:
822 pass
824 def destroy(self):
825 """Cleanup vm and destroy domain.
826 """
828 log.debug("XendDomainInfo.destroy")
830 self.destroy_domain()
831 self.cleanup()
832 self.exportToDB()
833 return 0
835 def is_terminated(self):
836 """Check if a domain has been terminated.
837 """
838 return self.state == STATE_VM_TERMINATED
840 def release_devices(self):
841 """Release all vm devices.
842 """
844 while True:
845 t = xstransact("%s/device" % self.path)
846 for n in controllerClasses.keys():
847 for d in t.list(n):
848 try:
849 t.remove(d)
850 except ex:
851 # Log and swallow any exceptions in removal --
852 # there's nothing more we can do.
853 log.exception(
854 "Device release failed: %s; %s; %s; %s" %
855 (self.info['name'], n, d, str(ex)))
856 if t.commit():
857 break
859 def eventChannel(self, path=None):
860 """Create an event channel to the domain.
862 @param path under which port is stored in db
863 """
864 port = 0
865 if path:
866 try:
867 port = int(self.readDom(path))
868 except:
869 # if anything goes wrong, assume the port was not yet set
870 pass
871 ret = EventChannel.interdomain(0, self.domid, port1=port, port2=0)
872 self.storeDom(path, ret.port1)
873 return ret
875 def create_channel(self):
876 """Create the channels to the domain.
877 """
878 self.store_channel = self.eventChannel("store/port")
879 self.console_channel = self.eventChannel("console/port")
881 def create_configured_devices(self):
882 for (n, c) in self.info['device']:
883 self.createDevice(n, c)
886 def create_devices(self):
887 """Create the devices for a vm.
889 @raise: VmError for invalid devices
890 """
891 if not self.rebooting():
892 self.create_configured_devices()
893 if self.image:
894 self.image.createDeviceModel()
896 def device_create(self, dev_config):
897 """Create a new device.
899 @param dev_config: device configuration
900 """
901 dev_type = sxp.name(dev_config)
902 devid = self.createDevice(dev_type, dev_config)
903 # self.config.append(['device', dev.getConfig()])
904 return self.getDeviceController(dev_type).sxpr(devid)
907 def device_configure(self, dev_config, devid):
908 """Configure an existing device.
909 @param dev_config: device configuration
910 @param devid: device id
911 """
912 deviceClass = sxp.name(dev_config)
913 self.configureDevice(deviceClass, devid, dev_config)
916 def restart_needed(self, reason):
917 """Determine if the vm needs to be restarted when shutdown
918 for the given reason.
920 @param reason: shutdown reason
921 @return True if needs restart, False otherwise
922 """
923 if self.info['restart_mode'] == RESTART_NEVER:
924 return False
925 if self.info['restart_mode'] == RESTART_ALWAYS:
926 return True
927 if self.info['restart_mode'] == RESTART_ONREBOOT:
928 return reason == 'reboot'
929 return False
931 def restart_cancel(self):
932 """Cancel a vm restart.
933 """
934 self.restart_state = None
936 def restarting(self):
937 """Put the vm into restart mode.
938 """
939 self.restart_state = STATE_RESTART_PENDING
941 def restart_pending(self):
942 """Test if the vm has a pending restart.
943 """
944 return self.restart_state == STATE_RESTART_PENDING
946 def rebooting(self):
947 return self.restart_state == STATE_RESTART_BOOTING
949 def restart_check(self):
950 """Check if domain restart is OK.
951 To prevent restart loops, raise an error if it is
952 less than MINIMUM_RESTART_TIME seconds since the last restart.
953 """
954 tnow = time.time()
955 if self.restart_time is not None:
956 tdelta = tnow - self.restart_time
957 if tdelta < self.MINIMUM_RESTART_TIME:
958 self.restart_cancel()
959 msg = 'VM %s restarting too fast' % self.info['name']
960 log.error(msg)
961 raise VmError(msg)
962 self.restart_time = tnow
963 self.restart_count += 1
965 def restart(self):
966 """Restart the domain after it has exited.
967 Reuses the domain id
969 """
970 try:
971 self.clear_shutdown()
972 self.state = STATE_VM_OK
973 self.shutdown_pending = None
974 self.restart_check()
975 self.exportToDB()
976 self.restart_state = STATE_RESTART_BOOTING
977 self.configure_bootloader()
978 self.construct()
979 self.exportToDB()
980 finally:
981 self.restart_state = None
983 def configure_bootloader(self):
984 if not self.info['bootloader']:
985 return
986 # if we're restarting with a bootloader, we need to run it
987 # FIXME: this assumes the disk is the first device and
988 # that we're booting from the first disk
989 blcfg = None
990 # FIXME: this assumes that we want to use the first disk
991 dev = sxp.child_value(self.config, "device")
992 if dev:
993 disk = sxp.child_value(dev, "uname")
994 fn = blkdev_uname_to_file(disk)
995 blcfg = bootloader(self.info['bootloader'], fn, 1, self.info['vcpus'])
996 if blcfg is None:
997 msg = "Had a bootloader specified, but can't find disk"
998 log.error(msg)
999 raise VmError(msg)
1000 self.config = sxp.merge(['vm', ['image', blcfg]], self.config)
1003 def configure(self):
1004 """Configure a vm.
1006 """
1007 self.configure_maxmem()
1008 self.create_devices()
1011 def configure_maxmem(self):
1012 xc.domain_setmaxmem(self.domid, maxmem_kb = self.info['maxmem_KiB'])
1015 def vcpu_hotplug(self, vcpu, state):
1016 """Disable or enable VCPU in domain.
1017 """
1018 if vcpu > self.info['vcpus']:
1019 log.error("Invalid VCPU %d" % vcpu)
1020 return
1021 if int(state) == 0:
1022 availability = "offline"
1023 else:
1024 availability = "online"
1025 self.storeVm("cpu/%d/availability" % vcpu, availability)
1027 def shutdown(self, reason):
1028 if not reason in shutdown_reasons.values():
1029 raise XendError('invalid reason:' + reason)
1030 self.storeVm("control/shutdown", reason)
1031 if not reason in ['suspend']:
1032 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1034 def clear_shutdown(self):
1035 self.removeVm("control/shutdown")
1037 def send_sysrq(self, key=0):
1038 self.storeVm("control/sysrq", '%c' % key)
1040 def shutdown_time_left(self, timeout):
1041 if not self.shutdown_pending:
1042 return 0
1043 return timeout - (time.time() - self.shutdown_pending['start'])
1045 def dom0_init_store(self):
1046 if not self.store_channel:
1047 self.store_channel = self.eventChannel("store/port")
1048 if not self.store_channel:
1049 return
1050 ref = xc.init_store(self.store_channel.port2)
1051 if ref and ref >= 0:
1052 self.setStoreRef(ref)
1053 try:
1054 IntroduceDomain(self.domid, ref, self.store_channel.port1,
1055 self.path)
1056 except RuntimeError, ex:
1057 if ex.args[0] == errno.EISCONN:
1058 pass
1059 else:
1060 raise
1061 # get run-time value of vcpus and update store
1062 self.configure_vcpus(dom_get(self.domid)['vcpus'])
1064 def dom0_enforce_vcpus(self):
1065 dom = 0
1066 # get max number of vcpus to use for dom0 from config
1067 from xen.xend import XendRoot
1068 xroot = XendRoot.instance()
1069 target = int(xroot.get_dom0_vcpus())
1070 log.debug("number of vcpus to use is %d" % (target))
1072 # target = 0 means use all processors
1073 if target > 0:
1074 # count the number of online vcpus (cpu values in v2c map >= 0)
1075 vcpu_to_cpu = dom_get(dom)['vcpu_to_cpu']
1076 vcpus_online = len(filter(lambda x: x >= 0, vcpu_to_cpu))
1077 log.debug("found %d vcpus online" % (vcpus_online))
1079 # disable any extra vcpus that are online over the requested target
1080 for vcpu in range(target, vcpus_online):
1081 log.info("enforcement is disabling DOM%d VCPU%d" % (dom, vcpu))
1082 self.vcpu_hotplug(vcpu, 0)
1085 def infoIsSet(self, name):
1086 return name in self.info and self.info[name] is not None
1089 #============================================================================
1090 # Register device controllers and their device config types.
1092 """A map from device-class names to the subclass of DevController that
1093 implements the device control specific to that device-class."""
1094 controllerClasses = {}
1097 """A map of backend names and the corresponding flag."""
1098 backendFlags = {}
1101 def addControllerClass(device_class, backend_name, backend_flag, cls):
1102 """Register a subclass of DevController to handle the named device-class.
1104 @param backend_flag One of the SIF_XYZ_BE_DOMAIN constants, or None if
1105 no flag is to be set.
1106 """
1107 cls.deviceClass = device_class
1108 backendFlags[backend_name] = backend_flag
1109 controllerClasses[device_class] = cls
1112 from xen.xend.server import blkif, netif, tpmif, pciif, usbif
1113 addControllerClass('vbd', 'blkif', SIF_BLK_BE_DOMAIN, blkif.BlkifController)
1114 addControllerClass('vif', 'netif', SIF_NET_BE_DOMAIN, netif.NetifController)
1115 addControllerClass('vtpm', 'tpmif', SIF_TPM_BE_DOMAIN, tpmif.TPMifController)
1116 addControllerClass('pci', 'pciif', None, pciif.PciController)
1117 addControllerClass('usb', 'usbif', None, usbif.UsbifController)