ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6422:e24fd7012ffb

merge?
author cl349@firebug.cl.cam.ac.uk
date Thu Aug 25 10:09:39 2005 +0000 (2005-08-25)
parents 2f20c2fce2c5 522bc50588ed
children 4abd299ef2f6
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, re
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
39 from xen.util.blkif import blkdev_name_to_number, expand_dev_name
41 from xen.xend import sxp
42 from xen.xend import Blkctl
43 from xen.xend.PrettyPrint import prettyprintstring
44 from xen.xend.XendBootloader import bootloader
45 from xen.xend.XendLogging import log
46 from xen.xend.XendError import XendError, VmError
47 from xen.xend.XendRoot import get_component
49 from xen.xend.uuid import getUuid
50 from xen.xend.xenstore import DBVar
52 """Shutdown code for poweroff."""
53 DOMAIN_POWEROFF = 0
55 """Shutdown code for reboot."""
56 DOMAIN_REBOOT = 1
58 """Shutdown code for suspend."""
59 DOMAIN_SUSPEND = 2
61 """Shutdown code for crash."""
62 DOMAIN_CRASH = 3
64 """Map shutdown codes to strings."""
65 shutdown_reasons = {
66 DOMAIN_POWEROFF: "poweroff",
67 DOMAIN_REBOOT : "reboot",
68 DOMAIN_SUSPEND : "suspend",
69 DOMAIN_CRASH : "crash",
70 }
72 RESTART_ALWAYS = 'always'
73 RESTART_ONREBOOT = 'onreboot'
74 RESTART_NEVER = 'never'
76 restart_modes = [
77 RESTART_ALWAYS,
78 RESTART_ONREBOOT,
79 RESTART_NEVER,
80 ]
82 STATE_RESTART_PENDING = 'pending'
83 STATE_RESTART_BOOTING = 'booting'
85 STATE_VM_OK = "ok"
86 STATE_VM_TERMINATED = "terminated"
87 STATE_VM_SUSPENDED = "suspended"
90 def domain_exists(name):
91 # See comment in XendDomain constructor.
92 xd = get_component('xen.xend.XendDomain')
93 return xd.domain_lookup_by_name(name)
95 def shutdown_reason(code):
96 """Get a shutdown reason from a code.
98 @param code: shutdown code
99 @type code: int
100 @return: shutdown reason
101 @rtype: string
102 """
103 return shutdown_reasons.get(code, "?")
105 config_handlers = {}
107 def add_config_handler(name, h):
108 """Add a handler for a config field.
110 @param name: field name
111 @param h: handler: fn(vm, config, field, index)
112 """
113 config_handlers[name] = h
115 def get_config_handler(name):
116 """Get a handler for a config field.
118 returns handler or None
119 """
120 return config_handlers.get(name)
122 """Table of handlers for devices.
123 Indexed by device type.
124 """
125 device_handlers = {}
127 def add_device_handler(name, type):
128 device_handlers[name] = type
130 def get_device_handler(name):
131 return device_handlers[name]
133 def dom_get(dom):
134 """Get info from xen for an existing domain.
136 @param dom: domain id
137 @return: info or None
138 """
139 domlist = xc.domain_getinfo(dom, 1)
140 if domlist and dom == domlist[0]['dom']:
141 return domlist[0]
142 return None
144 class XendDomainInfo:
145 """Virtual machine object."""
147 """Minimum time between domain restarts in seconds.
148 """
149 MINIMUM_RESTART_TIME = 20
151 def create(cls, parentdb, config):
152 """Create a VM from a configuration.
154 @param parentdb: parent db
155 @param config configuration
156 @raise: VmError for invalid configuration
157 """
158 uuid = getUuid()
159 db = parentdb.addChild(uuid)
160 vm = cls(db)
161 vm.construct(config)
162 vm.saveToDB(sync=True)
164 return vm
166 create = classmethod(create)
168 def recreate(cls, db, info):
169 """Create the VM object for an existing domain.
171 @param db: domain db
172 @param info: domain info from xc
173 """
174 dom = info['dom']
175 vm = cls(db)
176 vm.setdom(dom)
177 db.readDB()
178 vm.importFromDB()
179 config = vm.config
180 log.debug('info=' + str(info))
181 log.debug('config=' + prettyprintstring(config))
183 vm.memory = info['mem_kb']/1024
184 vm.target = info['mem_kb'] * 1024
186 if config:
187 try:
188 vm.recreate = True
189 vm.construct(config)
190 finally:
191 vm.recreate = False
192 else:
193 vm.setName("Domain-%d" % dom)
195 vm.exportToDB(save=True)
196 return vm
198 recreate = classmethod(recreate)
200 def restore(cls, parentdb, config, uuid=None):
201 """Create a domain and a VM object to do a restore.
203 @param parentdb: parent db
204 @param config: domain configuration
205 @param uuid: uuid to use
206 """
207 if not uuid:
208 uuid = getUuid()
209 db = parentdb.addChild(uuid)
210 vm = cls(db)
211 ssidref = int(sxp.child_value(config, 'ssidref'))
212 log.debug('restoring with ssidref='+str(ssidref))
213 id = xc.domain_create(ssidref = ssidref)
214 vm.setdom(id)
215 vm.clear_shutdown()
216 try:
217 vm.restore = True
218 vm.construct(config)
219 finally:
220 vm.restore = False
221 vm.exportToDB(save=True, sync=True)
222 return vm
224 restore = classmethod(restore)
226 __exports__ = [
227 DBVar('id', ty='int'),
228 DBVar('name', ty='str'),
229 DBVar('uuid', ty='str'),
230 DBVar('config', ty='sxpr'),
231 DBVar('start_time', ty='float'),
232 DBVar('state', ty='str'),
233 DBVar('store_mfn', ty='long'),
234 DBVar('restart_mode', ty='str'),
235 DBVar('restart_state', ty='str'),
236 DBVar('restart_time', ty='float'),
237 DBVar('restart_count', ty='int'),
238 DBVar('target', ty='long', path="memory/target"),
239 DBVar('device_model_pid', ty='int'),
240 ]
242 def __init__(self, db):
243 self.db = db
244 self.uuid = db.getName()
246 self.recreate = 0
247 self.restore = 0
249 self.config = None
250 self.id = None
251 self.cpu_weight = 1
252 self.start_time = None
253 self.name = None
254 self.memory = None
255 self.ssidref = None
256 self.image = None
258 self.target = None
260 self.channel = None
261 self.store_channel = None
262 self.store_mfn = None
263 self.controllers = {}
265 self.info = None
266 self.blkif_backend = False
267 self.netif_backend = False
268 self.netif_idx = 0
270 #todo: state: running, suspended
271 self.state = STATE_VM_OK
272 self.state_updated = threading.Condition()
273 self.shutdown_pending = None
275 #todo: set to migrate info if migrating
276 self.migrate = None
278 self.restart_mode = RESTART_ONREBOOT
279 self.restart_state = None
280 self.restart_time = None
281 self.restart_count = 0
283 self.vcpus = 1
284 self.vcpusdb = {}
285 self.bootloader = None
286 self.device_model_pid = 0
288 def setDB(self, db):
289 self.db = db
291 def saveToDB(self, save=False, sync=False):
292 self.db.saveDB(save=save, sync=sync)
294 def exportToDB(self, save=False, sync=False):
295 if self.channel:
296 self.channel.saveToDB(self.db.addChild("channel"), save=save)
297 if self.store_channel:
298 self.store_channel.saveToDB(self.db.addChild("store_channel"),
299 save=save)
300 if self.image:
301 self.image.exportToDB(save=save, sync=sync)
302 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
304 def importFromDB(self):
305 self.db.importFromDB(self, fields=self.__exports__)
306 self.store_channel = self.eventChannel("store_channel")
308 def setdom(self, dom):
309 """Set the domain id.
311 @param dom: domain id
312 """
313 self.id = int(dom)
314 #self.db.id = self.id
316 def getDomain(self):
317 return self.id
319 def setName(self, name):
320 self.name = name
321 self.db.name = self.name
323 def getName(self):
324 return self.name
326 def getChannel(self):
327 return self.channel
329 def getStoreChannel(self):
330 return self.store_channel
332 def update(self, info):
333 """Update with info from xc.domain_getinfo().
334 """
335 self.info = info
336 self.memory = self.info['mem_kb'] / 1024
337 self.ssidref = self.info['ssidref']
338 self.target = self.info['mem_kb'] * 1024
340 def state_set(self, state):
341 self.state_updated.acquire()
342 if self.state != state:
343 self.state = state
344 self.state_updated.notifyAll()
345 self.state_updated.release()
346 self.saveToDB()
348 def state_wait(self, state):
349 self.state_updated.acquire()
350 while self.state != state:
351 self.state_updated.wait()
352 self.state_updated.release()
354 def __str__(self):
355 s = "<domain"
356 s += " id=" + str(self.id)
357 s += " name=" + self.name
358 s += " memory=" + str(self.memory)
359 s += " ssidref=" + str(self.ssidref)
360 s += ">"
361 return s
363 __repr__ = __str__
365 def getDeviceTypes(self):
366 return self.controllers.keys()
368 def getDeviceControllers(self):
369 return self.controllers.values()
371 def getDeviceController(self, type, error=True):
372 ctrl = self.controllers.get(type)
373 if not ctrl and error:
374 raise XendError("invalid device type:" + type)
375 return ctrl
377 def findDeviceController(self, type):
378 return (self.getDeviceController(type, error=False)
379 or self.createDeviceController(type))
381 def createDeviceController(self, type):
382 ctrl = controller.createDevController(type, self, recreate=self.recreate)
383 self.controllers[type] = ctrl
384 return ctrl
386 def createDevice(self, type, devconfig, change=False):
387 if type == 'vbd':
388 typedev = sxp.child_value(devconfig, 'dev')
389 if re.match('^ioemu:', typedev):
390 return;
391 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
393 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
395 # create backend db
396 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
397 (type, self.uuid, devnum))
399 # create frontend db
400 db = self.db.addChild("/device/%s/%d" % (type, devnum))
402 db['virtual-device'] = "%i" % devnum
403 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
404 db['backend'] = backdb.getPath()
405 db['backend-id'] = "%i" % backdom.id
407 backdb['frontend'] = db.getPath()
408 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
409 node = Blkctl.block('bind', type, params)
410 backdb['frontend-id'] = "%i" % self.id
411 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
412 backdb.saveDB(save=True)
414 # Ok, super gross, this really doesn't belong in the frontend db...
415 db['type'] = type
416 db['node'] = node
417 db['params'] = params
418 db.saveDB(save=True)
420 return
422 if type == 'vif':
423 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
425 log.error(devconfig)
427 devnum = self.netif_idx
428 self.netif_idx += 1
430 # create backend db
431 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
432 (type, self.uuid, devnum))
434 # create frontend db
435 db = self.db.addChild("/device/%s/%d" % (type, devnum))
437 backdb['frontend'] = db.getPath()
438 backdb['frontend-id'] = "%i" % self.id
439 backdb['handle'] = "%i" % devnum
440 backdb.saveDB(save=True)
442 db['backend'] = backdb.getPath()
443 db['backend-id'] = "%i" % backdom.id
444 db['handle'] = "%i" % devnum
445 log.error(sxp.child_value(devconfig, 'mac'))
446 db['mac'] = sxp.child_value(devconfig, 'mac')
448 db.saveDB(save=True)
450 return
452 ctrl = self.findDeviceController(type)
453 return ctrl.createDevice(devconfig, recreate=self.recreate,
454 change=change)
456 def configureDevice(self, type, id, devconfig):
457 ctrl = self.getDeviceController(type)
458 return ctrl.configureDevice(id, devconfig)
460 def destroyDevice(self, type, id, change=False, reboot=False):
461 ctrl = self.getDeviceController(type)
462 return ctrl.destroyDevice(id, change=change, reboot=reboot)
464 def deleteDevice(self, type, id):
465 ctrl = self.getDeviceController(type)
466 return ctrl.deleteDevice(id)
468 def getDevice(self, type, id, error=True):
469 ctrl = self.getDeviceController(type)
470 return ctrl.getDevice(id, error=error)
472 def getDeviceIds(self, type):
473 ctrl = self.getDeviceController(type)
474 return ctrl.getDeviceIds()
476 def getDeviceSxprs(self, type):
477 ctrl = self.getDeviceController(type)
478 return ctrl.getDeviceSxprs()
480 def sxpr(self):
481 sxpr = ['domain',
482 ['id', self.id],
483 ['name', self.name],
484 ['memory', self.memory],
485 ['ssidref', self.ssidref],
486 ['target', self.target] ]
487 if self.uuid:
488 sxpr.append(['uuid', self.uuid])
489 if self.info:
490 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
491 run = (self.info['running'] and 'r') or '-'
492 block = (self.info['blocked'] and 'b') or '-'
493 pause = (self.info['paused'] and 'p') or '-'
494 shut = (self.info['shutdown'] and 's') or '-'
495 crash = (self.info['crashed'] and 'c') or '-'
496 state = run + block + pause + shut + crash
497 sxpr.append(['state', state])
498 if self.info['shutdown']:
499 reason = shutdown_reason(self.info['shutdown_reason'])
500 sxpr.append(['shutdown_reason', reason])
501 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
502 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
503 sxpr.append(['vcpus', self.info['vcpus']])
504 sxpr.append(['cpumap', self.info['cpumap']])
505 # build a string, using '|' to seperate items, show only up
506 # to number of vcpus in domain, and trim the trailing '|'
507 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
508 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
510 if self.start_time:
511 up_time = time.time() - self.start_time
512 sxpr.append(['up_time', str(up_time) ])
513 sxpr.append(['start_time', str(self.start_time) ])
515 if self.channel:
516 sxpr.append(self.channel.sxpr())
517 if self.store_channel:
518 sxpr.append(self.store_channel.sxpr())
519 if self.store_mfn:
520 sxpr.append(['store_mfn', self.store_mfn])
522 if self.restart_count:
523 sxpr.append(['restart_count', self.restart_count])
524 if self.restart_state:
525 sxpr.append(['restart_state', self.restart_state])
526 if self.restart_time:
527 sxpr.append(['restart_time', str(self.restart_time)])
529 devs = self.sxpr_devices()
530 if devs:
531 sxpr.append(devs)
532 if self.config:
533 sxpr.append(['config', self.config])
534 if self.device_model_pid:
535 sxpr.append(['device_model_pid',self.device_model_pid])
536 return sxpr
538 def sxpr_devices(self):
539 sxpr = []
540 for ty in self.getDeviceTypes():
541 devs = self.getDeviceSxprs(ty)
542 sxpr += devs
543 if sxpr:
544 sxpr.insert(0, 'devices')
545 else:
546 sxpr = None
547 return sxpr
549 def check_name(self, name):
550 """Check if a vm name is valid. Valid names contain alphabetic characters,
551 digits, or characters in '_-.:/+'.
552 The same name cannot be used for more than one vm at the same time.
554 @param name: name
555 @raise: VMerror if invalid
556 """
557 if self.recreate: return
558 if name is None or name == '':
559 raise VmError('missing vm name')
560 for c in name:
561 if c in string.digits: continue
562 if c in '_-.:/+': continue
563 if c in string.ascii_letters: continue
564 raise VmError('invalid vm name')
565 dominfo = domain_exists(name)
566 # When creating or rebooting, a domain with my name should not exist.
567 # When restoring, a domain with my name will exist, but it should have
568 # my domain id.
569 if not dominfo:
570 return
571 if dominfo.is_terminated():
572 return
573 if not self.id or (dominfo.id != self.id):
574 raise VmError('vm name clash: ' + name)
576 def construct(self, config):
577 """Construct the vm instance from its configuration.
579 @param config: configuration
580 @raise: VmError on error
581 """
582 # todo - add support for scheduling params?
583 self.config = config
584 try:
585 # Initial domain create.
586 self.setName(sxp.child_value(config, 'name'))
587 self.check_name(self.name)
588 self.init_image()
589 self.configure_cpus(config)
590 self.init_domain()
591 self.register_domain()
592 self.configure_bootloader()
594 # Create domain devices.
595 self.configure_backends()
596 self.configure_restart()
597 self.construct_image()
598 self.configure()
599 self.exportToDB(save=True)
600 except Exception, ex:
601 # Catch errors, cleanup and re-raise.
602 print 'Domain construction error:', ex
603 import traceback
604 traceback.print_exc()
605 self.destroy()
606 raise
608 def register_domain(self):
609 xd = get_component('xen.xend.XendDomain')
610 xd._add_domain(self)
611 self.exportToDB(save=True)
613 def configure_cpus(self, config):
614 try:
615 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
616 except:
617 raise VmError('invalid cpu weight')
618 self.memory = int(sxp.child_value(config, 'memory'))
619 if self.memory is None:
620 raise VmError('missing memory size')
621 self.target = self.memory * (1 << 20)
622 self.ssidref = int(sxp.child_value(config, 'ssidref'))
623 cpu = sxp.child_value(config, 'cpu')
624 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
625 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
626 try:
627 image = sxp.child_value(self.config, 'image')
628 vcpus = sxp.child_value(image, 'vcpus')
629 if vcpus:
630 self.vcpus = int(vcpus)
631 except:
632 raise VmError('invalid vcpus value')
634 def exportVCPUSToDB(self, vcpus):
635 for v in range(0,vcpus):
636 path = "/cpu/%d"%(v)
637 if not self.vcpusdb.has_key(path):
638 self.vcpusdb[path] = self.db.addChild(path)
639 db = self.vcpusdb[path]
640 log.debug("writing key availability=online to path %s in store"%(path))
641 db['availability'] = "online"
642 db.saveDB(save=True)
644 def init_image(self):
645 """Create boot image handler for the domain.
646 """
647 image = sxp.child_value(self.config, 'image')
648 if image is None:
649 raise VmError('missing image')
650 self.image = ImageHandler.create(self, image)
652 def construct_image(self):
653 """Construct the boot image for the domain.
654 """
655 self.create_channel()
656 self.image.createImage()
657 self.exportToDB()
658 if self.store_channel and self.store_mfn >= 0:
659 self.db.introduceDomain(self.id,
660 self.store_mfn,
661 self.store_channel)
662 # get the configured value of vcpus and update store
663 self.exportVCPUSToDB(self.vcpus)
665 def delete(self):
666 """Delete the vm's db.
667 """
668 if dom_get(self.id):
669 return
670 self.id = None
671 self.saveToDB(sync=True)
672 try:
673 # Todo: eventually will have to wait for devices to signal
674 # destruction before can delete the db.
675 if self.db:
676 self.db.delete()
677 except Exception, ex:
678 log.warning("error in domain db delete: %s", ex)
679 pass
681 def destroy_domain(self):
682 """Destroy the vm's domain.
683 The domain will not finally go away unless all vm
684 devices have been released.
685 """
686 if self.id is None:
687 return
688 try:
689 xc.domain_destroy(dom=self.id)
690 except Exception, err:
691 log.exception("Domain destroy failed: %s", self.name)
693 def cleanup(self):
694 """Cleanup vm resources: release devices.
695 """
696 self.state = STATE_VM_TERMINATED
697 self.release_devices()
698 if self.channel:
699 try:
700 self.channel.close()
701 self.channel = None
702 except:
703 pass
704 if self.store_channel:
705 try:
706 self.store_channel.close()
707 self.store_channel = None
708 except:
709 pass
710 try:
711 self.db.releaseDomain(self.id)
712 except Exception, ex:
713 log.warning("error in domain release on xenstore: %s", ex)
714 pass
715 if self.image:
716 try:
717 self.device_model_pid = 0
718 self.image.destroy()
719 self.image = None
720 except:
721 pass
723 def destroy(self):
724 """Clenup vm and destroy domain.
725 """
726 self.cleanup()
727 self.destroy_domain()
728 self.saveToDB()
729 return 0
731 def is_terminated(self):
732 """Check if a domain has been terminated.
733 """
734 return self.state == STATE_VM_TERMINATED
736 def release_devices(self):
737 """Release all vm devices.
738 """
739 reboot = self.restart_pending()
740 for ctrl in self.getDeviceControllers():
741 if ctrl.isDestroyed(): continue
742 ctrl.destroyController(reboot=reboot)
743 ddb = self.db.addChild("/device")
744 for type in ddb.keys():
745 if type == 'vbd':
746 typedb = ddb.addChild(type)
747 for dev in typedb.keys():
748 devdb = typedb.addChild(str(dev))
749 Blkctl.block('unbind', devdb['type'].getData(),
750 devdb['node'].getData())
751 typedb[dev].delete()
752 typedb.saveDB(save=True)
754 def show(self):
755 """Print virtual machine info.
756 """
757 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
758 print "image:"
759 sxp.show(self.image)
760 print "]"
762 def init_domain(self):
763 """Initialize the domain memory.
764 """
765 if self.recreate:
766 return
767 if self.start_time is None:
768 self.start_time = time.time()
769 try:
770 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
771 except:
772 raise VmError('invalid cpu')
773 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
774 log.debug('init_domain> Created domain=%d name=%s memory=%d',
775 id, self.name, self.memory)
776 self.setdom(id)
778 def openChannel(self, key, local, remote):
779 """Create a control channel to the domain.
780 If saved info is available recreate the channel.
782 @param key db key for the saved data (if any)
783 @param local default local port
784 @param remote default remote port
785 """
786 db = self.db.addChild(key)
787 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
788 #todo: save here?
789 #chan.saveToDB(db)
790 return chan
792 def eventChannel(self, key):
793 """Create an event channel to the domain.
794 If saved info is available recreate the channel.
796 @param key db key for the saved data (if any)
797 """
798 db = self.db.addChild(key)
799 return EventChannel.restoreFromDB(db, 0, self.id)
801 def create_channel(self):
802 """Create the channels to the domain.
803 """
804 self.channel = self.openChannel("channel", 0, 1)
805 self.store_channel = self.eventChannel("store_channel")
807 def create_configured_devices(self):
808 devices = sxp.children(self.config, 'device')
809 for d in devices:
810 dev_config = sxp.child0(d)
811 if dev_config is None:
812 raise VmError('invalid device')
813 dev_type = sxp.name(dev_config)
814 ctrl_type = get_device_handler(dev_type)
815 if ctrl_type is None:
816 raise VmError('unknown device type: ' + dev_type)
817 self.createDevice(ctrl_type, dev_config)
819 def create_devices(self):
820 """Create the devices for a vm.
822 @raise: VmError for invalid devices
823 """
824 if self.rebooting():
825 for ctrl in self.getDeviceControllers():
826 ctrl.initController(reboot=True)
827 else:
828 self.create_configured_devices()
829 if not self.device_model_pid:
830 self.device_model_pid = self.image.createDeviceModel()
832 def device_create(self, dev_config):
833 """Create a new device.
835 @param dev_config: device configuration
836 """
837 dev_type = sxp.name(dev_config)
838 dev = self.createDevice(dev_type, dev_config, change=True)
839 self.config.append(['device', dev.getConfig()])
840 return dev.sxpr()
842 def device_configure(self, dev_config, id):
843 """Configure an existing device.
845 @param dev_config: device configuration
846 @param id: device id
847 """
848 type = sxp.name(dev_config)
849 dev = self.getDevice(type, id)
850 old_config = dev.getConfig()
851 new_config = dev.configure(dev_config, change=True)
852 # Patch new config into vm config.
853 new_full_config = ['device', new_config]
854 old_full_config = ['device', old_config]
855 old_index = self.config.index(old_full_config)
856 self.config[old_index] = new_full_config
857 return new_config
859 def device_refresh(self, type, id):
860 """Refresh a device.
862 @param type: device type
863 @param id: device id
864 """
865 dev = self.getDevice(type, id)
866 dev.refresh()
868 def device_delete(self, type, id):
869 """Destroy and remove a device.
871 @param type: device type
872 @param id: device id
873 """
874 dev = self.getDevice(type, id)
875 dev_config = dev.getConfig()
876 if dev_config:
877 self.config.remove(['device', dev_config])
878 self.deleteDevice(type, dev.getId())
880 def configure_bootloader(self):
881 """Configure boot loader.
882 """
883 self.bootloader = sxp.child_value(self.config, "bootloader")
885 def configure_restart(self):
886 """Configure the vm restart mode.
887 """
888 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
889 if r not in restart_modes:
890 raise VmError('invalid restart mode: ' + str(r))
891 self.restart_mode = r;
893 def restart_needed(self, reason):
894 """Determine if the vm needs to be restarted when shutdown
895 for the given reason.
897 @param reason: shutdown reason
898 @return True if needs restart, False otherwise
899 """
900 if self.restart_mode == RESTART_NEVER:
901 return False
902 if self.restart_mode == RESTART_ALWAYS:
903 return True
904 if self.restart_mode == RESTART_ONREBOOT:
905 return reason == 'reboot'
906 return False
908 def restart_cancel(self):
909 """Cancel a vm restart.
910 """
911 self.restart_state = None
913 def restarting(self):
914 """Put the vm into restart mode.
915 """
916 self.restart_state = STATE_RESTART_PENDING
918 def restart_pending(self):
919 """Test if the vm has a pending restart.
920 """
921 return self.restart_state == STATE_RESTART_PENDING
923 def rebooting(self):
924 return self.restart_state == STATE_RESTART_BOOTING
926 def restart_check(self):
927 """Check if domain restart is OK.
928 To prevent restart loops, raise an error if it is
929 less than MINIMUM_RESTART_TIME seconds since the last restart.
930 """
931 tnow = time.time()
932 if self.restart_time is not None:
933 tdelta = tnow - self.restart_time
934 if tdelta < self.MINIMUM_RESTART_TIME:
935 self.restart_cancel()
936 msg = 'VM %s restarting too fast' % self.name
937 log.error(msg)
938 raise VmError(msg)
939 self.restart_time = tnow
940 self.restart_count += 1
942 def restart(self):
943 """Restart the domain after it has exited.
944 Reuses the domain id
946 """
947 try:
948 self.state = STATE_VM_OK
949 self.shutdown_pending = None
950 self.restart_check()
951 self.exportToDB()
952 self.restart_state = STATE_RESTART_BOOTING
953 if self.bootloader:
954 self.config = self.bootloader_config()
955 self.construct(self.config)
956 self.saveToDB()
957 finally:
958 self.restart_state = None
960 def bootloader_config(self):
961 # if we're restarting with a bootloader, we need to run it
962 # FIXME: this assumes the disk is the first device and
963 # that we're booting from the first disk
964 blcfg = None
965 # FIXME: this assumes that we want to use the first disk
966 dev = sxp.child_value(self.config, "device")
967 if dev:
968 disk = sxp.child_value(dev, "uname")
969 fn = blkdev_uname_to_file(disk)
970 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
971 if blcfg is None:
972 msg = "Had a bootloader specified, but can't find disk"
973 log.error(msg)
974 raise VmError(msg)
975 config = sxp.merge(['vm', blconfig ], self.config)
976 return config
978 def configure_backends(self):
979 """Set configuration flags if the vm is a backend for netif or blkif.
980 Configure the backends to use for vbd and vif if specified.
981 """
982 for c in sxp.children(self.config, 'backend'):
983 v = sxp.child0(c)
984 name = sxp.name(v)
985 if name == 'blkif':
986 self.blkif_backend = True
987 elif name == 'netif':
988 self.netif_backend = True
989 elif name == 'usbif':
990 self.usbif_backend = True
991 else:
992 raise VmError('invalid backend type:' + str(name))
994 def configure(self):
995 """Configure a vm.
997 """
998 self.configure_fields()
999 self.create_devices()
1000 self.create_blkif()
1002 def create_blkif(self):
1003 """Create the block device interface (blkif) for the vm.
1004 The vm needs a blkif even if it doesn't have any disks
1005 at creation time, for example when it uses NFS root.
1007 """
1008 return
1009 blkif = self.getDeviceController("vbd", error=False)
1010 if not blkif:
1011 blkif = self.createDeviceController("vbd")
1012 backend = blkif.getBackend(0)
1013 backend.connect(recreate=self.recreate)
1015 def configure_fields(self):
1016 """Process the vm configuration fields using the registered handlers.
1017 """
1018 index = {}
1019 for field in sxp.children(self.config):
1020 field_name = sxp.name(field)
1021 field_index = index.get(field_name, 0)
1022 field_handler = get_config_handler(field_name)
1023 # Ignore unknown fields. Warn?
1024 if field_handler:
1025 v = field_handler(self, self.config, field, field_index)
1026 else:
1027 log.warning("Unknown config field %s", field_name)
1028 index[field_name] = field_index + 1
1030 def mem_target_set(self, target):
1031 """Set domain memory target in bytes.
1032 """
1033 if target:
1034 self.target = target * (1 << 20)
1035 # Commit to XenStore immediately
1036 self.exportToDB()
1038 def vcpu_hotplug(self, vcpu, state):
1039 """Disable or enable VCPU in domain.
1040 """
1041 db = ""
1042 try:
1043 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1044 except:
1045 log.error("Invalid VCPU")
1046 return
1048 if self.store_channel:
1049 if int(state) == 0:
1050 db['availability'] = "offline"
1051 else:
1052 db['availability'] = "online"
1054 db.saveDB(save=True)
1056 def shutdown(self, reason):
1057 if not reason in shutdown_reasons.values():
1058 raise XendError('invalid reason:' + reason)
1059 db = self.db.addChild("/control");
1060 db['shutdown'] = reason;
1061 db.saveDB(save=True);
1062 if not reason in ['suspend']:
1063 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1065 def clear_shutdown(self):
1066 db = self.db.addChild("/control")
1067 db['shutdown'] = ""
1068 db.saveDB(save=True)
1070 def send_sysrq(self, key=0):
1071 db = self.db.addChild("/control");
1072 db['sysrq'] = '%c' % key;
1073 db.saveDB(save=True);
1075 def shutdown_time_left(self, timeout):
1076 if not self.shutdown_pending:
1077 return 0
1078 return timeout - (time.time() - self.shutdown_pending['start'])
1080 def dom0_init_store(self):
1081 if not self.store_channel:
1082 self.store_channel = self.eventChannel("store_channel")
1083 self.store_mfn = xc.init_store(self.store_channel.port2)
1084 if self.store_mfn >= 0:
1085 self.db.introduceDomain(self.id, self.store_mfn,
1086 self.store_channel)
1087 self.exportToDB(save=True, sync=True)
1088 # get run-time value of vcpus and update store
1089 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1091 def vm_field_ignore(vm, config, val, index):
1092 """Dummy config field handler used for fields with built-in handling.
1094 @param vm: virtual machine
1095 @param config: vm config
1096 @param val: config field
1097 @param index: field index
1098 """
1099 pass
1101 def vm_field_maxmem(vm, config, val, index):
1102 """Configure vm memory limit.
1104 @param vm: virtual machine
1105 @param config: vm config
1106 @param val: config field
1107 @param index: field index
1108 """
1109 maxmem = sxp.child0(val)
1110 if maxmem is None:
1111 maxmem = vm.memory
1112 try:
1113 maxmem = int(maxmem)
1114 except:
1115 raise VmError("invalid maxmem: " + str(maxmem))
1116 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1118 #============================================================================
1119 # Register image handlers.
1120 from image import \
1121 addImageHandlerClass, \
1122 ImageHandler, \
1123 LinuxImageHandler, \
1124 VmxImageHandler
1126 addImageHandlerClass(LinuxImageHandler)
1127 addImageHandlerClass(VmxImageHandler)
1129 # Ignore the fields we already handle.
1130 add_config_handler('name', vm_field_ignore)
1131 add_config_handler('memory', vm_field_ignore)
1132 add_config_handler('ssidref', vm_field_ignore)
1133 add_config_handler('cpu', vm_field_ignore)
1134 add_config_handler('cpu_weight', vm_field_ignore)
1135 add_config_handler('restart', vm_field_ignore)
1136 add_config_handler('image', vm_field_ignore)
1137 add_config_handler('device', vm_field_ignore)
1138 add_config_handler('backend', vm_field_ignore)
1139 add_config_handler('vcpus', vm_field_ignore)
1140 add_config_handler('bootloader', vm_field_ignore)
1142 # Register other config handlers.
1143 add_config_handler('maxmem', vm_field_maxmem)
1145 #============================================================================
1146 # Register device controllers and their device config types.
1148 from server import blkif
1149 controller.addDevControllerClass("vbd", blkif.BlkifController)
1150 add_device_handler("vbd", "vbd")
1152 from server import netif
1153 controller.addDevControllerClass("vif", netif.NetifController)
1154 add_device_handler("vif", "vif")
1156 from server import pciif
1157 controller.addDevControllerClass("pci", pciif.PciController)
1158 add_device_handler("pci", "pci")
1160 from xen.xend.server import usbif
1161 controller.addDevControllerClass("usb", usbif.UsbifController)
1162 add_device_handler("usb", "usb")
1164 #============================================================================