debuggers.hg

view tools/python/xen/xend/XendDomainInfo.py @ 6713:64ca0d3ea97f

Disable device info getting written to store when xend restarts.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 21:15:41 2005 +0000 (2005-09-07)
parents 7d0fb56b4a91
children a39b1fa10edc
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.channel import EventChannel
38 from xen.util.blkif import blkdev_name_to_number, expand_dev_name
40 from xen.xend import sxp
41 from xen.xend import Blkctl
42 from xen.xend.PrettyPrint import prettyprintstring
43 from xen.xend.XendBootloader import bootloader
44 from xen.xend.XendLogging import log
45 from xen.xend.XendError import XendError, VmError
46 from xen.xend.XendRoot import get_component
48 from xen.xend.uuid import getUuid
49 from xen.xend.xenstore import DBVar, XenNode, DBMap
51 """Shutdown code for poweroff."""
52 DOMAIN_POWEROFF = 0
54 """Shutdown code for reboot."""
55 DOMAIN_REBOOT = 1
57 """Shutdown code for suspend."""
58 DOMAIN_SUSPEND = 2
60 """Shutdown code for crash."""
61 DOMAIN_CRASH = 3
63 """Map shutdown codes to strings."""
64 shutdown_reasons = {
65 DOMAIN_POWEROFF: "poweroff",
66 DOMAIN_REBOOT : "reboot",
67 DOMAIN_SUSPEND : "suspend",
68 DOMAIN_CRASH : "crash",
69 }
71 RESTART_ALWAYS = 'always'
72 RESTART_ONREBOOT = 'onreboot'
73 RESTART_NEVER = 'never'
75 restart_modes = [
76 RESTART_ALWAYS,
77 RESTART_ONREBOOT,
78 RESTART_NEVER,
79 ]
81 STATE_RESTART_PENDING = 'pending'
82 STATE_RESTART_BOOTING = 'booting'
84 STATE_VM_OK = "ok"
85 STATE_VM_TERMINATED = "terminated"
86 STATE_VM_SUSPENDED = "suspended"
89 def domain_exists(name):
90 # See comment in XendDomain constructor.
91 xd = get_component('xen.xend.XendDomain')
92 return xd.domain_lookup_by_name(name)
94 def shutdown_reason(code):
95 """Get a shutdown reason from a code.
97 @param code: shutdown code
98 @type code: int
99 @return: shutdown reason
100 @rtype: string
101 """
102 return shutdown_reasons.get(code, "?")
104 config_handlers = {}
106 def add_config_handler(name, h):
107 """Add a handler for a config field.
109 @param name: field name
110 @param h: handler: fn(vm, config, field, index)
111 """
112 config_handlers[name] = h
114 def get_config_handler(name):
115 """Get a handler for a config field.
117 returns handler or None
118 """
119 return config_handlers.get(name)
121 """Table of handlers for devices.
122 Indexed by device type.
123 """
124 device_handlers = {}
126 def add_device_handler(name, type):
127 device_handlers[name] = type
129 def get_device_handler(name):
130 return device_handlers[name]
132 def dom_get(dom):
133 """Get info from xen for an existing domain.
135 @param dom: domain id
136 @return: info or None
137 """
138 domlist = xc.domain_getinfo(dom, 1)
139 if domlist and dom == domlist[0]['dom']:
140 return domlist[0]
141 return None
143 class XendDomainInfo:
144 """Virtual machine object."""
146 """Minimum time between domain restarts in seconds.
147 """
148 MINIMUM_RESTART_TIME = 20
150 def create(cls, parentdb, config):
151 """Create a VM from a configuration.
153 @param parentdb: parent db
154 @param config configuration
155 @raise: VmError for invalid configuration
156 """
157 uuid = getUuid()
158 db = parentdb.addChild(uuid)
159 vm = cls(db)
160 vm.construct(config)
161 vm.saveToDB(sync=True)
163 return vm
165 create = classmethod(create)
167 def recreate(cls, db, info):
168 """Create the VM object for an existing domain.
170 @param db: domain db
171 @param info: domain info from xc
172 """
173 dom = info['dom']
174 vm = cls(db)
175 vm.setdom(dom)
176 db.readDB()
177 vm.importFromDB()
178 config = vm.config
179 log.debug('info=' + str(info))
180 log.debug('config=' + prettyprintstring(config))
182 vm.memory = info['mem_kb']/1024
183 vm.target = info['mem_kb'] * 1024
185 if config:
186 try:
187 vm.recreate = True
188 vm.construct(config)
189 finally:
190 vm.recreate = False
191 else:
192 vm.setName("Domain-%d" % dom)
194 vm.exportToDB(save=True)
195 return vm
197 recreate = classmethod(recreate)
199 def restore(cls, parentdb, config, uuid=None):
200 """Create a domain and a VM object to do a restore.
202 @param parentdb: parent db
203 @param config: domain configuration
204 @param uuid: uuid to use
205 """
206 if not uuid:
207 uuid = getUuid()
208 db = parentdb.addChild(uuid)
209 vm = cls(db)
210 ssidref = int(sxp.child_value(config, 'ssidref'))
211 log.debug('restoring with ssidref='+str(ssidref))
212 id = xc.domain_create(ssidref = ssidref)
213 vm.setdom(id)
214 vm.clear_shutdown()
215 try:
216 vm.restore = True
217 vm.construct(config)
218 finally:
219 vm.restore = False
220 vm.exportToDB(save=True, sync=True)
221 return vm
223 restore = classmethod(restore)
225 __exports__ = [
226 DBVar('id', ty='int'),
227 DBVar('name', ty='str'),
228 DBVar('uuid', ty='str'),
229 DBVar('config', ty='sxpr'),
230 DBVar('start_time', ty='float'),
231 DBVar('state', ty='str'),
232 DBVar('store_mfn', ty='long'),
233 DBVar('console_mfn', ty='long', path="console/ring-ref"),
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.store_channel = None
261 self.store_mfn = None
262 self.console_channel = None
263 self.console_mfn = None
264 self.controllers = {}
266 self.info = None
267 self.blkif_backend = False
268 self.netif_backend = False
269 self.netif_idx = 0
270 self.tpmif_backend = False
272 #todo: state: running, suspended
273 self.state = STATE_VM_OK
274 self.state_updated = threading.Condition()
275 self.shutdown_pending = None
277 #todo: set to migrate info if migrating
278 self.migrate = None
280 self.restart_mode = RESTART_ONREBOOT
281 self.restart_state = None
282 self.restart_time = None
283 self.restart_count = 0
285 self.vcpus = 1
286 self.vcpusdb = {}
287 self.bootloader = None
288 self.device_model_pid = 0
290 def setDB(self, db):
291 self.db = db
293 def saveToDB(self, save=False, sync=False):
294 self.db.saveDB(save=save, sync=sync)
296 def exportToDB(self, save=False, sync=False):
297 if self.store_channel:
298 self.store_channel.saveToDB(self.db.addChild("store_channel"),
299 save=save)
300 if self.console_channel:
301 self.console_channel.saveToDB(self.db.addChild("console/console_channel"),
302 save=save)
303 if self.image:
304 self.image.exportToDB(save=save, sync=sync)
305 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
307 def importFromDB(self):
308 self.db.importFromDB(self, fields=self.__exports__)
309 self.store_channel = self.eventChannel("store_channel")
311 def setdom(self, dom):
312 """Set the domain id.
314 @param dom: domain id
315 """
316 self.id = int(dom)
317 #self.db.id = self.id
319 def getDomain(self):
320 return self.id
322 def setName(self, name):
323 self.name = name
324 self.db.name = self.name
326 def getName(self):
327 return self.name
329 def getStoreChannel(self):
330 return self.store_channel
332 def getConsoleChannel(self):
333 return self.console_channel
335 def update(self, info=None):
336 """Update with info from xc.domain_getinfo().
337 """
338 self.info = info or dom_get(self.id)
339 self.memory = self.info['mem_kb'] / 1024
340 self.ssidref = self.info['ssidref']
341 self.target = self.info['mem_kb'] * 1024
343 def state_set(self, state):
344 self.state_updated.acquire()
345 if self.state != state:
346 self.state = state
347 self.state_updated.notifyAll()
348 self.state_updated.release()
349 self.saveToDB()
351 def state_wait(self, state):
352 self.state_updated.acquire()
353 while self.state != state:
354 self.state_updated.wait()
355 self.state_updated.release()
357 def __str__(self):
358 s = "<domain"
359 s += " id=" + str(self.id)
360 s += " name=" + self.name
361 s += " memory=" + str(self.memory)
362 s += " ssidref=" + str(self.ssidref)
363 s += ">"
364 return s
366 __repr__ = __str__
368 def getDeviceTypes(self):
369 return self.controllers.keys()
371 def getDeviceControllers(self):
372 return self.controllers.values()
374 def getDeviceController(self, type, error=True):
375 ctrl = self.controllers.get(type)
376 if not ctrl and error:
377 raise XendError("invalid device type:" + type)
378 return ctrl
380 def findDeviceController(self, type):
381 return (self.getDeviceController(type, error=False)
382 or self.createDeviceController(type))
384 def createDeviceController(self, type):
385 ctrl = controller.createDevController(type, self, recreate=self.recreate)
386 self.controllers[type] = ctrl
387 return ctrl
389 def createDevice(self, type, devconfig, change=False):
390 if self.recreate:
391 return
392 if type == 'vbd':
393 typedev = sxp.child_value(devconfig, 'dev')
394 if re.match('^ioemu:', typedev):
395 return;
396 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
398 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
400 # create backend db
401 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
402 (type, self.uuid, devnum))
404 # create frontend db
405 db = self.db.addChild("/device/%s/%d" % (type, devnum))
407 db['virtual-device'] = "%i" % devnum
408 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
409 db['backend'] = backdb.getPath()
410 db['backend-id'] = "%i" % backdom.id
412 backdb['frontend'] = db.getPath()
413 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
414 node = Blkctl.block('bind', type, params)
415 backdb['frontend-id'] = "%i" % self.id
416 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
417 backdb.saveDB(save=True)
419 # Ok, super gross, this really doesn't belong in the frontend db...
420 db['type'] = type
421 db['node'] = node
422 db['params'] = params
423 db.saveDB(save=True)
425 return
427 if type == 'vif':
428 from xen.xend import XendRoot
429 xroot = XendRoot.instance()
431 def _get_config_ipaddr(config):
432 val = []
433 for ipaddr in sxp.children(config, elt='ip'):
434 val.append(sxp.child0(ipaddr))
435 return val
437 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
439 log.error(devconfig)
441 devnum = self.netif_idx
442 self.netif_idx += 1
444 script = sxp.child_value(devconfig, 'script',
445 xroot.get_vif_script())
446 script = os.path.join(xroot.network_script_dir, script)
447 bridge = sxp.child_value(devconfig, 'bridge',
448 xroot.get_vif_bridge())
449 mac = sxp.child_value(devconfig, 'mac')
450 ipaddr = _get_config_ipaddr(devconfig)
452 # create backend db
453 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
454 (type, self.uuid, devnum))
456 # create frontend db
457 db = self.db.addChild("/device/%s/%d" % (type, devnum))
459 backdb['script'] = script
460 backdb['domain'] = self.name
461 backdb['mac'] = mac
462 backdb['bridge'] = bridge
463 if ipaddr:
464 backdb['ip'] = ' '.join(ipaddr)
465 backdb['frontend'] = db.getPath()
466 backdb['frontend-id'] = "%i" % self.id
467 backdb['handle'] = "%i" % devnum
468 backdb.saveDB(save=True)
470 db['backend'] = backdb.getPath()
471 db['backend-id'] = "%i" % backdom.id
472 db['handle'] = "%i" % devnum
473 db['mac'] = mac
475 db.saveDB(save=True)
477 return
479 if type == 'vtpm':
480 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
482 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
483 log.error("The domain has a TPM with instance %d." % devnum)
485 # create backend db
486 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
487 (type, self.uuid, devnum))
488 # create frontend db
489 db = self.db.addChild("/device/%s/%d" % (type, devnum))
491 backdb['frontend'] = db.getPath()
492 backdb['frontend-id'] = "%i" % self.id
493 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
494 backdb.saveDB(save=True)
496 db['handle'] = "%i" % devnum
497 db['backend'] = backdb.getPath()
498 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
499 'backend', '0'))
500 db.saveDB(save=True)
502 return
504 ctrl = self.findDeviceController(type)
505 return ctrl.createDevice(devconfig, recreate=self.recreate,
506 change=change)
508 def configureDevice(self, type, id, devconfig):
509 ctrl = self.getDeviceController(type)
510 return ctrl.configureDevice(id, devconfig)
512 def destroyDevice(self, type, id, change=False, reboot=False):
513 ctrl = self.getDeviceController(type)
514 return ctrl.destroyDevice(id, change=change, reboot=reboot)
516 def deleteDevice(self, type, id):
517 ctrl = self.getDeviceController(type)
518 return ctrl.deleteDevice(id)
520 def getDevice(self, type, id, error=True):
521 ctrl = self.getDeviceController(type)
522 return ctrl.getDevice(id, error=error)
524 def getDeviceIds(self, type):
525 ctrl = self.getDeviceController(type)
526 return ctrl.getDeviceIds()
528 def getDeviceSxprs(self, type):
529 ctrl = self.getDeviceController(type)
530 return ctrl.getDeviceSxprs()
532 def sxpr(self):
533 sxpr = ['domain',
534 ['id', self.id],
535 ['name', self.name],
536 ['memory', self.memory],
537 ['ssidref', self.ssidref],
538 ['target', self.target] ]
539 if self.uuid:
540 sxpr.append(['uuid', self.uuid])
541 if self.info:
542 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
543 run = (self.info['running'] and 'r') or '-'
544 block = (self.info['blocked'] and 'b') or '-'
545 pause = (self.info['paused'] and 'p') or '-'
546 shut = (self.info['shutdown'] and 's') or '-'
547 crash = (self.info['crashed'] and 'c') or '-'
548 state = run + block + pause + shut + crash
549 sxpr.append(['state', state])
550 if self.info['shutdown']:
551 reason = shutdown_reason(self.info['shutdown_reason'])
552 sxpr.append(['shutdown_reason', reason])
553 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
554 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
555 sxpr.append(['vcpus', self.info['vcpus']])
556 sxpr.append(['cpumap', self.info['cpumap']])
557 # build a string, using '|' to seperate items, show only up
558 # to number of vcpus in domain, and trim the trailing '|'
559 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
560 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
562 if self.start_time:
563 up_time = time.time() - self.start_time
564 sxpr.append(['up_time', str(up_time) ])
565 sxpr.append(['start_time', str(self.start_time) ])
567 if self.store_channel:
568 sxpr.append(self.store_channel.sxpr())
569 if self.store_mfn:
570 sxpr.append(['store_mfn', self.store_mfn])
571 if self.console_channel:
572 sxpr.append(['console_channel', self.console_channel.sxpr()])
573 if self.console_mfn:
574 sxpr.append(['console_mfn', self.console_mfn])
575 # already in (devices)
576 # console = self.getConsole()
577 # if console:
578 # sxpr.append(console.sxpr())
580 if self.restart_count:
581 sxpr.append(['restart_count', self.restart_count])
582 if self.restart_state:
583 sxpr.append(['restart_state', self.restart_state])
584 if self.restart_time:
585 sxpr.append(['restart_time', str(self.restart_time)])
587 devs = self.sxpr_devices()
588 if devs:
589 sxpr.append(devs)
590 if self.config:
591 sxpr.append(['config', self.config])
592 if self.device_model_pid:
593 sxpr.append(['device_model_pid',self.device_model_pid])
594 return sxpr
596 def sxpr_devices(self):
597 sxpr = []
598 for ty in self.getDeviceTypes():
599 devs = self.getDeviceSxprs(ty)
600 sxpr += devs
601 if sxpr:
602 sxpr.insert(0, 'devices')
603 else:
604 sxpr = None
605 return sxpr
607 def check_name(self, name):
608 """Check if a vm name is valid. Valid names contain alphabetic characters,
609 digits, or characters in '_-.:/+'.
610 The same name cannot be used for more than one vm at the same time.
612 @param name: name
613 @raise: VMerror if invalid
614 """
615 if self.recreate: return
616 if name is None or name == '':
617 raise VmError('missing vm name')
618 for c in name:
619 if c in string.digits: continue
620 if c in '_-.:/+': continue
621 if c in string.ascii_letters: continue
622 raise VmError('invalid vm name')
623 dominfo = domain_exists(name)
624 # When creating or rebooting, a domain with my name should not exist.
625 # When restoring, a domain with my name will exist, but it should have
626 # my domain id.
627 if not dominfo:
628 return
629 if dominfo.is_terminated():
630 return
631 if not self.id or (dominfo.id != self.id):
632 raise VmError('vm name clash: ' + name)
634 def construct(self, config):
635 """Construct the vm instance from its configuration.
637 @param config: configuration
638 @raise: VmError on error
639 """
640 # todo - add support for scheduling params?
641 self.config = config
642 try:
643 # Initial domain create.
644 self.setName(sxp.child_value(config, 'name'))
645 self.check_name(self.name)
646 self.init_image()
647 self.configure_cpus(config)
648 self.init_domain()
649 self.register_domain()
650 self.configure_bootloader()
652 # Create domain devices.
653 self.configure_backends()
654 self.configure_restart()
655 self.construct_image()
656 self.configure()
657 self.publish_console()
658 self.exportToDB(save=True)
659 except Exception, ex:
660 # Catch errors, cleanup and re-raise.
661 print 'Domain construction error:', ex
662 import traceback
663 traceback.print_exc()
664 self.destroy()
665 raise
667 def register_domain(self):
668 xd = get_component('xen.xend.XendDomain')
669 xd._add_domain(self)
670 self.exportToDB(save=True)
672 def configure_cpus(self, config):
673 try:
674 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
675 except:
676 raise VmError('invalid cpu weight')
677 self.memory = int(sxp.child_value(config, 'memory'))
678 if self.memory is None:
679 raise VmError('missing memory size')
680 self.target = self.memory * (1 << 20)
681 self.ssidref = int(sxp.child_value(config, 'ssidref'))
682 cpu = sxp.child_value(config, 'cpu')
683 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
684 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
685 try:
686 image = sxp.child_value(self.config, 'image')
687 vcpus = sxp.child_value(image, 'vcpus')
688 if vcpus:
689 self.vcpus = int(vcpus)
690 except:
691 raise VmError('invalid vcpus value')
693 def exportVCPUSToDB(self, vcpus):
694 for v in range(0,vcpus):
695 path = "/cpu/%d"%(v)
696 if not self.vcpusdb.has_key(path):
697 self.vcpusdb[path] = self.db.addChild(path)
698 db = self.vcpusdb[path]
699 log.debug("writing key availability=online to path %s in store"%(path))
700 db['availability'] = "online"
701 db.saveDB(save=True)
703 def init_image(self):
704 """Create boot image handler for the domain.
705 """
706 image = sxp.child_value(self.config, 'image')
707 if image is None:
708 raise VmError('missing image')
709 self.image = ImageHandler.create(self, image)
711 def construct_image(self):
712 """Construct the boot image for the domain.
713 """
714 self.create_channel()
715 self.image.createImage()
716 self.exportToDB()
717 if self.store_channel and self.store_mfn >= 0:
718 self.db.introduceDomain(self.id,
719 self.store_mfn,
720 self.store_channel)
721 # get the configured value of vcpus and update store
722 self.exportVCPUSToDB(self.vcpus)
724 def delete(self):
725 """Delete the vm's db.
726 """
727 if dom_get(self.id):
728 return
729 self.id = None
730 self.saveToDB(sync=True)
731 try:
732 # Todo: eventually will have to wait for devices to signal
733 # destruction before can delete the db.
734 if self.db:
735 self.db.delete()
736 except Exception, ex:
737 log.warning("error in domain db delete: %s", ex)
738 pass
740 def destroy_domain(self):
741 """Destroy the vm's domain.
742 The domain will not finally go away unless all vm
743 devices have been released.
744 """
745 if self.id is None:
746 return
747 try:
748 xc.domain_destroy(dom=self.id)
749 except Exception, err:
750 log.exception("Domain destroy failed: %s", self.name)
752 def cleanup(self):
753 """Cleanup vm resources: release devices.
754 """
755 self.state = STATE_VM_TERMINATED
756 self.release_devices()
757 if self.store_channel:
758 try:
759 self.store_channel.close()
760 self.store_channel = None
761 except:
762 pass
763 try:
764 self.db.releaseDomain(self.id)
765 except Exception, ex:
766 log.warning("error in domain release on xenstore: %s", ex)
767 pass
768 if self.console_channel:
769 # notify processes using this cosole?
770 try:
771 self.console_channel.close()
772 self.console_channel = None
773 except:
774 pass
775 if self.image:
776 try:
777 self.device_model_pid = 0
778 self.image.destroy()
779 self.image = None
780 except:
781 pass
783 def destroy(self):
784 """Clenup vm and destroy domain.
785 """
786 self.destroy_domain()
787 self.cleanup()
788 self.saveToDB()
789 return 0
791 def is_terminated(self):
792 """Check if a domain has been terminated.
793 """
794 return self.state == STATE_VM_TERMINATED
796 def release_devices(self):
797 """Release all vm devices.
798 """
799 reboot = self.restart_pending()
800 for ctrl in self.getDeviceControllers():
801 if ctrl.isDestroyed(): continue
802 ctrl.destroyController(reboot=reboot)
803 ddb = self.db.addChild("/device")
804 for type in ddb.keys():
805 if type == 'vbd':
806 typedb = ddb.addChild(type)
807 for dev in typedb.keys():
808 devdb = typedb.addChild(str(dev))
809 Blkctl.block('unbind', devdb['type'].getData(),
810 devdb['node'].getData())
811 typedb[dev].delete()
812 typedb.saveDB(save=True)
813 if type == 'vif':
814 typedb = ddb.addChild(type)
815 for dev in typedb.keys():
816 typedb[dev].delete()
817 typedb.saveDB(save=True)
818 if type == 'vtpm':
819 typedb = ddb.addChild(type)
820 for dev in typedb.keys():
821 typedb[dev].delete()
822 typedb.saveDB(save=True)
824 def show(self):
825 """Print virtual machine info.
826 """
827 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
828 print "image:"
829 sxp.show(self.image)
830 print "]"
832 def init_domain(self):
833 """Initialize the domain memory.
834 """
835 if self.recreate:
836 return
837 if self.start_time is None:
838 self.start_time = time.time()
839 try:
840 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
841 except:
842 raise VmError('invalid cpu')
843 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
844 log.debug('init_domain> Created domain=%d name=%s memory=%d',
845 id, self.name, self.memory)
846 self.setdom(id)
848 def eventChannel(self, key):
849 """Create an event channel to the domain.
850 If saved info is available recreate the channel.
852 @param key db key for the saved data (if any)
853 """
854 db = self.db.addChild(key)
855 return EventChannel.restoreFromDB(db, 0, self.id)
857 def create_channel(self):
858 """Create the channels to the domain.
859 """
860 self.store_channel = self.eventChannel("store_channel")
861 self.console_channel = self.eventChannel("console/console_channel")
863 def create_configured_devices(self):
864 devices = sxp.children(self.config, 'device')
865 for d in devices:
866 dev_config = sxp.child0(d)
867 if dev_config is None:
868 raise VmError('invalid device')
869 dev_type = sxp.name(dev_config)
870 ctrl_type = get_device_handler(dev_type)
871 if ctrl_type is None:
872 raise VmError('unknown device type: ' + dev_type)
873 self.createDevice(ctrl_type, dev_config)
875 def create_devices(self):
876 """Create the devices for a vm.
878 @raise: VmError for invalid devices
879 """
880 if self.rebooting():
881 for ctrl in self.getDeviceControllers():
882 ctrl.initController(reboot=True)
883 else:
884 self.create_configured_devices()
885 if not self.device_model_pid:
886 self.device_model_pid = self.image.createDeviceModel()
888 def device_create(self, dev_config):
889 """Create a new device.
891 @param dev_config: device configuration
892 """
893 dev_type = sxp.name(dev_config)
894 dev = self.createDevice(dev_type, dev_config, change=True)
895 self.config.append(['device', dev.getConfig()])
896 return dev.sxpr()
898 def device_configure(self, dev_config, id):
899 """Configure an existing device.
901 @param dev_config: device configuration
902 @param id: device id
903 """
904 type = sxp.name(dev_config)
905 dev = self.getDevice(type, id)
906 old_config = dev.getConfig()
907 new_config = dev.configure(dev_config, change=True)
908 # Patch new config into vm config.
909 new_full_config = ['device', new_config]
910 old_full_config = ['device', old_config]
911 old_index = self.config.index(old_full_config)
912 self.config[old_index] = new_full_config
913 return new_config
915 def device_refresh(self, type, id):
916 """Refresh a device.
918 @param type: device type
919 @param id: device id
920 """
921 dev = self.getDevice(type, id)
922 dev.refresh()
924 def device_delete(self, type, id):
925 """Destroy and remove a device.
927 @param type: device type
928 @param id: device id
929 """
930 dev = self.getDevice(type, id)
931 dev_config = dev.getConfig()
932 if dev_config:
933 self.config.remove(['device', dev_config])
934 self.deleteDevice(type, dev.getId())
936 def configure_bootloader(self):
937 """Configure boot loader.
938 """
939 self.bootloader = sxp.child_value(self.config, "bootloader")
941 def configure_restart(self):
942 """Configure the vm restart mode.
943 """
944 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
945 if r not in restart_modes:
946 raise VmError('invalid restart mode: ' + str(r))
947 self.restart_mode = r;
949 def restart_needed(self, reason):
950 """Determine if the vm needs to be restarted when shutdown
951 for the given reason.
953 @param reason: shutdown reason
954 @return True if needs restart, False otherwise
955 """
956 if self.restart_mode == RESTART_NEVER:
957 return False
958 if self.restart_mode == RESTART_ALWAYS:
959 return True
960 if self.restart_mode == RESTART_ONREBOOT:
961 return reason == 'reboot'
962 return False
964 def restart_cancel(self):
965 """Cancel a vm restart.
966 """
967 self.restart_state = None
969 def restarting(self):
970 """Put the vm into restart mode.
971 """
972 self.restart_state = STATE_RESTART_PENDING
974 def restart_pending(self):
975 """Test if the vm has a pending restart.
976 """
977 return self.restart_state == STATE_RESTART_PENDING
979 def rebooting(self):
980 return self.restart_state == STATE_RESTART_BOOTING
982 def restart_check(self):
983 """Check if domain restart is OK.
984 To prevent restart loops, raise an error if it is
985 less than MINIMUM_RESTART_TIME seconds since the last restart.
986 """
987 tnow = time.time()
988 if self.restart_time is not None:
989 tdelta = tnow - self.restart_time
990 if tdelta < self.MINIMUM_RESTART_TIME:
991 self.restart_cancel()
992 msg = 'VM %s restarting too fast' % self.name
993 log.error(msg)
994 raise VmError(msg)
995 self.restart_time = tnow
996 self.restart_count += 1
998 def restart(self):
999 """Restart the domain after it has exited.
1000 Reuses the domain id
1002 """
1003 try:
1004 self.clear_shutdown()
1005 self.state = STATE_VM_OK
1006 self.shutdown_pending = None
1007 self.restart_check()
1008 self.exportToDB()
1009 self.restart_state = STATE_RESTART_BOOTING
1010 if self.bootloader:
1011 self.config = self.bootloader_config()
1012 self.construct(self.config)
1013 self.saveToDB()
1014 finally:
1015 self.restart_state = None
1017 def bootloader_config(self):
1018 # if we're restarting with a bootloader, we need to run it
1019 # FIXME: this assumes the disk is the first device and
1020 # that we're booting from the first disk
1021 blcfg = None
1022 # FIXME: this assumes that we want to use the first disk
1023 dev = sxp.child_value(self.config, "device")
1024 if dev:
1025 disk = sxp.child_value(dev, "uname")
1026 fn = blkdev_uname_to_file(disk)
1027 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1028 if blcfg is None:
1029 msg = "Had a bootloader specified, but can't find disk"
1030 log.error(msg)
1031 raise VmError(msg)
1032 config = sxp.merge(['vm', blconfig ], self.config)
1033 return config
1035 def configure_backends(self):
1036 """Set configuration flags if the vm is a backend for netif or blkif.
1037 Configure the backends to use for vbd and vif if specified.
1038 """
1039 for c in sxp.children(self.config, 'backend'):
1040 v = sxp.child0(c)
1041 name = sxp.name(v)
1042 if name == 'blkif':
1043 self.blkif_backend = True
1044 elif name == 'netif':
1045 self.netif_backend = True
1046 elif name == 'usbif':
1047 self.usbif_backend = True
1048 elif name == 'tpmif':
1049 self.tpmif_backend = True
1050 else:
1051 raise VmError('invalid backend type:' + str(name))
1053 def configure(self):
1054 """Configure a vm.
1056 """
1057 self.configure_fields()
1058 self.create_devices()
1059 self.create_blkif()
1061 def create_blkif(self):
1062 """Create the block device interface (blkif) for the vm.
1063 The vm needs a blkif even if it doesn't have any disks
1064 at creation time, for example when it uses NFS root.
1066 """
1067 return
1068 blkif = self.getDeviceController("vbd", error=False)
1069 if not blkif:
1070 blkif = self.createDeviceController("vbd")
1071 backend = blkif.getBackend(0)
1072 backend.connect(recreate=self.recreate)
1074 def publish_console(self):
1075 db = DBMap(db=XenNode("/console/%d" % self.id))
1076 db['domain'] = self.db.getPath()
1077 db.saveDB(save=True)
1079 def configure_fields(self):
1080 """Process the vm configuration fields using the registered handlers.
1081 """
1082 index = {}
1083 for field in sxp.children(self.config):
1084 field_name = sxp.name(field)
1085 field_index = index.get(field_name, 0)
1086 field_handler = get_config_handler(field_name)
1087 # Ignore unknown fields. Warn?
1088 if field_handler:
1089 v = field_handler(self, self.config, field, field_index)
1090 else:
1091 log.warning("Unknown config field %s", field_name)
1092 index[field_name] = field_index + 1
1094 def mem_target_set(self, target):
1095 """Set domain memory target in bytes.
1096 """
1097 if target:
1098 self.target = target * (1 << 20)
1099 # Commit to XenStore immediately
1100 self.exportToDB()
1102 def vcpu_hotplug(self, vcpu, state):
1103 """Disable or enable VCPU in domain.
1104 """
1105 db = ""
1106 try:
1107 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1108 except:
1109 log.error("Invalid VCPU")
1110 return
1112 if self.store_channel:
1113 if int(state) == 0:
1114 db['availability'] = "offline"
1115 else:
1116 db['availability'] = "online"
1118 db.saveDB(save=True)
1120 def shutdown(self, reason):
1121 if not reason in shutdown_reasons.values():
1122 raise XendError('invalid reason:' + reason)
1123 db = self.db.addChild("/control");
1124 db['shutdown'] = reason;
1125 db.saveDB(save=True);
1126 if not reason in ['suspend']:
1127 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1129 def clear_shutdown(self):
1130 db = self.db.addChild("/control")
1131 db['shutdown'] = ""
1132 db.saveDB(save=True)
1134 def send_sysrq(self, key=0):
1135 db = self.db.addChild("/control");
1136 db['sysrq'] = '%c' % key;
1137 db.saveDB(save=True);
1139 def shutdown_time_left(self, timeout):
1140 if not self.shutdown_pending:
1141 return 0
1142 return timeout - (time.time() - self.shutdown_pending['start'])
1144 def dom0_init_store(self):
1145 if not self.store_channel:
1146 self.store_channel = self.eventChannel("store_channel")
1147 self.store_mfn = xc.init_store(self.store_channel.port2)
1148 if self.store_mfn >= 0:
1149 self.db.introduceDomain(self.id, self.store_mfn,
1150 self.store_channel)
1151 self.exportToDB(save=True, sync=True)
1152 # get run-time value of vcpus and update store
1153 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1155 def vm_field_ignore(vm, config, val, index):
1156 """Dummy config field handler used for fields with built-in handling.
1158 @param vm: virtual machine
1159 @param config: vm config
1160 @param val: config field
1161 @param index: field index
1162 """
1163 pass
1165 def vm_field_maxmem(vm, config, val, index):
1166 """Configure vm memory limit.
1168 @param vm: virtual machine
1169 @param config: vm config
1170 @param val: config field
1171 @param index: field index
1172 """
1173 maxmem = sxp.child0(val)
1174 if maxmem is None:
1175 maxmem = vm.memory
1176 try:
1177 maxmem = int(maxmem)
1178 except:
1179 raise VmError("invalid maxmem: " + str(maxmem))
1180 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1182 #============================================================================
1183 # Register image handlers.
1184 from image import \
1185 addImageHandlerClass, \
1186 ImageHandler, \
1187 LinuxImageHandler, \
1188 VmxImageHandler
1190 addImageHandlerClass(LinuxImageHandler)
1191 addImageHandlerClass(VmxImageHandler)
1193 # Ignore the fields we already handle.
1194 add_config_handler('name', vm_field_ignore)
1195 add_config_handler('memory', vm_field_ignore)
1196 add_config_handler('ssidref', vm_field_ignore)
1197 add_config_handler('cpu', vm_field_ignore)
1198 add_config_handler('cpu_weight', vm_field_ignore)
1199 add_config_handler('restart', vm_field_ignore)
1200 add_config_handler('image', vm_field_ignore)
1201 add_config_handler('device', vm_field_ignore)
1202 add_config_handler('backend', vm_field_ignore)
1203 add_config_handler('vcpus', vm_field_ignore)
1204 add_config_handler('bootloader', vm_field_ignore)
1206 # Register other config handlers.
1207 add_config_handler('maxmem', vm_field_maxmem)
1209 #============================================================================
1210 # Register device controllers and their device config types.
1212 from server import blkif
1213 controller.addDevControllerClass("vbd", blkif.BlkifController)
1214 add_device_handler("vbd", "vbd")
1216 from server import netif
1217 controller.addDevControllerClass("vif", netif.NetifController)
1218 add_device_handler("vif", "vif")
1220 from server import tpmif
1221 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1222 add_device_handler("vtpm", "vtpm")
1224 from server import pciif
1225 controller.addDevControllerClass("pci", pciif.PciController)
1226 add_device_handler("pci", "pci")
1228 from xen.xend.server import usbif
1229 controller.addDevControllerClass("usb", usbif.UsbifController)
1230 add_device_handler("usb", "usb")
1232 #============================================================================