ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6764:38a29ec8d021

Switch block device setup/teardown over to xstransact.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Mon Sep 12 19:55:57 2005 +0000 (2005-09-12)
parents 3aa853185afe
children cdaaaa027bf8
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;
401 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
403 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
405 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
406 self.uuid, devnum)
407 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
409 front = { 'backend' : backpath,
410 'backend-id' : "%i" % backdom.id,
411 'virtual-device' : "%i" % devnum }
412 xstransact.Write(frontpath, front)
414 (type, params) = string.split(sxp.child_value(devconfig,
415 'uname'), ':', 1)
416 back = { 'type' : type,
417 'params' : params,
418 'frontend' : frontpath,
419 'frontend-id' : "%i" % self.id }
420 xstransact.Write(backpath, back)
422 return
424 if type == 'vif':
425 from xen.xend import XendRoot
426 xroot = XendRoot.instance()
428 def _get_config_ipaddr(config):
429 val = []
430 for ipaddr in sxp.children(config, elt='ip'):
431 val.append(sxp.child0(ipaddr))
432 return val
434 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
436 devnum = self.netif_idx
437 self.netif_idx += 1
439 script = sxp.child_value(devconfig, 'script',
440 xroot.get_vif_script())
441 script = os.path.join(xroot.network_script_dir, script)
442 bridge = sxp.child_value(devconfig, 'bridge',
443 xroot.get_vif_bridge())
444 mac = sxp.child_value(devconfig, 'mac')
445 ipaddr = _get_config_ipaddr(devconfig)
447 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
448 self.uuid, devnum)
449 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
451 front = { 'backend' : backpath,
452 'backend-id' : "%i" % backdom.id,
453 'handle' : "%i" % devnum,
454 'mac' : mac }
455 xstransact.Write(frontpath, front)
457 back = { 'script' : script,
458 'domain' : self.name,
459 'mac' : mac,
460 'bridge' : bridge,
461 'frontend' : frontpath,
462 'frontend-id' : "%i" % self.id,
463 'handle' : "%i" % devnum }
464 if ipaddr:
465 back['ip'] = ' '.join(ipaddr)
466 xstransact.Write(backpath, back)
468 return
470 if type == 'vtpm':
471 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
473 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
474 log.error("The domain has a TPM with instance %d." % devnum)
476 # create backend db
477 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
478 (type, self.uuid, devnum))
479 # create frontend db
480 db = self.db.addChild("/device/%s/%d" % (type, devnum))
482 backdb['frontend'] = db.getPath()
483 backdb['frontend-id'] = "%i" % self.id
484 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
485 backdb.saveDB(save=True)
487 db['handle'] = "%i" % devnum
488 db['backend'] = backdb.getPath()
489 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
490 'backend', '0'))
491 db.saveDB(save=True)
493 return
495 ctrl = self.findDeviceController(type)
496 return ctrl.createDevice(devconfig, recreate=self.recreate,
497 change=change)
499 def configureDevice(self, type, id, devconfig):
500 ctrl = self.getDeviceController(type)
501 return ctrl.configureDevice(id, devconfig)
503 def destroyDevice(self, type, id, change=False, reboot=False):
504 ctrl = self.getDeviceController(type)
505 return ctrl.destroyDevice(id, change=change, reboot=reboot)
507 def deleteDevice(self, type, id):
508 ctrl = self.getDeviceController(type)
509 return ctrl.deleteDevice(id)
511 def getDevice(self, type, id, error=True):
512 ctrl = self.getDeviceController(type)
513 return ctrl.getDevice(id, error=error)
515 def getDeviceIds(self, type):
516 ctrl = self.getDeviceController(type)
517 return ctrl.getDeviceIds()
519 def getDeviceSxprs(self, type):
520 ctrl = self.getDeviceController(type)
521 return ctrl.getDeviceSxprs()
523 def sxpr(self):
524 sxpr = ['domain',
525 ['id', self.id],
526 ['name', self.name],
527 ['memory', self.memory],
528 ['ssidref', self.ssidref],
529 ['target', self.target] ]
530 if self.uuid:
531 sxpr.append(['uuid', self.uuid])
532 if self.info:
533 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
534 run = (self.info['running'] and 'r') or '-'
535 block = (self.info['blocked'] and 'b') or '-'
536 pause = (self.info['paused'] and 'p') or '-'
537 shut = (self.info['shutdown'] and 's') or '-'
538 crash = (self.info['crashed'] and 'c') or '-'
539 state = run + block + pause + shut + crash
540 sxpr.append(['state', state])
541 if self.info['shutdown']:
542 reason = shutdown_reason(self.info['shutdown_reason'])
543 sxpr.append(['shutdown_reason', reason])
544 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
545 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
546 sxpr.append(['vcpus', self.info['vcpus']])
547 sxpr.append(['cpumap', self.info['cpumap']])
548 # build a string, using '|' to seperate items, show only up
549 # to number of vcpus in domain, and trim the trailing '|'
550 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
551 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
553 if self.start_time:
554 up_time = time.time() - self.start_time
555 sxpr.append(['up_time', str(up_time) ])
556 sxpr.append(['start_time', str(self.start_time) ])
558 if self.store_channel:
559 sxpr.append(self.store_channel.sxpr())
560 if self.store_mfn:
561 sxpr.append(['store_mfn', self.store_mfn])
562 if self.console_channel:
563 sxpr.append(['console_channel', self.console_channel.sxpr()])
564 if self.console_mfn:
565 sxpr.append(['console_mfn', self.console_mfn])
566 # already in (devices)
567 # console = self.getConsole()
568 # if console:
569 # sxpr.append(console.sxpr())
571 if self.restart_count:
572 sxpr.append(['restart_count', self.restart_count])
573 if self.restart_state:
574 sxpr.append(['restart_state', self.restart_state])
575 if self.restart_time:
576 sxpr.append(['restart_time', str(self.restart_time)])
578 devs = self.sxpr_devices()
579 if devs:
580 sxpr.append(devs)
581 if self.config:
582 sxpr.append(['config', self.config])
583 if self.device_model_pid:
584 sxpr.append(['device_model_pid',self.device_model_pid])
585 return sxpr
587 def sxpr_devices(self):
588 sxpr = []
589 for ty in self.getDeviceTypes():
590 devs = self.getDeviceSxprs(ty)
591 sxpr += devs
592 if sxpr:
593 sxpr.insert(0, 'devices')
594 else:
595 sxpr = None
596 return sxpr
598 def check_name(self, name):
599 """Check if a vm name is valid. Valid names contain alphabetic characters,
600 digits, or characters in '_-.:/+'.
601 The same name cannot be used for more than one vm at the same time.
603 @param name: name
604 @raise: VMerror if invalid
605 """
606 if self.recreate: return
607 if name is None or name == '':
608 raise VmError('missing vm name')
609 for c in name:
610 if c in string.digits: continue
611 if c in '_-.:/+': continue
612 if c in string.ascii_letters: continue
613 raise VmError('invalid vm name')
614 dominfo = domain_exists(name)
615 # When creating or rebooting, a domain with my name should not exist.
616 # When restoring, a domain with my name will exist, but it should have
617 # my domain id.
618 if not dominfo:
619 return
620 if dominfo.is_terminated():
621 return
622 if not self.id or (dominfo.id != self.id):
623 raise VmError('vm name clash: ' + name)
625 def construct(self, config):
626 """Construct the vm instance from its configuration.
628 @param config: configuration
629 @raise: VmError on error
630 """
631 # todo - add support for scheduling params?
632 self.config = config
633 try:
634 # Initial domain create.
635 self.setName(sxp.child_value(config, 'name'))
636 self.check_name(self.name)
637 self.init_image()
638 self.configure_cpus(config)
639 self.init_domain()
640 self.register_domain()
641 self.configure_bootloader()
643 # Create domain devices.
644 self.configure_backends()
645 self.configure_restart()
646 self.construct_image()
647 self.configure()
648 self.exportToDB(save=True)
649 except Exception, ex:
650 # Catch errors, cleanup and re-raise.
651 print 'Domain construction error:', ex
652 import traceback
653 traceback.print_exc()
654 self.destroy()
655 raise
657 def register_domain(self):
658 xd = get_component('xen.xend.XendDomain')
659 xd._add_domain(self)
660 self.exportToDB(save=True)
662 def configure_cpus(self, config):
663 try:
664 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
665 except:
666 raise VmError('invalid cpu weight')
667 self.memory = int(sxp.child_value(config, 'memory'))
668 if self.memory is None:
669 raise VmError('missing memory size')
670 self.target = self.memory * (1 << 20)
671 self.ssidref = int(sxp.child_value(config, 'ssidref'))
672 cpu = sxp.child_value(config, 'cpu')
673 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
674 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
675 try:
676 image = sxp.child_value(self.config, 'image')
677 vcpus = sxp.child_value(image, 'vcpus')
678 if vcpus:
679 self.vcpus = int(vcpus)
680 except:
681 raise VmError('invalid vcpus value')
683 def exportVCPUSToDB(self, vcpus):
684 for v in range(0,vcpus):
685 path = "/cpu/%d"%(v)
686 if not self.vcpusdb.has_key(path):
687 self.vcpusdb[path] = self.db.addChild(path)
688 db = self.vcpusdb[path]
689 log.debug("writing key availability=online to path %s in store"%(path))
690 db['availability'] = "online"
691 db.saveDB(save=True)
693 def init_image(self):
694 """Create boot image handler for the domain.
695 """
696 image = sxp.child_value(self.config, 'image')
697 if image is None:
698 raise VmError('missing image')
699 self.image = ImageHandler.create(self, image)
701 def construct_image(self):
702 """Construct the boot image for the domain.
703 """
704 self.create_channel()
705 self.image.createImage()
706 self.exportToDB()
707 if self.store_channel and self.store_mfn >= 0:
708 self.db.introduceDomain(self.id,
709 self.store_mfn,
710 self.store_channel)
711 # get the configured value of vcpus and update store
712 self.exportVCPUSToDB(self.vcpus)
714 def delete(self):
715 """Delete the vm's db.
716 """
717 if dom_get(self.id):
718 return
719 self.id = None
720 self.saveToDB(sync=True)
721 try:
722 # Todo: eventually will have to wait for devices to signal
723 # destruction before can delete the db.
724 if self.db:
725 self.db.delete()
726 except Exception, ex:
727 log.warning("error in domain db delete: %s", ex)
728 pass
730 def destroy_domain(self):
731 """Destroy the vm's domain.
732 The domain will not finally go away unless all vm
733 devices have been released.
734 """
735 if self.id is None:
736 return
737 try:
738 xc.domain_destroy(dom=self.id)
739 except Exception, err:
740 log.exception("Domain destroy failed: %s", self.name)
742 def cleanup(self):
743 """Cleanup vm resources: release devices.
744 """
745 self.state = STATE_VM_TERMINATED
746 self.release_devices()
747 if self.store_channel:
748 try:
749 self.store_channel.close()
750 self.store_channel = None
751 except:
752 pass
753 try:
754 self.db.releaseDomain(self.id)
755 except Exception, ex:
756 log.warning("error in domain release on xenstore: %s", ex)
757 pass
758 if self.console_channel:
759 # notify processes using this cosole?
760 try:
761 self.console_channel.close()
762 self.console_channel = None
763 except:
764 pass
765 if self.image:
766 try:
767 self.device_model_pid = 0
768 self.image.destroy()
769 self.image = None
770 except:
771 pass
773 def destroy(self):
774 """Clenup vm and destroy domain.
775 """
776 self.destroy_domain()
777 self.cleanup()
778 self.saveToDB()
779 return 0
781 def is_terminated(self):
782 """Check if a domain has been terminated.
783 """
784 return self.state == STATE_VM_TERMINATED
786 def release_devices(self):
787 """Release all vm devices.
788 """
789 reboot = self.restart_pending()
790 for ctrl in self.getDeviceControllers():
791 if ctrl.isDestroyed(): continue
792 ctrl.destroyController(reboot=reboot)
793 t = xstransact("%s/device" % self.path)
794 for d in t.list("vbd"):
795 t.remove(d)
796 for d in t.list("vif"):
797 t.remove(d)
798 t.commit()
799 ddb = self.db.addChild("/device")
800 for type in ddb.keys():
801 if type == 'vtpm':
802 typedb = ddb.addChild(type)
803 for dev in typedb.keys():
804 typedb[dev].delete()
805 typedb.saveDB(save=True)
807 def show(self):
808 """Print virtual machine info.
809 """
810 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
811 print "image:"
812 sxp.show(self.image)
813 print "]"
815 def init_domain(self):
816 """Initialize the domain memory.
817 """
818 if self.recreate:
819 return
820 if self.start_time is None:
821 self.start_time = time.time()
822 try:
823 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
824 except:
825 raise VmError('invalid cpu')
826 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
827 log.debug('init_domain> Created domain=%d name=%s memory=%d',
828 id, self.name, self.memory)
829 self.setdom(id)
831 def eventChannelOld(self, key):
832 """Create an event channel to the domain.
833 If saved info is available recreate the channel.
835 @param key db key for the saved data (if any)
836 """
837 db = self.db.addChild(key)
838 return EventChannel.restoreFromDB(db, 0, self.id)
840 def eventChannel(self, path=None, key=None):
841 """Create an event channel to the domain.
843 @param path under which port is stored in db
844 """
845 port = 0
846 try:
847 if path and key:
848 if path:
849 db = self.db.addChild(path)
850 else:
851 db = self.db
852 port = int(db[key].getData())
853 except: pass
854 return EventChannel.interdomain(0, self.id, port1=port, port2=0)
856 def create_channel(self):
857 """Create the channels to the domain.
858 """
859 self.store_channel = self.eventChannelOld("store_channel")
860 self.console_channel = self.eventChannel("console", "port")
862 def create_configured_devices(self):
863 devices = sxp.children(self.config, 'device')
864 for d in devices:
865 dev_config = sxp.child0(d)
866 if dev_config is None:
867 raise VmError('invalid device')
868 dev_type = sxp.name(dev_config)
869 ctrl_type = get_device_handler(dev_type)
870 if ctrl_type is None:
871 raise VmError('unknown device type: ' + dev_type)
872 self.createDevice(ctrl_type, dev_config)
874 def create_devices(self):
875 """Create the devices for a vm.
877 @raise: VmError for invalid devices
878 """
879 if self.rebooting():
880 for ctrl in self.getDeviceControllers():
881 ctrl.initController(reboot=True)
882 else:
883 self.create_configured_devices()
884 if not self.device_model_pid:
885 self.device_model_pid = self.image.createDeviceModel()
887 def device_create(self, dev_config):
888 """Create a new device.
890 @param dev_config: device configuration
891 """
892 dev_type = sxp.name(dev_config)
893 dev = self.createDevice(dev_type, dev_config, change=True)
894 self.config.append(['device', dev.getConfig()])
895 return dev.sxpr()
897 def device_configure(self, dev_config, id):
898 """Configure an existing device.
900 @param dev_config: device configuration
901 @param id: device id
902 """
903 type = sxp.name(dev_config)
904 dev = self.getDevice(type, id)
905 old_config = dev.getConfig()
906 new_config = dev.configure(dev_config, change=True)
907 # Patch new config into vm config.
908 new_full_config = ['device', new_config]
909 old_full_config = ['device', old_config]
910 old_index = self.config.index(old_full_config)
911 self.config[old_index] = new_full_config
912 return new_config
914 def device_refresh(self, type, id):
915 """Refresh a device.
917 @param type: device type
918 @param id: device id
919 """
920 dev = self.getDevice(type, id)
921 dev.refresh()
923 def device_delete(self, type, id):
924 """Destroy and remove a device.
926 @param type: device type
927 @param id: device id
928 """
929 dev = self.getDevice(type, id)
930 dev_config = dev.getConfig()
931 if dev_config:
932 self.config.remove(['device', dev_config])
933 self.deleteDevice(type, dev.getId())
935 def configure_bootloader(self):
936 """Configure boot loader.
937 """
938 self.bootloader = sxp.child_value(self.config, "bootloader")
940 def configure_restart(self):
941 """Configure the vm restart mode.
942 """
943 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
944 if r not in restart_modes:
945 raise VmError('invalid restart mode: ' + str(r))
946 self.restart_mode = r;
948 def restart_needed(self, reason):
949 """Determine if the vm needs to be restarted when shutdown
950 for the given reason.
952 @param reason: shutdown reason
953 @return True if needs restart, False otherwise
954 """
955 if self.restart_mode == RESTART_NEVER:
956 return False
957 if self.restart_mode == RESTART_ALWAYS:
958 return True
959 if self.restart_mode == RESTART_ONREBOOT:
960 return reason == 'reboot'
961 return False
963 def restart_cancel(self):
964 """Cancel a vm restart.
965 """
966 self.restart_state = None
968 def restarting(self):
969 """Put the vm into restart mode.
970 """
971 self.restart_state = STATE_RESTART_PENDING
973 def restart_pending(self):
974 """Test if the vm has a pending restart.
975 """
976 return self.restart_state == STATE_RESTART_PENDING
978 def rebooting(self):
979 return self.restart_state == STATE_RESTART_BOOTING
981 def restart_check(self):
982 """Check if domain restart is OK.
983 To prevent restart loops, raise an error if it is
984 less than MINIMUM_RESTART_TIME seconds since the last restart.
985 """
986 tnow = time.time()
987 if self.restart_time is not None:
988 tdelta = tnow - self.restart_time
989 if tdelta < self.MINIMUM_RESTART_TIME:
990 self.restart_cancel()
991 msg = 'VM %s restarting too fast' % self.name
992 log.error(msg)
993 raise VmError(msg)
994 self.restart_time = tnow
995 self.restart_count += 1
997 def restart(self):
998 """Restart the domain after it has exited.
999 Reuses the domain id
1001 """
1002 try:
1003 self.clear_shutdown()
1004 self.state = STATE_VM_OK
1005 self.shutdown_pending = None
1006 self.restart_check()
1007 self.exportToDB()
1008 self.restart_state = STATE_RESTART_BOOTING
1009 if self.bootloader:
1010 self.config = self.bootloader_config()
1011 self.construct(self.config)
1012 self.saveToDB()
1013 finally:
1014 self.restart_state = None
1016 def bootloader_config(self):
1017 # if we're restarting with a bootloader, we need to run it
1018 # FIXME: this assumes the disk is the first device and
1019 # that we're booting from the first disk
1020 blcfg = None
1021 # FIXME: this assumes that we want to use the first disk
1022 dev = sxp.child_value(self.config, "device")
1023 if dev:
1024 disk = sxp.child_value(dev, "uname")
1025 fn = blkdev_uname_to_file(disk)
1026 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1027 if blcfg is None:
1028 msg = "Had a bootloader specified, but can't find disk"
1029 log.error(msg)
1030 raise VmError(msg)
1031 config = sxp.merge(['vm', blconfig ], self.config)
1032 return config
1034 def configure_backends(self):
1035 """Set configuration flags if the vm is a backend for netif or blkif.
1036 Configure the backends to use for vbd and vif if specified.
1037 """
1038 for c in sxp.children(self.config, 'backend'):
1039 v = sxp.child0(c)
1040 name = sxp.name(v)
1041 if name == 'blkif':
1042 self.blkif_backend = True
1043 elif name == 'netif':
1044 self.netif_backend = True
1045 elif name == 'usbif':
1046 self.usbif_backend = True
1047 elif name == 'tpmif':
1048 self.tpmif_backend = True
1049 else:
1050 raise VmError('invalid backend type:' + str(name))
1052 def configure(self):
1053 """Configure a vm.
1055 """
1056 self.configure_fields()
1057 self.create_devices()
1058 self.create_blkif()
1060 def create_blkif(self):
1061 """Create the block device interface (blkif) for the vm.
1062 The vm needs a blkif even if it doesn't have any disks
1063 at creation time, for example when it uses NFS root.
1065 """
1066 return
1067 blkif = self.getDeviceController("vbd", error=False)
1068 if not blkif:
1069 blkif = self.createDeviceController("vbd")
1070 backend = blkif.getBackend(0)
1071 backend.connect(recreate=self.recreate)
1073 def configure_fields(self):
1074 """Process the vm configuration fields using the registered handlers.
1075 """
1076 index = {}
1077 for field in sxp.children(self.config):
1078 field_name = sxp.name(field)
1079 field_index = index.get(field_name, 0)
1080 field_handler = get_config_handler(field_name)
1081 # Ignore unknown fields. Warn?
1082 if field_handler:
1083 v = field_handler(self, self.config, field, field_index)
1084 else:
1085 log.warning("Unknown config field %s", field_name)
1086 index[field_name] = field_index + 1
1088 def mem_target_set(self, target):
1089 """Set domain memory target in bytes.
1090 """
1091 if target:
1092 self.target = target * (1 << 20)
1093 # Commit to XenStore immediately
1094 self.exportToDB()
1096 def vcpu_hotplug(self, vcpu, state):
1097 """Disable or enable VCPU in domain.
1098 """
1099 db = ""
1100 try:
1101 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1102 except:
1103 log.error("Invalid VCPU")
1104 return
1106 if self.store_channel:
1107 if int(state) == 0:
1108 db['availability'] = "offline"
1109 else:
1110 db['availability'] = "online"
1112 db.saveDB(save=True)
1114 def shutdown(self, reason):
1115 if not reason in shutdown_reasons.values():
1116 raise XendError('invalid reason:' + reason)
1117 db = self.db.addChild("/control");
1118 db['shutdown'] = reason;
1119 db.saveDB(save=True);
1120 if not reason in ['suspend']:
1121 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1123 def clear_shutdown(self):
1124 db = self.db.addChild("/control")
1125 db['shutdown'] = ""
1126 db.saveDB(save=True)
1128 def send_sysrq(self, key=0):
1129 db = self.db.addChild("/control");
1130 db['sysrq'] = '%c' % key;
1131 db.saveDB(save=True);
1133 def shutdown_time_left(self, timeout):
1134 if not self.shutdown_pending:
1135 return 0
1136 return timeout - (time.time() - self.shutdown_pending['start'])
1138 def dom0_init_store(self):
1139 if not self.store_channel:
1140 self.store_channel = self.eventChannelOld("store_channel")
1141 self.store_mfn = xc.init_store(self.store_channel.port2)
1142 if self.store_mfn >= 0:
1143 self.db.introduceDomain(self.id, self.store_mfn,
1144 self.store_channel)
1145 self.exportToDB(save=True, sync=True)
1146 # get run-time value of vcpus and update store
1147 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1149 def vm_field_ignore(vm, config, val, index):
1150 """Dummy config field handler used for fields with built-in handling.
1152 @param vm: virtual machine
1153 @param config: vm config
1154 @param val: config field
1155 @param index: field index
1156 """
1157 pass
1159 def vm_field_maxmem(vm, config, val, index):
1160 """Configure vm memory limit.
1162 @param vm: virtual machine
1163 @param config: vm config
1164 @param val: config field
1165 @param index: field index
1166 """
1167 maxmem = sxp.child0(val)
1168 if maxmem is None:
1169 maxmem = vm.memory
1170 try:
1171 maxmem = int(maxmem)
1172 except:
1173 raise VmError("invalid maxmem: " + str(maxmem))
1174 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1176 #============================================================================
1177 # Register image handlers.
1178 from image import \
1179 addImageHandlerClass, \
1180 ImageHandler, \
1181 LinuxImageHandler, \
1182 VmxImageHandler
1184 addImageHandlerClass(LinuxImageHandler)
1185 addImageHandlerClass(VmxImageHandler)
1187 # Ignore the fields we already handle.
1188 add_config_handler('name', vm_field_ignore)
1189 add_config_handler('memory', vm_field_ignore)
1190 add_config_handler('ssidref', vm_field_ignore)
1191 add_config_handler('cpu', vm_field_ignore)
1192 add_config_handler('cpu_weight', vm_field_ignore)
1193 add_config_handler('restart', vm_field_ignore)
1194 add_config_handler('image', vm_field_ignore)
1195 add_config_handler('device', vm_field_ignore)
1196 add_config_handler('backend', vm_field_ignore)
1197 add_config_handler('vcpus', vm_field_ignore)
1198 add_config_handler('bootloader', vm_field_ignore)
1200 # Register other config handlers.
1201 add_config_handler('maxmem', vm_field_maxmem)
1203 #============================================================================
1204 # Register device controllers and their device config types.
1206 from server import blkif
1207 controller.addDevControllerClass("vbd", blkif.BlkifController)
1208 add_device_handler("vbd", "vbd")
1210 from server import netif
1211 controller.addDevControllerClass("vif", netif.NetifController)
1212 add_device_handler("vif", "vif")
1214 from server import tpmif
1215 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1216 add_device_handler("vtpm", "vtpm")
1218 from server import pciif
1219 controller.addDevControllerClass("pci", pciif.PciController)
1220 add_device_handler("pci", "pci")
1222 from xen.xend.server import usbif
1223 controller.addDevControllerClass("usb", usbif.UsbifController)
1224 add_device_handler("usb", "usb")
1226 #============================================================================