ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6538:84ee014ebd41

Merge xen-vtx-unstable.hg
author adsharma@los-vmm.sc.intel.com
date Wed Aug 17 12:34:38 2005 -0800 (2005-08-17)
parents 23979fb12c49 18f04796ea89
children 99914b54f7bf
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=None):
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 if not uuid:
206 uuid = getUuid()
207 db = parentdb.addChild(uuid)
208 vm = cls(db)
209 ssidref = int(sxp.child_value(config, 'ssidref'))
210 log.debug('restoring with ssidref='+str(ssidref))
211 id = xc.domain_create(ssidref = ssidref)
212 vm.setdom(id)
213 vm.clear_shutdown()
214 try:
215 vm.restore = True
216 vm.construct(config)
217 finally:
218 vm.restore = False
219 vm.exportToDB(save=True, sync=True)
220 return vm
222 restore = classmethod(restore)
224 __exports__ = [
225 DBVar('id', ty='int'),
226 DBVar('name', ty='str'),
227 DBVar('uuid', ty='str'),
228 DBVar('config', ty='sxpr'),
229 DBVar('start_time', ty='float'),
230 DBVar('state', ty='str'),
231 DBVar('store_mfn', ty='long'),
232 DBVar('restart_mode', ty='str'),
233 DBVar('restart_state', ty='str'),
234 DBVar('restart_time', ty='float'),
235 DBVar('restart_count', ty='int'),
236 DBVar('target', ty='long', path="memory/target"),
237 DBVar('device_model_pid', ty='int'),
238 ]
240 def __init__(self, db):
241 self.db = db
242 self.uuid = db.getName()
244 self.recreate = 0
245 self.restore = 0
247 self.config = None
248 self.id = None
249 self.cpu_weight = 1
250 self.start_time = None
251 self.name = None
252 self.memory = None
253 self.ssidref = None
254 self.image = None
256 self.target = None
258 self.channel = None
259 self.store_channel = None
260 self.store_mfn = None
261 self.controllers = {}
263 self.info = None
264 self.blkif_backend = False
265 self.netif_backend = False
266 #todo: state: running, suspended
267 self.state = STATE_VM_OK
268 self.state_updated = threading.Condition()
269 self.shutdown_pending = None
271 #todo: set to migrate info if migrating
272 self.migrate = None
274 self.restart_mode = RESTART_ONREBOOT
275 self.restart_state = None
276 self.restart_time = None
277 self.restart_count = 0
279 self.vcpus = 1
280 self.vcpusdb = {}
281 self.bootloader = None
282 self.device_model_pid = 0
284 def setDB(self, db):
285 self.db = db
287 def saveToDB(self, save=False, sync=False):
288 self.db.saveDB(save=save, sync=sync)
290 def exportToDB(self, save=False, sync=False):
291 if self.channel:
292 self.channel.saveToDB(self.db.addChild("channel"), save=save)
293 if self.store_channel:
294 self.store_channel.saveToDB(self.db.addChild("store_channel"),
295 save=save)
296 if self.image:
297 self.image.exportToDB(save=save, sync=sync)
298 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
300 def importFromDB(self):
301 self.db.importFromDB(self, fields=self.__exports__)
302 self.store_channel = self.eventChannel("store_channel")
304 def setdom(self, dom):
305 """Set the domain id.
307 @param dom: domain id
308 """
309 self.id = int(dom)
310 #self.db.id = self.id
312 def getDomain(self):
313 return self.id
315 def setName(self, name):
316 self.name = name
317 self.db.name = self.name
319 def getName(self):
320 return self.name
322 def getChannel(self):
323 return self.channel
325 def getStoreChannel(self):
326 return self.store_channel
328 def update(self, info):
329 """Update with info from xc.domain_getinfo().
330 """
331 self.info = info
332 self.memory = self.info['mem_kb'] / 1024
333 self.ssidref = self.info['ssidref']
334 self.target = self.info['mem_kb'] * 1024
336 def state_set(self, state):
337 self.state_updated.acquire()
338 if self.state != state:
339 self.state = state
340 self.state_updated.notifyAll()
341 self.state_updated.release()
342 self.saveToDB()
344 def state_wait(self, state):
345 self.state_updated.acquire()
346 while self.state != state:
347 self.state_updated.wait()
348 self.state_updated.release()
350 def __str__(self):
351 s = "<domain"
352 s += " id=" + str(self.id)
353 s += " name=" + self.name
354 s += " memory=" + str(self.memory)
355 s += " ssidref=" + str(self.ssidref)
356 s += ">"
357 return s
359 __repr__ = __str__
361 def getDeviceTypes(self):
362 return self.controllers.keys()
364 def getDeviceControllers(self):
365 return self.controllers.values()
367 def getDeviceController(self, type, error=True):
368 ctrl = self.controllers.get(type)
369 if not ctrl and error:
370 raise XendError("invalid device type:" + type)
371 return ctrl
373 def findDeviceController(self, type):
374 return (self.getDeviceController(type, error=False)
375 or self.createDeviceController(type))
377 def createDeviceController(self, type):
378 ctrl = controller.createDevController(type, self, recreate=self.recreate)
379 self.controllers[type] = ctrl
380 return ctrl
382 def createDevice(self, type, devconfig, change=False):
383 ctrl = self.findDeviceController(type)
384 return ctrl.createDevice(devconfig, recreate=self.recreate,
385 change=change)
387 def configureDevice(self, type, id, devconfig):
388 ctrl = self.getDeviceController(type)
389 return ctrl.configureDevice(id, devconfig)
391 def destroyDevice(self, type, id, change=False, reboot=False):
392 ctrl = self.getDeviceController(type)
393 return ctrl.destroyDevice(id, change=change, reboot=reboot)
395 def deleteDevice(self, type, id):
396 ctrl = self.getDeviceController(type)
397 return ctrl.deleteDevice(id)
399 def getDevice(self, type, id, error=True):
400 ctrl = self.getDeviceController(type)
401 return ctrl.getDevice(id, error=error)
403 def getDeviceIds(self, type):
404 ctrl = self.getDeviceController(type)
405 return ctrl.getDeviceIds()
407 def getDeviceSxprs(self, type):
408 ctrl = self.getDeviceController(type)
409 return ctrl.getDeviceSxprs()
411 def sxpr(self):
412 sxpr = ['domain',
413 ['id', self.id],
414 ['name', self.name],
415 ['memory', self.memory],
416 ['ssidref', self.ssidref],
417 ['target', self.target] ]
418 if self.uuid:
419 sxpr.append(['uuid', self.uuid])
420 if self.info:
421 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
422 run = (self.info['running'] and 'r') or '-'
423 block = (self.info['blocked'] and 'b') or '-'
424 pause = (self.info['paused'] and 'p') or '-'
425 shut = (self.info['shutdown'] and 's') or '-'
426 crash = (self.info['crashed'] and 'c') or '-'
427 state = run + block + pause + shut + crash
428 sxpr.append(['state', state])
429 if self.info['shutdown']:
430 reason = shutdown_reason(self.info['shutdown_reason'])
431 sxpr.append(['shutdown_reason', reason])
432 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
433 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
434 sxpr.append(['vcpus', self.info['vcpus']])
435 sxpr.append(['cpumap', self.info['cpumap']])
436 # build a string, using '|' to seperate items, show only up
437 # to number of vcpus in domain, and trim the trailing '|'
438 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
439 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
441 if self.start_time:
442 up_time = time.time() - self.start_time
443 sxpr.append(['up_time', str(up_time) ])
444 sxpr.append(['start_time', str(self.start_time) ])
446 if self.channel:
447 sxpr.append(self.channel.sxpr())
448 if self.store_channel:
449 sxpr.append(self.store_channel.sxpr())
450 if self.store_mfn:
451 sxpr.append(['store_mfn', self.store_mfn])
453 if self.restart_count:
454 sxpr.append(['restart_count', self.restart_count])
455 if self.restart_state:
456 sxpr.append(['restart_state', self.restart_state])
457 if self.restart_time:
458 sxpr.append(['restart_time', str(self.restart_time)])
460 devs = self.sxpr_devices()
461 if devs:
462 sxpr.append(devs)
463 if self.config:
464 sxpr.append(['config', self.config])
465 if self.device_model_pid:
466 sxpr.append(['device_model_pid',self.device_model_pid])
467 return sxpr
469 def sxpr_devices(self):
470 sxpr = []
471 for ty in self.getDeviceTypes():
472 devs = self.getDeviceSxprs(ty)
473 sxpr += devs
474 if sxpr:
475 sxpr.insert(0, 'devices')
476 else:
477 sxpr = None
478 return sxpr
480 def check_name(self, name):
481 """Check if a vm name is valid. Valid names contain alphabetic characters,
482 digits, or characters in '_-.:/+'.
483 The same name cannot be used for more than one vm at the same time.
485 @param name: name
486 @raise: VMerror if invalid
487 """
488 if self.recreate: return
489 if name is None or name == '':
490 raise VmError('missing vm name')
491 for c in name:
492 if c in string.digits: continue
493 if c in '_-.:/+': continue
494 if c in string.ascii_letters: continue
495 raise VmError('invalid vm name')
496 dominfo = domain_exists(name)
497 # When creating or rebooting, a domain with my name should not exist.
498 # When restoring, a domain with my name will exist, but it should have
499 # my domain id.
500 if not dominfo:
501 return
502 if dominfo.is_terminated():
503 return
504 if not self.id or (dominfo.id != self.id):
505 raise VmError('vm name clash: ' + name)
507 def construct(self, config):
508 """Construct the vm instance from its configuration.
510 @param config: configuration
511 @raise: VmError on error
512 """
513 # todo - add support for scheduling params?
514 self.config = config
515 try:
516 # Initial domain create.
517 self.setName(sxp.child_value(config, 'name'))
518 self.check_name(self.name)
519 self.init_image()
520 self.configure_cpus(config)
521 self.init_domain()
522 self.register_domain()
523 self.configure_bootloader()
525 # Create domain devices.
526 self.configure_backends()
527 self.configure_restart()
528 self.construct_image()
529 self.configure()
530 self.exportToDB(save=True)
531 except Exception, ex:
532 # Catch errors, cleanup and re-raise.
533 print 'Domain construction error:', ex
534 import traceback
535 traceback.print_exc()
536 self.destroy()
537 raise
539 def register_domain(self):
540 xd = get_component('xen.xend.XendDomain')
541 xd._add_domain(self)
542 self.exportToDB(save=True)
544 def configure_cpus(self, config):
545 try:
546 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
547 except:
548 raise VmError('invalid cpu weight')
549 self.memory = int(sxp.child_value(config, 'memory'))
550 if self.memory is None:
551 raise VmError('missing memory size')
552 self.target = self.memory * (1 << 20)
553 self.ssidref = int(sxp.child_value(config, 'ssidref'))
554 cpu = sxp.child_value(config, 'cpu')
555 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
556 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
557 try:
558 image = sxp.child_value(self.config, 'image')
559 vcpus = sxp.child_value(image, 'vcpus')
560 if vcpus:
561 self.vcpus = int(vcpus)
562 except:
563 raise VmError('invalid vcpus value')
565 def exportVCPUSToDB(self, vcpus):
566 for v in range(0,vcpus):
567 path = "/cpu/%d"%(v)
568 if not self.vcpusdb.has_key(path):
569 self.vcpusdb[path] = self.db.addChild(path)
570 db = self.vcpusdb[path]
571 log.debug("writing key availability=online to path %s in store"%(path))
572 db['availability'] = "online"
573 db.saveDB(save=True)
575 def init_image(self):
576 """Create boot image handler for the domain.
577 """
578 image = sxp.child_value(self.config, 'image')
579 if image is None:
580 raise VmError('missing image')
581 self.image = ImageHandler.create(self, image)
583 def construct_image(self):
584 """Construct the boot image for the domain.
585 """
586 self.create_channel()
587 self.image.createImage()
588 self.exportToDB()
589 if self.store_channel and self.store_mfn >= 0:
590 self.db.introduceDomain(self.id,
591 self.store_mfn,
592 self.store_channel)
593 # get the configured value of vcpus and update store
594 self.exportVCPUSToDB(self.vcpus)
596 def delete(self):
597 """Delete the vm's db.
598 """
599 if dom_get(self.id):
600 return
601 self.id = None
602 self.saveToDB(sync=True)
603 try:
604 # Todo: eventually will have to wait for devices to signal
605 # destruction before can delete the db.
606 if self.db:
607 self.db.delete()
608 except Exception, ex:
609 log.warning("error in domain db delete: %s", ex)
610 pass
612 def destroy_domain(self):
613 """Destroy the vm's domain.
614 The domain will not finally go away unless all vm
615 devices have been released.
616 """
617 if self.id is None:
618 return
619 try:
620 xc.domain_destroy(dom=self.id)
621 except Exception, err:
622 log.exception("Domain destroy failed: %s", self.name)
624 def cleanup(self):
625 """Cleanup vm resources: release devices.
626 """
627 self.state = STATE_VM_TERMINATED
628 self.release_devices()
629 if self.channel:
630 try:
631 self.channel.close()
632 self.channel = None
633 except:
634 pass
635 if self.store_channel:
636 try:
637 self.store_channel.close()
638 self.store_channel = None
639 except:
640 pass
641 try:
642 self.db.releaseDomain(self.id)
643 except Exception, ex:
644 log.warning("error in domain release on xenstore: %s", ex)
645 pass
646 if self.image:
647 try:
648 self.device_model_pid = 0
649 self.image.destroy()
650 self.image = None
651 except:
652 pass
654 def destroy(self):
655 """Clenup vm and destroy domain.
656 """
657 self.cleanup()
658 self.destroy_domain()
659 self.saveToDB()
660 return 0
662 def is_terminated(self):
663 """Check if a domain has been terminated.
664 """
665 return self.state == STATE_VM_TERMINATED
667 def release_devices(self):
668 """Release all vm devices.
669 """
670 reboot = self.restart_pending()
671 for ctrl in self.getDeviceControllers():
672 if ctrl.isDestroyed(): continue
673 ctrl.destroyController(reboot=reboot)
675 def show(self):
676 """Print virtual machine info.
677 """
678 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
679 print "image:"
680 sxp.show(self.image)
681 print "]"
683 def init_domain(self):
684 """Initialize the domain memory.
685 """
686 if self.recreate:
687 return
688 if self.start_time is None:
689 self.start_time = time.time()
690 try:
691 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
692 except:
693 raise VmError('invalid cpu')
694 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
695 log.debug('init_domain> Created domain=%d name=%s memory=%d',
696 id, self.name, self.memory)
697 self.setdom(id)
699 def openChannel(self, key, local, remote):
700 """Create a control channel to the domain.
701 If saved info is available recreate the channel.
703 @param key db key for the saved data (if any)
704 @param local default local port
705 @param remote default remote port
706 """
707 db = self.db.addChild(key)
708 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
709 #todo: save here?
710 #chan.saveToDB(db)
711 return chan
713 def eventChannel(self, key):
714 """Create an event channel to the domain.
715 If saved info is available recreate the channel.
717 @param key db key for the saved data (if any)
718 """
719 db = self.db.addChild(key)
720 return EventChannel.restoreFromDB(db, 0, self.id)
722 def create_channel(self):
723 """Create the channels to the domain.
724 """
725 self.channel = self.openChannel("channel", 0, 1)
726 self.store_channel = self.eventChannel("store_channel")
728 def create_configured_devices(self):
729 devices = sxp.children(self.config, 'device')
730 for d in devices:
731 dev_config = sxp.child0(d)
732 if dev_config is None:
733 raise VmError('invalid device')
734 dev_type = sxp.name(dev_config)
735 ctrl_type = get_device_handler(dev_type)
736 if ctrl_type is None:
737 raise VmError('unknown device type: ' + dev_type)
738 self.createDevice(ctrl_type, dev_config)
740 def create_devices(self):
741 """Create the devices for a vm.
743 @raise: VmError for invalid devices
744 """
745 if self.rebooting():
746 for ctrl in self.getDeviceControllers():
747 ctrl.initController(reboot=True)
748 else:
749 self.create_configured_devices()
750 if not self.device_model_pid:
751 self.device_model_pid = 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 dev = self.createDevice(dev_type, dev_config, change=True)
760 self.config.append(['device', dev.getConfig()])
761 return dev.sxpr()
763 def device_configure(self, dev_config, id):
764 """Configure an existing device.
766 @param dev_config: device configuration
767 @param id: device id
768 """
769 type = sxp.name(dev_config)
770 dev = self.getDevice(type, id)
771 old_config = dev.getConfig()
772 new_config = dev.configure(dev_config, change=True)
773 # Patch new config into vm config.
774 new_full_config = ['device', new_config]
775 old_full_config = ['device', old_config]
776 old_index = self.config.index(old_full_config)
777 self.config[old_index] = new_full_config
778 return new_config
780 def device_refresh(self, type, id):
781 """Refresh a device.
783 @param type: device type
784 @param id: device id
785 """
786 dev = self.getDevice(type, id)
787 dev.refresh()
789 def device_delete(self, type, id):
790 """Destroy and remove a device.
792 @param type: device type
793 @param id: device id
794 """
795 dev = self.getDevice(type, id)
796 dev_config = dev.getConfig()
797 if dev_config:
798 self.config.remove(['device', dev_config])
799 self.deleteDevice(type, dev.getId())
801 def configure_bootloader(self):
802 """Configure boot loader.
803 """
804 self.bootloader = sxp.child_value(self.config, "bootloader")
806 def configure_restart(self):
807 """Configure the vm restart mode.
808 """
809 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
810 if r not in restart_modes:
811 raise VmError('invalid restart mode: ' + str(r))
812 self.restart_mode = r;
814 def restart_needed(self, reason):
815 """Determine if the vm needs to be restarted when shutdown
816 for the given reason.
818 @param reason: shutdown reason
819 @return True if needs restart, False otherwise
820 """
821 if self.restart_mode == RESTART_NEVER:
822 return False
823 if self.restart_mode == RESTART_ALWAYS:
824 return True
825 if self.restart_mode == RESTART_ONREBOOT:
826 return reason == 'reboot'
827 return False
829 def restart_cancel(self):
830 """Cancel a vm restart.
831 """
832 self.restart_state = None
834 def restarting(self):
835 """Put the vm into restart mode.
836 """
837 self.restart_state = STATE_RESTART_PENDING
839 def restart_pending(self):
840 """Test if the vm has a pending restart.
841 """
842 return self.restart_state == STATE_RESTART_PENDING
844 def rebooting(self):
845 return self.restart_state == STATE_RESTART_BOOTING
847 def restart_check(self):
848 """Check if domain restart is OK.
849 To prevent restart loops, raise an error if it is
850 less than MINIMUM_RESTART_TIME seconds since the last restart.
851 """
852 tnow = time.time()
853 if self.restart_time is not None:
854 tdelta = tnow - self.restart_time
855 if tdelta < self.MINIMUM_RESTART_TIME:
856 self.restart_cancel()
857 msg = 'VM %s restarting too fast' % self.name
858 log.error(msg)
859 raise VmError(msg)
860 self.restart_time = tnow
861 self.restart_count += 1
863 def restart(self):
864 """Restart the domain after it has exited.
865 Reuses the domain id
867 """
868 try:
869 self.state = STATE_VM_OK
870 self.shutdown_pending = None
871 self.restart_check()
872 self.exportToDB()
873 self.restart_state = STATE_RESTART_BOOTING
874 if self.bootloader:
875 self.config = self.bootloader_config()
876 self.construct(self.config)
877 self.saveToDB()
878 finally:
879 self.restart_state = None
881 def bootloader_config(self):
882 # if we're restarting with a bootloader, we need to run it
883 # FIXME: this assumes the disk is the first device and
884 # that we're booting from the first disk
885 blcfg = None
886 # FIXME: this assumes that we want to use the first disk
887 dev = sxp.child_value(self.config, "device")
888 if dev:
889 disk = sxp.child_value(dev, "uname")
890 fn = blkdev_uname_to_file(disk)
891 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
892 if blcfg is None:
893 msg = "Had a bootloader specified, but can't find disk"
894 log.error(msg)
895 raise VmError(msg)
896 config = sxp.merge(['vm', blconfig ], self.config)
897 return config
899 def configure_backends(self):
900 """Set configuration flags if the vm is a backend for netif or blkif.
901 Configure the backends to use for vbd and vif if specified.
902 """
903 for c in sxp.children(self.config, 'backend'):
904 v = sxp.child0(c)
905 name = sxp.name(v)
906 if name == 'blkif':
907 self.blkif_backend = True
908 elif name == 'netif':
909 self.netif_backend = True
910 elif name == 'usbif':
911 self.usbif_backend = True
912 else:
913 raise VmError('invalid backend type:' + str(name))
915 def configure(self):
916 """Configure a vm.
918 """
919 self.configure_fields()
920 self.create_devices()
921 self.create_blkif()
923 def create_blkif(self):
924 """Create the block device interface (blkif) for the vm.
925 The vm needs a blkif even if it doesn't have any disks
926 at creation time, for example when it uses NFS root.
928 """
929 blkif = self.getDeviceController("vbd", error=False)
930 if not blkif:
931 blkif = self.createDeviceController("vbd")
932 backend = blkif.getBackend(0)
933 backend.connect(recreate=self.recreate)
935 def configure_fields(self):
936 """Process the vm configuration fields using the registered handlers.
937 """
938 index = {}
939 for field in sxp.children(self.config):
940 field_name = sxp.name(field)
941 field_index = index.get(field_name, 0)
942 field_handler = get_config_handler(field_name)
943 # Ignore unknown fields. Warn?
944 if field_handler:
945 v = field_handler(self, self.config, field, field_index)
946 else:
947 log.warning("Unknown config field %s", field_name)
948 index[field_name] = field_index + 1
950 def mem_target_set(self, target):
951 """Set domain memory target in bytes.
952 """
953 if target:
954 self.target = target * (1 << 20)
955 # Commit to XenStore immediately
956 self.exportToDB()
958 def vcpu_hotplug(self, vcpu, state):
959 """Disable or enable VCPU in domain.
960 """
961 db = ""
962 try:
963 db = self.vcpusdb['/cpu/%d'%(vcpu)]
964 except:
965 log.error("Invalid VCPU")
966 return
968 if self.store_channel:
969 if int(state) == 0:
970 db['availability'] = "offline"
971 else:
972 db['availability'] = "online"
974 db.saveDB(save=True)
976 def shutdown(self, reason):
977 if not reason in shutdown_reasons.values():
978 raise XendError('invalid reason:' + reason)
979 db = self.db.addChild("/control");
980 db['shutdown'] = reason;
981 db.saveDB(save=True);
982 if not reason in ['suspend']:
983 self.shutdown_pending = {'start':time.time(), 'reason':reason}
985 def clear_shutdown(self):
986 db = self.db.addChild("/control")
987 db['shutdown'] = ""
988 db.saveDB(save=True)
990 def send_sysrq(self, key=0):
991 db = self.db.addChild("/control");
992 db['sysrq'] = '%c' % key;
993 db.saveDB(save=True);
995 def shutdown_time_left(self, timeout):
996 if not self.shutdown_pending:
997 return 0
998 return timeout - (time.time() - self.shutdown_pending['start'])
1000 def dom0_init_store(self):
1001 if not self.store_channel:
1002 self.store_channel = self.eventChannel("store_channel")
1003 self.store_mfn = xc.init_store(self.store_channel.port2)
1004 if self.store_mfn >= 0:
1005 self.db.introduceDomain(self.id, self.store_mfn,
1006 self.store_channel)
1007 self.exportToDB(save=True, sync=True)
1008 # get run-time value of vcpus and update store
1009 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1011 def vm_field_ignore(vm, config, val, index):
1012 """Dummy config field handler used for fields with built-in handling.
1014 @param vm: virtual machine
1015 @param config: vm config
1016 @param val: config field
1017 @param index: field index
1018 """
1019 pass
1021 def vm_field_maxmem(vm, config, val, index):
1022 """Configure vm memory limit.
1024 @param vm: virtual machine
1025 @param config: vm config
1026 @param val: config field
1027 @param index: field index
1028 """
1029 maxmem = sxp.child0(val)
1030 if maxmem is None:
1031 maxmem = vm.memory
1032 try:
1033 maxmem = int(maxmem)
1034 except:
1035 raise VmError("invalid maxmem: " + str(maxmem))
1036 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1038 #============================================================================
1039 # Register image handlers.
1040 from image import \
1041 addImageHandlerClass, \
1042 ImageHandler, \
1043 LinuxImageHandler, \
1044 VmxImageHandler
1046 addImageHandlerClass(LinuxImageHandler)
1047 addImageHandlerClass(VmxImageHandler)
1049 # Ignore the fields we already handle.
1050 add_config_handler('name', vm_field_ignore)
1051 add_config_handler('memory', vm_field_ignore)
1052 add_config_handler('ssidref', vm_field_ignore)
1053 add_config_handler('cpu', vm_field_ignore)
1054 add_config_handler('cpu_weight', vm_field_ignore)
1055 add_config_handler('restart', vm_field_ignore)
1056 add_config_handler('image', vm_field_ignore)
1057 add_config_handler('device', vm_field_ignore)
1058 add_config_handler('backend', vm_field_ignore)
1059 add_config_handler('vcpus', vm_field_ignore)
1060 add_config_handler('bootloader', vm_field_ignore)
1062 # Register other config handlers.
1063 add_config_handler('maxmem', vm_field_maxmem)
1065 #============================================================================
1066 # Register device controllers and their device config types.
1068 from server import blkif
1069 controller.addDevControllerClass("vbd", blkif.BlkifController)
1070 add_device_handler("vbd", "vbd")
1072 from server import netif
1073 controller.addDevControllerClass("vif", netif.NetifController)
1074 add_device_handler("vif", "vif")
1076 from server import pciif
1077 controller.addDevControllerClass("pci", pciif.PciController)
1078 add_device_handler("pci", "pci")
1080 from xen.xend.server import usbif
1081 controller.addDevControllerClass("usb", usbif.UsbifController)
1082 add_device_handler("usb", "usb")
1084 #============================================================================