ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6695:7bc32f4c67fb

merge?
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 21:34:17 2005 +0000 (2005-09-07)
parents a39b1fa10edc 7d0fb56b4a91
children b2f4823b6ff0 fbdb56cd611b 813c37b68376
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.db['console/port'] = "%i" % self.console_channel.port1
302 if self.image:
303 self.image.exportToDB(save=save, sync=sync)
304 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
306 def importFromDB(self):
307 self.db.importFromDB(self, fields=self.__exports__)
308 self.store_channel = self.eventChannelOld("store_channel")
310 def setdom(self, dom):
311 """Set the domain id.
313 @param dom: domain id
314 """
315 self.id = int(dom)
316 #self.db.id = self.id
318 def getDomain(self):
319 return self.id
321 def setName(self, name):
322 self.name = name
323 self.db.name = self.name
325 def getName(self):
326 return self.name
328 def getStoreChannel(self):
329 return self.store_channel
331 def getConsoleChannel(self):
332 return self.console_channel
334 def update(self, info=None):
335 """Update with info from xc.domain_getinfo().
336 """
337 self.info = info or dom_get(self.id)
338 self.memory = self.info['mem_kb'] / 1024
339 self.ssidref = self.info['ssidref']
340 self.target = self.info['mem_kb'] * 1024
342 def state_set(self, state):
343 self.state_updated.acquire()
344 if self.state != state:
345 self.state = state
346 self.state_updated.notifyAll()
347 self.state_updated.release()
348 self.saveToDB()
350 def state_wait(self, state):
351 self.state_updated.acquire()
352 while self.state != state:
353 self.state_updated.wait()
354 self.state_updated.release()
356 def __str__(self):
357 s = "<domain"
358 s += " id=" + str(self.id)
359 s += " name=" + self.name
360 s += " memory=" + str(self.memory)
361 s += " ssidref=" + str(self.ssidref)
362 s += ">"
363 return s
365 __repr__ = __str__
367 def getDeviceTypes(self):
368 return self.controllers.keys()
370 def getDeviceControllers(self):
371 return self.controllers.values()
373 def getDeviceController(self, type, error=True):
374 ctrl = self.controllers.get(type)
375 if not ctrl and error:
376 raise XendError("invalid device type:" + type)
377 return ctrl
379 def findDeviceController(self, type):
380 return (self.getDeviceController(type, error=False)
381 or self.createDeviceController(type))
383 def createDeviceController(self, type):
384 ctrl = controller.createDevController(type, self, recreate=self.recreate)
385 self.controllers[type] = ctrl
386 return ctrl
388 def createDevice(self, type, devconfig, change=False):
389 if self.recreate:
390 return
391 if type == 'vbd':
392 typedev = sxp.child_value(devconfig, 'dev')
393 if re.match('^ioemu:', typedev):
394 return;
395 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
397 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
399 # create backend db
400 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
401 (type, self.uuid, devnum))
403 # create frontend db
404 db = self.db.addChild("/device/%s/%d" % (type, devnum))
406 db['virtual-device'] = "%i" % devnum
407 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
408 db['backend'] = backdb.getPath()
409 db['backend-id'] = "%i" % backdom.id
411 backdb['frontend'] = db.getPath()
412 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
413 node = Blkctl.block('bind', type, params)
414 backdb['frontend-id'] = "%i" % self.id
415 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
416 backdb.saveDB(save=True)
418 # Ok, super gross, this really doesn't belong in the frontend db...
419 db['type'] = type
420 db['node'] = node
421 db['params'] = params
422 db.saveDB(save=True)
424 return
426 if type == 'vif':
427 from xen.xend import XendRoot
428 xroot = XendRoot.instance()
430 def _get_config_ipaddr(config):
431 val = []
432 for ipaddr in sxp.children(config, elt='ip'):
433 val.append(sxp.child0(ipaddr))
434 return val
436 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
438 log.error(devconfig)
440 devnum = self.netif_idx
441 self.netif_idx += 1
443 script = sxp.child_value(devconfig, 'script',
444 xroot.get_vif_script())
445 script = os.path.join(xroot.network_script_dir, script)
446 bridge = sxp.child_value(devconfig, 'bridge',
447 xroot.get_vif_bridge())
448 mac = sxp.child_value(devconfig, 'mac')
449 ipaddr = _get_config_ipaddr(devconfig)
451 # create backend db
452 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
453 (type, self.uuid, devnum))
455 # create frontend db
456 db = self.db.addChild("/device/%s/%d" % (type, devnum))
458 backdb['script'] = script
459 backdb['domain'] = self.name
460 backdb['mac'] = mac
461 backdb['bridge'] = bridge
462 if ipaddr:
463 backdb['ip'] = ' '.join(ipaddr)
464 backdb['frontend'] = db.getPath()
465 backdb['frontend-id'] = "%i" % self.id
466 backdb['handle'] = "%i" % devnum
467 backdb.saveDB(save=True)
469 db['backend'] = backdb.getPath()
470 db['backend-id'] = "%i" % backdom.id
471 db['handle'] = "%i" % devnum
472 db['mac'] = mac
474 db.saveDB(save=True)
476 return
478 if type == 'vtpm':
479 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
481 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
482 log.error("The domain has a TPM with instance %d." % devnum)
484 # create backend db
485 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
486 (type, self.uuid, devnum))
487 # create frontend db
488 db = self.db.addChild("/device/%s/%d" % (type, devnum))
490 backdb['frontend'] = db.getPath()
491 backdb['frontend-id'] = "%i" % self.id
492 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
493 backdb.saveDB(save=True)
495 db['handle'] = "%i" % devnum
496 db['backend'] = backdb.getPath()
497 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
498 'backend', '0'))
499 db.saveDB(save=True)
501 return
503 ctrl = self.findDeviceController(type)
504 return ctrl.createDevice(devconfig, recreate=self.recreate,
505 change=change)
507 def configureDevice(self, type, id, devconfig):
508 ctrl = self.getDeviceController(type)
509 return ctrl.configureDevice(id, devconfig)
511 def destroyDevice(self, type, id, change=False, reboot=False):
512 ctrl = self.getDeviceController(type)
513 return ctrl.destroyDevice(id, change=change, reboot=reboot)
515 def deleteDevice(self, type, id):
516 ctrl = self.getDeviceController(type)
517 return ctrl.deleteDevice(id)
519 def getDevice(self, type, id, error=True):
520 ctrl = self.getDeviceController(type)
521 return ctrl.getDevice(id, error=error)
523 def getDeviceIds(self, type):
524 ctrl = self.getDeviceController(type)
525 return ctrl.getDeviceIds()
527 def getDeviceSxprs(self, type):
528 ctrl = self.getDeviceController(type)
529 return ctrl.getDeviceSxprs()
531 def sxpr(self):
532 sxpr = ['domain',
533 ['id', self.id],
534 ['name', self.name],
535 ['memory', self.memory],
536 ['ssidref', self.ssidref],
537 ['target', self.target] ]
538 if self.uuid:
539 sxpr.append(['uuid', self.uuid])
540 if self.info:
541 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
542 run = (self.info['running'] and 'r') or '-'
543 block = (self.info['blocked'] and 'b') or '-'
544 pause = (self.info['paused'] and 'p') or '-'
545 shut = (self.info['shutdown'] and 's') or '-'
546 crash = (self.info['crashed'] and 'c') or '-'
547 state = run + block + pause + shut + crash
548 sxpr.append(['state', state])
549 if self.info['shutdown']:
550 reason = shutdown_reason(self.info['shutdown_reason'])
551 sxpr.append(['shutdown_reason', reason])
552 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
553 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
554 sxpr.append(['vcpus', self.info['vcpus']])
555 sxpr.append(['cpumap', self.info['cpumap']])
556 # build a string, using '|' to seperate items, show only up
557 # to number of vcpus in domain, and trim the trailing '|'
558 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
559 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
561 if self.start_time:
562 up_time = time.time() - self.start_time
563 sxpr.append(['up_time', str(up_time) ])
564 sxpr.append(['start_time', str(self.start_time) ])
566 if self.store_channel:
567 sxpr.append(self.store_channel.sxpr())
568 if self.store_mfn:
569 sxpr.append(['store_mfn', self.store_mfn])
570 if self.console_channel:
571 sxpr.append(['console_channel', self.console_channel.sxpr()])
572 if self.console_mfn:
573 sxpr.append(['console_mfn', self.console_mfn])
574 # already in (devices)
575 # console = self.getConsole()
576 # if console:
577 # sxpr.append(console.sxpr())
579 if self.restart_count:
580 sxpr.append(['restart_count', self.restart_count])
581 if self.restart_state:
582 sxpr.append(['restart_state', self.restart_state])
583 if self.restart_time:
584 sxpr.append(['restart_time', str(self.restart_time)])
586 devs = self.sxpr_devices()
587 if devs:
588 sxpr.append(devs)
589 if self.config:
590 sxpr.append(['config', self.config])
591 if self.device_model_pid:
592 sxpr.append(['device_model_pid',self.device_model_pid])
593 return sxpr
595 def sxpr_devices(self):
596 sxpr = []
597 for ty in self.getDeviceTypes():
598 devs = self.getDeviceSxprs(ty)
599 sxpr += devs
600 if sxpr:
601 sxpr.insert(0, 'devices')
602 else:
603 sxpr = None
604 return sxpr
606 def check_name(self, name):
607 """Check if a vm name is valid. Valid names contain alphabetic characters,
608 digits, or characters in '_-.:/+'.
609 The same name cannot be used for more than one vm at the same time.
611 @param name: name
612 @raise: VMerror if invalid
613 """
614 if self.recreate: return
615 if name is None or name == '':
616 raise VmError('missing vm name')
617 for c in name:
618 if c in string.digits: continue
619 if c in '_-.:/+': continue
620 if c in string.ascii_letters: continue
621 raise VmError('invalid vm name')
622 dominfo = domain_exists(name)
623 # When creating or rebooting, a domain with my name should not exist.
624 # When restoring, a domain with my name will exist, but it should have
625 # my domain id.
626 if not dominfo:
627 return
628 if dominfo.is_terminated():
629 return
630 if not self.id or (dominfo.id != self.id):
631 raise VmError('vm name clash: ' + name)
633 def construct(self, config):
634 """Construct the vm instance from its configuration.
636 @param config: configuration
637 @raise: VmError on error
638 """
639 # todo - add support for scheduling params?
640 self.config = config
641 try:
642 # Initial domain create.
643 self.setName(sxp.child_value(config, 'name'))
644 self.check_name(self.name)
645 self.init_image()
646 self.configure_cpus(config)
647 self.init_domain()
648 self.register_domain()
649 self.configure_bootloader()
651 # Create domain devices.
652 self.configure_backends()
653 self.configure_restart()
654 self.construct_image()
655 self.configure()
656 self.exportToDB(save=True)
657 except Exception, ex:
658 # Catch errors, cleanup and re-raise.
659 print 'Domain construction error:', ex
660 import traceback
661 traceback.print_exc()
662 self.destroy()
663 raise
665 def register_domain(self):
666 xd = get_component('xen.xend.XendDomain')
667 xd._add_domain(self)
668 self.exportToDB(save=True)
670 def configure_cpus(self, config):
671 try:
672 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
673 except:
674 raise VmError('invalid cpu weight')
675 self.memory = int(sxp.child_value(config, 'memory'))
676 if self.memory is None:
677 raise VmError('missing memory size')
678 self.target = self.memory * (1 << 20)
679 self.ssidref = int(sxp.child_value(config, 'ssidref'))
680 cpu = sxp.child_value(config, 'cpu')
681 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
682 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
683 try:
684 image = sxp.child_value(self.config, 'image')
685 vcpus = sxp.child_value(image, 'vcpus')
686 if vcpus:
687 self.vcpus = int(vcpus)
688 except:
689 raise VmError('invalid vcpus value')
691 def exportVCPUSToDB(self, vcpus):
692 for v in range(0,vcpus):
693 path = "/cpu/%d"%(v)
694 if not self.vcpusdb.has_key(path):
695 self.vcpusdb[path] = self.db.addChild(path)
696 db = self.vcpusdb[path]
697 log.debug("writing key availability=online to path %s in store"%(path))
698 db['availability'] = "online"
699 db.saveDB(save=True)
701 def init_image(self):
702 """Create boot image handler for the domain.
703 """
704 image = sxp.child_value(self.config, 'image')
705 if image is None:
706 raise VmError('missing image')
707 self.image = ImageHandler.create(self, image)
709 def construct_image(self):
710 """Construct the boot image for the domain.
711 """
712 self.create_channel()
713 self.image.createImage()
714 self.exportToDB()
715 if self.store_channel and self.store_mfn >= 0:
716 self.db.introduceDomain(self.id,
717 self.store_mfn,
718 self.store_channel)
719 # get the configured value of vcpus and update store
720 self.exportVCPUSToDB(self.vcpus)
722 def delete(self):
723 """Delete the vm's db.
724 """
725 if dom_get(self.id):
726 return
727 self.id = None
728 self.saveToDB(sync=True)
729 try:
730 # Todo: eventually will have to wait for devices to signal
731 # destruction before can delete the db.
732 if self.db:
733 self.db.delete()
734 except Exception, ex:
735 log.warning("error in domain db delete: %s", ex)
736 pass
738 def destroy_domain(self):
739 """Destroy the vm's domain.
740 The domain will not finally go away unless all vm
741 devices have been released.
742 """
743 if self.id is None:
744 return
745 try:
746 xc.domain_destroy(dom=self.id)
747 except Exception, err:
748 log.exception("Domain destroy failed: %s", self.name)
750 def cleanup(self):
751 """Cleanup vm resources: release devices.
752 """
753 self.state = STATE_VM_TERMINATED
754 self.release_devices()
755 if self.store_channel:
756 try:
757 self.store_channel.close()
758 self.store_channel = None
759 except:
760 pass
761 try:
762 self.db.releaseDomain(self.id)
763 except Exception, ex:
764 log.warning("error in domain release on xenstore: %s", ex)
765 pass
766 if self.console_channel:
767 # notify processes using this cosole?
768 try:
769 self.console_channel.close()
770 self.console_channel = None
771 except:
772 pass
773 if self.image:
774 try:
775 self.device_model_pid = 0
776 self.image.destroy()
777 self.image = None
778 except:
779 pass
781 def destroy(self):
782 """Clenup vm and destroy domain.
783 """
784 self.destroy_domain()
785 self.cleanup()
786 self.saveToDB()
787 return 0
789 def is_terminated(self):
790 """Check if a domain has been terminated.
791 """
792 return self.state == STATE_VM_TERMINATED
794 def release_devices(self):
795 """Release all vm devices.
796 """
797 reboot = self.restart_pending()
798 for ctrl in self.getDeviceControllers():
799 if ctrl.isDestroyed(): continue
800 ctrl.destroyController(reboot=reboot)
801 ddb = self.db.addChild("/device")
802 for type in ddb.keys():
803 if type == 'vbd':
804 typedb = ddb.addChild(type)
805 for dev in typedb.keys():
806 devdb = typedb.addChild(str(dev))
807 Blkctl.block('unbind', devdb['type'].getData(),
808 devdb['node'].getData())
809 typedb[dev].delete()
810 typedb.saveDB(save=True)
811 if type == 'vif':
812 typedb = ddb.addChild(type)
813 for dev in typedb.keys():
814 typedb[dev].delete()
815 typedb.saveDB(save=True)
816 if type == 'vtpm':
817 typedb = ddb.addChild(type)
818 for dev in typedb.keys():
819 typedb[dev].delete()
820 typedb.saveDB(save=True)
822 def show(self):
823 """Print virtual machine info.
824 """
825 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
826 print "image:"
827 sxp.show(self.image)
828 print "]"
830 def init_domain(self):
831 """Initialize the domain memory.
832 """
833 if self.recreate:
834 return
835 if self.start_time is None:
836 self.start_time = time.time()
837 try:
838 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
839 except:
840 raise VmError('invalid cpu')
841 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
842 log.debug('init_domain> Created domain=%d name=%s memory=%d',
843 id, self.name, self.memory)
844 self.setdom(id)
846 def eventChannelOld(self, key):
847 """Create an event channel to the domain.
848 If saved info is available recreate the channel.
850 @param key db key for the saved data (if any)
851 """
852 db = self.db.addChild(key)
853 return EventChannel.restoreFromDB(db, 0, self.id)
855 def eventChannel(self, path=None, key=None):
856 """Create an event channel to the domain.
858 @param path under which port is stored in db
859 """
860 port = 0
861 try:
862 if path and key:
863 if path:
864 db = self.db.addChild(path)
865 else:
866 db = self.db
867 port = int(db[key].getData())
868 except: pass
869 return EventChannel.interdomain(0, self.id, port1=port, port2=0)
871 def create_channel(self):
872 """Create the channels to the domain.
873 """
874 self.store_channel = self.eventChannelOld("store_channel")
875 self.console_channel = self.eventChannel("console", "port")
877 def create_configured_devices(self):
878 devices = sxp.children(self.config, 'device')
879 for d in devices:
880 dev_config = sxp.child0(d)
881 if dev_config is None:
882 raise VmError('invalid device')
883 dev_type = sxp.name(dev_config)
884 ctrl_type = get_device_handler(dev_type)
885 if ctrl_type is None:
886 raise VmError('unknown device type: ' + dev_type)
887 self.createDevice(ctrl_type, dev_config)
889 def create_devices(self):
890 """Create the devices for a vm.
892 @raise: VmError for invalid devices
893 """
894 if self.rebooting():
895 for ctrl in self.getDeviceControllers():
896 ctrl.initController(reboot=True)
897 else:
898 self.create_configured_devices()
899 if not self.device_model_pid:
900 self.device_model_pid = self.image.createDeviceModel()
902 def device_create(self, dev_config):
903 """Create a new device.
905 @param dev_config: device configuration
906 """
907 dev_type = sxp.name(dev_config)
908 dev = self.createDevice(dev_type, dev_config, change=True)
909 self.config.append(['device', dev.getConfig()])
910 return dev.sxpr()
912 def device_configure(self, dev_config, id):
913 """Configure an existing device.
915 @param dev_config: device configuration
916 @param id: device id
917 """
918 type = sxp.name(dev_config)
919 dev = self.getDevice(type, id)
920 old_config = dev.getConfig()
921 new_config = dev.configure(dev_config, change=True)
922 # Patch new config into vm config.
923 new_full_config = ['device', new_config]
924 old_full_config = ['device', old_config]
925 old_index = self.config.index(old_full_config)
926 self.config[old_index] = new_full_config
927 return new_config
929 def device_refresh(self, type, id):
930 """Refresh a device.
932 @param type: device type
933 @param id: device id
934 """
935 dev = self.getDevice(type, id)
936 dev.refresh()
938 def device_delete(self, type, id):
939 """Destroy and remove a device.
941 @param type: device type
942 @param id: device id
943 """
944 dev = self.getDevice(type, id)
945 dev_config = dev.getConfig()
946 if dev_config:
947 self.config.remove(['device', dev_config])
948 self.deleteDevice(type, dev.getId())
950 def configure_bootloader(self):
951 """Configure boot loader.
952 """
953 self.bootloader = sxp.child_value(self.config, "bootloader")
955 def configure_restart(self):
956 """Configure the vm restart mode.
957 """
958 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
959 if r not in restart_modes:
960 raise VmError('invalid restart mode: ' + str(r))
961 self.restart_mode = r;
963 def restart_needed(self, reason):
964 """Determine if the vm needs to be restarted when shutdown
965 for the given reason.
967 @param reason: shutdown reason
968 @return True if needs restart, False otherwise
969 """
970 if self.restart_mode == RESTART_NEVER:
971 return False
972 if self.restart_mode == RESTART_ALWAYS:
973 return True
974 if self.restart_mode == RESTART_ONREBOOT:
975 return reason == 'reboot'
976 return False
978 def restart_cancel(self):
979 """Cancel a vm restart.
980 """
981 self.restart_state = None
983 def restarting(self):
984 """Put the vm into restart mode.
985 """
986 self.restart_state = STATE_RESTART_PENDING
988 def restart_pending(self):
989 """Test if the vm has a pending restart.
990 """
991 return self.restart_state == STATE_RESTART_PENDING
993 def rebooting(self):
994 return self.restart_state == STATE_RESTART_BOOTING
996 def restart_check(self):
997 """Check if domain restart is OK.
998 To prevent restart loops, raise an error if it is
999 less than MINIMUM_RESTART_TIME seconds since the last restart.
1000 """
1001 tnow = time.time()
1002 if self.restart_time is not None:
1003 tdelta = tnow - self.restart_time
1004 if tdelta < self.MINIMUM_RESTART_TIME:
1005 self.restart_cancel()
1006 msg = 'VM %s restarting too fast' % self.name
1007 log.error(msg)
1008 raise VmError(msg)
1009 self.restart_time = tnow
1010 self.restart_count += 1
1012 def restart(self):
1013 """Restart the domain after it has exited.
1014 Reuses the domain id
1016 """
1017 try:
1018 self.clear_shutdown()
1019 self.state = STATE_VM_OK
1020 self.shutdown_pending = None
1021 self.restart_check()
1022 self.exportToDB()
1023 self.restart_state = STATE_RESTART_BOOTING
1024 if self.bootloader:
1025 self.config = self.bootloader_config()
1026 self.construct(self.config)
1027 self.saveToDB()
1028 finally:
1029 self.restart_state = None
1031 def bootloader_config(self):
1032 # if we're restarting with a bootloader, we need to run it
1033 # FIXME: this assumes the disk is the first device and
1034 # that we're booting from the first disk
1035 blcfg = None
1036 # FIXME: this assumes that we want to use the first disk
1037 dev = sxp.child_value(self.config, "device")
1038 if dev:
1039 disk = sxp.child_value(dev, "uname")
1040 fn = blkdev_uname_to_file(disk)
1041 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1042 if blcfg is None:
1043 msg = "Had a bootloader specified, but can't find disk"
1044 log.error(msg)
1045 raise VmError(msg)
1046 config = sxp.merge(['vm', blconfig ], self.config)
1047 return config
1049 def configure_backends(self):
1050 """Set configuration flags if the vm is a backend for netif or blkif.
1051 Configure the backends to use for vbd and vif if specified.
1052 """
1053 for c in sxp.children(self.config, 'backend'):
1054 v = sxp.child0(c)
1055 name = sxp.name(v)
1056 if name == 'blkif':
1057 self.blkif_backend = True
1058 elif name == 'netif':
1059 self.netif_backend = True
1060 elif name == 'usbif':
1061 self.usbif_backend = True
1062 elif name == 'tpmif':
1063 self.tpmif_backend = True
1064 else:
1065 raise VmError('invalid backend type:' + str(name))
1067 def configure(self):
1068 """Configure a vm.
1070 """
1071 self.configure_fields()
1072 self.create_devices()
1073 self.create_blkif()
1075 def create_blkif(self):
1076 """Create the block device interface (blkif) for the vm.
1077 The vm needs a blkif even if it doesn't have any disks
1078 at creation time, for example when it uses NFS root.
1080 """
1081 return
1082 blkif = self.getDeviceController("vbd", error=False)
1083 if not blkif:
1084 blkif = self.createDeviceController("vbd")
1085 backend = blkif.getBackend(0)
1086 backend.connect(recreate=self.recreate)
1088 def configure_fields(self):
1089 """Process the vm configuration fields using the registered handlers.
1090 """
1091 index = {}
1092 for field in sxp.children(self.config):
1093 field_name = sxp.name(field)
1094 field_index = index.get(field_name, 0)
1095 field_handler = get_config_handler(field_name)
1096 # Ignore unknown fields. Warn?
1097 if field_handler:
1098 v = field_handler(self, self.config, field, field_index)
1099 else:
1100 log.warning("Unknown config field %s", field_name)
1101 index[field_name] = field_index + 1
1103 def mem_target_set(self, target):
1104 """Set domain memory target in bytes.
1105 """
1106 if target:
1107 self.target = target * (1 << 20)
1108 # Commit to XenStore immediately
1109 self.exportToDB()
1111 def vcpu_hotplug(self, vcpu, state):
1112 """Disable or enable VCPU in domain.
1113 """
1114 db = ""
1115 try:
1116 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1117 except:
1118 log.error("Invalid VCPU")
1119 return
1121 if self.store_channel:
1122 if int(state) == 0:
1123 db['availability'] = "offline"
1124 else:
1125 db['availability'] = "online"
1127 db.saveDB(save=True)
1129 def shutdown(self, reason):
1130 if not reason in shutdown_reasons.values():
1131 raise XendError('invalid reason:' + reason)
1132 db = self.db.addChild("/control");
1133 db['shutdown'] = reason;
1134 db.saveDB(save=True);
1135 if not reason in ['suspend']:
1136 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1138 def clear_shutdown(self):
1139 db = self.db.addChild("/control")
1140 db['shutdown'] = ""
1141 db.saveDB(save=True)
1143 def send_sysrq(self, key=0):
1144 db = self.db.addChild("/control");
1145 db['sysrq'] = '%c' % key;
1146 db.saveDB(save=True);
1148 def shutdown_time_left(self, timeout):
1149 if not self.shutdown_pending:
1150 return 0
1151 return timeout - (time.time() - self.shutdown_pending['start'])
1153 def dom0_init_store(self):
1154 if not self.store_channel:
1155 self.store_channel = self.eventChannelOld("store_channel")
1156 self.store_mfn = xc.init_store(self.store_channel.port2)
1157 if self.store_mfn >= 0:
1158 self.db.introduceDomain(self.id, self.store_mfn,
1159 self.store_channel)
1160 self.exportToDB(save=True, sync=True)
1161 # get run-time value of vcpus and update store
1162 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1164 def vm_field_ignore(vm, config, val, index):
1165 """Dummy config field handler used for fields with built-in handling.
1167 @param vm: virtual machine
1168 @param config: vm config
1169 @param val: config field
1170 @param index: field index
1171 """
1172 pass
1174 def vm_field_maxmem(vm, config, val, index):
1175 """Configure vm memory limit.
1177 @param vm: virtual machine
1178 @param config: vm config
1179 @param val: config field
1180 @param index: field index
1181 """
1182 maxmem = sxp.child0(val)
1183 if maxmem is None:
1184 maxmem = vm.memory
1185 try:
1186 maxmem = int(maxmem)
1187 except:
1188 raise VmError("invalid maxmem: " + str(maxmem))
1189 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1191 #============================================================================
1192 # Register image handlers.
1193 from image import \
1194 addImageHandlerClass, \
1195 ImageHandler, \
1196 LinuxImageHandler, \
1197 VmxImageHandler
1199 addImageHandlerClass(LinuxImageHandler)
1200 addImageHandlerClass(VmxImageHandler)
1202 # Ignore the fields we already handle.
1203 add_config_handler('name', vm_field_ignore)
1204 add_config_handler('memory', vm_field_ignore)
1205 add_config_handler('ssidref', vm_field_ignore)
1206 add_config_handler('cpu', vm_field_ignore)
1207 add_config_handler('cpu_weight', vm_field_ignore)
1208 add_config_handler('restart', vm_field_ignore)
1209 add_config_handler('image', vm_field_ignore)
1210 add_config_handler('device', vm_field_ignore)
1211 add_config_handler('backend', vm_field_ignore)
1212 add_config_handler('vcpus', vm_field_ignore)
1213 add_config_handler('bootloader', vm_field_ignore)
1215 # Register other config handlers.
1216 add_config_handler('maxmem', vm_field_maxmem)
1218 #============================================================================
1219 # Register device controllers and their device config types.
1221 from server import blkif
1222 controller.addDevControllerClass("vbd", blkif.BlkifController)
1223 add_device_handler("vbd", "vbd")
1225 from server import netif
1226 controller.addDevControllerClass("vif", netif.NetifController)
1227 add_device_handler("vif", "vif")
1229 from server import tpmif
1230 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1231 add_device_handler("vtpm", "vtpm")
1233 from server import pciif
1234 controller.addDevControllerClass("pci", pciif.PciController)
1235 add_device_handler("pci", "pci")
1237 from xen.xend.server import usbif
1238 controller.addDevControllerClass("usb", usbif.UsbifController)
1239 add_device_handler("usb", "usb")
1241 #============================================================================