ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6729:578c19d1ed49

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