xen-vtx-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6742:e9d01c5dc7b4

Move block device bind/unbind into hotplug scripts.
Fixes file: devices since unbind now runs after the backend driver
closes the loopback device.
Also moves name -> node translation into the backend domain.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Mon Sep 12 19:49:03 2005 +0000 (2005-09-12)
parents 813c37b68376
children 3aa853185afe
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
50 from xen.xend.xenstore.xstransact import xstransact
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 path = parentdb.getPath()
161 vm = cls(uuid, path, db)
162 vm.construct(config)
163 vm.saveToDB(sync=True)
165 return vm
167 create = classmethod(create)
169 def recreate(cls, db, info):
170 """Create the VM object for an existing domain.
172 @param db: domain db
173 @param info: domain info from xc
174 """
175 dom = info['dom']
176 path = "/".join(db.getPath().split("/")[0:-1])
177 vm = cls(db.getName(), path, db)
178 vm.setdom(dom)
179 db.readDB()
180 vm.importFromDB()
181 config = vm.config
182 log.debug('info=' + str(info))
183 log.debug('config=' + prettyprintstring(config))
185 vm.memory = info['mem_kb']/1024
186 vm.target = info['mem_kb'] * 1024
188 if config:
189 try:
190 vm.recreate = True
191 vm.construct(config)
192 finally:
193 vm.recreate = False
194 else:
195 vm.setName("Domain-%d" % dom)
197 vm.exportToDB(save=True)
198 return vm
200 recreate = classmethod(recreate)
202 def restore(cls, parentdb, config, uuid=None):
203 """Create a domain and a VM object to do a restore.
205 @param parentdb: parent db
206 @param config: domain configuration
207 @param uuid: uuid to use
208 """
209 if not uuid:
210 uuid = getUuid()
211 db = parentdb.addChild(uuid)
212 path = parentdb.getPath()
213 vm = cls(uuid, path, db)
214 ssidref = int(sxp.child_value(config, 'ssidref'))
215 log.debug('restoring with ssidref='+str(ssidref))
216 id = xc.domain_create(ssidref = ssidref)
217 vm.setdom(id)
218 vm.clear_shutdown()
219 try:
220 vm.restore = True
221 vm.construct(config)
222 finally:
223 vm.restore = False
224 vm.exportToDB(save=True, sync=True)
225 return vm
227 restore = classmethod(restore)
229 __exports__ = [
230 DBVar('id', ty='int'),
231 DBVar('name', ty='str'),
232 DBVar('uuid', ty='str'),
233 DBVar('config', ty='sxpr'),
234 DBVar('start_time', ty='float'),
235 DBVar('state', ty='str'),
236 DBVar('store_mfn', ty='long'),
237 DBVar('console_mfn', ty='long', path="console/ring-ref"),
238 DBVar('restart_mode', ty='str'),
239 DBVar('restart_state', ty='str'),
240 DBVar('restart_time', ty='float'),
241 DBVar('restart_count', ty='int'),
242 DBVar('target', ty='long', path="memory/target"),
243 DBVar('device_model_pid', ty='int'),
244 ]
246 def __init__(self, uuid, path, db):
247 self.uuid = uuid
248 self.path = path + "/" + uuid
249 self.db = db
251 self.recreate = 0
252 self.restore = 0
254 self.config = None
255 self.id = None
256 self.cpu_weight = 1
257 self.start_time = None
258 self.name = None
259 self.memory = None
260 self.ssidref = None
261 self.image = None
263 self.target = None
265 self.store_channel = None
266 self.store_mfn = None
267 self.console_channel = None
268 self.console_mfn = None
269 self.controllers = {}
271 self.info = None
272 self.blkif_backend = False
273 self.netif_backend = False
274 self.netif_idx = 0
275 self.tpmif_backend = False
277 #todo: state: running, suspended
278 self.state = STATE_VM_OK
279 self.state_updated = threading.Condition()
280 self.shutdown_pending = None
282 #todo: set to migrate info if migrating
283 self.migrate = None
285 self.restart_mode = RESTART_ONREBOOT
286 self.restart_state = None
287 self.restart_time = None
288 self.restart_count = 0
290 self.vcpus = 1
291 self.vcpusdb = {}
292 self.bootloader = None
293 self.device_model_pid = 0
295 def setDB(self, db):
296 self.db = db
298 def saveToDB(self, save=False, sync=False):
299 self.db.saveDB(save=save, sync=sync)
301 def exportToDB(self, save=False, sync=False):
302 if self.store_channel:
303 self.store_channel.saveToDB(self.db.addChild("store_channel"),
304 save=save)
305 if self.console_channel:
306 self.db['console/port'] = "%i" % self.console_channel.port1
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.eventChannelOld("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 getStoreChannel(self):
334 return self.store_channel
336 def getConsoleChannel(self):
337 return self.console_channel
339 def update(self, info=None):
340 """Update with info from xc.domain_getinfo().
341 """
342 self.info = info or dom_get(self.id)
343 self.memory = self.info['mem_kb'] / 1024
344 self.ssidref = self.info['ssidref']
345 self.target = self.info['mem_kb'] * 1024
347 def state_set(self, state):
348 self.state_updated.acquire()
349 if self.state != state:
350 self.state = state
351 self.state_updated.notifyAll()
352 self.state_updated.release()
353 self.saveToDB()
355 def state_wait(self, state):
356 self.state_updated.acquire()
357 while self.state != state:
358 self.state_updated.wait()
359 self.state_updated.release()
361 def __str__(self):
362 s = "<domain"
363 s += " id=" + str(self.id)
364 s += " name=" + self.name
365 s += " memory=" + str(self.memory)
366 s += " ssidref=" + str(self.ssidref)
367 s += ">"
368 return s
370 __repr__ = __str__
372 def getDeviceTypes(self):
373 return self.controllers.keys()
375 def getDeviceControllers(self):
376 return self.controllers.values()
378 def getDeviceController(self, type, error=True):
379 ctrl = self.controllers.get(type)
380 if not ctrl and error:
381 raise XendError("invalid device type:" + type)
382 return ctrl
384 def findDeviceController(self, type):
385 return (self.getDeviceController(type, error=False)
386 or self.createDeviceController(type))
388 def createDeviceController(self, type):
389 ctrl = controller.createDevController(type, self, recreate=self.recreate)
390 self.controllers[type] = ctrl
391 return ctrl
393 def createDevice(self, type, devconfig, change=False):
394 if self.recreate:
395 return
396 if type == 'vbd':
397 typedev = sxp.child_value(devconfig, 'dev')
398 if re.match('^ioemu:', typedev):
399 return;
400 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
402 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
404 # create backend db
405 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
406 (type, self.uuid, devnum))
408 # create frontend db
409 db = self.db.addChild("/device/%s/%d" % (type, devnum))
411 db['virtual-device'] = "%i" % devnum
412 #db['backend'] = sxp.child_value(devconfig, 'backend', '0')
413 db['backend'] = backdb.getPath()
414 db['backend-id'] = "%i" % backdom.id
416 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
417 backdb['type'] = type
418 backdb['params'] = params
419 backdb['frontend'] = db.getPath()
420 backdb['frontend-id'] = "%i" % self.id
421 backdb.saveDB(save=True)
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 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
453 self.uuid, devnum)
454 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
456 front = { 'backend' : backpath,
457 'backend-id' : "%i" % backdom.id,
458 'handle' : "%i" % devnum,
459 'mac' : mac }
460 xstransact.Write(frontpath, front)
462 back = { 'script' : script,
463 'domain' : self.name,
464 'mac' : mac,
465 'bridge' : bridge,
466 'frontend' : frontpath,
467 'frontend-id' : "%i" % self.id,
468 'handle' : "%i" % devnum }
469 if ipaddr:
470 back['ip'] = ' '.join(ipaddr)
471 xstransact.Write(backpath, back)
473 return
475 if type == 'vtpm':
476 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
478 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
479 log.error("The domain has a TPM with instance %d." % devnum)
481 # create backend db
482 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
483 (type, self.uuid, devnum))
484 # create frontend db
485 db = self.db.addChild("/device/%s/%d" % (type, devnum))
487 backdb['frontend'] = db.getPath()
488 backdb['frontend-id'] = "%i" % self.id
489 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
490 backdb.saveDB(save=True)
492 db['handle'] = "%i" % devnum
493 db['backend'] = backdb.getPath()
494 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
495 'backend', '0'))
496 db.saveDB(save=True)
498 return
500 ctrl = self.findDeviceController(type)
501 return ctrl.createDevice(devconfig, recreate=self.recreate,
502 change=change)
504 def configureDevice(self, type, id, devconfig):
505 ctrl = self.getDeviceController(type)
506 return ctrl.configureDevice(id, devconfig)
508 def destroyDevice(self, type, id, change=False, reboot=False):
509 ctrl = self.getDeviceController(type)
510 return ctrl.destroyDevice(id, change=change, reboot=reboot)
512 def deleteDevice(self, type, id):
513 ctrl = self.getDeviceController(type)
514 return ctrl.deleteDevice(id)
516 def getDevice(self, type, id, error=True):
517 ctrl = self.getDeviceController(type)
518 return ctrl.getDevice(id, error=error)
520 def getDeviceIds(self, type):
521 ctrl = self.getDeviceController(type)
522 return ctrl.getDeviceIds()
524 def getDeviceSxprs(self, type):
525 ctrl = self.getDeviceController(type)
526 return ctrl.getDeviceSxprs()
528 def sxpr(self):
529 sxpr = ['domain',
530 ['id', self.id],
531 ['name', self.name],
532 ['memory', self.memory],
533 ['ssidref', self.ssidref],
534 ['target', self.target] ]
535 if self.uuid:
536 sxpr.append(['uuid', self.uuid])
537 if self.info:
538 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
539 run = (self.info['running'] and 'r') or '-'
540 block = (self.info['blocked'] and 'b') or '-'
541 pause = (self.info['paused'] and 'p') or '-'
542 shut = (self.info['shutdown'] and 's') or '-'
543 crash = (self.info['crashed'] and 'c') or '-'
544 state = run + block + pause + shut + crash
545 sxpr.append(['state', state])
546 if self.info['shutdown']:
547 reason = shutdown_reason(self.info['shutdown_reason'])
548 sxpr.append(['shutdown_reason', reason])
549 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
550 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
551 sxpr.append(['vcpus', self.info['vcpus']])
552 sxpr.append(['cpumap', self.info['cpumap']])
553 # build a string, using '|' to seperate items, show only up
554 # to number of vcpus in domain, and trim the trailing '|'
555 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
556 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
558 if self.start_time:
559 up_time = time.time() - self.start_time
560 sxpr.append(['up_time', str(up_time) ])
561 sxpr.append(['start_time', str(self.start_time) ])
563 if self.store_channel:
564 sxpr.append(self.store_channel.sxpr())
565 if self.store_mfn:
566 sxpr.append(['store_mfn', self.store_mfn])
567 if self.console_channel:
568 sxpr.append(['console_channel', self.console_channel.sxpr()])
569 if self.console_mfn:
570 sxpr.append(['console_mfn', self.console_mfn])
571 # already in (devices)
572 # console = self.getConsole()
573 # if console:
574 # sxpr.append(console.sxpr())
576 if self.restart_count:
577 sxpr.append(['restart_count', self.restart_count])
578 if self.restart_state:
579 sxpr.append(['restart_state', self.restart_state])
580 if self.restart_time:
581 sxpr.append(['restart_time', str(self.restart_time)])
583 devs = self.sxpr_devices()
584 if devs:
585 sxpr.append(devs)
586 if self.config:
587 sxpr.append(['config', self.config])
588 if self.device_model_pid:
589 sxpr.append(['device_model_pid',self.device_model_pid])
590 return sxpr
592 def sxpr_devices(self):
593 sxpr = []
594 for ty in self.getDeviceTypes():
595 devs = self.getDeviceSxprs(ty)
596 sxpr += devs
597 if sxpr:
598 sxpr.insert(0, 'devices')
599 else:
600 sxpr = None
601 return sxpr
603 def check_name(self, name):
604 """Check if a vm name is valid. Valid names contain alphabetic characters,
605 digits, or characters in '_-.:/+'.
606 The same name cannot be used for more than one vm at the same time.
608 @param name: name
609 @raise: VMerror if invalid
610 """
611 if self.recreate: return
612 if name is None or name == '':
613 raise VmError('missing vm name')
614 for c in name:
615 if c in string.digits: continue
616 if c in '_-.:/+': continue
617 if c in string.ascii_letters: continue
618 raise VmError('invalid vm name')
619 dominfo = domain_exists(name)
620 # When creating or rebooting, a domain with my name should not exist.
621 # When restoring, a domain with my name will exist, but it should have
622 # my domain id.
623 if not dominfo:
624 return
625 if dominfo.is_terminated():
626 return
627 if not self.id or (dominfo.id != self.id):
628 raise VmError('vm name clash: ' + name)
630 def construct(self, config):
631 """Construct the vm instance from its configuration.
633 @param config: configuration
634 @raise: VmError on error
635 """
636 # todo - add support for scheduling params?
637 self.config = config
638 try:
639 # Initial domain create.
640 self.setName(sxp.child_value(config, 'name'))
641 self.check_name(self.name)
642 self.init_image()
643 self.configure_cpus(config)
644 self.init_domain()
645 self.register_domain()
646 self.configure_bootloader()
648 # Create domain devices.
649 self.configure_backends()
650 self.configure_restart()
651 self.construct_image()
652 self.configure()
653 self.exportToDB(save=True)
654 except Exception, ex:
655 # Catch errors, cleanup and re-raise.
656 print 'Domain construction error:', ex
657 import traceback
658 traceback.print_exc()
659 self.destroy()
660 raise
662 def register_domain(self):
663 xd = get_component('xen.xend.XendDomain')
664 xd._add_domain(self)
665 self.exportToDB(save=True)
667 def configure_cpus(self, config):
668 try:
669 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
670 except:
671 raise VmError('invalid cpu weight')
672 self.memory = int(sxp.child_value(config, 'memory'))
673 if self.memory is None:
674 raise VmError('missing memory size')
675 self.target = self.memory * (1 << 20)
676 self.ssidref = int(sxp.child_value(config, 'ssidref'))
677 cpu = sxp.child_value(config, 'cpu')
678 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
679 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
680 try:
681 image = sxp.child_value(self.config, 'image')
682 vcpus = sxp.child_value(image, 'vcpus')
683 if vcpus:
684 self.vcpus = int(vcpus)
685 except:
686 raise VmError('invalid vcpus value')
688 def exportVCPUSToDB(self, vcpus):
689 for v in range(0,vcpus):
690 path = "/cpu/%d"%(v)
691 if not self.vcpusdb.has_key(path):
692 self.vcpusdb[path] = self.db.addChild(path)
693 db = self.vcpusdb[path]
694 log.debug("writing key availability=online to path %s in store"%(path))
695 db['availability'] = "online"
696 db.saveDB(save=True)
698 def init_image(self):
699 """Create boot image handler for the domain.
700 """
701 image = sxp.child_value(self.config, 'image')
702 if image is None:
703 raise VmError('missing image')
704 self.image = ImageHandler.create(self, image)
706 def construct_image(self):
707 """Construct the boot image for the domain.
708 """
709 self.create_channel()
710 self.image.createImage()
711 self.exportToDB()
712 if self.store_channel and self.store_mfn >= 0:
713 self.db.introduceDomain(self.id,
714 self.store_mfn,
715 self.store_channel)
716 # get the configured value of vcpus and update store
717 self.exportVCPUSToDB(self.vcpus)
719 def delete(self):
720 """Delete the vm's db.
721 """
722 if dom_get(self.id):
723 return
724 self.id = None
725 self.saveToDB(sync=True)
726 try:
727 # Todo: eventually will have to wait for devices to signal
728 # destruction before can delete the db.
729 if self.db:
730 self.db.delete()
731 except Exception, ex:
732 log.warning("error in domain db delete: %s", ex)
733 pass
735 def destroy_domain(self):
736 """Destroy the vm's domain.
737 The domain will not finally go away unless all vm
738 devices have been released.
739 """
740 if self.id is None:
741 return
742 try:
743 xc.domain_destroy(dom=self.id)
744 except Exception, err:
745 log.exception("Domain destroy failed: %s", self.name)
747 def cleanup(self):
748 """Cleanup vm resources: release devices.
749 """
750 self.state = STATE_VM_TERMINATED
751 self.release_devices()
752 if self.store_channel:
753 try:
754 self.store_channel.close()
755 self.store_channel = None
756 except:
757 pass
758 try:
759 self.db.releaseDomain(self.id)
760 except Exception, ex:
761 log.warning("error in domain release on xenstore: %s", ex)
762 pass
763 if self.console_channel:
764 # notify processes using this cosole?
765 try:
766 self.console_channel.close()
767 self.console_channel = None
768 except:
769 pass
770 if self.image:
771 try:
772 self.device_model_pid = 0
773 self.image.destroy()
774 self.image = None
775 except:
776 pass
778 def destroy(self):
779 """Clenup vm and destroy domain.
780 """
781 self.destroy_domain()
782 self.cleanup()
783 self.saveToDB()
784 return 0
786 def is_terminated(self):
787 """Check if a domain has been terminated.
788 """
789 return self.state == STATE_VM_TERMINATED
791 def release_devices(self):
792 """Release all vm devices.
793 """
794 reboot = self.restart_pending()
795 for ctrl in self.getDeviceControllers():
796 if ctrl.isDestroyed(): continue
797 ctrl.destroyController(reboot=reboot)
798 t = xstransact("%s/device" % self.path)
799 for d in t.list("vif"):
800 t.remove(d)
801 t.commit()
802 ddb = self.db.addChild("/device")
803 for type in ddb.keys():
804 if type == 'vbd':
805 typedb = ddb.addChild(type)
806 for dev in typedb.keys():
807 typedb[dev].delete()
808 typedb.saveDB(save=True)
809 if type == 'vtpm':
810 typedb = ddb.addChild(type)
811 for dev in typedb.keys():
812 typedb[dev].delete()
813 typedb.saveDB(save=True)
815 def show(self):
816 """Print virtual machine info.
817 """
818 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
819 print "image:"
820 sxp.show(self.image)
821 print "]"
823 def init_domain(self):
824 """Initialize the domain memory.
825 """
826 if self.recreate:
827 return
828 if self.start_time is None:
829 self.start_time = time.time()
830 try:
831 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
832 except:
833 raise VmError('invalid cpu')
834 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
835 log.debug('init_domain> Created domain=%d name=%s memory=%d',
836 id, self.name, self.memory)
837 self.setdom(id)
839 def eventChannelOld(self, key):
840 """Create an event channel to the domain.
841 If saved info is available recreate the channel.
843 @param key db key for the saved data (if any)
844 """
845 db = self.db.addChild(key)
846 return EventChannel.restoreFromDB(db, 0, self.id)
848 def eventChannel(self, path=None, key=None):
849 """Create an event channel to the domain.
851 @param path under which port is stored in db
852 """
853 port = 0
854 try:
855 if path and key:
856 if path:
857 db = self.db.addChild(path)
858 else:
859 db = self.db
860 port = int(db[key].getData())
861 except: pass
862 return EventChannel.interdomain(0, self.id, port1=port, port2=0)
864 def create_channel(self):
865 """Create the channels to the domain.
866 """
867 self.store_channel = self.eventChannelOld("store_channel")
868 self.console_channel = self.eventChannel("console", "port")
870 def create_configured_devices(self):
871 devices = sxp.children(self.config, 'device')
872 for d in devices:
873 dev_config = sxp.child0(d)
874 if dev_config is None:
875 raise VmError('invalid device')
876 dev_type = sxp.name(dev_config)
877 ctrl_type = get_device_handler(dev_type)
878 if ctrl_type is None:
879 raise VmError('unknown device type: ' + dev_type)
880 self.createDevice(ctrl_type, dev_config)
882 def create_devices(self):
883 """Create the devices for a vm.
885 @raise: VmError for invalid devices
886 """
887 if self.rebooting():
888 for ctrl in self.getDeviceControllers():
889 ctrl.initController(reboot=True)
890 else:
891 self.create_configured_devices()
892 if not self.device_model_pid:
893 self.device_model_pid = self.image.createDeviceModel()
895 def device_create(self, dev_config):
896 """Create a new device.
898 @param dev_config: device configuration
899 """
900 dev_type = sxp.name(dev_config)
901 dev = self.createDevice(dev_type, dev_config, change=True)
902 self.config.append(['device', dev.getConfig()])
903 return dev.sxpr()
905 def device_configure(self, dev_config, id):
906 """Configure an existing device.
908 @param dev_config: device configuration
909 @param id: device id
910 """
911 type = sxp.name(dev_config)
912 dev = self.getDevice(type, id)
913 old_config = dev.getConfig()
914 new_config = dev.configure(dev_config, change=True)
915 # Patch new config into vm config.
916 new_full_config = ['device', new_config]
917 old_full_config = ['device', old_config]
918 old_index = self.config.index(old_full_config)
919 self.config[old_index] = new_full_config
920 return new_config
922 def device_refresh(self, type, id):
923 """Refresh a device.
925 @param type: device type
926 @param id: device id
927 """
928 dev = self.getDevice(type, id)
929 dev.refresh()
931 def device_delete(self, type, id):
932 """Destroy and remove a device.
934 @param type: device type
935 @param id: device id
936 """
937 dev = self.getDevice(type, id)
938 dev_config = dev.getConfig()
939 if dev_config:
940 self.config.remove(['device', dev_config])
941 self.deleteDevice(type, dev.getId())
943 def configure_bootloader(self):
944 """Configure boot loader.
945 """
946 self.bootloader = sxp.child_value(self.config, "bootloader")
948 def configure_restart(self):
949 """Configure the vm restart mode.
950 """
951 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
952 if r not in restart_modes:
953 raise VmError('invalid restart mode: ' + str(r))
954 self.restart_mode = r;
956 def restart_needed(self, reason):
957 """Determine if the vm needs to be restarted when shutdown
958 for the given reason.
960 @param reason: shutdown reason
961 @return True if needs restart, False otherwise
962 """
963 if self.restart_mode == RESTART_NEVER:
964 return False
965 if self.restart_mode == RESTART_ALWAYS:
966 return True
967 if self.restart_mode == RESTART_ONREBOOT:
968 return reason == 'reboot'
969 return False
971 def restart_cancel(self):
972 """Cancel a vm restart.
973 """
974 self.restart_state = None
976 def restarting(self):
977 """Put the vm into restart mode.
978 """
979 self.restart_state = STATE_RESTART_PENDING
981 def restart_pending(self):
982 """Test if the vm has a pending restart.
983 """
984 return self.restart_state == STATE_RESTART_PENDING
986 def rebooting(self):
987 return self.restart_state == STATE_RESTART_BOOTING
989 def restart_check(self):
990 """Check if domain restart is OK.
991 To prevent restart loops, raise an error if it is
992 less than MINIMUM_RESTART_TIME seconds since the last restart.
993 """
994 tnow = time.time()
995 if self.restart_time is not None:
996 tdelta = tnow - self.restart_time
997 if tdelta < self.MINIMUM_RESTART_TIME:
998 self.restart_cancel()
999 msg = 'VM %s restarting too fast' % self.name
1000 log.error(msg)
1001 raise VmError(msg)
1002 self.restart_time = tnow
1003 self.restart_count += 1
1005 def restart(self):
1006 """Restart the domain after it has exited.
1007 Reuses the domain id
1009 """
1010 try:
1011 self.clear_shutdown()
1012 self.state = STATE_VM_OK
1013 self.shutdown_pending = None
1014 self.restart_check()
1015 self.exportToDB()
1016 self.restart_state = STATE_RESTART_BOOTING
1017 if self.bootloader:
1018 self.config = self.bootloader_config()
1019 self.construct(self.config)
1020 self.saveToDB()
1021 finally:
1022 self.restart_state = None
1024 def bootloader_config(self):
1025 # if we're restarting with a bootloader, we need to run it
1026 # FIXME: this assumes the disk is the first device and
1027 # that we're booting from the first disk
1028 blcfg = None
1029 # FIXME: this assumes that we want to use the first disk
1030 dev = sxp.child_value(self.config, "device")
1031 if dev:
1032 disk = sxp.child_value(dev, "uname")
1033 fn = blkdev_uname_to_file(disk)
1034 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1035 if blcfg is None:
1036 msg = "Had a bootloader specified, but can't find disk"
1037 log.error(msg)
1038 raise VmError(msg)
1039 config = sxp.merge(['vm', blconfig ], self.config)
1040 return config
1042 def configure_backends(self):
1043 """Set configuration flags if the vm is a backend for netif or blkif.
1044 Configure the backends to use for vbd and vif if specified.
1045 """
1046 for c in sxp.children(self.config, 'backend'):
1047 v = sxp.child0(c)
1048 name = sxp.name(v)
1049 if name == 'blkif':
1050 self.blkif_backend = True
1051 elif name == 'netif':
1052 self.netif_backend = True
1053 elif name == 'usbif':
1054 self.usbif_backend = True
1055 elif name == 'tpmif':
1056 self.tpmif_backend = True
1057 else:
1058 raise VmError('invalid backend type:' + str(name))
1060 def configure(self):
1061 """Configure a vm.
1063 """
1064 self.configure_fields()
1065 self.create_devices()
1066 self.create_blkif()
1068 def create_blkif(self):
1069 """Create the block device interface (blkif) for the vm.
1070 The vm needs a blkif even if it doesn't have any disks
1071 at creation time, for example when it uses NFS root.
1073 """
1074 return
1075 blkif = self.getDeviceController("vbd", error=False)
1076 if not blkif:
1077 blkif = self.createDeviceController("vbd")
1078 backend = blkif.getBackend(0)
1079 backend.connect(recreate=self.recreate)
1081 def configure_fields(self):
1082 """Process the vm configuration fields using the registered handlers.
1083 """
1084 index = {}
1085 for field in sxp.children(self.config):
1086 field_name = sxp.name(field)
1087 field_index = index.get(field_name, 0)
1088 field_handler = get_config_handler(field_name)
1089 # Ignore unknown fields. Warn?
1090 if field_handler:
1091 v = field_handler(self, self.config, field, field_index)
1092 else:
1093 log.warning("Unknown config field %s", field_name)
1094 index[field_name] = field_index + 1
1096 def mem_target_set(self, target):
1097 """Set domain memory target in bytes.
1098 """
1099 if target:
1100 self.target = target * (1 << 20)
1101 # Commit to XenStore immediately
1102 self.exportToDB()
1104 def vcpu_hotplug(self, vcpu, state):
1105 """Disable or enable VCPU in domain.
1106 """
1107 db = ""
1108 try:
1109 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1110 except:
1111 log.error("Invalid VCPU")
1112 return
1114 if self.store_channel:
1115 if int(state) == 0:
1116 db['availability'] = "offline"
1117 else:
1118 db['availability'] = "online"
1120 db.saveDB(save=True)
1122 def shutdown(self, reason):
1123 if not reason in shutdown_reasons.values():
1124 raise XendError('invalid reason:' + reason)
1125 db = self.db.addChild("/control");
1126 db['shutdown'] = reason;
1127 db.saveDB(save=True);
1128 if not reason in ['suspend']:
1129 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1131 def clear_shutdown(self):
1132 db = self.db.addChild("/control")
1133 db['shutdown'] = ""
1134 db.saveDB(save=True)
1136 def send_sysrq(self, key=0):
1137 db = self.db.addChild("/control");
1138 db['sysrq'] = '%c' % key;
1139 db.saveDB(save=True);
1141 def shutdown_time_left(self, timeout):
1142 if not self.shutdown_pending:
1143 return 0
1144 return timeout - (time.time() - self.shutdown_pending['start'])
1146 def dom0_init_store(self):
1147 if not self.store_channel:
1148 self.store_channel = self.eventChannelOld("store_channel")
1149 self.store_mfn = xc.init_store(self.store_channel.port2)
1150 if self.store_mfn >= 0:
1151 self.db.introduceDomain(self.id, self.store_mfn,
1152 self.store_channel)
1153 self.exportToDB(save=True, sync=True)
1154 # get run-time value of vcpus and update store
1155 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1157 def vm_field_ignore(vm, config, val, index):
1158 """Dummy config field handler used for fields with built-in handling.
1160 @param vm: virtual machine
1161 @param config: vm config
1162 @param val: config field
1163 @param index: field index
1164 """
1165 pass
1167 def vm_field_maxmem(vm, config, val, index):
1168 """Configure vm memory limit.
1170 @param vm: virtual machine
1171 @param config: vm config
1172 @param val: config field
1173 @param index: field index
1174 """
1175 maxmem = sxp.child0(val)
1176 if maxmem is None:
1177 maxmem = vm.memory
1178 try:
1179 maxmem = int(maxmem)
1180 except:
1181 raise VmError("invalid maxmem: " + str(maxmem))
1182 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1184 #============================================================================
1185 # Register image handlers.
1186 from image import \
1187 addImageHandlerClass, \
1188 ImageHandler, \
1189 LinuxImageHandler, \
1190 VmxImageHandler
1192 addImageHandlerClass(LinuxImageHandler)
1193 addImageHandlerClass(VmxImageHandler)
1195 # Ignore the fields we already handle.
1196 add_config_handler('name', vm_field_ignore)
1197 add_config_handler('memory', vm_field_ignore)
1198 add_config_handler('ssidref', vm_field_ignore)
1199 add_config_handler('cpu', vm_field_ignore)
1200 add_config_handler('cpu_weight', vm_field_ignore)
1201 add_config_handler('restart', vm_field_ignore)
1202 add_config_handler('image', vm_field_ignore)
1203 add_config_handler('device', vm_field_ignore)
1204 add_config_handler('backend', vm_field_ignore)
1205 add_config_handler('vcpus', vm_field_ignore)
1206 add_config_handler('bootloader', vm_field_ignore)
1208 # Register other config handlers.
1209 add_config_handler('maxmem', vm_field_maxmem)
1211 #============================================================================
1212 # Register device controllers and their device config types.
1214 from server import blkif
1215 controller.addDevControllerClass("vbd", blkif.BlkifController)
1216 add_device_handler("vbd", "vbd")
1218 from server import netif
1219 controller.addDevControllerClass("vif", netif.NetifController)
1220 add_device_handler("vif", "vif")
1222 from server import tpmif
1223 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1224 add_device_handler("vtpm", "vtpm")
1226 from server import pciif
1227 controller.addDevControllerClass("pci", pciif.PciController)
1228 add_device_handler("pci", "pci")
1230 from xen.xend.server import usbif
1231 controller.addDevControllerClass("usb", usbif.UsbifController)
1232 add_device_handler("usb", "usb")
1234 #============================================================================