xen-vt-testing

view tools/python/xen/xend/XendDomainInfo.py @ 6712:31c257b9a360

Make vif cleanup use xstransact and use classmethods for setup.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Fri Sep 09 17:36:39 2005 +0000 (2005-09-09)
parents 578c19d1ed49
children 813c37b68376
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """Representation of a single domain.
19 Includes support for domain construction, using
20 open-ended configurations.
22 Author: Mike Wray <mike.wray@hp.com>
24 """
26 import string, re
27 import os
28 import time
29 import threading
31 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
32 from xen.util.ip import check_subnet, get_current_ipgw
33 from xen.util.blkif import blkdev_uname_to_file
35 from xen.xend.server import controller
36 from xen.xend.server import SrvDaemon; xend = SrvDaemon.instance()
37 from xen.xend.server.channel import EventChannel
38 from xen.util.blkif import blkdev_name_to_number, expand_dev_name
40 from xen.xend import sxp
41 from xen.xend import Blkctl
42 from xen.xend.PrettyPrint import prettyprintstring
43 from xen.xend.XendBootloader import bootloader
44 from xen.xend.XendLogging import log
45 from xen.xend.XendError import XendError, VmError
46 from xen.xend.XendRoot import get_component
48 from xen.xend.uuid import getUuid
49 from xen.xend.xenstore import DBVar, XenNode, DBMap
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 backdb['frontend'] = db.getPath()
417 (type, params) = string.split(sxp.child_value(devconfig, 'uname'), ':', 1)
418 node = Blkctl.block('bind', type, params)
419 backdb['frontend-id'] = "%i" % self.id
420 backdb['physical-device'] = "%li" % blkdev_name_to_number(node)
421 backdb.saveDB(save=True)
423 # Ok, super gross, this really doesn't belong in the frontend db...
424 db['type'] = type
425 db['node'] = node
426 db['params'] = params
427 db.saveDB(save=True)
429 return
431 if type == 'vif':
432 from xen.xend import XendRoot
433 xroot = XendRoot.instance()
435 def _get_config_ipaddr(config):
436 val = []
437 for ipaddr in sxp.children(config, elt='ip'):
438 val.append(sxp.child0(ipaddr))
439 return val
441 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
443 log.error(devconfig)
445 devnum = self.netif_idx
446 self.netif_idx += 1
448 script = sxp.child_value(devconfig, 'script',
449 xroot.get_vif_script())
450 script = os.path.join(xroot.network_script_dir, script)
451 bridge = sxp.child_value(devconfig, 'bridge',
452 xroot.get_vif_bridge())
453 mac = sxp.child_value(devconfig, 'mac')
454 ipaddr = _get_config_ipaddr(devconfig)
456 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
457 self.uuid, devnum)
458 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
460 front = { 'backend' : backpath,
461 'backend-id' : "%i" % backdom.id,
462 'handle' : "%i" % devnum,
463 'mac' : mac }
464 xstransact.Write(frontpath, front)
466 back = { 'script' : script,
467 'domain' : self.name,
468 'mac' : mac,
469 'bridge' : bridge,
470 'frontend' : frontpath,
471 'frontend-id' : "%i" % self.id,
472 'handle' : "%i" % devnum }
473 if ipaddr:
474 back['ip'] = ' '.join(ipaddr)
475 xstransact.Write(backpath, back)
477 return
479 if type == 'vtpm':
480 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
482 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
483 log.error("The domain has a TPM with instance %d." % devnum)
485 # create backend db
486 backdb = backdom.db.addChild("/backend/%s/%s/%d" %
487 (type, self.uuid, devnum))
488 # create frontend db
489 db = self.db.addChild("/device/%s/%d" % (type, devnum))
491 backdb['frontend'] = db.getPath()
492 backdb['frontend-id'] = "%i" % self.id
493 backdb['instance'] = sxp.child_value(devconfig, 'instance', '0')
494 backdb.saveDB(save=True)
496 db['handle'] = "%i" % devnum
497 db['backend'] = backdb.getPath()
498 db['backend-id'] = "%i" % int(sxp.child_value(devconfig,
499 'backend', '0'))
500 db.saveDB(save=True)
502 return
504 ctrl = self.findDeviceController(type)
505 return ctrl.createDevice(devconfig, recreate=self.recreate,
506 change=change)
508 def configureDevice(self, type, id, devconfig):
509 ctrl = self.getDeviceController(type)
510 return ctrl.configureDevice(id, devconfig)
512 def destroyDevice(self, type, id, change=False, reboot=False):
513 ctrl = self.getDeviceController(type)
514 return ctrl.destroyDevice(id, change=change, reboot=reboot)
516 def deleteDevice(self, type, id):
517 ctrl = self.getDeviceController(type)
518 return ctrl.deleteDevice(id)
520 def getDevice(self, type, id, error=True):
521 ctrl = self.getDeviceController(type)
522 return ctrl.getDevice(id, error=error)
524 def getDeviceIds(self, type):
525 ctrl = self.getDeviceController(type)
526 return ctrl.getDeviceIds()
528 def getDeviceSxprs(self, type):
529 ctrl = self.getDeviceController(type)
530 return ctrl.getDeviceSxprs()
532 def sxpr(self):
533 sxpr = ['domain',
534 ['id', self.id],
535 ['name', self.name],
536 ['memory', self.memory],
537 ['ssidref', self.ssidref],
538 ['target', self.target] ]
539 if self.uuid:
540 sxpr.append(['uuid', self.uuid])
541 if self.info:
542 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
543 run = (self.info['running'] and 'r') or '-'
544 block = (self.info['blocked'] and 'b') or '-'
545 pause = (self.info['paused'] and 'p') or '-'
546 shut = (self.info['shutdown'] and 's') or '-'
547 crash = (self.info['crashed'] and 'c') or '-'
548 state = run + block + pause + shut + crash
549 sxpr.append(['state', state])
550 if self.info['shutdown']:
551 reason = shutdown_reason(self.info['shutdown_reason'])
552 sxpr.append(['shutdown_reason', reason])
553 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
554 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
555 sxpr.append(['vcpus', self.info['vcpus']])
556 sxpr.append(['cpumap', self.info['cpumap']])
557 # build a string, using '|' to seperate items, show only up
558 # to number of vcpus in domain, and trim the trailing '|'
559 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
560 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
562 if self.start_time:
563 up_time = time.time() - self.start_time
564 sxpr.append(['up_time', str(up_time) ])
565 sxpr.append(['start_time', str(self.start_time) ])
567 if self.store_channel:
568 sxpr.append(self.store_channel.sxpr())
569 if self.store_mfn:
570 sxpr.append(['store_mfn', self.store_mfn])
571 if self.console_channel:
572 sxpr.append(['console_channel', self.console_channel.sxpr()])
573 if self.console_mfn:
574 sxpr.append(['console_mfn', self.console_mfn])
575 # already in (devices)
576 # console = self.getConsole()
577 # if console:
578 # sxpr.append(console.sxpr())
580 if self.restart_count:
581 sxpr.append(['restart_count', self.restart_count])
582 if self.restart_state:
583 sxpr.append(['restart_state', self.restart_state])
584 if self.restart_time:
585 sxpr.append(['restart_time', str(self.restart_time)])
587 devs = self.sxpr_devices()
588 if devs:
589 sxpr.append(devs)
590 if self.config:
591 sxpr.append(['config', self.config])
592 if self.device_model_pid:
593 sxpr.append(['device_model_pid',self.device_model_pid])
594 return sxpr
596 def sxpr_devices(self):
597 sxpr = []
598 for ty in self.getDeviceTypes():
599 devs = self.getDeviceSxprs(ty)
600 sxpr += devs
601 if sxpr:
602 sxpr.insert(0, 'devices')
603 else:
604 sxpr = None
605 return sxpr
607 def check_name(self, name):
608 """Check if a vm name is valid. Valid names contain alphabetic characters,
609 digits, or characters in '_-.:/+'.
610 The same name cannot be used for more than one vm at the same time.
612 @param name: name
613 @raise: VMerror if invalid
614 """
615 if self.recreate: return
616 if name is None or name == '':
617 raise VmError('missing vm name')
618 for c in name:
619 if c in string.digits: continue
620 if c in '_-.:/+': continue
621 if c in string.ascii_letters: continue
622 raise VmError('invalid vm name')
623 dominfo = domain_exists(name)
624 # When creating or rebooting, a domain with my name should not exist.
625 # When restoring, a domain with my name will exist, but it should have
626 # my domain id.
627 if not dominfo:
628 return
629 if dominfo.is_terminated():
630 return
631 if not self.id or (dominfo.id != self.id):
632 raise VmError('vm name clash: ' + name)
634 def construct(self, config):
635 """Construct the vm instance from its configuration.
637 @param config: configuration
638 @raise: VmError on error
639 """
640 # todo - add support for scheduling params?
641 self.config = config
642 try:
643 # Initial domain create.
644 self.setName(sxp.child_value(config, 'name'))
645 self.check_name(self.name)
646 self.init_image()
647 self.configure_cpus(config)
648 self.init_domain()
649 self.register_domain()
650 self.configure_bootloader()
652 # Create domain devices.
653 self.configure_backends()
654 self.configure_restart()
655 self.construct_image()
656 self.configure()
657 self.exportToDB(save=True)
658 except Exception, ex:
659 # Catch errors, cleanup and re-raise.
660 print 'Domain construction error:', ex
661 import traceback
662 traceback.print_exc()
663 self.destroy()
664 raise
666 def register_domain(self):
667 xd = get_component('xen.xend.XendDomain')
668 xd._add_domain(self)
669 self.exportToDB(save=True)
671 def configure_cpus(self, config):
672 try:
673 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
674 except:
675 raise VmError('invalid cpu weight')
676 self.memory = int(sxp.child_value(config, 'memory'))
677 if self.memory is None:
678 raise VmError('missing memory size')
679 self.target = self.memory * (1 << 20)
680 self.ssidref = int(sxp.child_value(config, 'ssidref'))
681 cpu = sxp.child_value(config, 'cpu')
682 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
683 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
684 try:
685 image = sxp.child_value(self.config, 'image')
686 vcpus = sxp.child_value(image, 'vcpus')
687 if vcpus:
688 self.vcpus = int(vcpus)
689 except:
690 raise VmError('invalid vcpus value')
692 def exportVCPUSToDB(self, vcpus):
693 for v in range(0,vcpus):
694 path = "/cpu/%d"%(v)
695 if not self.vcpusdb.has_key(path):
696 self.vcpusdb[path] = self.db.addChild(path)
697 db = self.vcpusdb[path]
698 log.debug("writing key availability=online to path %s in store"%(path))
699 db['availability'] = "online"
700 db.saveDB(save=True)
702 def init_image(self):
703 """Create boot image handler for the domain.
704 """
705 image = sxp.child_value(self.config, 'image')
706 if image is None:
707 raise VmError('missing image')
708 self.image = ImageHandler.create(self, image)
710 def construct_image(self):
711 """Construct the boot image for the domain.
712 """
713 self.create_channel()
714 self.image.createImage()
715 self.exportToDB()
716 if self.store_channel and self.store_mfn >= 0:
717 self.db.introduceDomain(self.id,
718 self.store_mfn,
719 self.store_channel)
720 # get the configured value of vcpus and update store
721 self.exportVCPUSToDB(self.vcpus)
723 def delete(self):
724 """Delete the vm's db.
725 """
726 if dom_get(self.id):
727 return
728 self.id = None
729 self.saveToDB(sync=True)
730 try:
731 # Todo: eventually will have to wait for devices to signal
732 # destruction before can delete the db.
733 if self.db:
734 self.db.delete()
735 except Exception, ex:
736 log.warning("error in domain db delete: %s", ex)
737 pass
739 def destroy_domain(self):
740 """Destroy the vm's domain.
741 The domain will not finally go away unless all vm
742 devices have been released.
743 """
744 if self.id is None:
745 return
746 try:
747 xc.domain_destroy(dom=self.id)
748 except Exception, err:
749 log.exception("Domain destroy failed: %s", self.name)
751 def cleanup(self):
752 """Cleanup vm resources: release devices.
753 """
754 self.state = STATE_VM_TERMINATED
755 self.release_devices()
756 if self.store_channel:
757 try:
758 self.store_channel.close()
759 self.store_channel = None
760 except:
761 pass
762 try:
763 self.db.releaseDomain(self.id)
764 except Exception, ex:
765 log.warning("error in domain release on xenstore: %s", ex)
766 pass
767 if self.console_channel:
768 # notify processes using this cosole?
769 try:
770 self.console_channel.close()
771 self.console_channel = None
772 except:
773 pass
774 if self.image:
775 try:
776 self.device_model_pid = 0
777 self.image.destroy()
778 self.image = None
779 except:
780 pass
782 def destroy(self):
783 """Clenup vm and destroy domain.
784 """
785 self.destroy_domain()
786 self.cleanup()
787 self.saveToDB()
788 return 0
790 def is_terminated(self):
791 """Check if a domain has been terminated.
792 """
793 return self.state == STATE_VM_TERMINATED
795 def release_devices(self):
796 """Release all vm devices.
797 """
798 reboot = self.restart_pending()
799 for ctrl in self.getDeviceControllers():
800 if ctrl.isDestroyed(): continue
801 ctrl.destroyController(reboot=reboot)
802 t = xstransact("%s/device" % self.path)
803 for d in t.list("vif"):
804 t.remove(d)
805 t.commit()
806 ddb = self.db.addChild("/device")
807 for type in ddb.keys():
808 if type == 'vbd':
809 typedb = ddb.addChild(type)
810 for dev in typedb.keys():
811 devdb = typedb.addChild(str(dev))
812 Blkctl.block('unbind', devdb['type'].getData(),
813 devdb['node'].getData())
814 typedb[dev].delete()
815 typedb.saveDB(save=True)
816 if type == 'vtpm':
817 typedb = ddb.addChild(type)
818 for dev in typedb.keys():
819 typedb[dev].delete()
820 typedb.saveDB(save=True)
822 def show(self):
823 """Print virtual machine info.
824 """
825 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
826 print "image:"
827 sxp.show(self.image)
828 print "]"
830 def init_domain(self):
831 """Initialize the domain memory.
832 """
833 if self.recreate:
834 return
835 if self.start_time is None:
836 self.start_time = time.time()
837 try:
838 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
839 except:
840 raise VmError('invalid cpu')
841 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
842 log.debug('init_domain> Created domain=%d name=%s memory=%d',
843 id, self.name, self.memory)
844 self.setdom(id)
846 def eventChannelOld(self, key):
847 """Create an event channel to the domain.
848 If saved info is available recreate the channel.
850 @param key db key for the saved data (if any)
851 """
852 db = self.db.addChild(key)
853 return EventChannel.restoreFromDB(db, 0, self.id)
855 def eventChannel(self, path=None, key=None):
856 """Create an event channel to the domain.
858 @param path under which port is stored in db
859 """
860 port = 0
861 try:
862 if path and key:
863 if path:
864 db = self.db.addChild(path)
865 else:
866 db = self.db
867 port = int(db[key].getData())
868 except: pass
869 return EventChannel.interdomain(0, self.id, port1=port, port2=0)
871 def create_channel(self):
872 """Create the channels to the domain.
873 """
874 self.store_channel = self.eventChannelOld("store_channel")
875 self.console_channel = self.eventChannel("console", "port")
877 def create_configured_devices(self):
878 devices = sxp.children(self.config, 'device')
879 for d in devices:
880 dev_config = sxp.child0(d)
881 if dev_config is None:
882 raise VmError('invalid device')
883 dev_type = sxp.name(dev_config)
884 ctrl_type = get_device_handler(dev_type)
885 if ctrl_type is None:
886 raise VmError('unknown device type: ' + dev_type)
887 self.createDevice(ctrl_type, dev_config)
889 def create_devices(self):
890 """Create the devices for a vm.
892 @raise: VmError for invalid devices
893 """
894 if self.rebooting():
895 for ctrl in self.getDeviceControllers():
896 ctrl.initController(reboot=True)
897 else:
898 self.create_configured_devices()
899 if not self.device_model_pid:
900 self.device_model_pid = self.image.createDeviceModel()
902 def device_create(self, dev_config):
903 """Create a new device.
905 @param dev_config: device configuration
906 """
907 dev_type = sxp.name(dev_config)
908 dev = self.createDevice(dev_type, dev_config, change=True)
909 self.config.append(['device', dev.getConfig()])
910 return dev.sxpr()
912 def device_configure(self, dev_config, id):
913 """Configure an existing device.
915 @param dev_config: device configuration
916 @param id: device id
917 """
918 type = sxp.name(dev_config)
919 dev = self.getDevice(type, id)
920 old_config = dev.getConfig()
921 new_config = dev.configure(dev_config, change=True)
922 # Patch new config into vm config.
923 new_full_config = ['device', new_config]
924 old_full_config = ['device', old_config]
925 old_index = self.config.index(old_full_config)
926 self.config[old_index] = new_full_config
927 return new_config
929 def device_refresh(self, type, id):
930 """Refresh a device.
932 @param type: device type
933 @param id: device id
934 """
935 dev = self.getDevice(type, id)
936 dev.refresh()
938 def device_delete(self, type, id):
939 """Destroy and remove a device.
941 @param type: device type
942 @param id: device id
943 """
944 dev = self.getDevice(type, id)
945 dev_config = dev.getConfig()
946 if dev_config:
947 self.config.remove(['device', dev_config])
948 self.deleteDevice(type, dev.getId())
950 def configure_bootloader(self):
951 """Configure boot loader.
952 """
953 self.bootloader = sxp.child_value(self.config, "bootloader")
955 def configure_restart(self):
956 """Configure the vm restart mode.
957 """
958 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
959 if r not in restart_modes:
960 raise VmError('invalid restart mode: ' + str(r))
961 self.restart_mode = r;
963 def restart_needed(self, reason):
964 """Determine if the vm needs to be restarted when shutdown
965 for the given reason.
967 @param reason: shutdown reason
968 @return True if needs restart, False otherwise
969 """
970 if self.restart_mode == RESTART_NEVER:
971 return False
972 if self.restart_mode == RESTART_ALWAYS:
973 return True
974 if self.restart_mode == RESTART_ONREBOOT:
975 return reason == 'reboot'
976 return False
978 def restart_cancel(self):
979 """Cancel a vm restart.
980 """
981 self.restart_state = None
983 def restarting(self):
984 """Put the vm into restart mode.
985 """
986 self.restart_state = STATE_RESTART_PENDING
988 def restart_pending(self):
989 """Test if the vm has a pending restart.
990 """
991 return self.restart_state == STATE_RESTART_PENDING
993 def rebooting(self):
994 return self.restart_state == STATE_RESTART_BOOTING
996 def restart_check(self):
997 """Check if domain restart is OK.
998 To prevent restart loops, raise an error if it is
999 less than MINIMUM_RESTART_TIME seconds since the last restart.
1000 """
1001 tnow = time.time()
1002 if self.restart_time is not None:
1003 tdelta = tnow - self.restart_time
1004 if tdelta < self.MINIMUM_RESTART_TIME:
1005 self.restart_cancel()
1006 msg = 'VM %s restarting too fast' % self.name
1007 log.error(msg)
1008 raise VmError(msg)
1009 self.restart_time = tnow
1010 self.restart_count += 1
1012 def restart(self):
1013 """Restart the domain after it has exited.
1014 Reuses the domain id
1016 """
1017 try:
1018 self.clear_shutdown()
1019 self.state = STATE_VM_OK
1020 self.shutdown_pending = None
1021 self.restart_check()
1022 self.exportToDB()
1023 self.restart_state = STATE_RESTART_BOOTING
1024 if self.bootloader:
1025 self.config = self.bootloader_config()
1026 self.construct(self.config)
1027 self.saveToDB()
1028 finally:
1029 self.restart_state = None
1031 def bootloader_config(self):
1032 # if we're restarting with a bootloader, we need to run it
1033 # FIXME: this assumes the disk is the first device and
1034 # that we're booting from the first disk
1035 blcfg = None
1036 # FIXME: this assumes that we want to use the first disk
1037 dev = sxp.child_value(self.config, "device")
1038 if dev:
1039 disk = sxp.child_value(dev, "uname")
1040 fn = blkdev_uname_to_file(disk)
1041 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1042 if blcfg is None:
1043 msg = "Had a bootloader specified, but can't find disk"
1044 log.error(msg)
1045 raise VmError(msg)
1046 config = sxp.merge(['vm', blconfig ], self.config)
1047 return config
1049 def configure_backends(self):
1050 """Set configuration flags if the vm is a backend for netif or blkif.
1051 Configure the backends to use for vbd and vif if specified.
1052 """
1053 for c in sxp.children(self.config, 'backend'):
1054 v = sxp.child0(c)
1055 name = sxp.name(v)
1056 if name == 'blkif':
1057 self.blkif_backend = True
1058 elif name == 'netif':
1059 self.netif_backend = True
1060 elif name == 'usbif':
1061 self.usbif_backend = True
1062 elif name == 'tpmif':
1063 self.tpmif_backend = True
1064 else:
1065 raise VmError('invalid backend type:' + str(name))
1067 def configure(self):
1068 """Configure a vm.
1070 """
1071 self.configure_fields()
1072 self.create_devices()
1073 self.create_blkif()
1075 def create_blkif(self):
1076 """Create the block device interface (blkif) for the vm.
1077 The vm needs a blkif even if it doesn't have any disks
1078 at creation time, for example when it uses NFS root.
1080 """
1081 return
1082 blkif = self.getDeviceController("vbd", error=False)
1083 if not blkif:
1084 blkif = self.createDeviceController("vbd")
1085 backend = blkif.getBackend(0)
1086 backend.connect(recreate=self.recreate)
1088 def configure_fields(self):
1089 """Process the vm configuration fields using the registered handlers.
1090 """
1091 index = {}
1092 for field in sxp.children(self.config):
1093 field_name = sxp.name(field)
1094 field_index = index.get(field_name, 0)
1095 field_handler = get_config_handler(field_name)
1096 # Ignore unknown fields. Warn?
1097 if field_handler:
1098 v = field_handler(self, self.config, field, field_index)
1099 else:
1100 log.warning("Unknown config field %s", field_name)
1101 index[field_name] = field_index + 1
1103 def mem_target_set(self, target):
1104 """Set domain memory target in bytes.
1105 """
1106 if target:
1107 self.target = target * (1 << 20)
1108 # Commit to XenStore immediately
1109 self.exportToDB()
1111 def vcpu_hotplug(self, vcpu, state):
1112 """Disable or enable VCPU in domain.
1113 """
1114 db = ""
1115 try:
1116 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1117 except:
1118 log.error("Invalid VCPU")
1119 return
1121 if self.store_channel:
1122 if int(state) == 0:
1123 db['availability'] = "offline"
1124 else:
1125 db['availability'] = "online"
1127 db.saveDB(save=True)
1129 def shutdown(self, reason):
1130 if not reason in shutdown_reasons.values():
1131 raise XendError('invalid reason:' + reason)
1132 db = self.db.addChild("/control");
1133 db['shutdown'] = reason;
1134 db.saveDB(save=True);
1135 if not reason in ['suspend']:
1136 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1138 def clear_shutdown(self):
1139 db = self.db.addChild("/control")
1140 db['shutdown'] = ""
1141 db.saveDB(save=True)
1143 def send_sysrq(self, key=0):
1144 db = self.db.addChild("/control");
1145 db['sysrq'] = '%c' % key;
1146 db.saveDB(save=True);
1148 def shutdown_time_left(self, timeout):
1149 if not self.shutdown_pending:
1150 return 0
1151 return timeout - (time.time() - self.shutdown_pending['start'])
1153 def dom0_init_store(self):
1154 if not self.store_channel:
1155 self.store_channel = self.eventChannelOld("store_channel")
1156 self.store_mfn = xc.init_store(self.store_channel.port2)
1157 if self.store_mfn >= 0:
1158 self.db.introduceDomain(self.id, self.store_mfn,
1159 self.store_channel)
1160 self.exportToDB(save=True, sync=True)
1161 # get run-time value of vcpus and update store
1162 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1164 def vm_field_ignore(vm, config, val, index):
1165 """Dummy config field handler used for fields with built-in handling.
1167 @param vm: virtual machine
1168 @param config: vm config
1169 @param val: config field
1170 @param index: field index
1171 """
1172 pass
1174 def vm_field_maxmem(vm, config, val, index):
1175 """Configure vm memory limit.
1177 @param vm: virtual machine
1178 @param config: vm config
1179 @param val: config field
1180 @param index: field index
1181 """
1182 maxmem = sxp.child0(val)
1183 if maxmem is None:
1184 maxmem = vm.memory
1185 try:
1186 maxmem = int(maxmem)
1187 except:
1188 raise VmError("invalid maxmem: " + str(maxmem))
1189 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1191 #============================================================================
1192 # Register image handlers.
1193 from image import \
1194 addImageHandlerClass, \
1195 ImageHandler, \
1196 LinuxImageHandler, \
1197 VmxImageHandler
1199 addImageHandlerClass(LinuxImageHandler)
1200 addImageHandlerClass(VmxImageHandler)
1202 # Ignore the fields we already handle.
1203 add_config_handler('name', vm_field_ignore)
1204 add_config_handler('memory', vm_field_ignore)
1205 add_config_handler('ssidref', vm_field_ignore)
1206 add_config_handler('cpu', vm_field_ignore)
1207 add_config_handler('cpu_weight', vm_field_ignore)
1208 add_config_handler('restart', vm_field_ignore)
1209 add_config_handler('image', vm_field_ignore)
1210 add_config_handler('device', vm_field_ignore)
1211 add_config_handler('backend', vm_field_ignore)
1212 add_config_handler('vcpus', vm_field_ignore)
1213 add_config_handler('bootloader', vm_field_ignore)
1215 # Register other config handlers.
1216 add_config_handler('maxmem', vm_field_maxmem)
1218 #============================================================================
1219 # Register device controllers and their device config types.
1221 from server import blkif
1222 controller.addDevControllerClass("vbd", blkif.BlkifController)
1223 add_device_handler("vbd", "vbd")
1225 from server import netif
1226 controller.addDevControllerClass("vif", netif.NetifController)
1227 add_device_handler("vif", "vif")
1229 from server import tpmif
1230 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1231 add_device_handler("vtpm", "vtpm")
1233 from server import pciif
1234 controller.addDevControllerClass("pci", pciif.PciController)
1235 add_device_handler("pci", "pci")
1237 from xen.xend.server import usbif
1238 controller.addDevControllerClass("usb", usbif.UsbifController)
1239 add_device_handler("usb", "usb")
1241 #============================================================================