ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6552:a9873d384da4

Merge.
author adsharma@los-vmm.sc.intel.com
date Thu Aug 25 12:24:48 2005 -0700 (2005-08-25)
parents 112d44270733 fa0754a9f64f
children dfaf788ab18c
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)
753 if type == 'vif':
754 typedb = ddb.addChild(type)
755 for dev in typedb.keys():
756 typedb[dev].delete()
757 typedb.saveDB(save=True)
759 def show(self):
760 """Print virtual machine info.
761 """
762 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
763 print "image:"
764 sxp.show(self.image)
765 print "]"
767 def init_domain(self):
768 """Initialize the domain memory.
769 """
770 if self.recreate:
771 return
772 if self.start_time is None:
773 self.start_time = time.time()
774 try:
775 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
776 except:
777 raise VmError('invalid cpu')
778 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
779 log.debug('init_domain> Created domain=%d name=%s memory=%d',
780 id, self.name, self.memory)
781 self.setdom(id)
783 def openChannel(self, key, local, remote):
784 """Create a control channel to the domain.
785 If saved info is available recreate the channel.
787 @param key db key for the saved data (if any)
788 @param local default local port
789 @param remote default remote port
790 """
791 db = self.db.addChild(key)
792 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
793 #todo: save here?
794 #chan.saveToDB(db)
795 return chan
797 def eventChannel(self, key):
798 """Create an event channel to the domain.
799 If saved info is available recreate the channel.
801 @param key db key for the saved data (if any)
802 """
803 db = self.db.addChild(key)
804 return EventChannel.restoreFromDB(db, 0, self.id)
806 def create_channel(self):
807 """Create the channels to the domain.
808 """
809 self.channel = self.openChannel("channel", 0, 1)
810 self.store_channel = self.eventChannel("store_channel")
812 def create_configured_devices(self):
813 devices = sxp.children(self.config, 'device')
814 for d in devices:
815 dev_config = sxp.child0(d)
816 if dev_config is None:
817 raise VmError('invalid device')
818 dev_type = sxp.name(dev_config)
819 ctrl_type = get_device_handler(dev_type)
820 if ctrl_type is None:
821 raise VmError('unknown device type: ' + dev_type)
822 self.createDevice(ctrl_type, dev_config)
824 def create_devices(self):
825 """Create the devices for a vm.
827 @raise: VmError for invalid devices
828 """
829 if self.rebooting():
830 for ctrl in self.getDeviceControllers():
831 ctrl.initController(reboot=True)
832 else:
833 self.create_configured_devices()
834 if not self.device_model_pid:
835 self.device_model_pid = self.image.createDeviceModel()
837 def device_create(self, dev_config):
838 """Create a new device.
840 @param dev_config: device configuration
841 """
842 dev_type = sxp.name(dev_config)
843 dev = self.createDevice(dev_type, dev_config, change=True)
844 self.config.append(['device', dev.getConfig()])
845 return dev.sxpr()
847 def device_configure(self, dev_config, id):
848 """Configure an existing device.
850 @param dev_config: device configuration
851 @param id: device id
852 """
853 type = sxp.name(dev_config)
854 dev = self.getDevice(type, id)
855 old_config = dev.getConfig()
856 new_config = dev.configure(dev_config, change=True)
857 # Patch new config into vm config.
858 new_full_config = ['device', new_config]
859 old_full_config = ['device', old_config]
860 old_index = self.config.index(old_full_config)
861 self.config[old_index] = new_full_config
862 return new_config
864 def device_refresh(self, type, id):
865 """Refresh a device.
867 @param type: device type
868 @param id: device id
869 """
870 dev = self.getDevice(type, id)
871 dev.refresh()
873 def device_delete(self, type, id):
874 """Destroy and remove a device.
876 @param type: device type
877 @param id: device id
878 """
879 dev = self.getDevice(type, id)
880 dev_config = dev.getConfig()
881 if dev_config:
882 self.config.remove(['device', dev_config])
883 self.deleteDevice(type, dev.getId())
885 def configure_bootloader(self):
886 """Configure boot loader.
887 """
888 self.bootloader = sxp.child_value(self.config, "bootloader")
890 def configure_restart(self):
891 """Configure the vm restart mode.
892 """
893 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
894 if r not in restart_modes:
895 raise VmError('invalid restart mode: ' + str(r))
896 self.restart_mode = r;
898 def restart_needed(self, reason):
899 """Determine if the vm needs to be restarted when shutdown
900 for the given reason.
902 @param reason: shutdown reason
903 @return True if needs restart, False otherwise
904 """
905 if self.restart_mode == RESTART_NEVER:
906 return False
907 if self.restart_mode == RESTART_ALWAYS:
908 return True
909 if self.restart_mode == RESTART_ONREBOOT:
910 return reason == 'reboot'
911 return False
913 def restart_cancel(self):
914 """Cancel a vm restart.
915 """
916 self.restart_state = None
918 def restarting(self):
919 """Put the vm into restart mode.
920 """
921 self.restart_state = STATE_RESTART_PENDING
923 def restart_pending(self):
924 """Test if the vm has a pending restart.
925 """
926 return self.restart_state == STATE_RESTART_PENDING
928 def rebooting(self):
929 return self.restart_state == STATE_RESTART_BOOTING
931 def restart_check(self):
932 """Check if domain restart is OK.
933 To prevent restart loops, raise an error if it is
934 less than MINIMUM_RESTART_TIME seconds since the last restart.
935 """
936 tnow = time.time()
937 if self.restart_time is not None:
938 tdelta = tnow - self.restart_time
939 if tdelta < self.MINIMUM_RESTART_TIME:
940 self.restart_cancel()
941 msg = 'VM %s restarting too fast' % self.name
942 log.error(msg)
943 raise VmError(msg)
944 self.restart_time = tnow
945 self.restart_count += 1
947 def restart(self):
948 """Restart the domain after it has exited.
949 Reuses the domain id
951 """
952 try:
953 self.state = STATE_VM_OK
954 self.shutdown_pending = None
955 self.restart_check()
956 self.exportToDB()
957 self.restart_state = STATE_RESTART_BOOTING
958 if self.bootloader:
959 self.config = self.bootloader_config()
960 self.construct(self.config)
961 self.saveToDB()
962 finally:
963 self.restart_state = None
965 def bootloader_config(self):
966 # if we're restarting with a bootloader, we need to run it
967 # FIXME: this assumes the disk is the first device and
968 # that we're booting from the first disk
969 blcfg = None
970 # FIXME: this assumes that we want to use the first disk
971 dev = sxp.child_value(self.config, "device")
972 if dev:
973 disk = sxp.child_value(dev, "uname")
974 fn = blkdev_uname_to_file(disk)
975 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
976 if blcfg is None:
977 msg = "Had a bootloader specified, but can't find disk"
978 log.error(msg)
979 raise VmError(msg)
980 config = sxp.merge(['vm', blconfig ], self.config)
981 return config
983 def configure_backends(self):
984 """Set configuration flags if the vm is a backend for netif or blkif.
985 Configure the backends to use for vbd and vif if specified.
986 """
987 for c in sxp.children(self.config, 'backend'):
988 v = sxp.child0(c)
989 name = sxp.name(v)
990 if name == 'blkif':
991 self.blkif_backend = True
992 elif name == 'netif':
993 self.netif_backend = True
994 elif name == 'usbif':
995 self.usbif_backend = True
996 else:
997 raise VmError('invalid backend type:' + str(name))
999 def configure(self):
1000 """Configure a vm.
1002 """
1003 self.configure_fields()
1004 self.create_devices()
1005 self.create_blkif()
1007 def create_blkif(self):
1008 """Create the block device interface (blkif) for the vm.
1009 The vm needs a blkif even if it doesn't have any disks
1010 at creation time, for example when it uses NFS root.
1012 """
1013 return
1014 blkif = self.getDeviceController("vbd", error=False)
1015 if not blkif:
1016 blkif = self.createDeviceController("vbd")
1017 backend = blkif.getBackend(0)
1018 backend.connect(recreate=self.recreate)
1020 def configure_fields(self):
1021 """Process the vm configuration fields using the registered handlers.
1022 """
1023 index = {}
1024 for field in sxp.children(self.config):
1025 field_name = sxp.name(field)
1026 field_index = index.get(field_name, 0)
1027 field_handler = get_config_handler(field_name)
1028 # Ignore unknown fields. Warn?
1029 if field_handler:
1030 v = field_handler(self, self.config, field, field_index)
1031 else:
1032 log.warning("Unknown config field %s", field_name)
1033 index[field_name] = field_index + 1
1035 def mem_target_set(self, target):
1036 """Set domain memory target in bytes.
1037 """
1038 if target:
1039 self.target = target * (1 << 20)
1040 # Commit to XenStore immediately
1041 self.exportToDB()
1043 def vcpu_hotplug(self, vcpu, state):
1044 """Disable or enable VCPU in domain.
1045 """
1046 db = ""
1047 try:
1048 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1049 except:
1050 log.error("Invalid VCPU")
1051 return
1053 if self.store_channel:
1054 if int(state) == 0:
1055 db['availability'] = "offline"
1056 else:
1057 db['availability'] = "online"
1059 db.saveDB(save=True)
1061 def shutdown(self, reason):
1062 if not reason in shutdown_reasons.values():
1063 raise XendError('invalid reason:' + reason)
1064 db = self.db.addChild("/control");
1065 db['shutdown'] = reason;
1066 db.saveDB(save=True);
1067 if not reason in ['suspend']:
1068 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1070 def clear_shutdown(self):
1071 db = self.db.addChild("/control")
1072 db['shutdown'] = ""
1073 db.saveDB(save=True)
1075 def send_sysrq(self, key=0):
1076 db = self.db.addChild("/control");
1077 db['sysrq'] = '%c' % key;
1078 db.saveDB(save=True);
1080 def shutdown_time_left(self, timeout):
1081 if not self.shutdown_pending:
1082 return 0
1083 return timeout - (time.time() - self.shutdown_pending['start'])
1085 def dom0_init_store(self):
1086 if not self.store_channel:
1087 self.store_channel = self.eventChannel("store_channel")
1088 self.store_mfn = xc.init_store(self.store_channel.port2)
1089 if self.store_mfn >= 0:
1090 self.db.introduceDomain(self.id, self.store_mfn,
1091 self.store_channel)
1092 self.exportToDB(save=True, sync=True)
1093 # get run-time value of vcpus and update store
1094 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1096 def vm_field_ignore(vm, config, val, index):
1097 """Dummy config field handler used for fields with built-in handling.
1099 @param vm: virtual machine
1100 @param config: vm config
1101 @param val: config field
1102 @param index: field index
1103 """
1104 pass
1106 def vm_field_maxmem(vm, config, val, index):
1107 """Configure vm memory limit.
1109 @param vm: virtual machine
1110 @param config: vm config
1111 @param val: config field
1112 @param index: field index
1113 """
1114 maxmem = sxp.child0(val)
1115 if maxmem is None:
1116 maxmem = vm.memory
1117 try:
1118 maxmem = int(maxmem)
1119 except:
1120 raise VmError("invalid maxmem: " + str(maxmem))
1121 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1123 #============================================================================
1124 # Register image handlers.
1125 from image import \
1126 addImageHandlerClass, \
1127 ImageHandler, \
1128 LinuxImageHandler, \
1129 VmxImageHandler
1131 addImageHandlerClass(LinuxImageHandler)
1132 addImageHandlerClass(VmxImageHandler)
1134 # Ignore the fields we already handle.
1135 add_config_handler('name', vm_field_ignore)
1136 add_config_handler('memory', vm_field_ignore)
1137 add_config_handler('ssidref', vm_field_ignore)
1138 add_config_handler('cpu', vm_field_ignore)
1139 add_config_handler('cpu_weight', vm_field_ignore)
1140 add_config_handler('restart', vm_field_ignore)
1141 add_config_handler('image', vm_field_ignore)
1142 add_config_handler('device', vm_field_ignore)
1143 add_config_handler('backend', vm_field_ignore)
1144 add_config_handler('vcpus', vm_field_ignore)
1145 add_config_handler('bootloader', vm_field_ignore)
1147 # Register other config handlers.
1148 add_config_handler('maxmem', vm_field_maxmem)
1150 #============================================================================
1151 # Register device controllers and their device config types.
1153 from server import blkif
1154 controller.addDevControllerClass("vbd", blkif.BlkifController)
1155 add_device_handler("vbd", "vbd")
1157 from server import netif
1158 controller.addDevControllerClass("vif", netif.NetifController)
1159 add_device_handler("vif", "vif")
1161 from server import pciif
1162 controller.addDevControllerClass("pci", pciif.PciController)
1163 add_device_handler("pci", "pci")
1165 from xen.xend.server import usbif
1166 controller.addDevControllerClass("usb", usbif.UsbifController)
1167 add_device_handler("usb", "usb")
1169 #============================================================================