ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6594:2c41ae58d6b2

The attached patch makes xend clear the reboot request from the store
when rebooting a domain. This prevents the lingering request from
being re-fulfilled, such as when executing a subsequent sysrq request.

Signed-off-by: Dan Smith <danms@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Sep 02 07:53:46 2005 +0000 (2005-09-02)
parents dd668f7527cb
children 291e816acbf4
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, XenNode, DBMap
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('console_mfn', ty='long'),
235 DBVar('restart_mode', ty='str'),
236 DBVar('restart_state', ty='str'),
237 DBVar('restart_time', ty='float'),
238 DBVar('restart_count', ty='int'),
239 DBVar('target', ty='long', path="memory/target"),
240 DBVar('device_model_pid', ty='int'),
241 ]
243 def __init__(self, db):
244 self.db = db
245 self.uuid = db.getName()
247 self.recreate = 0
248 self.restore = 0
250 self.config = None
251 self.id = None
252 self.cpu_weight = 1
253 self.start_time = None
254 self.name = None
255 self.memory = None
256 self.ssidref = None
257 self.image = None
259 self.target = None
261 self.channel = None
262 self.store_channel = None
263 self.store_mfn = None
264 self.console_channel = None
265 self.console_mfn = None
266 self.controllers = {}
268 self.info = None
269 self.blkif_backend = False
270 self.netif_backend = False
271 self.netif_idx = 0
272 self.tpmif_backend = False
274 #todo: state: running, suspended
275 self.state = STATE_VM_OK
276 self.state_updated = threading.Condition()
277 self.shutdown_pending = None
279 #todo: set to migrate info if migrating
280 self.migrate = None
282 self.restart_mode = RESTART_ONREBOOT
283 self.restart_state = None
284 self.restart_time = None
285 self.restart_count = 0
287 self.vcpus = 1
288 self.vcpusdb = {}
289 self.bootloader = None
290 self.device_model_pid = 0
292 def setDB(self, db):
293 self.db = db
295 def saveToDB(self, save=False, sync=False):
296 self.db.saveDB(save=save, sync=sync)
298 def exportToDB(self, save=False, sync=False):
299 if self.channel:
300 self.channel.saveToDB(self.db.addChild("channel"), save=save)
301 if self.store_channel:
302 self.store_channel.saveToDB(self.db.addChild("store_channel"),
303 save=save)
304 if self.console_channel:
305 self.console_channel.saveToDB(self.db.addChild("console_channel"),
306 save=save)
307 if self.image:
308 self.image.exportToDB(save=save, sync=sync)
309 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
311 def importFromDB(self):
312 self.db.importFromDB(self, fields=self.__exports__)
313 self.store_channel = self.eventChannel("store_channel")
315 def setdom(self, dom):
316 """Set the domain id.
318 @param dom: domain id
319 """
320 self.id = int(dom)
321 #self.db.id = self.id
323 def getDomain(self):
324 return self.id
326 def setName(self, name):
327 self.name = name
328 self.db.name = self.name
330 def getName(self):
331 return self.name
333 def getChannel(self):
334 return self.channel
336 def getStoreChannel(self):
337 return self.store_channel
339 def getConsoleChannel(self):
340 return self.console_channel
342 def update(self, info=None):
343 """Update with info from xc.domain_getinfo().
344 """
345 self.info = info or dom_get(self.id)
346 self.memory = self.info['mem_kb'] / 1024
347 self.ssidref = self.info['ssidref']
348 self.target = self.info['mem_kb'] * 1024
350 def state_set(self, state):
351 self.state_updated.acquire()
352 if self.state != state:
353 self.state = state
354 self.state_updated.notifyAll()
355 self.state_updated.release()
356 self.saveToDB()
358 def state_wait(self, state):
359 self.state_updated.acquire()
360 while self.state != state:
361 self.state_updated.wait()
362 self.state_updated.release()
364 def __str__(self):
365 s = "<domain"
366 s += " id=" + str(self.id)
367 s += " name=" + self.name
368 s += " memory=" + str(self.memory)
369 s += " ssidref=" + str(self.ssidref)
370 s += ">"
371 return s
373 __repr__ = __str__
375 def getDeviceTypes(self):
376 return self.controllers.keys()
378 def getDeviceControllers(self):
379 return self.controllers.values()
381 def getDeviceController(self, type, error=True):
382 ctrl = self.controllers.get(type)
383 if not ctrl and error:
384 raise XendError("invalid device type:" + type)
385 return ctrl
387 def findDeviceController(self, type):
388 return (self.getDeviceController(type, error=False)
389 or self.createDeviceController(type))
391 def createDeviceController(self, type):
392 ctrl = controller.createDevController(type, self, recreate=self.recreate)
393 self.controllers[type] = ctrl
394 return ctrl
396 def createDevice(self, type, devconfig, change=False):
397 if type == 'vbd':
398 typedev = sxp.child_value(devconfig, 'dev')
399 if re.match('^ioemu:', typedev):
400 return;
401 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
403 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
405 # create backend db
406 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
407 (type, self.uuid, devnum))
409 # create frontend db
410 db = self.db.addChild("/device/%s/%d" % (type, devnum))
412 db['virtual-device'] = "%i" % devnum
413 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
414 db['backend'] = backdb.getPath()
415 db['backend-id'] = "%i" % backdom.id
417 backdb['frontend'] = db.getPath()
418 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
419 node = Blkctl.block('bind', type, params)
420 backdb['frontend-id'] = "%i" % self.id
421 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
422 backdb.saveDB(save=True)
424 # Ok, super gross, this really doesn't belong in the frontend db...
425 db['type'] = type
426 db['node'] = node
427 db['params'] = params
428 db.saveDB(save=True)
430 return
432 if type == 'vif':
433 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
435 log.error(devconfig)
437 devnum = self.netif_idx
438 self.netif_idx += 1
440 # create backend db
441 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
442 (type, self.uuid, devnum))
444 # create frontend db
445 db = self.db.addChild("/device/%s/%d" % (type, devnum))
447 backdb['frontend'] = db.getPath()
448 backdb['frontend-id'] = "%i" % self.id
449 backdb['handle'] = "%i" % devnum
450 backdb.saveDB(save=True)
452 db['backend'] = backdb.getPath()
453 db['backend-id'] = "%i" % backdom.id
454 db['handle'] = "%i" % devnum
455 log.error(sxp.child_value(devconfig, 'mac'))
456 db['mac'] = sxp.child_value(devconfig, 'mac')
458 db.saveDB(save=True)
460 return
462 if type == 'vtpm':
463 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
465 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
466 log.error("The domain has a TPM with instance %d." % devnum)
468 # create backend db
469 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
470 (type, self.uuid, devnum))
471 # create frontend db
472 db = self.db.addChild("/device/%s/%d" % (type, devnum))
474 backdb['frontend'] = db.getPath()
475 backdb['frontend-id'] = "%i" % self.id
476 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
477 backdb.saveDB(save=True)
479 db['handle'] = "%i" % devnum
480 db['backend'] = backdb.getPath()
481 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
482 'backend', '0'))
483 db.saveDB(save=True)
485 return
487 ctrl = self.findDeviceController(type)
488 return ctrl.createDevice(devconfig, recreate=self.recreate,
489 change=change)
491 def configureDevice(self, type, id, devconfig):
492 ctrl = self.getDeviceController(type)
493 return ctrl.configureDevice(id, devconfig)
495 def destroyDevice(self, type, id, change=False, reboot=False):
496 ctrl = self.getDeviceController(type)
497 return ctrl.destroyDevice(id, change=change, reboot=reboot)
499 def deleteDevice(self, type, id):
500 ctrl = self.getDeviceController(type)
501 return ctrl.deleteDevice(id)
503 def getDevice(self, type, id, error=True):
504 ctrl = self.getDeviceController(type)
505 return ctrl.getDevice(id, error=error)
507 def getDeviceIds(self, type):
508 ctrl = self.getDeviceController(type)
509 return ctrl.getDeviceIds()
511 def getDeviceSxprs(self, type):
512 ctrl = self.getDeviceController(type)
513 return ctrl.getDeviceSxprs()
515 def sxpr(self):
516 sxpr = ['domain',
517 ['id', self.id],
518 ['name', self.name],
519 ['memory', self.memory],
520 ['ssidref', self.ssidref],
521 ['target', self.target] ]
522 if self.uuid:
523 sxpr.append(['uuid', self.uuid])
524 if self.info:
525 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
526 run = (self.info['running'] and 'r') or '-'
527 block = (self.info['blocked'] and 'b') or '-'
528 pause = (self.info['paused'] and 'p') or '-'
529 shut = (self.info['shutdown'] and 's') or '-'
530 crash = (self.info['crashed'] and 'c') or '-'
531 state = run + block + pause + shut + crash
532 sxpr.append(['state', state])
533 if self.info['shutdown']:
534 reason = shutdown_reason(self.info['shutdown_reason'])
535 sxpr.append(['shutdown_reason', reason])
536 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
537 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
538 sxpr.append(['vcpus', self.info['vcpus']])
539 sxpr.append(['cpumap', self.info['cpumap']])
540 # build a string, using '|' to seperate items, show only up
541 # to number of vcpus in domain, and trim the trailing '|'
542 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
543 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
545 if self.start_time:
546 up_time = time.time() - self.start_time
547 sxpr.append(['up_time', str(up_time) ])
548 sxpr.append(['start_time', str(self.start_time) ])
550 if self.channel:
551 sxpr.append(self.channel.sxpr())
552 if self.store_channel:
553 sxpr.append(self.store_channel.sxpr())
554 if self.store_mfn:
555 sxpr.append(['store_mfn', self.store_mfn])
556 if self.console_channel:
557 sxpr.append(['console_channel', self.console_channel.sxpr()])
558 if self.console_mfn:
559 sxpr.append(['console_mfn', self.console_mfn])
560 # already in (devices)
561 # console = self.getConsole()
562 # if console:
563 # sxpr.append(console.sxpr())
565 if self.restart_count:
566 sxpr.append(['restart_count', self.restart_count])
567 if self.restart_state:
568 sxpr.append(['restart_state', self.restart_state])
569 if self.restart_time:
570 sxpr.append(['restart_time', str(self.restart_time)])
572 devs = self.sxpr_devices()
573 if devs:
574 sxpr.append(devs)
575 if self.config:
576 sxpr.append(['config', self.config])
577 if self.device_model_pid:
578 sxpr.append(['device_model_pid',self.device_model_pid])
579 return sxpr
581 def sxpr_devices(self):
582 sxpr = []
583 for ty in self.getDeviceTypes():
584 devs = self.getDeviceSxprs(ty)
585 sxpr += devs
586 if sxpr:
587 sxpr.insert(0, 'devices')
588 else:
589 sxpr = None
590 return sxpr
592 def check_name(self, name):
593 """Check if a vm name is valid. Valid names contain alphabetic characters,
594 digits, or characters in '_-.:/+'.
595 The same name cannot be used for more than one vm at the same time.
597 @param name: name
598 @raise: VMerror if invalid
599 """
600 if self.recreate: return
601 if name is None or name == '':
602 raise VmError('missing vm name')
603 for c in name:
604 if c in string.digits: continue
605 if c in '_-.:/+': continue
606 if c in string.ascii_letters: continue
607 raise VmError('invalid vm name')
608 dominfo = domain_exists(name)
609 # When creating or rebooting, a domain with my name should not exist.
610 # When restoring, a domain with my name will exist, but it should have
611 # my domain id.
612 if not dominfo:
613 return
614 if dominfo.is_terminated():
615 return
616 if not self.id or (dominfo.id != self.id):
617 raise VmError('vm name clash: ' + name)
619 def construct(self, config):
620 """Construct the vm instance from its configuration.
622 @param config: configuration
623 @raise: VmError on error
624 """
625 # todo - add support for scheduling params?
626 self.config = config
627 try:
628 # Initial domain create.
629 self.setName(sxp.child_value(config, 'name'))
630 self.check_name(self.name)
631 self.init_image()
632 self.configure_cpus(config)
633 self.init_domain()
634 self.register_domain()
635 self.configure_bootloader()
637 # Create domain devices.
638 self.configure_backends()
639 self.configure_restart()
640 self.construct_image()
641 self.configure()
642 self.exportToDB(save=True)
643 except Exception, ex:
644 # Catch errors, cleanup and re-raise.
645 print 'Domain construction error:', ex
646 import traceback
647 traceback.print_exc()
648 self.destroy()
649 raise
651 def register_domain(self):
652 xd = get_component('xen.xend.XendDomain')
653 xd._add_domain(self)
654 self.exportToDB(save=True)
656 def configure_cpus(self, config):
657 try:
658 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
659 except:
660 raise VmError('invalid cpu weight')
661 self.memory = int(sxp.child_value(config, 'memory'))
662 if self.memory is None:
663 raise VmError('missing memory size')
664 self.target = self.memory * (1 << 20)
665 self.ssidref = int(sxp.child_value(config, 'ssidref'))
666 cpu = sxp.child_value(config, 'cpu')
667 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
668 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
669 try:
670 image = sxp.child_value(self.config, 'image')
671 vcpus = sxp.child_value(image, 'vcpus')
672 if vcpus:
673 self.vcpus = int(vcpus)
674 except:
675 raise VmError('invalid vcpus value')
677 def exportVCPUSToDB(self, vcpus):
678 for v in range(0,vcpus):
679 path = "/cpu/%d"%(v)
680 if not self.vcpusdb.has_key(path):
681 self.vcpusdb[path] = self.db.addChild(path)
682 db = self.vcpusdb[path]
683 log.debug("writing key availability=online to path %s in store"%(path))
684 db['availability'] = "online"
685 db.saveDB(save=True)
687 def init_image(self):
688 """Create boot image handler for the domain.
689 """
690 image = sxp.child_value(self.config, 'image')
691 if image is None:
692 raise VmError('missing image')
693 self.image = ImageHandler.create(self, image)
695 def construct_image(self):
696 """Construct the boot image for the domain.
697 """
698 self.create_channel()
699 self.image.createImage()
700 self.exportToDB()
701 if self.store_channel and self.store_mfn >= 0:
702 self.db.introduceDomain(self.id,
703 self.store_mfn,
704 self.store_channel)
705 # get the configured value of vcpus and update store
706 self.exportVCPUSToDB(self.vcpus)
708 def delete(self):
709 """Delete the vm's db.
710 """
711 if dom_get(self.id):
712 return
713 self.id = None
714 self.saveToDB(sync=True)
715 try:
716 # Todo: eventually will have to wait for devices to signal
717 # destruction before can delete the db.
718 if self.db:
719 self.db.delete()
720 except Exception, ex:
721 log.warning("error in domain db delete: %s", ex)
722 pass
724 def destroy_domain(self):
725 """Destroy the vm's domain.
726 The domain will not finally go away unless all vm
727 devices have been released.
728 """
729 if self.id is None:
730 return
731 try:
732 xc.domain_destroy(dom=self.id)
733 except Exception, err:
734 log.exception("Domain destroy failed: %s", self.name)
736 def cleanup(self):
737 """Cleanup vm resources: release devices.
738 """
739 self.state = STATE_VM_TERMINATED
740 self.release_devices()
741 if self.channel:
742 try:
743 self.channel.close()
744 self.channel = None
745 except:
746 pass
747 if self.store_channel:
748 try:
749 self.store_channel.close()
750 self.store_channel = None
751 except:
752 pass
753 try:
754 self.db.releaseDomain(self.id)
755 except Exception, ex:
756 log.warning("error in domain release on xenstore: %s", ex)
757 pass
758 if self.console_channel:
759 # notify processes using this cosole?
760 try:
761 self.console_channel.close()
762 self.console_channel = None
763 except:
764 pass
765 if self.image:
766 try:
767 self.device_model_pid = 0
768 self.image.destroy()
769 self.image = None
770 except:
771 pass
773 def destroy(self):
774 """Clenup vm and destroy domain.
775 """
776 self.cleanup()
777 self.destroy_domain()
778 self.saveToDB()
779 return 0
781 def is_terminated(self):
782 """Check if a domain has been terminated.
783 """
784 return self.state == STATE_VM_TERMINATED
786 def release_devices(self):
787 """Release all vm devices.
788 """
789 reboot = self.restart_pending()
790 for ctrl in self.getDeviceControllers():
791 if ctrl.isDestroyed(): continue
792 ctrl.destroyController(reboot=reboot)
793 ddb = self.db.addChild("/device")
794 for type in ddb.keys():
795 if type == 'vbd':
796 typedb = ddb.addChild(type)
797 for dev in typedb.keys():
798 devdb = typedb.addChild(str(dev))
799 Blkctl.block('unbind', devdb['type'].getData(),
800 devdb['node'].getData())
801 typedb[dev].delete()
802 typedb.saveDB(save=True)
803 if type == 'vif':
804 typedb = ddb.addChild(type)
805 for dev in typedb.keys():
806 typedb[dev].delete()
807 typedb.saveDB(save=True)
808 if type == 'vtpm':
809 typedb = ddb.addChild(type)
810 for dev in typedb.keys():
811 typedb[dev].delete()
812 typedb.saveDB(save=True)
814 def show(self):
815 """Print virtual machine info.
816 """
817 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
818 print "image:"
819 sxp.show(self.image)
820 print "]"
822 def init_domain(self):
823 """Initialize the domain memory.
824 """
825 if self.recreate:
826 return
827 if self.start_time is None:
828 self.start_time = time.time()
829 try:
830 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
831 except:
832 raise VmError('invalid cpu')
833 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
834 log.debug('init_domain> Created domain=%d name=%s memory=%d',
835 id, self.name, self.memory)
836 self.setdom(id)
838 def openChannel(self, key, local, remote):
839 """Create a control channel to the domain.
840 If saved info is available recreate the channel.
842 @param key db key for the saved data (if any)
843 @param local default local port
844 @param remote default remote port
845 """
846 db = self.db.addChild(key)
847 chan = channelFactory().restoreFromDB(db, self.id, local, remote)
848 #todo: save here?
849 #chan.saveToDB(db)
850 return chan
852 def eventChannel(self, key):
853 """Create an event channel to the domain.
854 If saved info is available recreate the channel.
856 @param key db key for the saved data (if any)
857 """
858 db = self.db.addChild(key)
859 return EventChannel.restoreFromDB(db, 0, self.id)
861 def create_channel(self):
862 """Create the channels to the domain.
863 """
864 self.channel = self.openChannel("channel", 0, 1)
865 self.store_channel = self.eventChannel("store_channel")
866 self.console_channel = self.eventChannel("console_channel")
868 def create_configured_devices(self):
869 devices = sxp.children(self.config, 'device')
870 for d in devices:
871 dev_config = sxp.child0(d)
872 if dev_config is None:
873 raise VmError('invalid device')
874 dev_type = sxp.name(dev_config)
875 ctrl_type = get_device_handler(dev_type)
876 if ctrl_type is None:
877 raise VmError('unknown device type: ' + dev_type)
878 self.createDevice(ctrl_type, dev_config)
880 def create_devices(self):
881 """Create the devices for a vm.
883 @raise: VmError for invalid devices
884 """
885 if self.rebooting():
886 for ctrl in self.getDeviceControllers():
887 ctrl.initController(reboot=True)
888 else:
889 self.create_configured_devices()
890 if not self.device_model_pid:
891 self.device_model_pid = self.image.createDeviceModel()
893 def device_create(self, dev_config):
894 """Create a new device.
896 @param dev_config: device configuration
897 """
898 dev_type = sxp.name(dev_config)
899 dev = self.createDevice(dev_type, dev_config, change=True)
900 self.config.append(['device', dev.getConfig()])
901 return dev.sxpr()
903 def device_configure(self, dev_config, id):
904 """Configure an existing device.
906 @param dev_config: device configuration
907 @param id: device id
908 """
909 type = sxp.name(dev_config)
910 dev = self.getDevice(type, id)
911 old_config = dev.getConfig()
912 new_config = dev.configure(dev_config, change=True)
913 # Patch new config into vm config.
914 new_full_config = ['device', new_config]
915 old_full_config = ['device', old_config]
916 old_index = self.config.index(old_full_config)
917 self.config[old_index] = new_full_config
918 return new_config
920 def device_refresh(self, type, id):
921 """Refresh a device.
923 @param type: device type
924 @param id: device id
925 """
926 dev = self.getDevice(type, id)
927 dev.refresh()
929 def device_delete(self, type, id):
930 """Destroy and remove a device.
932 @param type: device type
933 @param id: device id
934 """
935 dev = self.getDevice(type, id)
936 dev_config = dev.getConfig()
937 if dev_config:
938 self.config.remove(['device', dev_config])
939 self.deleteDevice(type, dev.getId())
941 def configure_bootloader(self):
942 """Configure boot loader.
943 """
944 self.bootloader = sxp.child_value(self.config, "bootloader")
946 def configure_restart(self):
947 """Configure the vm restart mode.
948 """
949 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
950 if r not in restart_modes:
951 raise VmError('invalid restart mode: ' + str(r))
952 self.restart_mode = r;
954 def restart_needed(self, reason):
955 """Determine if the vm needs to be restarted when shutdown
956 for the given reason.
958 @param reason: shutdown reason
959 @return True if needs restart, False otherwise
960 """
961 if self.restart_mode == RESTART_NEVER:
962 return False
963 if self.restart_mode == RESTART_ALWAYS:
964 return True
965 if self.restart_mode == RESTART_ONREBOOT:
966 return reason == 'reboot'
967 return False
969 def restart_cancel(self):
970 """Cancel a vm restart.
971 """
972 self.restart_state = None
974 def restarting(self):
975 """Put the vm into restart mode.
976 """
977 self.restart_state = STATE_RESTART_PENDING
979 def restart_pending(self):
980 """Test if the vm has a pending restart.
981 """
982 return self.restart_state == STATE_RESTART_PENDING
984 def rebooting(self):
985 return self.restart_state == STATE_RESTART_BOOTING
987 def restart_check(self):
988 """Check if domain restart is OK.
989 To prevent restart loops, raise an error if it is
990 less than MINIMUM_RESTART_TIME seconds since the last restart.
991 """
992 tnow = time.time()
993 if self.restart_time is not None:
994 tdelta = tnow - self.restart_time
995 if tdelta < self.MINIMUM_RESTART_TIME:
996 self.restart_cancel()
997 msg = 'VM %s restarting too fast' % self.name
998 log.error(msg)
999 raise VmError(msg)
1000 self.restart_time = tnow
1001 self.restart_count += 1
1003 def restart(self):
1004 """Restart the domain after it has exited.
1005 Reuses the domain id
1007 """
1008 try:
1009 self.clear_shutdown()
1010 self.state = STATE_VM_OK
1011 self.shutdown_pending = None
1012 self.restart_check()
1013 self.exportToDB()
1014 self.restart_state = STATE_RESTART_BOOTING
1015 if self.bootloader:
1016 self.config = self.bootloader_config()
1017 self.construct(self.config)
1018 self.saveToDB()
1019 finally:
1020 self.restart_state = None
1022 def bootloader_config(self):
1023 # if we're restarting with a bootloader, we need to run it
1024 # FIXME: this assumes the disk is the first device and
1025 # that we're booting from the first disk
1026 blcfg = None
1027 # FIXME: this assumes that we want to use the first disk
1028 dev = sxp.child_value(self.config, "device")
1029 if dev:
1030 disk = sxp.child_value(dev, "uname")
1031 fn = blkdev_uname_to_file(disk)
1032 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1033 if blcfg is None:
1034 msg = "Had a bootloader specified, but can't find disk"
1035 log.error(msg)
1036 raise VmError(msg)
1037 config = sxp.merge(['vm', blconfig ], self.config)
1038 return config
1040 def configure_backends(self):
1041 """Set configuration flags if the vm is a backend for netif or blkif.
1042 Configure the backends to use for vbd and vif if specified.
1043 """
1044 for c in sxp.children(self.config, 'backend'):
1045 v = sxp.child0(c)
1046 name = sxp.name(v)
1047 if name == 'blkif':
1048 self.blkif_backend = True
1049 elif name == 'netif':
1050 self.netif_backend = True
1051 elif name == 'usbif':
1052 self.usbif_backend = True
1053 elif name == 'tpmif':
1054 self.tpmif_backend = True
1055 else:
1056 raise VmError('invalid backend type:' + str(name))
1058 def configure(self):
1059 """Configure a vm.
1061 """
1062 self.configure_fields()
1063 self.create_devices()
1064 self.create_blkif()
1065 self.publish_console()
1067 def create_blkif(self):
1068 """Create the block device interface (blkif) for the vm.
1069 The vm needs a blkif even if it doesn't have any disks
1070 at creation time, for example when it uses NFS root.
1072 """
1073 return
1074 blkif = self.getDeviceController("vbd", error=False)
1075 if not blkif:
1076 blkif = self.createDeviceController("vbd")
1077 backend = blkif.getBackend(0)
1078 backend.connect(recreate=self.recreate)
1080 def publish_console(self):
1081 db = DBMap(db=XenNode("/console/%d" % self.id))
1082 db['domain'] = self.db.getPath()
1083 db.saveDB(save=True)
1085 def configure_fields(self):
1086 """Process the vm configuration fields using the registered handlers.
1087 """
1088 index = {}
1089 for field in sxp.children(self.config):
1090 field_name = sxp.name(field)
1091 field_index = index.get(field_name, 0)
1092 field_handler = get_config_handler(field_name)
1093 # Ignore unknown fields. Warn?
1094 if field_handler:
1095 v = field_handler(self, self.config, field, field_index)
1096 else:
1097 log.warning("Unknown config field %s", field_name)
1098 index[field_name] = field_index + 1
1100 def mem_target_set(self, target):
1101 """Set domain memory target in bytes.
1102 """
1103 if target:
1104 self.target = target * (1 << 20)
1105 # Commit to XenStore immediately
1106 self.exportToDB()
1108 def vcpu_hotplug(self, vcpu, state):
1109 """Disable or enable VCPU in domain.
1110 """
1111 db = ""
1112 try:
1113 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1114 except:
1115 log.error("Invalid VCPU")
1116 return
1118 if self.store_channel:
1119 if int(state) == 0:
1120 db['availability'] = "offline"
1121 else:
1122 db['availability'] = "online"
1124 db.saveDB(save=True)
1126 def shutdown(self, reason):
1127 if not reason in shutdown_reasons.values():
1128 raise XendError('invalid reason:' + reason)
1129 db = self.db.addChild("/control");
1130 db['shutdown'] = reason;
1131 db.saveDB(save=True);
1132 if not reason in ['suspend']:
1133 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1135 def clear_shutdown(self):
1136 db = self.db.addChild("/control")
1137 db['shutdown'] = ""
1138 db.saveDB(save=True)
1140 def send_sysrq(self, key=0):
1141 db = self.db.addChild("/control");
1142 db['sysrq'] = '%c' % key;
1143 db.saveDB(save=True);
1145 def shutdown_time_left(self, timeout):
1146 if not self.shutdown_pending:
1147 return 0
1148 return timeout - (time.time() - self.shutdown_pending['start'])
1150 def dom0_init_store(self):
1151 if not self.store_channel:
1152 self.store_channel = self.eventChannel("store_channel")
1153 self.store_mfn = xc.init_store(self.store_channel.port2)
1154 if self.store_mfn >= 0:
1155 self.db.introduceDomain(self.id, self.store_mfn,
1156 self.store_channel)
1157 self.exportToDB(save=True, sync=True)
1158 # get run-time value of vcpus and update store
1159 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1161 def vm_field_ignore(vm, config, val, index):
1162 """Dummy config field handler used for fields with built-in handling.
1164 @param vm: virtual machine
1165 @param config: vm config
1166 @param val: config field
1167 @param index: field index
1168 """
1169 pass
1171 def vm_field_maxmem(vm, config, val, index):
1172 """Configure vm memory limit.
1174 @param vm: virtual machine
1175 @param config: vm config
1176 @param val: config field
1177 @param index: field index
1178 """
1179 maxmem = sxp.child0(val)
1180 if maxmem is None:
1181 maxmem = vm.memory
1182 try:
1183 maxmem = int(maxmem)
1184 except:
1185 raise VmError("invalid maxmem: " + str(maxmem))
1186 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1188 #============================================================================
1189 # Register image handlers.
1190 from image import \
1191 addImageHandlerClass, \
1192 ImageHandler, \
1193 LinuxImageHandler, \
1194 VmxImageHandler
1196 addImageHandlerClass(LinuxImageHandler)
1197 addImageHandlerClass(VmxImageHandler)
1199 # Ignore the fields we already handle.
1200 add_config_handler('name', vm_field_ignore)
1201 add_config_handler('memory', vm_field_ignore)
1202 add_config_handler('ssidref', vm_field_ignore)
1203 add_config_handler('cpu', vm_field_ignore)
1204 add_config_handler('cpu_weight', vm_field_ignore)
1205 add_config_handler('restart', vm_field_ignore)
1206 add_config_handler('image', vm_field_ignore)
1207 add_config_handler('device', vm_field_ignore)
1208 add_config_handler('backend', vm_field_ignore)
1209 add_config_handler('vcpus', vm_field_ignore)
1210 add_config_handler('bootloader', vm_field_ignore)
1212 # Register other config handlers.
1213 add_config_handler('maxmem', vm_field_maxmem)
1215 #============================================================================
1216 # Register device controllers and their device config types.
1218 from server import blkif
1219 controller.addDevControllerClass("vbd", blkif.BlkifController)
1220 add_device_handler("vbd", "vbd")
1222 from server import netif
1223 controller.addDevControllerClass("vif", netif.NetifController)
1224 add_device_handler("vif", "vif")
1226 from server import tpmif
1227 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1228 add_device_handler("vtpm", "vtpm")
1230 from server import pciif
1231 controller.addDevControllerClass("pci", pciif.PciController)
1232 add_device_handler("pci", "pci")
1234 from xen.xend.server import usbif
1235 controller.addDevControllerClass("usb", usbif.UsbifController)
1236 add_device_handler("usb", "usb")
1238 #============================================================================