ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6953:8f9dfc5fb51c

Add and use XendDomainInfo.getBackendFlags to decouple image.py from the
internals of XendDomainInfo, in preparation for changing the latter.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Sun Sep 18 22:42:02 2005 +0100 (2005-09-18)
parents f7a09745ca56
children 308260e5868c 951c8ee275b7
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 #============================================================================
18 """Representation of a single domain.
19 Includes support for domain construction, using
20 open-ended configurations.
22 Author: Mike Wray <mike.wray@hp.com>
24 """
26 import string
27 import time
28 import threading
29 import errno
31 import xen.lowlevel.xc
32 from xen.util.blkif import blkdev_uname_to_file
34 from xen.xend.server import SrvDaemon
35 from xen.xend.server.channel import EventChannel
37 from xen.xend import sxp
38 from xen.xend.PrettyPrint import prettyprintstring
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 import DBVar
46 from xen.xend.xenstore.xstransact import xstransact
47 from xen.xend.xenstore.xsutil import IntroduceDomain
49 """Shutdown code for poweroff."""
50 DOMAIN_POWEROFF = 0
52 """Shutdown code for reboot."""
53 DOMAIN_REBOOT = 1
55 """Shutdown code for suspend."""
56 DOMAIN_SUSPEND = 2
58 """Shutdown code for crash."""
59 DOMAIN_CRASH = 3
61 """Map shutdown codes to strings."""
62 shutdown_reasons = {
63 DOMAIN_POWEROFF: "poweroff",
64 DOMAIN_REBOOT : "reboot",
65 DOMAIN_SUSPEND : "suspend",
66 DOMAIN_CRASH : "crash",
67 }
69 RESTART_ALWAYS = 'always'
70 RESTART_ONREBOOT = 'onreboot'
71 RESTART_NEVER = 'never'
73 restart_modes = [
74 RESTART_ALWAYS,
75 RESTART_ONREBOOT,
76 RESTART_NEVER,
77 ]
79 STATE_RESTART_PENDING = 'pending'
80 STATE_RESTART_BOOTING = 'booting'
82 STATE_VM_OK = "ok"
83 STATE_VM_TERMINATED = "terminated"
84 STATE_VM_SUSPENDED = "suspended"
86 """Flag for a block device backend domain."""
87 SIF_BLK_BE_DOMAIN = (1<<4)
89 """Flag for a net device backend domain."""
90 SIF_NET_BE_DOMAIN = (1<<5)
92 """Flag for a TPM device backend domain."""
93 SIF_TPM_BE_DOMAIN = (1<<7)
96 xc = xen.lowlevel.xc.new()
99 xend = SrvDaemon.instance()
102 def domain_exists(name):
103 # See comment in XendDomain constructor.
104 xd = get_component('xen.xend.XendDomain')
105 return xd.domain_lookup_by_name(name)
107 def shutdown_reason(code):
108 """Get a shutdown reason from a code.
110 @param code: shutdown code
111 @type code: int
112 @return: shutdown reason
113 @rtype: string
114 """
115 return shutdown_reasons.get(code, "?")
117 def dom_get(dom):
118 """Get info from xen for an existing domain.
120 @param dom: domain id
121 @return: info or None
122 """
123 try:
124 domlist = xc.domain_getinfo(dom, 1)
125 if domlist and dom == domlist[0]['dom']:
126 return domlist[0]
127 except Exception, err:
128 # ignore missing domain
129 log.exception("domain_getinfo(%d) failed, ignoring", dom)
130 return None
132 class XendDomainInfo:
133 """Virtual machine object."""
135 """Minimum time between domain restarts in seconds.
136 """
137 MINIMUM_RESTART_TIME = 20
139 def create(cls, parentdb, config):
140 """Create a VM from a configuration.
142 @param parentdb: parent db
143 @param config configuration
144 @raise: VmError for invalid configuration
145 """
146 uuid = getUuid()
147 db = parentdb.addChild("%s/xend" % uuid)
148 path = parentdb.getPath()
149 vm = cls(uuid, path, db)
150 vm.construct(config)
151 vm.saveToDB(sync=True)
153 return vm
155 create = classmethod(create)
157 def recreate(cls, uuid, path, domid, db, info):
158 """Create the VM object for an existing domain.
160 @param db: domain db
161 @param info: domain info from xc
162 """
163 vm = cls(uuid, path, db)
164 vm.setDomid(domid)
165 vm.name, vm.start_time = vm.gatherVm(("name", str),
166 ("start-time", float))
167 try:
168 db.readDB()
169 except: pass
170 vm.importFromDB()
171 config = vm.config
172 log.debug('info=' + str(info))
173 log.debug('config=' + prettyprintstring(config))
175 vm.memory = info['mem_kb'] / 1024
176 vm.target = info['mem_kb'] * 1024
178 if config:
179 try:
180 vm.recreate = True
181 vm.construct(config)
182 finally:
183 vm.recreate = False
184 else:
185 vm.setName("Domain-%d" % domid)
187 vm.exportToDB(save=True)
188 return vm
190 recreate = classmethod(recreate)
192 def restore(cls, parentdb, config, uuid=None):
193 """Create a domain and a VM object to do a restore.
195 @param parentdb: parent db
196 @param config: domain configuration
197 @param uuid: uuid to use
198 """
199 if not uuid:
200 uuid = getUuid()
201 db = parentdb.addChild("%s/xend" % uuid)
202 path = parentdb.getPath()
203 vm = cls(uuid, path, db)
204 ssidref = int(sxp.child_value(config, 'ssidref'))
205 log.debug('restoring with ssidref='+str(ssidref))
206 id = xc.domain_create(ssidref = ssidref)
207 vm.setDomid(id)
208 vm.clear_shutdown()
209 try:
210 vm.restore = True
211 vm.construct(config)
212 finally:
213 vm.restore = False
214 vm.exportToDB(save=True, sync=True)
215 return vm
217 restore = classmethod(restore)
219 __exports__ = [
220 DBVar('config', ty='sxpr'),
221 DBVar('state', ty='str'),
222 DBVar('restart_mode', ty='str'),
223 DBVar('restart_state', ty='str'),
224 DBVar('restart_time', ty='float'),
225 DBVar('restart_count', ty='int'),
226 ]
228 def __init__(self, uuid, path, db):
229 self.uuid = uuid
230 self.path = path + "/" + uuid
232 self.db = db
234 self.recreate = 0
235 self.restore = 0
237 self.config = None
238 self.domid = None
239 self.cpu_weight = 1
240 self.start_time = None
241 self.name = None
242 self.memory = None
243 self.ssidref = None
244 self.image = None
246 self.target = None
248 self.store_channel = None
249 self.store_mfn = None
250 self.console_channel = None
251 self.console_mfn = None
253 self.info = None
254 self.backend_flags = 0
256 #todo: state: running, suspended
257 self.state = STATE_VM_OK
258 self.state_updated = threading.Condition()
259 self.shutdown_pending = None
261 #todo: set to migrate info if migrating
262 self.migrate = None
264 self.restart_mode = RESTART_ONREBOOT
265 self.restart_state = None
266 self.restart_time = None
267 self.restart_count = 0
269 self.vcpus = 1
270 self.bootloader = None
272 self.writeVm("uuid", self.uuid)
273 self.storeDom("vm", self.path)
275 def readVm(self, *args):
276 return xstransact.Read(self.path, *args)
278 def writeVm(self, *args):
279 return xstransact.Write(self.path, *args)
281 def removeVm(self, *args):
282 return xstransact.Remove(self.path, *args)
284 def gatherVm(self, *args):
285 return xstransact.Gather(self.path, *args)
287 def storeVm(self, *args):
288 return xstransact.Store(self.path, *args)
290 def readDom(self, *args):
291 return xstransact.Read(self.path, *args)
293 def writeDom(self, *args):
294 return xstransact.Write(self.path, *args)
296 def removeDom(self, *args):
297 return xstransact.Remove(self.path, *args)
299 def gatherDom(self, *args):
300 return xstransact.Gather(self.path, *args)
302 def storeDom(self, *args):
303 return xstransact.Store(self.path, *args)
305 def setDB(self, db):
306 self.db = db
308 def saveToDB(self, save=False, sync=False):
309 self.db.saveDB(save=save, sync=sync)
311 def exportToDB(self, save=False, sync=False):
312 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
314 def importFromDB(self):
315 self.db.importFromDB(self, fields=self.__exports__)
316 self.store_channel = self.eventChannel("store/port")
318 def setDomid(self, domid):
319 """Set the domain id.
321 @param dom: domain id
322 """
323 self.domid = domid
324 self.storeDom("domid", self.domid)
326 def getDomain(self):
327 return self.domid
329 def setName(self, name):
330 self.name = name
331 self.storeVm("name", name)
333 def getName(self):
334 return self.name
336 def getPath(self):
337 return self.path
339 def getUuid(self):
340 return self.uuid
342 def getVCpuCount(self):
343 return self.vcpus
345 def getSsidref(self):
346 return self.ssidref
348 def getMemoryTarget(self):
349 """Get this domain's target memory size, in MiB."""
350 return self.memory
352 def setStoreRef(self, ref):
353 self.store_mfn = ref
354 self.storeDom("store/ring-ref", ref)
357 def getBackendFlags(self):
358 return self.backend_flags
361 def closeStoreChannel(self):
362 """Close the store channel, if any. Nothrow guarantee."""
364 try:
365 if self.store_channel:
366 try:
367 self.store_channel.close()
368 self.removeDom("store/port")
369 finally:
370 self.store_channel = None
371 except Exception, exn:
372 log.exception(exn)
375 def setConsoleRef(self, ref):
376 self.console_mfn = ref
377 self.storeDom("console/ring-ref", ref)
379 def setMemoryTarget(self, target):
380 self.storeDom("memory/target", target)
382 def update(self, info=None):
383 """Update with info from xc.domain_getinfo().
384 """
385 if info:
386 self.info = info
387 else:
388 di = dom_get(self.domid)
389 if not di:
390 return
391 self.info = di
392 self.memory = self.info['mem_kb'] / 1024
393 self.ssidref = self.info['ssidref']
395 def state_set(self, state):
396 self.state_updated.acquire()
397 if self.state != state:
398 self.state = state
399 self.state_updated.notifyAll()
400 self.state_updated.release()
401 self.saveToDB()
403 def state_wait(self, state):
404 self.state_updated.acquire()
405 while self.state != state:
406 self.state_updated.wait()
407 self.state_updated.release()
409 def __str__(self):
410 s = "<domain"
411 s += " id=" + str(self.domid)
412 s += " name=" + self.name
413 s += " memory=" + str(self.memory)
414 s += " ssidref=" + str(self.ssidref)
415 s += ">"
416 return s
418 __repr__ = __str__
421 def getDeviceController(self, name):
422 if name not in controllerClasses:
423 raise XendError("unknown device type: " + str(name))
425 return controllerClasses[name](self)
428 def createDevice(self, deviceClass, devconfig):
429 return self.getDeviceController(deviceClass).createDevice(devconfig)
432 def configureDevice(self, deviceClass, devid, devconfig):
433 return self.getDeviceController(deviceClass).configureDevice(
434 devid, devconfig)
437 def destroyDevice(self, deviceClass, devid):
438 return self.getDeviceController(deviceClass).destroyDevice(devid)
441 def sxpr(self):
442 sxpr = ['domain',
443 ['domid', self.domid],
444 ['name', self.name],
445 ['memory', self.memory],
446 ['ssidref', self.ssidref],
447 ['target', self.target] ]
448 if self.uuid:
449 sxpr.append(['uuid', self.uuid])
450 if self.info:
451 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
452 run = (self.info['running'] and 'r') or '-'
453 block = (self.info['blocked'] and 'b') or '-'
454 pause = (self.info['paused'] and 'p') or '-'
455 shut = (self.info['shutdown'] and 's') or '-'
456 crash = (self.info['crashed'] and 'c') or '-'
457 state = run + block + pause + shut + crash
458 sxpr.append(['state', state])
459 if self.info['shutdown']:
460 reason = shutdown_reason(self.info['shutdown_reason'])
461 sxpr.append(['shutdown_reason', reason])
462 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
463 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
464 sxpr.append(['vcpus', self.info['vcpus']])
465 sxpr.append(['cpumap', self.info['cpumap']])
466 # build a string, using '|' to seperate items, show only up
467 # to number of vcpus in domain, and trim the trailing '|'
468 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
469 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
471 if self.start_time:
472 up_time = time.time() - self.start_time
473 sxpr.append(['up_time', str(up_time) ])
474 sxpr.append(['start_time', str(self.start_time) ])
476 if self.store_channel:
477 sxpr.append(self.store_channel.sxpr())
478 if self.store_mfn:
479 sxpr.append(['store_mfn', self.store_mfn])
480 if self.console_channel:
481 sxpr.append(['console_channel', self.console_channel.sxpr()])
482 if self.console_mfn:
483 sxpr.append(['console_mfn', self.console_mfn])
484 # already in (devices)
485 # console = self.getConsole()
486 # if console:
487 # sxpr.append(console.sxpr())
489 if self.restart_count:
490 sxpr.append(['restart_count', self.restart_count])
491 if self.restart_state:
492 sxpr.append(['restart_state', self.restart_state])
493 if self.restart_time:
494 sxpr.append(['restart_time', str(self.restart_time)])
495 if self.config:
496 sxpr.append(['config', self.config])
497 return sxpr
499 def check_name(self, name):
500 """Check if a vm name is valid. Valid names contain alphabetic characters,
501 digits, or characters in '_-.:/+'.
502 The same name cannot be used for more than one vm at the same time.
504 @param name: name
505 @raise: VMerror if invalid
506 """
507 if self.recreate: return
508 if name is None or name == '':
509 raise VmError('missing vm name')
510 for c in name:
511 if c in string.digits: continue
512 if c in '_-.:/+': continue
513 if c in string.ascii_letters: continue
514 raise VmError('invalid vm name')
515 dominfo = domain_exists(name)
516 # When creating or rebooting, a domain with my name should not exist.
517 # When restoring, a domain with my name will exist, but it should have
518 # my domain id.
519 if not dominfo:
520 return
521 if dominfo.is_terminated():
522 return
523 if not self.domid or (dominfo.domid != self.domid):
524 raise VmError('vm name clash: ' + name)
526 def construct(self, config):
527 """Construct the vm instance from its configuration.
529 @param config: configuration
530 @raise: VmError on error
531 """
532 # todo - add support for scheduling params?
533 self.config = config
534 try:
535 # Initial domain create.
536 self.setName(sxp.child_value(config, 'name'))
537 self.check_name(self.name)
538 self.init_image()
539 self.configure_cpus(config)
540 self.init_domain()
541 self.register_domain()
543 # Create domain devices.
544 self.configure_backends()
545 self.configure_restart()
546 self.construct_image()
547 self.configure()
548 self.exportToDB(save=True)
549 except Exception, ex:
550 # Catch errors, cleanup and re-raise.
551 print 'Domain construction error:', ex
552 import traceback
553 traceback.print_exc()
554 self.destroy()
555 raise
557 def register_domain(self):
558 xd = get_component('xen.xend.XendDomain')
559 xd._add_domain(self)
560 self.exportToDB(save=True)
562 def configure_cpus(self, config):
563 try:
564 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
565 except:
566 raise VmError('invalid cpu weight')
567 self.memory = int(sxp.child_value(config, 'memory'))
568 if self.memory is None:
569 raise VmError('missing memory size')
570 self.setMemoryTarget(self.memory * (1 << 20))
571 self.ssidref = int(sxp.child_value(config, 'ssidref'))
572 cpu = sxp.child_value(config, 'cpu')
573 if self.recreate and self.domid and cpu is not None and int(cpu) >= 0:
574 xc.domain_pincpu(self.domid, 0, 1<<int(cpu))
575 try:
576 image = sxp.child_value(self.config, 'image')
577 vcpus = sxp.child_value(image, 'vcpus')
578 if vcpus:
579 self.vcpus = int(vcpus)
580 except:
581 raise VmError('invalid vcpus value')
583 def configure_vcpus(self, vcpus):
584 d = {}
585 for v in range(0, vcpus):
586 d["cpu/%d/availability" % v] = "online"
587 self.writeVm(d)
589 def init_image(self):
590 """Create boot image handler for the domain.
591 """
592 image = sxp.child_value(self.config, 'image')
593 if image is None:
594 raise VmError('missing image')
595 self.image = ImageHandler.create(self, image)
597 def construct_image(self):
598 """Construct the boot image for the domain.
599 """
600 self.create_channel()
601 self.image.createImage()
602 self.exportToDB()
603 if self.store_channel and self.store_mfn >= 0:
604 IntroduceDomain(self.domid, self.store_mfn,
605 self.store_channel.port1, self.path)
606 # get the configured value of vcpus and update store
607 self.configure_vcpus(self.vcpus)
609 def delete(self):
610 """Delete the vm's db.
611 """
612 self.domid = None
613 self.saveToDB(sync=True)
614 try:
615 # Todo: eventually will have to wait for devices to signal
616 # destruction before can delete the db.
617 if self.db:
618 self.db.delete()
619 except Exception, ex:
620 log.warning("error in domain db delete: %s", ex)
621 pass
623 def destroy_domain(self):
624 """Destroy the vm's domain.
625 The domain will not finally go away unless all vm
626 devices have been released.
627 """
628 if self.domid is None:
629 return
630 try:
631 xc.domain_destroy(dom=self.domid)
632 except Exception, err:
633 log.exception("Domain destroy failed: %s", self.name)
635 def cleanup(self):
636 """Cleanup vm resources: release devices.
637 """
638 self.state = STATE_VM_TERMINATED
639 self.release_devices()
640 self.closeStoreChannel()
641 if self.console_channel:
642 # notify processes using this console?
643 try:
644 self.console_channel.close()
645 self.console_channel = None
646 except:
647 pass
648 if self.image:
649 try:
650 self.image.destroy()
651 self.image = None
652 except:
653 pass
655 def destroy(self):
656 """Clenup vm and destroy domain.
657 """
658 self.destroy_domain()
659 self.cleanup()
660 self.saveToDB()
661 return 0
663 def is_terminated(self):
664 """Check if a domain has been terminated.
665 """
666 return self.state == STATE_VM_TERMINATED
668 def release_devices(self):
669 """Release all vm devices.
670 """
672 t = xstransact("%s/device" % self.path)
673 for n in controllerClasses.keys():
674 for d in t.list(n):
675 try:
676 t.remove(d)
677 except ex:
678 # Log and swallow any exceptions in removal -- there's
679 # nothing more we can do.
680 log.exception(
681 "Device release failed: %s; %s; %s; %s" %
682 (self.info['name'], n, d, str(ex)))
683 t.commit()
686 def show(self):
687 """Print virtual machine info.
688 """
689 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.domid, self.name, self.memory, self.ssidref)
690 print "image:"
691 sxp.show(self.image)
692 print "]"
694 def init_domain(self):
695 """Initialize the domain memory.
696 """
697 if self.recreate:
698 return
699 if self.start_time is None:
700 self.start_time = time.time()
701 self.storeVm(("start-time", self.start_time))
702 try:
703 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
704 except:
705 raise VmError('invalid cpu')
706 id = self.image.initDomain(self.domid, self.memory, self.ssidref, cpu, self.cpu_weight)
707 log.debug('init_domain> Created domain=%d name=%s memory=%d',
708 id, self.name, self.memory)
709 self.setDomid(id)
711 def eventChannel(self, path=None):
712 """Create an event channel to the domain.
714 @param path under which port is stored in db
715 """
716 port = 0
717 if path:
718 try:
719 port = int(self.readDom(path))
720 except:
721 # if anything goes wrong, assume the port was not yet set
722 pass
723 ret = EventChannel.interdomain(0, self.domid, port1=port, port2=0)
724 self.storeDom(path, ret.port1)
725 return ret
727 def create_channel(self):
728 """Create the channels to the domain.
729 """
730 self.store_channel = self.eventChannel("store/port")
731 self.console_channel = self.eventChannel("console/port")
733 def create_configured_devices(self):
734 devices = sxp.children(self.config, 'device')
735 for d in devices:
736 dev_config = sxp.child0(d)
737 if dev_config is None:
738 raise VmError('invalid device')
739 dev_type = sxp.name(dev_config)
741 self.createDevice(dev_type, dev_config)
744 def create_devices(self):
745 """Create the devices for a vm.
747 @raise: VmError for invalid devices
748 """
749 if not self.rebooting():
750 self.create_configured_devices()
751 self.image.createDeviceModel()
753 def device_create(self, dev_config):
754 """Create a new device.
756 @param dev_config: device configuration
757 """
758 dev_type = sxp.name(dev_config)
759 devid = self.createDevice(dev_type, dev_config)
760 # self.config.append(['device', dev.getConfig()])
761 return self.getDeviceController(dev_type).sxpr(devid)
764 def device_configure(self, dev_config, devid):
765 """Configure an existing device.
766 @param dev_config: device configuration
767 @param devid: device id
768 """
769 deviceClass = sxp.name(dev_config)
770 self.configureDevice(deviceClass, devid, dev_config)
773 def configure_restart(self):
774 """Configure the vm restart mode.
775 """
776 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
777 if r not in restart_modes:
778 raise VmError('invalid restart mode: ' + str(r))
779 self.restart_mode = r;
781 def restart_needed(self, reason):
782 """Determine if the vm needs to be restarted when shutdown
783 for the given reason.
785 @param reason: shutdown reason
786 @return True if needs restart, False otherwise
787 """
788 if self.restart_mode == RESTART_NEVER:
789 return False
790 if self.restart_mode == RESTART_ALWAYS:
791 return True
792 if self.restart_mode == RESTART_ONREBOOT:
793 return reason == 'reboot'
794 return False
796 def restart_cancel(self):
797 """Cancel a vm restart.
798 """
799 self.restart_state = None
801 def restarting(self):
802 """Put the vm into restart mode.
803 """
804 self.restart_state = STATE_RESTART_PENDING
806 def restart_pending(self):
807 """Test if the vm has a pending restart.
808 """
809 return self.restart_state == STATE_RESTART_PENDING
811 def rebooting(self):
812 return self.restart_state == STATE_RESTART_BOOTING
814 def restart_check(self):
815 """Check if domain restart is OK.
816 To prevent restart loops, raise an error if it is
817 less than MINIMUM_RESTART_TIME seconds since the last restart.
818 """
819 tnow = time.time()
820 if self.restart_time is not None:
821 tdelta = tnow - self.restart_time
822 if tdelta < self.MINIMUM_RESTART_TIME:
823 self.restart_cancel()
824 msg = 'VM %s restarting too fast' % self.name
825 log.error(msg)
826 raise VmError(msg)
827 self.restart_time = tnow
828 self.restart_count += 1
830 def restart(self):
831 """Restart the domain after it has exited.
832 Reuses the domain id
834 """
835 try:
836 self.clear_shutdown()
837 self.state = STATE_VM_OK
838 self.shutdown_pending = None
839 self.restart_check()
840 self.exportToDB()
841 self.restart_state = STATE_RESTART_BOOTING
842 self.configure_bootloader()
843 self.construct(self.config)
844 self.saveToDB()
845 finally:
846 self.restart_state = None
848 def configure_bootloader(self):
849 self.bootloader = sxp.child_value(self.config, "bootloader")
850 if not self.bootloader:
851 return
852 # if we're restarting with a bootloader, we need to run it
853 # FIXME: this assumes the disk is the first device and
854 # that we're booting from the first disk
855 blcfg = None
856 # FIXME: this assumes that we want to use the first disk
857 dev = sxp.child_value(self.config, "device")
858 if dev:
859 disk = sxp.child_value(dev, "uname")
860 fn = blkdev_uname_to_file(disk)
861 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
862 if blcfg is None:
863 msg = "Had a bootloader specified, but can't find disk"
864 log.error(msg)
865 raise VmError(msg)
866 self.config = sxp.merge(['vm', ['image', blcfg]], self.config)
868 def configure_backends(self):
869 """Set configuration flags if the vm is a backend for netif or blkif.
870 Configure the backends to use for vbd and vif if specified.
871 """
872 for c in sxp.children(self.config, 'backend'):
873 v = sxp.child0(c)
874 name = sxp.name(v)
875 if name == 'blkif':
876 self.backend_flags |= SIF_BLK_BE_DOMAIN
877 elif name == 'netif':
878 self.backend_flags |= SIF_NET_BE_DOMAIN
879 elif name == 'usbif':
880 pass
881 elif name == 'tpmif':
882 self.backend_flags |= SIF_TPM_BE_DOMAIN
883 else:
884 raise VmError('invalid backend type:' + str(name))
886 def configure(self):
887 """Configure a vm.
889 """
890 self.configure_maxmem()
891 self.create_devices()
892 self.create_blkif()
894 def create_blkif(self):
895 """Create the block device interface (blkif) for the vm.
896 The vm needs a blkif even if it doesn't have any disks
897 at creation time, for example when it uses NFS root.
899 """
900 return
902 def configure_maxmem(self):
903 try:
904 maxmem = int(sxp.child_value(self.config, 'maxmem', self.memory))
905 xc.domain_setmaxmem(self.domid, maxmem_kb = maxmem * 1024)
906 except:
907 raise VmError("invalid maxmem: " +
908 sxp.child_value(self.config, 'maxmem'))
911 def vcpu_hotplug(self, vcpu, state):
912 """Disable or enable VCPU in domain.
913 """
914 if vcpu > self.vcpus:
915 log.error("Invalid VCPU %d" % vcpu)
916 return
917 if int(state) == 0:
918 availability = "offline"
919 else:
920 availability = "online"
921 self.storeVm("cpu/%d/availability" % vcpu, availability)
923 def shutdown(self, reason):
924 if not reason in shutdown_reasons.values():
925 raise XendError('invalid reason:' + reason)
926 self.storeVm("control/shutdown", reason)
927 if not reason in ['suspend']:
928 self.shutdown_pending = {'start':time.time(), 'reason':reason}
930 def clear_shutdown(self):
931 self.removeVm("control/shutdown")
933 def send_sysrq(self, key=0):
934 self.storeVm("control/sysrq", '%c' % key)
936 def shutdown_time_left(self, timeout):
937 if not self.shutdown_pending:
938 return 0
939 return timeout - (time.time() - self.shutdown_pending['start'])
941 def dom0_init_store(self):
942 if not self.store_channel:
943 self.store_channel = self.eventChannel("store/port")
944 if not self.store_channel:
945 return
946 ref = xc.init_store(self.store_channel.port2)
947 if ref and ref >= 0:
948 self.setStoreRef(ref)
949 try:
950 IntroduceDomain(self.domid, ref, self.store_channel.port1,
951 self.path)
952 except RuntimeError, ex:
953 if ex.args[0] == errno.EISCONN:
954 pass
955 else:
956 raise
957 # get run-time value of vcpus and update store
958 self.configure_vcpus(dom_get(self.domid)['vcpus'])
960 def dom0_enforce_vcpus(self):
961 dom = 0
962 # get max number of vcpus to use for dom0 from config
963 from xen.xend import XendRoot
964 xroot = XendRoot.instance()
965 target = int(xroot.get_dom0_vcpus())
966 log.debug("number of vcpus to use is %d" % (target))
968 # target = 0 means use all processors
969 if target > 0:
970 # count the number of online vcpus (cpu values in v2c map >= 0)
971 vcpu_to_cpu = dom_get(dom)['vcpu_to_cpu']
972 vcpus_online = len(filter(lambda x: x >= 0, vcpu_to_cpu))
973 log.debug("found %d vcpus online" % (vcpus_online))
975 # disable any extra vcpus that are online over the requested target
976 for vcpu in range(target, vcpus_online):
977 log.info("enforcement is disabling DOM%d VCPU%d" % (dom, vcpu))
978 self.vcpu_hotplug(vcpu, 0)
982 #============================================================================
983 # Register image handlers.
985 from image import \
986 addImageHandlerClass, \
987 ImageHandler, \
988 LinuxImageHandler, \
989 VmxImageHandler
991 addImageHandlerClass(LinuxImageHandler)
992 addImageHandlerClass(VmxImageHandler)
995 #============================================================================
996 # Register device controllers and their device config types.
998 """A map from device-class names to the subclass of DevController that
999 implements the device control specific to that device-class."""
1000 controllerClasses = {}
1003 def addControllerClass(device_class, cls):
1004 """Register a subclass of DevController to handle the named device-class.
1005 """
1006 cls.deviceClass = device_class
1007 controllerClasses[device_class] = cls
1010 from xen.xend.server import blkif, netif, tpmif, pciif, usbif
1011 addControllerClass('vbd', blkif.BlkifController)
1012 addControllerClass('vif', netif.NetifController)
1013 addControllerClass('vtpm', tpmif.TPMifController)
1014 addControllerClass('pci', pciif.PciController)
1015 addControllerClass('usb', usbif.UsbifController)