ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6515:ac8cae1f2c47

Don't attempt to create paravirtualized devices for VMX domains for now.

Signed-off-by: Arun Sharma <arun.sharma@intel.com>
author adsharma@los-vmm.sc.intel.com
date Tue Aug 09 11:06:45 2005 -0800 (2005-08-09)
parents c463720c5439
children dc61689b4781
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 if self.image.ostype != 'vmx':
747 self.create_configured_devices()
748 if not self.device_model_pid:
749 self.device_model_pid = self.image.createDeviceModel()
751 def device_create(self, dev_config):
752 """Create a new device.
754 @param dev_config: device configuration
755 """
756 dev_type = sxp.name(dev_config)
757 dev = self.createDevice(self, dev_config, change=True)
758 self.config.append(['device', dev.getConfig()])
759 return dev.sxpr()
761 def device_configure(self, dev_config, id):
762 """Configure an existing device.
764 @param dev_config: device configuration
765 @param id: device id
766 """
767 type = sxp.name(dev_config)
768 dev = self.getDevice(type, id)
769 old_config = dev.getConfig()
770 new_config = dev.configure(dev_config, change=True)
771 # Patch new config into vm config.
772 new_full_config = ['device', new_config]
773 old_full_config = ['device', old_config]
774 old_index = self.config.index(old_full_config)
775 self.config[old_index] = new_full_config
776 return new_config
778 def device_refresh(self, type, id):
779 """Refresh a device.
781 @param type: device type
782 @param id: device id
783 """
784 dev = self.getDevice(type, id)
785 dev.refresh()
787 def device_delete(self, type, id):
788 """Destroy and remove a device.
790 @param type: device type
791 @param id: device id
792 """
793 dev = self.getDevice(type, id)
794 dev_config = dev.getConfig()
795 if dev_config:
796 self.config.remove(['device', dev_config])
797 self.deleteDevice(type, dev.getId())
799 def configure_bootloader(self):
800 """Configure boot loader.
801 """
802 self.bootloader = sxp.child_value(self.config, "bootloader")
804 def configure_restart(self):
805 """Configure the vm restart mode.
806 """
807 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
808 if r not in restart_modes:
809 raise VmError('invalid restart mode: ' + str(r))
810 self.restart_mode = r;
812 def restart_needed(self, reason):
813 """Determine if the vm needs to be restarted when shutdown
814 for the given reason.
816 @param reason: shutdown reason
817 @return True if needs restart, False otherwise
818 """
819 if self.restart_mode == RESTART_NEVER:
820 return False
821 if self.restart_mode == RESTART_ALWAYS:
822 return True
823 if self.restart_mode == RESTART_ONREBOOT:
824 return reason == 'reboot'
825 return False
827 def restart_cancel(self):
828 """Cancel a vm restart.
829 """
830 self.restart_state = None
832 def restarting(self):
833 """Put the vm into restart mode.
834 """
835 self.restart_state = STATE_RESTART_PENDING
837 def restart_pending(self):
838 """Test if the vm has a pending restart.
839 """
840 return self.restart_state == STATE_RESTART_PENDING
842 def rebooting(self):
843 return self.restart_state == STATE_RESTART_BOOTING
845 def restart_check(self):
846 """Check if domain restart is OK.
847 To prevent restart loops, raise an error if it is
848 less than MINIMUM_RESTART_TIME seconds since the last restart.
849 """
850 tnow = time.time()
851 if self.restart_time is not None:
852 tdelta = tnow - self.restart_time
853 if tdelta < self.MINIMUM_RESTART_TIME:
854 self.restart_cancel()
855 msg = 'VM %s restarting too fast' % self.name
856 log.error(msg)
857 raise VmError(msg)
858 self.restart_time = tnow
859 self.restart_count += 1
861 def restart(self):
862 """Restart the domain after it has exited.
863 Reuses the domain id
865 """
866 try:
867 self.state = STATE_VM_OK
868 self.shutdown_pending = None
869 self.restart_check()
870 self.exportToDB()
871 self.restart_state = STATE_RESTART_BOOTING
872 if self.bootloader:
873 self.config = self.bootloader_config()
874 self.construct(self.config)
875 self.saveToDB()
876 finally:
877 self.restart_state = None
879 def bootloader_config(self):
880 # if we're restarting with a bootloader, we need to run it
881 # FIXME: this assumes the disk is the first device and
882 # that we're booting from the first disk
883 blcfg = None
884 # FIXME: this assumes that we want to use the first disk
885 dev = sxp.child_value(self.config, "device")
886 if dev:
887 disk = sxp.child_value(dev, "uname")
888 fn = blkdev_uname_to_file(disk)
889 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
890 if blcfg is None:
891 msg = "Had a bootloader specified, but can't find disk"
892 log.error(msg)
893 raise VmError(msg)
894 config = sxp.merge(['vm', blconfig ], self.config)
895 return config
897 def configure_backends(self):
898 """Set configuration flags if the vm is a backend for netif or blkif.
899 Configure the backends to use for vbd and vif if specified.
900 """
901 for c in sxp.children(self.config, 'backend'):
902 v = sxp.child0(c)
903 name = sxp.name(v)
904 if name == 'blkif':
905 self.blkif_backend = True
906 elif name == 'netif':
907 self.netif_backend = True
908 elif name == 'usbif':
909 self.usbif_backend = True
910 else:
911 raise VmError('invalid backend type:' + str(name))
913 def configure(self):
914 """Configure a vm.
916 """
917 self.configure_fields()
918 self.create_devices()
919 if self.image.ostype != 'vmx':
920 self.create_blkif()
922 def create_blkif(self):
923 """Create the block device interface (blkif) for the vm.
924 The vm needs a blkif even if it doesn't have any disks
925 at creation time, for example when it uses NFS root.
927 """
928 blkif = self.getDeviceController("vbd", error=False)
929 if not blkif:
930 blkif = self.createDeviceController("vbd")
931 backend = blkif.getBackend(0)
932 backend.connect(recreate=self.recreate)
934 def configure_fields(self):
935 """Process the vm configuration fields using the registered handlers.
936 """
937 index = {}
938 for field in sxp.children(self.config):
939 field_name = sxp.name(field)
940 field_index = index.get(field_name, 0)
941 field_handler = get_config_handler(field_name)
942 # Ignore unknown fields. Warn?
943 if field_handler:
944 v = field_handler(self, self.config, field, field_index)
945 else:
946 log.warning("Unknown config field %s", field_name)
947 index[field_name] = field_index + 1
949 def mem_target_set(self, target):
950 """Set domain memory target in bytes.
951 """
952 if target:
953 self.target = target * (1 << 20)
954 # Commit to XenStore immediately
955 self.exportToDB()
957 def vcpu_hotplug(self, vcpu, state):
958 """Disable or enable VCPU in domain.
959 """
960 db = ""
961 try:
962 db = self.vcpusdb['/cpus/cpu%d'%(vcpu)]
963 except:
964 log.error("Invalid VCPU")
965 return
967 if self.store_channel:
968 if int(state) == 0:
969 db['online'] = "0"
970 else:
971 db['online'] = "1"
973 db.saveDB(save=True)
975 def shutdown(self, reason):
976 if not reason in shutdown_reasons.values():
977 raise XendError('invalid reason:' + reason)
978 db = self.db.addChild("/control");
979 db['shutdown'] = reason;
980 db.saveDB(save=True);
981 if not reason in ['suspend']:
982 self.shutdown_pending = {'start':time.time(), 'reason':reason}
984 def send_sysrq(self, key=0):
985 db = self.db.addChild("/control");
986 db['sysrq'] = '%c' % key;
987 db.saveDB(save=True);
989 def shutdown_time_left(self, timeout):
990 if not self.shutdown_pending:
991 return 0
992 return timeout - (time.time() - self.shutdown_pending['start'])
994 def dom0_init_store(self):
995 if not self.store_channel:
996 self.store_channel = self.eventChannel("store_channel")
997 self.store_mfn = xc.init_store(self.store_channel.port2)
998 if self.store_mfn >= 0:
999 self.db.introduceDomain(self.id, self.store_mfn,
1000 self.store_channel)
1001 self.exportToDB(save=True, sync=True)
1002 # get run-time value of vcpus and update store
1003 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1005 def vm_field_ignore(vm, config, val, index):
1006 """Dummy config field handler used for fields with built-in handling.
1008 @param vm: virtual machine
1009 @param config: vm config
1010 @param val: config field
1011 @param index: field index
1012 """
1013 pass
1015 def vm_field_maxmem(vm, config, val, index):
1016 """Configure vm memory limit.
1018 @param vm: virtual machine
1019 @param config: vm config
1020 @param val: config field
1021 @param index: field index
1022 """
1023 maxmem = sxp.child0(val)
1024 if maxmem is None:
1025 maxmem = vm.memory
1026 try:
1027 maxmem = int(maxmem)
1028 except:
1029 raise VmError("invalid maxmem: " + str(maxmem))
1030 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1032 #============================================================================
1033 # Register image handlers.
1034 from image import \
1035 addImageHandlerClass, \
1036 ImageHandler, \
1037 LinuxImageHandler, \
1038 VmxImageHandler
1040 addImageHandlerClass(LinuxImageHandler)
1041 addImageHandlerClass(VmxImageHandler)
1043 # Ignore the fields we already handle.
1044 add_config_handler('name', vm_field_ignore)
1045 add_config_handler('memory', vm_field_ignore)
1046 add_config_handler('ssidref', vm_field_ignore)
1047 add_config_handler('cpu', vm_field_ignore)
1048 add_config_handler('cpu_weight', vm_field_ignore)
1049 add_config_handler('restart', vm_field_ignore)
1050 add_config_handler('image', vm_field_ignore)
1051 add_config_handler('device', vm_field_ignore)
1052 add_config_handler('backend', vm_field_ignore)
1053 add_config_handler('vcpus', vm_field_ignore)
1054 add_config_handler('bootloader', vm_field_ignore)
1056 # Register other config handlers.
1057 add_config_handler('maxmem', vm_field_maxmem)
1059 #============================================================================
1060 # Register device controllers and their device config types.
1062 from server import blkif
1063 controller.addDevControllerClass("vbd", blkif.BlkifController)
1064 add_device_handler("vbd", "vbd")
1066 from server import netif
1067 controller.addDevControllerClass("vif", netif.NetifController)
1068 add_device_handler("vif", "vif")
1070 from server import pciif
1071 controller.addDevControllerClass("pci", pciif.PciController)
1072 add_device_handler("pci", "pci")
1074 from xen.xend.server import usbif
1075 controller.addDevControllerClass("usb", usbif.UsbifController)
1076 add_device_handler("usb", "usb")
1078 #============================================================================