direct-io.hg

view tools/python/xen/xend/XendDomainInfo.py @ 6507:2ea4790cbeaa

Revert ac8cae1f2c47ee72b670159be01684e84f7dcf12
author adsharma@los-vmm.sc.intel.com
date Fri Aug 12 09:24:05 2005 -0800 (2005-08-12)
parents c589ca6d292b
children 40b887fa79d0
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 os
28 import time
29 import threading
31 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
32 from xen.util.ip import check_subnet, get_current_ipgw
33 from xen.util.blkif import blkdev_uname_to_file
35 from xen.xend.server import controller
36 from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
37 from xen.xend.server import messages
38 from xen.xend.server.channel import EventChannel, channelFactory
40 from xen.xend import sxp
41 from xen.xend.PrettyPrint import prettyprintstring
42 from xen.xend.XendBootloader import bootloader
43 from xen.xend.XendLogging import log
44 from xen.xend.XendError import XendError, VmError
45 from xen.xend.XendRoot import get_component
47 from xen.xend.uuid import getUuid
48 from xen.xend.xenstore import DBVar
50 """Shutdown code for poweroff."""
51 DOMAIN_POWEROFF = 0
53 """Shutdown code for reboot."""
54 DOMAIN_REBOOT = 1
56 """Shutdown code for suspend."""
57 DOMAIN_SUSPEND = 2
59 """Shutdown code for crash."""
60 DOMAIN_CRASH = 3
62 """Map shutdown codes to strings."""
63 shutdown_reasons = {
64 DOMAIN_POWEROFF: "poweroff",
65 DOMAIN_REBOOT : "reboot",
66 DOMAIN_SUSPEND : "suspend",
67 DOMAIN_CRASH : "crash",
68 }
70 RESTART_ALWAYS = 'always'
71 RESTART_ONREBOOT = 'onreboot'
72 RESTART_NEVER = 'never'
74 restart_modes = [
75 RESTART_ALWAYS,
76 RESTART_ONREBOOT,
77 RESTART_NEVER,
78 ]
80 STATE_RESTART_PENDING = 'pending'
81 STATE_RESTART_BOOTING = 'booting'
83 STATE_VM_OK = "ok"
84 STATE_VM_TERMINATED = "terminated"
85 STATE_VM_SUSPENDED = "suspended"
88 def domain_exists(name):
89 # See comment in XendDomain constructor.
90 xd = get_component('xen.xend.XendDomain')
91 return xd.domain_lookup_by_name(name)
93 def shutdown_reason(code):
94 """Get a shutdown reason from a code.
96 @param code: shutdown code
97 @type code: int
98 @return: shutdown reason
99 @rtype: string
100 """
101 return shutdown_reasons.get(code, "?")
103 config_handlers = {}
105 def add_config_handler(name, h):
106 """Add a handler for a config field.
108 @param name: field name
109 @param h: handler: fn(vm, config, field, index)
110 """
111 config_handlers[name] = h
113 def get_config_handler(name):
114 """Get a handler for a config field.
116 returns handler or None
117 """
118 return config_handlers.get(name)
120 """Table of handlers for devices.
121 Indexed by device type.
122 """
123 device_handlers = {}
125 def add_device_handler(name, type):
126 device_handlers[name] = type
128 def get_device_handler(name):
129 return device_handlers[name]
131 def dom_get(dom):
132 """Get info from xen for an existing domain.
134 @param dom: domain id
135 @return: info or None
136 """
137 domlist = xc.domain_getinfo(dom, 1)
138 if domlist and dom == domlist[0]['dom']:
139 return domlist[0]
140 return None
142 class XendDomainInfo:
143 """Virtual machine object."""
145 """Minimum time between domain restarts in seconds.
146 """
147 MINIMUM_RESTART_TIME = 20
149 def create(cls, parentdb, config):
150 """Create a VM from a configuration.
152 @param parentdb: parent db
153 @param config configuration
154 @raise: VmError for invalid configuration
155 """
156 uuid = getUuid()
157 db = parentdb.addChild(uuid)
158 vm = cls(db)
159 vm.construct(config)
160 vm.saveToDB(sync=True)
162 return vm
164 create = classmethod(create)
166 def recreate(cls, db, info):
167 """Create the VM object for an existing domain.
169 @param db: domain db
170 @param info: domain info from xc
171 """
172 dom = info['dom']
173 vm = cls(db)
174 vm.setdom(dom)
175 db.readDB()
176 vm.importFromDB()
177 config = vm.config
178 log.debug('info=' + str(info))
179 log.debug('config=' + prettyprintstring(config))
181 vm.memory = info['mem_kb']/1024
182 vm.target = info['mem_kb'] * 1024
184 if config:
185 try:
186 vm.recreate = True
187 vm.construct(config)
188 finally:
189 vm.recreate = False
190 else:
191 vm.setName("Domain-%d" % dom)
193 vm.exportToDB(save=True)
194 return vm
196 recreate = classmethod(recreate)
198 def restore(cls, parentdb, config, uuid):
199 """Create a domain and a VM object to do a restore.
201 @param parentdb: parent db
202 @param config: domain configuration
203 @param uuid: uuid to use
204 """
205 db = parentdb.addChild(uuid)
206 vm = cls(db)
207 ssidref = int(sxp.child_value(config, 'ssidref'))
208 log.debug('restoring with ssidref='+str(ssidref))
209 id = xc.domain_create(ssidref = ssidref)
210 vm.setdom(id)
211 try:
212 vm.restore = True
213 vm.construct(config)
214 finally:
215 vm.restore = False
216 vm.exportToDB(save=True, sync=True)
217 return vm
219 restore = classmethod(restore)
221 __exports__ = [
222 DBVar('id', ty='int'),
223 DBVar('name', ty='str'),
224 DBVar('uuid', ty='str'),
225 DBVar('config', ty='sxpr'),
226 DBVar('start_time', ty='float'),
227 DBVar('state', ty='str'),
228 DBVar('store_mfn', ty='long'),
229 DBVar('restart_mode', ty='str'),
230 DBVar('restart_state', ty='str'),
231 DBVar('restart_time', ty='float'),
232 DBVar('restart_count', ty='int'),
233 DBVar('target', ty='long', path="memory/target"),
234 DBVar('device_model_pid', ty='int'),
235 ]
237 def __init__(self, db):
238 self.db = db
239 self.uuid = db.getName()
241 self.recreate = 0
242 self.restore = 0
244 self.config = None
245 self.id = None
246 self.cpu_weight = 1
247 self.start_time = None
248 self.name = None
249 self.memory = None
250 self.ssidref = None
251 self.image = None
253 self.target = None
255 self.channel = None
256 self.store_channel = None
257 self.store_mfn = None
258 self.controllers = {}
260 self.info = None
261 self.blkif_backend = False
262 self.netif_backend = False
263 #todo: state: running, suspended
264 self.state = STATE_VM_OK
265 self.state_updated = threading.Condition()
266 self.shutdown_pending = None
268 #todo: set to migrate info if migrating
269 self.migrate = None
271 self.restart_mode = RESTART_ONREBOOT
272 self.restart_state = None
273 self.restart_time = None
274 self.restart_count = 0
276 self.vcpus = 1
277 self.vcpusdb = {}
278 self.bootloader = None
279 self.device_model_pid = 0
281 def setDB(self, db):
282 self.db = db
284 def saveToDB(self, save=False, sync=False):
285 self.db.saveDB(save=save, sync=sync)
287 def exportToDB(self, save=False, sync=False):
288 if self.channel:
289 self.channel.saveToDB(self.db.addChild("channel"), save=save)
290 if self.store_channel:
291 self.store_channel.saveToDB(self.db.addChild("store_channel"),
292 save=save)
293 if self.image:
294 self.image.exportToDB(save=save, sync=sync)
295 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
297 def importFromDB(self):
298 self.db.importFromDB(self, fields=self.__exports__)
299 self.store_channel = self.eventChannel("store_channel")
301 def setdom(self, dom):
302 """Set the domain id.
304 @param dom: domain id
305 """
306 self.id = int(dom)
307 #self.db.id = self.id
309 def getDomain(self):
310 return self.id
312 def setName(self, name):
313 self.name = name
314 self.db.name = self.name
316 def getName(self):
317 return self.name
319 def getChannel(self):
320 return self.channel
322 def getStoreChannel(self):
323 return self.store_channel
325 def update(self, info):
326 """Update with info from xc.domain_getinfo().
327 """
328 self.info = info
329 self.memory = self.info['mem_kb'] / 1024
330 self.ssidref = self.info['ssidref']
331 self.target = self.info['mem_kb'] * 1024
333 def state_set(self, state):
334 self.state_updated.acquire()
335 if self.state != state:
336 self.state = state
337 self.state_updated.notifyAll()
338 self.state_updated.release()
339 self.saveToDB()
341 def state_wait(self, state):
342 self.state_updated.acquire()
343 while self.state != state:
344 self.state_updated.wait()
345 self.state_updated.release()
347 def __str__(self):
348 s = "<domain"
349 s += " id=" + str(self.id)
350 s += " name=" + self.name
351 s += " memory=" + str(self.memory)
352 s += " ssidref=" + str(self.ssidref)
353 s += ">"
354 return s
356 __repr__ = __str__
358 def getDeviceTypes(self):
359 return self.controllers.keys()
361 def getDeviceControllers(self):
362 return self.controllers.values()
364 def getDeviceController(self, type, error=True):
365 ctrl = self.controllers.get(type)
366 if not ctrl and error:
367 raise XendError("invalid device type:" + type)
368 return ctrl
370 def findDeviceController(self, type):
371 return (self.getDeviceController(type, error=False)
372 or self.createDeviceController(type))
374 def createDeviceController(self, type):
375 ctrl = controller.createDevController(type, self, recreate=self.recreate)
376 self.controllers[type] = ctrl
377 return ctrl
379 def createDevice(self, type, devconfig, change=False):
380 ctrl = self.findDeviceController(type)
381 return ctrl.createDevice(devconfig, recreate=self.recreate,
382 change=change)
384 def configureDevice(self, type, id, devconfig):
385 ctrl = self.getDeviceController(type)
386 return ctrl.configureDevice(id, devconfig)
388 def destroyDevice(self, type, id, change=False, reboot=False):
389 ctrl = self.getDeviceController(type)
390 return ctrl.destroyDevice(id, change=change, reboot=reboot)
392 def deleteDevice(self, type, id):
393 ctrl = self.getDeviceController(type)
394 return ctrl.deleteDevice(id)
396 def getDevice(self, type, id, error=True):
397 ctrl = self.getDeviceController(type)
398 return ctrl.getDevice(id, error=error)
400 def getDeviceIds(self, type):
401 ctrl = self.getDeviceController(type)
402 return ctrl.getDeviceIds()
404 def getDeviceSxprs(self, type):
405 ctrl = self.getDeviceController(type)
406 return ctrl.getDeviceSxprs()
408 def sxpr(self):
409 sxpr = ['domain',
410 ['id', self.id],
411 ['name', self.name],
412 ['memory', self.memory],
413 ['ssidref', self.ssidref],
414 ['target', self.target] ]
415 if self.uuid:
416 sxpr.append(['uuid', self.uuid])
417 if self.info:
418 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
419 run = (self.info['running'] and 'r') or '-'
420 block = (self.info['blocked'] and 'b') or '-'
421 pause = (self.info['paused'] and 'p') or '-'
422 shut = (self.info['shutdown'] and 's') or '-'
423 crash = (self.info['crashed'] and 'c') or '-'
424 state = run + block + pause + shut + crash
425 sxpr.append(['state', state])
426 if self.info['shutdown']:
427 reason = shutdown_reason(self.info['shutdown_reason'])
428 sxpr.append(['shutdown_reason', reason])
429 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
430 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
431 sxpr.append(['vcpus', self.info['vcpus']])
432 sxpr.append(['cpumap', self.info['cpumap']])
433 # build a string, using '|' to seperate items, show only up
434 # to number of vcpus in domain, and trim the trailing '|'
435 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
436 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
438 if self.start_time:
439 up_time = time.time() - self.start_time
440 sxpr.append(['up_time', str(up_time) ])
441 sxpr.append(['start_time', str(self.start_time) ])
443 if self.channel:
444 sxpr.append(self.channel.sxpr())
445 if self.store_channel:
446 sxpr.append(self.store_channel.sxpr())
447 if self.store_mfn:
448 sxpr.append(['store_mfn', self.store_mfn])
450 if self.restart_count:
451 sxpr.append(['restart_count', self.restart_count])
452 if self.restart_state:
453 sxpr.append(['restart_state', self.restart_state])
454 if self.restart_time:
455 sxpr.append(['restart_time', str(self.restart_time)])
457 devs = self.sxpr_devices()
458 if devs:
459 sxpr.append(devs)
460 if self.config:
461 sxpr.append(['config', self.config])
462 if self.device_model_pid:
463 sxpr.append(['device_model_pid',self.device_model_pid])
464 return sxpr
466 def sxpr_devices(self):
467 sxpr = []
468 for ty in self.getDeviceTypes():
469 devs = self.getDeviceSxprs(ty)
470 sxpr += devs
471 if sxpr:
472 sxpr.insert(0, 'devices')
473 else:
474 sxpr = None
475 return sxpr
477 def check_name(self, name):
478 """Check if a vm name is valid. Valid names contain alphabetic characters,
479 digits, or characters in '_-.:/+'.
480 The same name cannot be used for more than one vm at the same time.
482 @param name: name
483 @raise: VMerror if invalid
484 """
485 if self.recreate: return
486 if name is None or name == '':
487 raise VmError('missing vm name')
488 for c in name:
489 if c in string.digits: continue
490 if c in '_-.:/+': continue
491 if c in string.ascii_letters: continue
492 raise VmError('invalid vm name')
493 dominfo = domain_exists(name)
494 # When creating or rebooting, a domain with my name should not exist.
495 # When restoring, a domain with my name will exist, but it should have
496 # my domain id.
497 if not dominfo:
498 return
499 if dominfo.is_terminated():
500 return
501 if not self.id or (dominfo.id != self.id):
502 raise VmError('vm name clash: ' + name)
504 def construct(self, config):
505 """Construct the vm instance from its configuration.
507 @param config: configuration
508 @raise: VmError on error
509 """
510 # todo - add support for scheduling params?
511 self.config = config
512 try:
513 # Initial domain create.
514 self.setName(sxp.child_value(config, 'name'))
515 self.check_name(self.name)
516 self.init_image()
517 self.configure_cpus(config)
518 self.init_domain()
519 self.register_domain()
520 self.configure_bootloader()
522 # Create domain devices.
523 self.configure_backends()
524 self.configure_restart()
525 self.construct_image()
526 self.configure()
527 self.exportToDB(save=True)
528 except Exception, ex:
529 # Catch errors, cleanup and re-raise.
530 print 'Domain construction error:', ex
531 import traceback
532 traceback.print_exc()
533 self.destroy()
534 raise
536 def register_domain(self):
537 xd = get_component('xen.xend.XendDomain')
538 xd._add_domain(self)
539 self.exportToDB(save=True)
541 def configure_cpus(self, config):
542 try:
543 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
544 except:
545 raise VmError('invalid cpu weight')
546 self.memory = int(sxp.child_value(config, 'memory'))
547 if self.memory is None:
548 raise VmError('missing memory size')
549 self.target = self.memory * (1 << 20)
550 self.ssidref = int(sxp.child_value(config, 'ssidref'))
551 cpu = sxp.child_value(config, 'cpu')
552 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
553 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
554 try:
555 image = sxp.child_value(self.config, 'image')
556 vcpus = sxp.child_value(image, 'vcpus')
557 if vcpus:
558 self.vcpus = int(vcpus)
559 except:
560 raise VmError('invalid vcpus value')
562 def exportVCPUSToDB(self, vcpus):
563 for v in range(0,vcpus):
564 path = "/cpus/cpu%d"%(v)
565 if not self.vcpusdb.has_key(path):
566 self.vcpusdb[path] = self.db.addChild(path)
567 db = self.vcpusdb[path]
568 log.debug("writing key online=1 to path %s in store"%(path))
569 db['online'] = "1"
570 db.saveDB(save=True)
572 def init_image(self):
573 """Create boot image handler for the domain.
574 """
575 image = sxp.child_value(self.config, 'image')
576 if image is None:
577 raise VmError('missing image')
578 self.image = ImageHandler.create(self, image)
580 def construct_image(self):
581 """Construct the boot image for the domain.
582 """
583 self.create_channel()
584 self.image.createImage()
585 self.exportToDB()
586 if self.store_channel:
587 self.db.introduceDomain(self.id,
588 self.store_mfn,
589 self.store_channel)
590 # get the configured value of vcpus and update store
591 self.exportVCPUSToDB(self.vcpus)
593 def delete(self):
594 """Delete the vm's db.
595 """
596 if self.dom_get(self.id):
597 return
598 self.id = None
599 self.saveToDB(sync=True)
600 try:
601 # Todo: eventually will have to wait for devices to signal
602 # destruction before can delete the db.
603 if self.db:
604 self.db.delete()
605 except Exception, ex:
606 log.warning("error in domain db delete: %s", ex)
607 pass
609 def destroy_domain(self):
610 """Destroy the vm's domain.
611 The domain will not finally go away unless all vm
612 devices have been released.
613 """
614 if self.id is None:
615 return
616 try:
617 xc.domain_destroy(dom=self.id)
618 except Exception, err:
619 log.exception("Domain destroy failed: %s", self.name)
621 def cleanup(self):
622 """Cleanup vm resources: release devices.
623 """
624 self.state = STATE_VM_TERMINATED
625 self.release_devices()
626 if self.channel:
627 try:
628 self.channel.close()
629 self.channel = None
630 except:
631 pass
632 if self.store_channel:
633 try:
634 self.store_channel.close()
635 self.store_channel = None
636 except:
637 pass
638 try:
639 self.db.releaseDomain(self.id)
640 except Exception, ex:
641 log.warning("error in domain release on xenstore: %s", ex)
642 pass
643 if self.image:
644 try:
645 self.device_model_pid = 0
646 self.image.destroy()
647 self.image = None
648 except:
649 pass
651 def destroy(self):
652 """Clenup vm and destroy domain.
653 """
654 self.cleanup()
655 self.destroy_domain()
656 self.saveToDB()
657 return 0
659 def is_terminated(self):
660 """Check if a domain has been terminated.
661 """
662 return self.state == STATE_VM_TERMINATED
664 def release_devices(self):
665 """Release all vm devices.
666 """
667 reboot = self.restart_pending()
668 for ctrl in self.getDeviceControllers():
669 if ctrl.isDestroyed(): continue
670 ctrl.destroyController(reboot=reboot)
672 def show(self):
673 """Print virtual machine info.
674 """
675 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
676 print "image:"
677 sxp.show(self.image)
678 print "]"
680 def init_domain(self):
681 """Initialize the domain memory.
682 """
683 if self.recreate:
684 return
685 if self.start_time is None:
686 self.start_time = time.time()
687 try:
688 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
689 except:
690 raise VmError('invalid cpu')
691 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
692 log.debug('init_domain> Created domain=%d name=%s memory=%d',
693 id, self.name, self.memory)
694 self.setdom(id)
696 def openChannel(self, key, local, remote):
697 """Create a control channel to the domain.
698 If saved info is available recreate the channel.
700 @param key db key for the saved data (if any)
701 @param local default local port
702 @param remote default remote port
703 """
704 db = self.db.addChild(key)
705 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
706 #todo: save here?
707 #chan.saveToDB(db)
708 return chan
710 def eventChannel(self, key):
711 """Create an event channel to the domain.
712 If saved info is available recreate the channel.
714 @param key db key for the saved data (if any)
715 """
716 db = self.db.addChild(key)
717 return EventChannel.restoreFromDB(db, 0, self.id)
719 def create_channel(self):
720 """Create the channels to the domain.
721 """
722 self.channel = self.openChannel("channel", 0, 1)
723 self.store_channel = self.eventChannel("store_channel")
725 def create_configured_devices(self):
726 devices = sxp.children(self.config, 'device')
727 for d in devices:
728 dev_config = sxp.child0(d)
729 if dev_config is None:
730 raise VmError('invalid device')
731 dev_type = sxp.name(dev_config)
732 ctrl_type = get_device_handler(dev_type)
733 if ctrl_type is None:
734 raise VmError('unknown device type: ' + dev_type)
735 self.createDevice(ctrl_type, dev_config)
737 def create_devices(self):
738 """Create the devices for a vm.
740 @raise: VmError for invalid devices
741 """
742 if self.rebooting():
743 for ctrl in self.getDeviceControllers():
744 ctrl.initController(reboot=True)
745 else:
746 self.create_configured_devices()
747 if not self.device_model_pid:
748 self.device_model_pid = self.image.createDeviceModel()
750 def device_create(self, dev_config):
751 """Create a new device.
753 @param dev_config: device configuration
754 """
755 dev_type = sxp.name(dev_config)
756 dev = self.createDevice(self, dev_config, change=True)
757 self.config.append(['device', dev.getConfig()])
758 return dev.sxpr()
760 def device_configure(self, dev_config, id):
761 """Configure an existing device.
763 @param dev_config: device configuration
764 @param id: device id
765 """
766 type = sxp.name(dev_config)
767 dev = self.getDevice(type, id)
768 old_config = dev.getConfig()
769 new_config = dev.configure(dev_config, change=True)
770 # Patch new config into vm config.
771 new_full_config = ['device', new_config]
772 old_full_config = ['device', old_config]
773 old_index = self.config.index(old_full_config)
774 self.config[old_index] = new_full_config
775 return new_config
777 def device_refresh(self, type, id):
778 """Refresh a device.
780 @param type: device type
781 @param id: device id
782 """
783 dev = self.getDevice(type, id)
784 dev.refresh()
786 def device_delete(self, type, id):
787 """Destroy and remove a device.
789 @param type: device type
790 @param id: device id
791 """
792 dev = self.getDevice(type, id)
793 dev_config = dev.getConfig()
794 if dev_config:
795 self.config.remove(['device', dev_config])
796 self.deleteDevice(type, dev.getId())
798 def configure_bootloader(self):
799 """Configure boot loader.
800 """
801 self.bootloader = sxp.child_value(self.config, "bootloader")
803 def configure_restart(self):
804 """Configure the vm restart mode.
805 """
806 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
807 if r not in restart_modes:
808 raise VmError('invalid restart mode: ' + str(r))
809 self.restart_mode = r;
811 def restart_needed(self, reason):
812 """Determine if the vm needs to be restarted when shutdown
813 for the given reason.
815 @param reason: shutdown reason
816 @return True if needs restart, False otherwise
817 """
818 if self.restart_mode == RESTART_NEVER:
819 return False
820 if self.restart_mode == RESTART_ALWAYS:
821 return True
822 if self.restart_mode == RESTART_ONREBOOT:
823 return reason == 'reboot'
824 return False
826 def restart_cancel(self):
827 """Cancel a vm restart.
828 """
829 self.restart_state = None
831 def restarting(self):
832 """Put the vm into restart mode.
833 """
834 self.restart_state = STATE_RESTART_PENDING
836 def restart_pending(self):
837 """Test if the vm has a pending restart.
838 """
839 return self.restart_state == STATE_RESTART_PENDING
841 def rebooting(self):
842 return self.restart_state == STATE_RESTART_BOOTING
844 def restart_check(self):
845 """Check if domain restart is OK.
846 To prevent restart loops, raise an error if it is
847 less than MINIMUM_RESTART_TIME seconds since the last restart.
848 """
849 tnow = time.time()
850 if self.restart_time is not None:
851 tdelta = tnow - self.restart_time
852 if tdelta < self.MINIMUM_RESTART_TIME:
853 self.restart_cancel()
854 msg = 'VM %s restarting too fast' % self.name
855 log.error(msg)
856 raise VmError(msg)
857 self.restart_time = tnow
858 self.restart_count += 1
860 def restart(self):
861 """Restart the domain after it has exited.
862 Reuses the domain id
864 """
865 try:
866 self.state = STATE_VM_OK
867 self.shutdown_pending = None
868 self.restart_check()
869 self.exportToDB()
870 self.restart_state = STATE_RESTART_BOOTING
871 if self.bootloader:
872 self.config = self.bootloader_config()
873 self.construct(self.config)
874 self.saveToDB()
875 finally:
876 self.restart_state = None
878 def bootloader_config(self):
879 # if we're restarting with a bootloader, we need to run it
880 # FIXME: this assumes the disk is the first device and
881 # that we're booting from the first disk
882 blcfg = None
883 # FIXME: this assumes that we want to use the first disk
884 dev = sxp.child_value(self.config, "device")
885 if dev:
886 disk = sxp.child_value(dev, "uname")
887 fn = blkdev_uname_to_file(disk)
888 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
889 if blcfg is None:
890 msg = "Had a bootloader specified, but can't find disk"
891 log.error(msg)
892 raise VmError(msg)
893 config = sxp.merge(['vm', blconfig ], self.config)
894 return config
896 def configure_backends(self):
897 """Set configuration flags if the vm is a backend for netif or blkif.
898 Configure the backends to use for vbd and vif if specified.
899 """
900 for c in sxp.children(self.config, 'backend'):
901 v = sxp.child0(c)
902 name = sxp.name(v)
903 if name == 'blkif':
904 self.blkif_backend = True
905 elif name == 'netif':
906 self.netif_backend = True
907 elif name == 'usbif':
908 self.usbif_backend = True
909 else:
910 raise VmError('invalid backend type:' + str(name))
912 def configure(self):
913 """Configure a vm.
915 """
916 self.configure_fields()
917 self.create_devices()
918 self.create_blkif()
920 def create_blkif(self):
921 """Create the block device interface (blkif) for the vm.
922 The vm needs a blkif even if it doesn't have any disks
923 at creation time, for example when it uses NFS root.
925 """
926 blkif = self.getDeviceController("vbd", error=False)
927 if not blkif:
928 blkif = self.createDeviceController("vbd")
929 backend = blkif.getBackend(0)
930 backend.connect(recreate=self.recreate)
932 def configure_fields(self):
933 """Process the vm configuration fields using the registered handlers.
934 """
935 index = {}
936 for field in sxp.children(self.config):
937 field_name = sxp.name(field)
938 field_index = index.get(field_name, 0)
939 field_handler = get_config_handler(field_name)
940 # Ignore unknown fields. Warn?
941 if field_handler:
942 v = field_handler(self, self.config, field, field_index)
943 else:
944 log.warning("Unknown config field %s", field_name)
945 index[field_name] = field_index + 1
947 def mem_target_set(self, target):
948 """Set domain memory target in bytes.
949 """
950 if target:
951 self.target = target * (1 << 20)
952 # Commit to XenStore immediately
953 self.exportToDB()
955 def vcpu_hotplug(self, vcpu, state):
956 """Disable or enable VCPU in domain.
957 """
958 db = ""
959 try:
960 db = self.vcpusdb['/cpus/cpu%d'%(vcpu)]
961 except:
962 log.error("Invalid VCPU")
963 return
965 if self.store_channel:
966 if int(state) == 0:
967 db['online'] = "0"
968 else:
969 db['online'] = "1"
971 db.saveDB(save=True)
973 def shutdown(self, reason):
974 if not reason in shutdown_reasons.values():
975 raise XendError('invalid reason:' + reason)
976 db = self.db.addChild("/control");
977 db['shutdown'] = reason;
978 db.saveDB(save=True);
979 if not reason in ['suspend']:
980 self.shutdown_pending = {'start':time.time(), 'reason':reason}
982 def send_sysrq(self, key=0):
983 db = self.db.addChild("/control");
984 db['sysrq'] = '%c' % key;
985 db.saveDB(save=True);
987 def shutdown_time_left(self, timeout):
988 if not self.shutdown_pending:
989 return 0
990 return timeout - (time.time() - self.shutdown_pending['start'])
992 def dom0_init_store(self):
993 if not self.store_channel:
994 self.store_channel = self.eventChannel("store_channel")
995 self.store_mfn = xc.init_store(self.store_channel.port2)
996 if self.store_mfn >= 0:
997 self.db.introduceDomain(self.id, self.store_mfn,
998 self.store_channel)
999 self.exportToDB(save=True, sync=True)
1000 # get run-time value of vcpus and update store
1001 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1003 def vm_field_ignore(vm, config, val, index):
1004 """Dummy config field handler used for fields with built-in handling.
1006 @param vm: virtual machine
1007 @param config: vm config
1008 @param val: config field
1009 @param index: field index
1010 """
1011 pass
1013 def vm_field_maxmem(vm, config, val, index):
1014 """Configure vm memory limit.
1016 @param vm: virtual machine
1017 @param config: vm config
1018 @param val: config field
1019 @param index: field index
1020 """
1021 maxmem = sxp.child0(val)
1022 if maxmem is None:
1023 maxmem = vm.memory
1024 try:
1025 maxmem = int(maxmem)
1026 except:
1027 raise VmError("invalid maxmem: " + str(maxmem))
1028 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1030 #============================================================================
1031 # Register image handlers.
1032 from image import \
1033 addImageHandlerClass, \
1034 ImageHandler, \
1035 LinuxImageHandler, \
1036 VmxImageHandler
1038 addImageHandlerClass(LinuxImageHandler)
1039 addImageHandlerClass(VmxImageHandler)
1041 # Ignore the fields we already handle.
1042 add_config_handler('name', vm_field_ignore)
1043 add_config_handler('memory', vm_field_ignore)
1044 add_config_handler('ssidref', vm_field_ignore)
1045 add_config_handler('cpu', vm_field_ignore)
1046 add_config_handler('cpu_weight', vm_field_ignore)
1047 add_config_handler('restart', vm_field_ignore)
1048 add_config_handler('image', vm_field_ignore)
1049 add_config_handler('device', vm_field_ignore)
1050 add_config_handler('backend', vm_field_ignore)
1051 add_config_handler('vcpus', vm_field_ignore)
1052 add_config_handler('bootloader', vm_field_ignore)
1054 # Register other config handlers.
1055 add_config_handler('maxmem', vm_field_maxmem)
1057 #============================================================================
1058 # Register device controllers and their device config types.
1060 from server import blkif
1061 controller.addDevControllerClass("vbd", blkif.BlkifController)
1062 add_device_handler("vbd", "vbd")
1064 from server import netif
1065 controller.addDevControllerClass("vif", netif.NetifController)
1066 add_device_handler("vif", "vif")
1068 from server import pciif
1069 controller.addDevControllerClass("pci", pciif.PciController)
1070 add_device_handler("pci", "pci")
1072 from xen.xend.server import usbif
1073 controller.addDevControllerClass("usb", usbif.UsbifController)
1074 add_device_handler("usb", "usb")
1076 #============================================================================