ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6766:219d96d545fc

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