ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6763:3aa853185afe

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