ia64/xen-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6806:4ad19fe76d50

Store dom0 store ring-ref in store.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:32:38 2005 +0000 (2005-09-13)
parents c2558a2fe658
children 8ca0f98ba8e2
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
51 from xen.xend.xenstore.xsutil import IntroduceDomain
53 """Shutdown code for poweroff."""
54 DOMAIN_POWEROFF = 0
56 """Shutdown code for reboot."""
57 DOMAIN_REBOOT = 1
59 """Shutdown code for suspend."""
60 DOMAIN_SUSPEND = 2
62 """Shutdown code for crash."""
63 DOMAIN_CRASH = 3
65 """Map shutdown codes to strings."""
66 shutdown_reasons = {
67 DOMAIN_POWEROFF: "poweroff",
68 DOMAIN_REBOOT : "reboot",
69 DOMAIN_SUSPEND : "suspend",
70 DOMAIN_CRASH : "crash",
71 }
73 RESTART_ALWAYS = 'always'
74 RESTART_ONREBOOT = 'onreboot'
75 RESTART_NEVER = 'never'
77 restart_modes = [
78 RESTART_ALWAYS,
79 RESTART_ONREBOOT,
80 RESTART_NEVER,
81 ]
83 STATE_RESTART_PENDING = 'pending'
84 STATE_RESTART_BOOTING = 'booting'
86 STATE_VM_OK = "ok"
87 STATE_VM_TERMINATED = "terminated"
88 STATE_VM_SUSPENDED = "suspended"
91 def domain_exists(name):
92 # See comment in XendDomain constructor.
93 xd = get_component('xen.xend.XendDomain')
94 return xd.domain_lookup_by_name(name)
96 def shutdown_reason(code):
97 """Get a shutdown reason from a code.
99 @param code: shutdown code
100 @type code: int
101 @return: shutdown reason
102 @rtype: string
103 """
104 return shutdown_reasons.get(code, "?")
106 config_handlers = {}
108 def add_config_handler(name, h):
109 """Add a handler for a config field.
111 @param name: field name
112 @param h: handler: fn(vm, config, field, index)
113 """
114 config_handlers[name] = h
116 def get_config_handler(name):
117 """Get a handler for a config field.
119 returns handler or None
120 """
121 return config_handlers.get(name)
123 """Table of handlers for devices.
124 Indexed by device type.
125 """
126 device_handlers = {}
128 def add_device_handler(name, type):
129 device_handlers[name] = type
131 def get_device_handler(name):
132 return device_handlers[name]
134 def dom_get(dom):
135 """Get info from xen for an existing domain.
137 @param dom: domain id
138 @return: info or None
139 """
140 domlist = xc.domain_getinfo(dom, 1)
141 if domlist and dom == domlist[0]['dom']:
142 return domlist[0]
143 return None
145 class XendDomainInfo:
146 """Virtual machine object."""
148 """Minimum time between domain restarts in seconds.
149 """
150 MINIMUM_RESTART_TIME = 20
152 def create(cls, parentdb, config):
153 """Create a VM from a configuration.
155 @param parentdb: parent db
156 @param config configuration
157 @raise: VmError for invalid configuration
158 """
159 uuid = getUuid()
160 db = parentdb.addChild("%s/xend" % uuid)
161 path = parentdb.getPath()
162 vm = cls(uuid, path, db)
163 vm.construct(config)
164 vm.saveToDB(sync=True)
166 return vm
168 create = classmethod(create)
170 def recreate(cls, uuid, db, info):
171 """Create the VM object for an existing domain.
173 @param db: domain db
174 @param info: domain info from xc
175 """
176 dom = info['dom']
177 path = "/".join(db.getPath().split("/")[0:-2])
178 vm = cls(uuid, path, db)
179 vm.setdom(dom)
180 try:
181 db.readDB()
182 except: pass
183 vm.importFromDB()
184 config = vm.config
185 log.debug('info=' + str(info))
186 log.debug('config=' + prettyprintstring(config))
188 vm.memory = info['mem_kb']/1024
189 vm.target = info['mem_kb'] * 1024
191 if config:
192 try:
193 vm.recreate = True
194 vm.construct(config)
195 finally:
196 vm.recreate = False
197 else:
198 vm.setName("Domain-%d" % dom)
200 vm.exportToDB(save=True)
201 return vm
203 recreate = classmethod(recreate)
205 def restore(cls, parentdb, config, uuid=None):
206 """Create a domain and a VM object to do a restore.
208 @param parentdb: parent db
209 @param config: domain configuration
210 @param uuid: uuid to use
211 """
212 if not uuid:
213 uuid = getUuid()
214 db = parentdb.addChild("%s/xend" % uuid)
215 path = parentdb.getPath()
216 vm = cls(uuid, path, db)
217 ssidref = int(sxp.child_value(config, 'ssidref'))
218 log.debug('restoring with ssidref='+str(ssidref))
219 id = xc.domain_create(ssidref = ssidref)
220 vm.setdom(id)
221 vm.clear_shutdown()
222 try:
223 vm.restore = True
224 vm.construct(config)
225 finally:
226 vm.restore = False
227 vm.exportToDB(save=True, sync=True)
228 return vm
230 restore = classmethod(restore)
232 __exports__ = [
233 DBVar('id', ty='int'),
234 DBVar('name', ty='str'),
235 DBVar('uuid', ty='str'),
236 DBVar('config', ty='sxpr'),
237 DBVar('start_time', ty='float'),
238 DBVar('state', ty='str'),
239 DBVar('restart_mode', ty='str'),
240 DBVar('restart_state', ty='str'),
241 DBVar('restart_time', ty='float'),
242 DBVar('restart_count', ty='int'),
243 DBVar('target', ty='long', path="memory/target"),
244 DBVar('device_model_pid', ty='int'),
245 ]
247 def __init__(self, uuid, path, db):
248 self.uuid = uuid
249 self.path = path + "/" + uuid
250 self.db = db
252 self.recreate = 0
253 self.restore = 0
255 self.config = None
256 self.id = None
257 self.cpu_weight = 1
258 self.start_time = None
259 self.name = None
260 self.memory = None
261 self.ssidref = None
262 self.image = None
264 self.target = None
266 self.store_channel = None
267 self.store_mfn = None
268 self.console_channel = None
269 self.console_mfn = None
270 self.controllers = {}
272 self.info = None
273 self.blkif_backend = False
274 self.netif_backend = False
275 self.netif_idx = 0
276 self.tpmif_backend = False
278 #todo: state: running, suspended
279 self.state = STATE_VM_OK
280 self.state_updated = threading.Condition()
281 self.shutdown_pending = None
283 #todo: set to migrate info if migrating
284 self.migrate = None
286 self.restart_mode = RESTART_ONREBOOT
287 self.restart_state = None
288 self.restart_time = None
289 self.restart_count = 0
291 self.vcpus = 1
292 self.vcpusdb = {}
293 self.bootloader = None
294 self.device_model_pid = 0
296 def setDB(self, db):
297 self.db = db
299 def saveToDB(self, save=False, sync=False):
300 self.db.saveDB(save=save, sync=sync)
302 def exportToDB(self, save=False, sync=False):
303 if self.image:
304 self.image.exportToDB(save=save, sync=sync)
305 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
307 def importFromDB(self):
308 self.db.importFromDB(self, fields=self.__exports__)
309 self.store_channel = self.eventChannel("store/port")
311 def setdom(self, dom):
312 """Set the domain id.
314 @param dom: domain id
315 """
316 self.id = int(dom)
317 #self.db.id = self.id
319 def getDomain(self):
320 return self.id
322 def setName(self, name):
323 self.name = name
324 self.db.name = self.name
326 def getName(self):
327 return self.name
329 def setStoreRef(self, ref):
330 self.store_mfn = ref
331 if ref:
332 xstransact.Write(self.path, "store/ring-ref", "%i" % ref)
333 else:
334 xstransact.Remove(self.path, "store/ring-ref")
336 def setStoreChannel(self, channel):
337 if self.store_channel and self.store_channel != channel:
338 self.store_channel.close()
339 self.store_channel = channel
340 if channel:
341 xstransact.Write(self.path, "store/port", "%i" % channel.port1)
342 else:
343 xstransact.Remove(self.path, "store/port")
345 def setConsoleRef(self, ref):
346 self.console_mfn = ref
347 if ref:
348 xstransact.Write(self.path, "console/ring-ref", "%i" % ref)
349 else:
350 xstransact.Remove(self.path, "console/ring-ref")
352 def update(self, info=None):
353 """Update with info from xc.domain_getinfo().
354 """
355 self.info = info or dom_get(self.id)
356 self.memory = self.info['mem_kb'] / 1024
357 self.ssidref = self.info['ssidref']
358 self.target = self.info['mem_kb'] * 1024
360 def state_set(self, state):
361 self.state_updated.acquire()
362 if self.state != state:
363 self.state = state
364 self.state_updated.notifyAll()
365 self.state_updated.release()
366 self.saveToDB()
368 def state_wait(self, state):
369 self.state_updated.acquire()
370 while self.state != state:
371 self.state_updated.wait()
372 self.state_updated.release()
374 def __str__(self):
375 s = "<domain"
376 s += " id=" + str(self.id)
377 s += " name=" + self.name
378 s += " memory=" + str(self.memory)
379 s += " ssidref=" + str(self.ssidref)
380 s += ">"
381 return s
383 __repr__ = __str__
385 def getDeviceTypes(self):
386 return self.controllers.keys()
388 def getDeviceControllers(self):
389 return self.controllers.values()
391 def getDeviceController(self, type, error=True):
392 ctrl = self.controllers.get(type)
393 if not ctrl and error:
394 raise XendError("invalid device type:" + type)
395 return ctrl
397 def findDeviceController(self, type):
398 return (self.getDeviceController(type, error=False)
399 or self.createDeviceController(type))
401 def createDeviceController(self, type):
402 ctrl = controller.createDevController(type, self, recreate=self.recreate)
403 self.controllers[type] = ctrl
404 return ctrl
406 def createDevice(self, type, devconfig, change=False):
407 if self.recreate:
408 return
409 if type == 'vbd':
410 typedev = sxp.child_value(devconfig, 'dev')
411 if re.match('^ioemu:', typedev):
412 return;
414 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
416 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
418 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
419 self.uuid, devnum)
420 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
422 front = { 'backend' : backpath,
423 'backend-id' : "%i" % backdom.id,
424 'virtual-device' : "%i" % devnum }
425 xstransact.Write(frontpath, front)
427 (type, params) = string.split(sxp.child_value(devconfig,
428 'uname'), ':', 1)
429 back = { 'type' : type,
430 'params' : params,
431 'frontend' : frontpath,
432 'frontend-id' : "%i" % self.id }
433 xstransact.Write(backpath, back)
435 return
437 if type == 'vif':
438 from xen.xend import XendRoot
439 xroot = XendRoot.instance()
441 def _get_config_ipaddr(config):
442 val = []
443 for ipaddr in sxp.children(config, elt='ip'):
444 val.append(sxp.child0(ipaddr))
445 return val
447 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
449 devnum = self.netif_idx
450 self.netif_idx += 1
452 script = sxp.child_value(devconfig, 'script',
453 xroot.get_vif_script())
454 script = os.path.join(xroot.network_script_dir, script)
455 bridge = sxp.child_value(devconfig, 'bridge',
456 xroot.get_vif_bridge())
457 mac = sxp.child_value(devconfig, 'mac')
458 ipaddr = _get_config_ipaddr(devconfig)
460 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
461 self.uuid, devnum)
462 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
464 front = { 'backend' : backpath,
465 'backend-id' : "%i" % backdom.id,
466 'handle' : "%i" % devnum,
467 'mac' : mac }
468 xstransact.Write(frontpath, front)
470 back = { 'script' : script,
471 'domain' : self.name,
472 'mac' : mac,
473 'bridge' : bridge,
474 'frontend' : frontpath,
475 'frontend-id' : "%i" % self.id,
476 'handle' : "%i" % devnum }
477 if ipaddr:
478 back['ip'] = ' '.join(ipaddr)
479 xstransact.Write(backpath, back)
481 return
483 if type == 'vtpm':
484 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
486 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
487 log.error("The domain has a TPM with instance %d." % devnum)
489 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
490 self.uuid, devnum)
491 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
493 front = { 'backend' : backpath,
494 'backend-id' : "%i" % backdom.id,
495 'handle' : "%i" % devnum }
496 xstransact.Write(frontpath, front)
498 back = { 'instance' : "%i" % devnum,
499 'frontend' : frontpath,
500 'frontend-id' : "%i" % self.id }
501 xstransact.Write(backpath, back)
503 return
505 ctrl = self.findDeviceController(type)
506 return ctrl.createDevice(devconfig, recreate=self.recreate,
507 change=change)
509 def configureDevice(self, type, id, devconfig):
510 ctrl = self.getDeviceController(type)
511 return ctrl.configureDevice(id, devconfig)
513 def destroyDevice(self, type, id, change=False, reboot=False):
514 ctrl = self.getDeviceController(type)
515 return ctrl.destroyDevice(id, change=change, reboot=reboot)
517 def deleteDevice(self, type, id):
518 ctrl = self.getDeviceController(type)
519 return ctrl.deleteDevice(id)
521 def getDevice(self, type, id, error=True):
522 ctrl = self.getDeviceController(type)
523 return ctrl.getDevice(id, error=error)
525 def getDeviceIds(self, type):
526 ctrl = self.getDeviceController(type)
527 return ctrl.getDeviceIds()
529 def getDeviceSxprs(self, type):
530 ctrl = self.getDeviceController(type)
531 return ctrl.getDeviceSxprs()
533 def sxpr(self):
534 sxpr = ['domain',
535 ['id', self.id],
536 ['name', self.name],
537 ['memory', self.memory],
538 ['ssidref', self.ssidref],
539 ['target', self.target] ]
540 if self.uuid:
541 sxpr.append(['uuid', self.uuid])
542 if self.info:
543 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
544 run = (self.info['running'] and 'r') or '-'
545 block = (self.info['blocked'] and 'b') or '-'
546 pause = (self.info['paused'] and 'p') or '-'
547 shut = (self.info['shutdown'] and 's') or '-'
548 crash = (self.info['crashed'] and 'c') or '-'
549 state = run + block + pause + shut + crash
550 sxpr.append(['state', state])
551 if self.info['shutdown']:
552 reason = shutdown_reason(self.info['shutdown_reason'])
553 sxpr.append(['shutdown_reason', reason])
554 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
555 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
556 sxpr.append(['vcpus', self.info['vcpus']])
557 sxpr.append(['cpumap', self.info['cpumap']])
558 # build a string, using '|' to seperate items, show only up
559 # to number of vcpus in domain, and trim the trailing '|'
560 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
561 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
563 if self.start_time:
564 up_time = time.time() - self.start_time
565 sxpr.append(['up_time', str(up_time) ])
566 sxpr.append(['start_time', str(self.start_time) ])
568 if self.store_channel:
569 sxpr.append(self.store_channel.sxpr())
570 if self.store_mfn:
571 sxpr.append(['store_mfn', self.store_mfn])
572 if self.console_channel:
573 sxpr.append(['console_channel', self.console_channel.sxpr()])
574 if self.console_mfn:
575 sxpr.append(['console_mfn', self.console_mfn])
576 # already in (devices)
577 # console = self.getConsole()
578 # if console:
579 # sxpr.append(console.sxpr())
581 if self.restart_count:
582 sxpr.append(['restart_count', self.restart_count])
583 if self.restart_state:
584 sxpr.append(['restart_state', self.restart_state])
585 if self.restart_time:
586 sxpr.append(['restart_time', str(self.restart_time)])
588 devs = self.sxpr_devices()
589 if devs:
590 sxpr.append(devs)
591 if self.config:
592 sxpr.append(['config', self.config])
593 if self.device_model_pid:
594 sxpr.append(['device_model_pid',self.device_model_pid])
595 return sxpr
597 def sxpr_devices(self):
598 sxpr = []
599 for ty in self.getDeviceTypes():
600 devs = self.getDeviceSxprs(ty)
601 sxpr += devs
602 if sxpr:
603 sxpr.insert(0, 'devices')
604 else:
605 sxpr = None
606 return sxpr
608 def check_name(self, name):
609 """Check if a vm name is valid. Valid names contain alphabetic characters,
610 digits, or characters in '_-.:/+'.
611 The same name cannot be used for more than one vm at the same time.
613 @param name: name
614 @raise: VMerror if invalid
615 """
616 if self.recreate: return
617 if name is None or name == '':
618 raise VmError('missing vm name')
619 for c in name:
620 if c in string.digits: continue
621 if c in '_-.:/+': continue
622 if c in string.ascii_letters: continue
623 raise VmError('invalid vm name')
624 dominfo = domain_exists(name)
625 # When creating or rebooting, a domain with my name should not exist.
626 # When restoring, a domain with my name will exist, but it should have
627 # my domain id.
628 if not dominfo:
629 return
630 if dominfo.is_terminated():
631 return
632 if not self.id or (dominfo.id != self.id):
633 raise VmError('vm name clash: ' + name)
635 def construct(self, config):
636 """Construct the vm instance from its configuration.
638 @param config: configuration
639 @raise: VmError on error
640 """
641 # todo - add support for scheduling params?
642 self.config = config
643 try:
644 # Initial domain create.
645 self.setName(sxp.child_value(config, 'name'))
646 self.check_name(self.name)
647 self.init_image()
648 self.configure_cpus(config)
649 self.init_domain()
650 self.register_domain()
651 self.configure_bootloader()
653 # Create domain devices.
654 self.configure_backends()
655 self.configure_restart()
656 self.construct_image()
657 self.configure()
658 self.exportToDB(save=True)
659 except Exception, ex:
660 # Catch errors, cleanup and re-raise.
661 print 'Domain construction error:', ex
662 import traceback
663 traceback.print_exc()
664 self.destroy()
665 raise
667 def register_domain(self):
668 xd = get_component('xen.xend.XendDomain')
669 xd._add_domain(self)
670 self.exportToDB(save=True)
672 def configure_cpus(self, config):
673 try:
674 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
675 except:
676 raise VmError('invalid cpu weight')
677 self.memory = int(sxp.child_value(config, 'memory'))
678 if self.memory is None:
679 raise VmError('missing memory size')
680 self.target = self.memory * (1 << 20)
681 self.ssidref = int(sxp.child_value(config, 'ssidref'))
682 cpu = sxp.child_value(config, 'cpu')
683 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
684 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
685 try:
686 image = sxp.child_value(self.config, 'image')
687 vcpus = sxp.child_value(image, 'vcpus')
688 if vcpus:
689 self.vcpus = int(vcpus)
690 except:
691 raise VmError('invalid vcpus value')
693 def exportVCPUSToDB(self, vcpus):
694 for v in range(0,vcpus):
695 path = "/cpu/%d"%(v)
696 if not self.vcpusdb.has_key(path):
697 self.vcpusdb[path] = self.db.addChild(path)
698 db = self.vcpusdb[path]
699 log.debug("writing key availability=online to path %s in store"%(path))
700 db['availability'] = "online"
701 db.saveDB(save=True)
703 def init_image(self):
704 """Create boot image handler for the domain.
705 """
706 image = sxp.child_value(self.config, 'image')
707 if image is None:
708 raise VmError('missing image')
709 self.image = ImageHandler.create(self, image)
711 def construct_image(self):
712 """Construct the boot image for the domain.
713 """
714 self.create_channel()
715 self.image.createImage()
716 self.exportToDB()
717 if self.store_channel and self.store_mfn >= 0:
718 IntroduceDomain(self.id, self.store_mfn, self.store_channel.port1,
719 self.path)
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 self.setStoreChannel(None)
758 if self.console_channel:
759 # notify processes using this cosole?
760 try:
761 self.console_channel.close()
762 self.console_channel = None
763 except:
764 pass
765 if self.image:
766 try:
767 self.device_model_pid = 0
768 self.image.destroy()
769 self.image = None
770 except:
771 pass
773 def destroy(self):
774 """Clenup vm and destroy domain.
775 """
776 self.destroy_domain()
777 self.cleanup()
778 self.saveToDB()
779 return 0
781 def is_terminated(self):
782 """Check if a domain has been terminated.
783 """
784 return self.state == STATE_VM_TERMINATED
786 def release_devices(self):
787 """Release all vm devices.
788 """
789 reboot = self.restart_pending()
790 for ctrl in self.getDeviceControllers():
791 if ctrl.isDestroyed(): continue
792 ctrl.destroyController(reboot=reboot)
793 t = xstransact("%s/device" % self.path)
794 for d in t.list("vbd"):
795 t.remove(d)
796 for d in t.list("vif"):
797 t.remove(d)
798 for d in t.list("vtpm"):
799 t.remove(d)
800 t.commit()
802 def show(self):
803 """Print virtual machine info.
804 """
805 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
806 print "image:"
807 sxp.show(self.image)
808 print "]"
810 def init_domain(self):
811 """Initialize the domain memory.
812 """
813 if self.recreate:
814 return
815 if self.start_time is None:
816 self.start_time = time.time()
817 try:
818 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
819 except:
820 raise VmError('invalid cpu')
821 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
822 log.debug('init_domain> Created domain=%d name=%s memory=%d',
823 id, self.name, self.memory)
824 self.setdom(id)
826 def eventChannel(self, path=None):
827 """Create an event channel to the domain.
829 @param path under which port is stored in db
830 """
831 port = 0
832 if path:
833 try:
834 port = int(xstransact.Read(self.path, path))
835 except:
836 # if anything goes wrong, assume the port was not yet set
837 pass
838 ret = EventChannel.interdomain(0, self.id, port1=port, port2=0)
839 xstransact.Write(self.path, path, "%i" % ret.port1)
840 return ret
842 def create_channel(self):
843 """Create the channels to the domain.
844 """
845 self.store_channel = self.eventChannel("store/port")
846 self.console_channel = self.eventChannel("console/port")
848 def create_configured_devices(self):
849 devices = sxp.children(self.config, 'device')
850 for d in devices:
851 dev_config = sxp.child0(d)
852 if dev_config is None:
853 raise VmError('invalid device')
854 dev_type = sxp.name(dev_config)
855 ctrl_type = get_device_handler(dev_type)
856 if ctrl_type is None:
857 raise VmError('unknown device type: ' + dev_type)
858 self.createDevice(ctrl_type, dev_config)
860 def create_devices(self):
861 """Create the devices for a vm.
863 @raise: VmError for invalid devices
864 """
865 if self.rebooting():
866 for ctrl in self.getDeviceControllers():
867 ctrl.initController(reboot=True)
868 else:
869 self.create_configured_devices()
870 if not self.device_model_pid:
871 self.device_model_pid = self.image.createDeviceModel()
873 def device_create(self, dev_config):
874 """Create a new device.
876 @param dev_config: device configuration
877 """
878 dev_type = sxp.name(dev_config)
879 dev = self.createDevice(dev_type, dev_config, change=True)
880 self.config.append(['device', dev.getConfig()])
881 return dev.sxpr()
883 def device_configure(self, dev_config, id):
884 """Configure an existing device.
886 @param dev_config: device configuration
887 @param id: device id
888 """
889 type = sxp.name(dev_config)
890 dev = self.getDevice(type, id)
891 old_config = dev.getConfig()
892 new_config = dev.configure(dev_config, change=True)
893 # Patch new config into vm config.
894 new_full_config = ['device', new_config]
895 old_full_config = ['device', old_config]
896 old_index = self.config.index(old_full_config)
897 self.config[old_index] = new_full_config
898 return new_config
900 def device_refresh(self, type, id):
901 """Refresh a device.
903 @param type: device type
904 @param id: device id
905 """
906 dev = self.getDevice(type, id)
907 dev.refresh()
909 def device_delete(self, type, id):
910 """Destroy and remove a device.
912 @param type: device type
913 @param id: device id
914 """
915 dev = self.getDevice(type, id)
916 dev_config = dev.getConfig()
917 if dev_config:
918 self.config.remove(['device', dev_config])
919 self.deleteDevice(type, dev.getId())
921 def configure_bootloader(self):
922 """Configure boot loader.
923 """
924 self.bootloader = sxp.child_value(self.config, "bootloader")
926 def configure_restart(self):
927 """Configure the vm restart mode.
928 """
929 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
930 if r not in restart_modes:
931 raise VmError('invalid restart mode: ' + str(r))
932 self.restart_mode = r;
934 def restart_needed(self, reason):
935 """Determine if the vm needs to be restarted when shutdown
936 for the given reason.
938 @param reason: shutdown reason
939 @return True if needs restart, False otherwise
940 """
941 if self.restart_mode == RESTART_NEVER:
942 return False
943 if self.restart_mode == RESTART_ALWAYS:
944 return True
945 if self.restart_mode == RESTART_ONREBOOT:
946 return reason == 'reboot'
947 return False
949 def restart_cancel(self):
950 """Cancel a vm restart.
951 """
952 self.restart_state = None
954 def restarting(self):
955 """Put the vm into restart mode.
956 """
957 self.restart_state = STATE_RESTART_PENDING
959 def restart_pending(self):
960 """Test if the vm has a pending restart.
961 """
962 return self.restart_state == STATE_RESTART_PENDING
964 def rebooting(self):
965 return self.restart_state == STATE_RESTART_BOOTING
967 def restart_check(self):
968 """Check if domain restart is OK.
969 To prevent restart loops, raise an error if it is
970 less than MINIMUM_RESTART_TIME seconds since the last restart.
971 """
972 tnow = time.time()
973 if self.restart_time is not None:
974 tdelta = tnow - self.restart_time
975 if tdelta < self.MINIMUM_RESTART_TIME:
976 self.restart_cancel()
977 msg = 'VM %s restarting too fast' % self.name
978 log.error(msg)
979 raise VmError(msg)
980 self.restart_time = tnow
981 self.restart_count += 1
983 def restart(self):
984 """Restart the domain after it has exited.
985 Reuses the domain id
987 """
988 try:
989 self.clear_shutdown()
990 self.state = STATE_VM_OK
991 self.shutdown_pending = None
992 self.restart_check()
993 self.exportToDB()
994 self.restart_state = STATE_RESTART_BOOTING
995 if self.bootloader:
996 self.config = self.bootloader_config()
997 self.construct(self.config)
998 self.saveToDB()
999 finally:
1000 self.restart_state = None
1002 def bootloader_config(self):
1003 # if we're restarting with a bootloader, we need to run it
1004 # FIXME: this assumes the disk is the first device and
1005 # that we're booting from the first disk
1006 blcfg = None
1007 # FIXME: this assumes that we want to use the first disk
1008 dev = sxp.child_value(self.config, "device")
1009 if dev:
1010 disk = sxp.child_value(dev, "uname")
1011 fn = blkdev_uname_to_file(disk)
1012 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
1013 if blcfg is None:
1014 msg = "Had a bootloader specified, but can't find disk"
1015 log.error(msg)
1016 raise VmError(msg)
1017 config = sxp.merge(['vm', blconfig ], self.config)
1018 return config
1020 def configure_backends(self):
1021 """Set configuration flags if the vm is a backend for netif or blkif.
1022 Configure the backends to use for vbd and vif if specified.
1023 """
1024 for c in sxp.children(self.config, 'backend'):
1025 v = sxp.child0(c)
1026 name = sxp.name(v)
1027 if name == 'blkif':
1028 self.blkif_backend = True
1029 elif name == 'netif':
1030 self.netif_backend = True
1031 elif name == 'usbif':
1032 self.usbif_backend = True
1033 elif name == 'tpmif':
1034 self.tpmif_backend = True
1035 else:
1036 raise VmError('invalid backend type:' + str(name))
1038 def configure(self):
1039 """Configure a vm.
1041 """
1042 self.configure_fields()
1043 self.create_devices()
1044 self.create_blkif()
1046 def create_blkif(self):
1047 """Create the block device interface (blkif) for the vm.
1048 The vm needs a blkif even if it doesn't have any disks
1049 at creation time, for example when it uses NFS root.
1051 """
1052 return
1053 blkif = self.getDeviceController("vbd", error=False)
1054 if not blkif:
1055 blkif = self.createDeviceController("vbd")
1056 backend = blkif.getBackend(0)
1057 backend.connect(recreate=self.recreate)
1059 def configure_fields(self):
1060 """Process the vm configuration fields using the registered handlers.
1061 """
1062 index = {}
1063 for field in sxp.children(self.config):
1064 field_name = sxp.name(field)
1065 field_index = index.get(field_name, 0)
1066 field_handler = get_config_handler(field_name)
1067 # Ignore unknown fields. Warn?
1068 if field_handler:
1069 v = field_handler(self, self.config, field, field_index)
1070 else:
1071 log.warning("Unknown config field %s", field_name)
1072 index[field_name] = field_index + 1
1074 def mem_target_set(self, target):
1075 """Set domain memory target in bytes.
1076 """
1077 if target:
1078 self.target = target * (1 << 20)
1079 # Commit to XenStore immediately
1080 self.exportToDB()
1082 def vcpu_hotplug(self, vcpu, state):
1083 """Disable or enable VCPU in domain.
1084 """
1085 db = ""
1086 try:
1087 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1088 except:
1089 log.error("Invalid VCPU")
1090 return
1092 if self.store_channel:
1093 if int(state) == 0:
1094 db['availability'] = "offline"
1095 else:
1096 db['availability'] = "online"
1098 db.saveDB(save=True)
1100 def shutdown(self, reason):
1101 if not reason in shutdown_reasons.values():
1102 raise XendError('invalid reason:' + reason)
1103 db = self.db.addChild("/control");
1104 db['shutdown'] = reason;
1105 db.saveDB(save=True);
1106 if not reason in ['suspend']:
1107 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1109 def clear_shutdown(self):
1110 db = self.db.addChild("/control")
1111 db['shutdown'] = ""
1112 db.saveDB(save=True)
1114 def send_sysrq(self, key=0):
1115 db = self.db.addChild("/control");
1116 db['sysrq'] = '%c' % key;
1117 db.saveDB(save=True);
1119 def shutdown_time_left(self, timeout):
1120 if not self.shutdown_pending:
1121 return 0
1122 return timeout - (time.time() - self.shutdown_pending['start'])
1124 def dom0_init_store(self):
1125 if not self.store_channel:
1126 self.store_channel = self.eventChannel("store/port")
1127 if not self.store_channel:
1128 return
1129 ref = xc.init_store(self.store_channel.port2)
1130 if ref and ref >= 0:
1131 self.setStoreRef(ref)
1132 IntroduceDomain(self.id, ref, self.store_channel.port1, self.path)
1133 # get run-time value of vcpus and update store
1134 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1136 def vm_field_ignore(vm, config, val, index):
1137 """Dummy config field handler used for fields with built-in handling.
1139 @param vm: virtual machine
1140 @param config: vm config
1141 @param val: config field
1142 @param index: field index
1143 """
1144 pass
1146 def vm_field_maxmem(vm, config, val, index):
1147 """Configure vm memory limit.
1149 @param vm: virtual machine
1150 @param config: vm config
1151 @param val: config field
1152 @param index: field index
1153 """
1154 maxmem = sxp.child0(val)
1155 if maxmem is None:
1156 maxmem = vm.memory
1157 try:
1158 maxmem = int(maxmem)
1159 except:
1160 raise VmError("invalid maxmem: " + str(maxmem))
1161 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1163 #============================================================================
1164 # Register image handlers.
1165 from image import \
1166 addImageHandlerClass, \
1167 ImageHandler, \
1168 LinuxImageHandler, \
1169 VmxImageHandler
1171 addImageHandlerClass(LinuxImageHandler)
1172 addImageHandlerClass(VmxImageHandler)
1174 # Ignore the fields we already handle.
1175 add_config_handler('name', vm_field_ignore)
1176 add_config_handler('memory', vm_field_ignore)
1177 add_config_handler('ssidref', vm_field_ignore)
1178 add_config_handler('cpu', vm_field_ignore)
1179 add_config_handler('cpu_weight', vm_field_ignore)
1180 add_config_handler('restart', vm_field_ignore)
1181 add_config_handler('image', vm_field_ignore)
1182 add_config_handler('device', vm_field_ignore)
1183 add_config_handler('backend', vm_field_ignore)
1184 add_config_handler('vcpus', vm_field_ignore)
1185 add_config_handler('bootloader', vm_field_ignore)
1187 # Register other config handlers.
1188 add_config_handler('maxmem', vm_field_maxmem)
1190 #============================================================================
1191 # Register device controllers and their device config types.
1193 from server import blkif
1194 controller.addDevControllerClass("vbd", blkif.BlkifController)
1195 add_device_handler("vbd", "vbd")
1197 from server import netif
1198 controller.addDevControllerClass("vif", netif.NetifController)
1199 add_device_handler("vif", "vif")
1201 from server import tpmif
1202 controller.addDevControllerClass("vtpm", tpmif.TPMifController)
1203 add_device_handler("vtpm", "vtpm")
1205 from server import pciif
1206 controller.addDevControllerClass("pci", pciif.PciController)
1207 add_device_handler("pci", "pci")
1209 from xen.xend.server import usbif
1210 controller.addDevControllerClass("usb", usbif.UsbifController)
1211 add_device_handler("usb", "usb")
1213 #============================================================================