xen-vtx-unstable

view tools/python/xen/xend/XendDomainInfo.py @ 6774:4d899a738d59

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:05:49 2005 +0000 (2005-09-13)
parents 219d96d545fc c5045197dcb7
children e7c7196fa329 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
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 def dom_get(dom):
106 """Get info from xen for an existing domain.
108 @param dom: domain id
109 @return: info or None
110 """
111 domlist = xc.domain_getinfo(dom, 1)
112 if domlist and dom == domlist[0]['dom']:
113 return domlist[0]
114 return None
116 class XendDomainInfo:
117 """Virtual machine object."""
119 """Minimum time between domain restarts in seconds.
120 """
121 MINIMUM_RESTART_TIME = 20
123 def create(cls, parentdb, config):
124 """Create a VM from a configuration.
126 @param parentdb: parent db
127 @param config configuration
128 @raise: VmError for invalid configuration
129 """
130 uuid = getUuid()
131 db = parentdb.addChild(uuid)
132 path = parentdb.getPath()
133 vm = cls(uuid, path, db)
134 vm.construct(config)
135 vm.saveToDB(sync=True)
137 return vm
139 create = classmethod(create)
141 def recreate(cls, db, info):
142 """Create the VM object for an existing domain.
144 @param db: domain db
145 @param info: domain info from xc
146 """
147 dom = info['dom']
148 path = "/".join(db.getPath().split("/")[0:-1])
149 vm = cls(db.getName(), path, db)
150 vm.setdom(dom)
151 db.readDB()
152 vm.importFromDB()
153 config = vm.config
154 log.debug('info=' + str(info))
155 log.debug('config=' + prettyprintstring(config))
157 vm.memory = info['mem_kb']/1024
158 vm.target = info['mem_kb'] * 1024
160 if config:
161 try:
162 vm.recreate = True
163 vm.construct(config)
164 finally:
165 vm.recreate = False
166 else:
167 vm.setName("Domain-%d" % dom)
169 vm.exportToDB(save=True)
170 return vm
172 recreate = classmethod(recreate)
174 def restore(cls, parentdb, config, uuid=None):
175 """Create a domain and a VM object to do a restore.
177 @param parentdb: parent db
178 @param config: domain configuration
179 @param uuid: uuid to use
180 """
181 if not uuid:
182 uuid = getUuid()
183 db = parentdb.addChild(uuid)
184 path = parentdb.getPath()
185 vm = cls(uuid, path, db)
186 ssidref = int(sxp.child_value(config, 'ssidref'))
187 log.debug('restoring with ssidref='+str(ssidref))
188 id = xc.domain_create(ssidref = ssidref)
189 vm.setdom(id)
190 vm.clear_shutdown()
191 try:
192 vm.restore = True
193 vm.construct(config)
194 finally:
195 vm.restore = False
196 vm.exportToDB(save=True, sync=True)
197 return vm
199 restore = classmethod(restore)
201 __exports__ = [
202 DBVar('id', ty='int'),
203 DBVar('name', ty='str'),
204 DBVar('uuid', ty='str'),
205 DBVar('config', ty='sxpr'),
206 DBVar('start_time', ty='float'),
207 DBVar('state', ty='str'),
208 DBVar('store_mfn', ty='long'),
209 DBVar('console_mfn', ty='long', path="console/ring-ref"),
210 DBVar('restart_mode', ty='str'),
211 DBVar('restart_state', ty='str'),
212 DBVar('restart_time', ty='float'),
213 DBVar('restart_count', ty='int'),
214 DBVar('target', ty='long', path="memory/target"),
215 DBVar('device_model_pid', ty='int'),
216 ]
218 def __init__(self, uuid, path, db):
219 self.uuid = uuid
220 self.path = path + "/" + uuid
221 self.db = db
223 self.recreate = 0
224 self.restore = 0
226 self.config = None
227 self.id = None
228 self.cpu_weight = 1
229 self.start_time = None
230 self.name = None
231 self.memory = None
232 self.ssidref = None
233 self.image = None
235 self.target = None
237 self.store_channel = None
238 self.store_mfn = None
239 self.console_channel = None
240 self.console_mfn = None
241 self.controllers = {}
243 self.info = None
244 self.blkif_backend = False
245 self.netif_backend = False
246 self.netif_idx = 0
247 self.tpmif_backend = False
249 #todo: state: running, suspended
250 self.state = STATE_VM_OK
251 self.state_updated = threading.Condition()
252 self.shutdown_pending = None
254 #todo: set to migrate info if migrating
255 self.migrate = None
257 self.restart_mode = RESTART_ONREBOOT
258 self.restart_state = None
259 self.restart_time = None
260 self.restart_count = 0
262 self.vcpus = 1
263 self.vcpusdb = {}
264 self.bootloader = None
265 self.device_model_pid = 0
267 def setDB(self, db):
268 self.db = db
270 def saveToDB(self, save=False, sync=False):
271 self.db.saveDB(save=save, sync=sync)
273 def exportToDB(self, save=False, sync=False):
274 if self.store_channel:
275 self.store_channel.saveToDB(self.db.addChild("store_channel"),
276 save=save)
277 if self.console_channel:
278 self.db['console/port'] = "%i" % self.console_channel.port1
279 if self.image:
280 self.image.exportToDB(save=save, sync=sync)
281 self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
283 def importFromDB(self):
284 self.db.importFromDB(self, fields=self.__exports__)
285 self.store_channel = self.eventChannelOld("store_channel")
287 def setdom(self, dom):
288 """Set the domain id.
290 @param dom: domain id
291 """
292 self.id = int(dom)
293 #self.db.id = self.id
295 def getDomain(self):
296 return self.id
298 def setName(self, name):
299 self.name = name
300 self.db.name = self.name
302 def getName(self):
303 return self.name
305 def getStoreChannel(self):
306 return self.store_channel
308 def getConsoleChannel(self):
309 return self.console_channel
311 def update(self, info=None):
312 """Update with info from xc.domain_getinfo().
313 """
314 self.info = info or dom_get(self.id)
315 self.memory = self.info['mem_kb'] / 1024
316 self.ssidref = self.info['ssidref']
317 self.target = self.info['mem_kb'] * 1024
319 def state_set(self, state):
320 self.state_updated.acquire()
321 if self.state != state:
322 self.state = state
323 self.state_updated.notifyAll()
324 self.state_updated.release()
325 self.saveToDB()
327 def state_wait(self, state):
328 self.state_updated.acquire()
329 while self.state != state:
330 self.state_updated.wait()
331 self.state_updated.release()
333 def __str__(self):
334 s = "<domain"
335 s += " id=" + str(self.id)
336 s += " name=" + self.name
337 s += " memory=" + str(self.memory)
338 s += " ssidref=" + str(self.ssidref)
339 s += ">"
340 return s
342 __repr__ = __str__
344 def getDeviceController(self, type, error=True):
345 ctrl = self.controllers.get(type)
346 if not ctrl and error:
347 raise XendError("invalid device type:" + type)
348 return ctrl
350 def findDeviceController(self, type):
351 return (self.getDeviceController(type, error=False)
352 or self.createDeviceController(type))
354 def createDeviceController(self, type):
355 ctrl = controller.createDevController(type, self, recreate=self.recreate)
356 self.controllers[type] = ctrl
357 return ctrl
359 def createDevice(self, type, devconfig, change=False):
360 if self.recreate:
361 return
362 if type == 'vbd':
363 typedev = sxp.child_value(devconfig, 'dev')
364 if re.match('^ioemu:', typedev):
365 return;
367 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
369 devnum = blkdev_name_to_number(sxp.child_value(devconfig, 'dev'))
371 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
372 self.uuid, devnum)
373 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
375 front = { 'backend' : backpath,
376 'backend-id' : "%i" % backdom.id,
377 'virtual-device' : "%i" % devnum }
378 xstransact.Write(frontpath, front)
380 (type, params) = string.split(sxp.child_value(devconfig,
381 'uname'), ':', 1)
382 back = { 'type' : type,
383 'params' : params,
384 'frontend' : frontpath,
385 'frontend-id' : "%i" % self.id }
386 xstransact.Write(backpath, back)
388 return
390 if type == 'vif':
391 from xen.xend import XendRoot
392 xroot = XendRoot.instance()
394 def _get_config_ipaddr(config):
395 val = []
396 for ipaddr in sxp.children(config, elt='ip'):
397 val.append(sxp.child0(ipaddr))
398 return val
400 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
402 devnum = self.netif_idx
403 self.netif_idx += 1
405 script = sxp.child_value(devconfig, 'script',
406 xroot.get_vif_script())
407 script = os.path.join(xroot.network_script_dir, script)
408 bridge = sxp.child_value(devconfig, 'bridge',
409 xroot.get_vif_bridge())
410 mac = sxp.child_value(devconfig, 'mac')
411 ipaddr = _get_config_ipaddr(devconfig)
413 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
414 self.uuid, devnum)
415 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
417 front = { 'backend' : backpath,
418 'backend-id' : "%i" % backdom.id,
419 'handle' : "%i" % devnum,
420 'mac' : mac }
421 xstransact.Write(frontpath, front)
423 back = { 'script' : script,
424 'domain' : self.name,
425 'mac' : mac,
426 'bridge' : bridge,
427 'frontend' : frontpath,
428 'frontend-id' : "%i" % self.id,
429 'handle' : "%i" % devnum }
430 if ipaddr:
431 back['ip'] = ' '.join(ipaddr)
432 xstransact.Write(backpath, back)
434 return
436 if type == 'vtpm':
437 backdom = domain_exists(sxp.child_value(devconfig, 'backend', '0'))
439 devnum = int(sxp.child_value(devconfig, 'instance', '0'))
440 log.error("The domain has a TPM with instance %d." % devnum)
442 backpath = "%s/backend/%s/%s/%d" % (backdom.path, type,
443 self.uuid, devnum)
444 frontpath = "%s/device/%s/%d" % (self.path, type, devnum)
446 front = { 'backend' : backpath,
447 'backend-id' : "%i" % backdom.id,
448 'handle' : "%i" % devnum }
449 xstransact.Write(frontpath, front)
451 back = { 'instance' : "%i" % devnum,
452 'frontend' : frontpath,
453 'frontend-id' : "%i" % self.id }
454 xstransact.Write(backpath, back)
456 return
458 ctrl = self.findDeviceController(type)
459 return ctrl.createDevice(devconfig, recreate=self.recreate,
460 change=change)
462 def configureDevice(self, type, id, devconfig):
463 ctrl = self.getDeviceController(type)
464 return ctrl.configureDevice(id, devconfig)
466 def destroyDevice(self, type, id, change=False, reboot=False):
467 ctrl = self.getDeviceController(type)
468 return ctrl.destroyDevice(id, change=change, reboot=reboot)
470 def deleteDevice(self, type, id):
471 ctrl = self.getDeviceController(type)
472 return ctrl.deleteDevice(id)
474 def getDevice(self, type, id, error=True):
475 ctrl = self.getDeviceController(type)
476 return ctrl.getDevice(id, error=error)
478 def getDeviceIds(self, type):
479 ctrl = self.getDeviceController(type)
480 return ctrl.getDeviceIds()
482 def getDeviceSxprs(self, type):
483 ctrl = self.getDeviceController(type)
484 return ctrl.getDeviceSxprs()
486 def sxpr(self):
487 sxpr = ['domain',
488 ['id', self.id],
489 ['name', self.name],
490 ['memory', self.memory],
491 ['ssidref', self.ssidref],
492 ['target', self.target] ]
493 if self.uuid:
494 sxpr.append(['uuid', self.uuid])
495 if self.info:
496 sxpr.append(['maxmem', self.info['maxmem_kb']/1024 ])
497 run = (self.info['running'] and 'r') or '-'
498 block = (self.info['blocked'] and 'b') or '-'
499 pause = (self.info['paused'] and 'p') or '-'
500 shut = (self.info['shutdown'] and 's') or '-'
501 crash = (self.info['crashed'] and 'c') or '-'
502 state = run + block + pause + shut + crash
503 sxpr.append(['state', state])
504 if self.info['shutdown']:
505 reason = shutdown_reason(self.info['shutdown_reason'])
506 sxpr.append(['shutdown_reason', reason])
507 sxpr.append(['cpu', self.info['vcpu_to_cpu'][0]])
508 sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
509 sxpr.append(['vcpus', self.info['vcpus']])
510 sxpr.append(['cpumap', self.info['cpumap']])
511 # build a string, using '|' to seperate items, show only up
512 # to number of vcpus in domain, and trim the trailing '|'
513 sxpr.append(['vcpu_to_cpu', ''.join(map(lambda x: str(x)+'|',
514 self.info['vcpu_to_cpu'][0:self.info['vcpus']]))[:-1]])
516 if self.start_time:
517 up_time = time.time() - self.start_time
518 sxpr.append(['up_time', str(up_time) ])
519 sxpr.append(['start_time', str(self.start_time) ])
521 if self.store_channel:
522 sxpr.append(self.store_channel.sxpr())
523 if self.store_mfn:
524 sxpr.append(['store_mfn', self.store_mfn])
525 if self.console_channel:
526 sxpr.append(['console_channel', self.console_channel.sxpr()])
527 if self.console_mfn:
528 sxpr.append(['console_mfn', self.console_mfn])
529 # already in (devices)
530 # console = self.getConsole()
531 # if console:
532 # sxpr.append(console.sxpr())
534 if self.restart_count:
535 sxpr.append(['restart_count', self.restart_count])
536 if self.restart_state:
537 sxpr.append(['restart_state', self.restart_state])
538 if self.restart_time:
539 sxpr.append(['restart_time', str(self.restart_time)])
541 devs = self.sxpr_devices()
542 if devs:
543 sxpr.append(devs)
544 if self.config:
545 sxpr.append(['config', self.config])
546 if self.device_model_pid:
547 sxpr.append(['device_model_pid',self.device_model_pid])
548 return sxpr
550 def sxpr_devices(self):
551 sxpr = []
552 for ty in self.controllers.keys():
553 devs = self.getDeviceSxprs(ty)
554 sxpr += devs
555 if sxpr:
556 sxpr.insert(0, 'devices')
557 else:
558 sxpr = None
559 return sxpr
561 def check_name(self, name):
562 """Check if a vm name is valid. Valid names contain alphabetic characters,
563 digits, or characters in '_-.:/+'.
564 The same name cannot be used for more than one vm at the same time.
566 @param name: name
567 @raise: VMerror if invalid
568 """
569 if self.recreate: return
570 if name is None or name == '':
571 raise VmError('missing vm name')
572 for c in name:
573 if c in string.digits: continue
574 if c in '_-.:/+': continue
575 if c in string.ascii_letters: continue
576 raise VmError('invalid vm name')
577 dominfo = domain_exists(name)
578 # When creating or rebooting, a domain with my name should not exist.
579 # When restoring, a domain with my name will exist, but it should have
580 # my domain id.
581 if not dominfo:
582 return
583 if dominfo.is_terminated():
584 return
585 if not self.id or (dominfo.id != self.id):
586 raise VmError('vm name clash: ' + name)
588 def construct(self, config):
589 """Construct the vm instance from its configuration.
591 @param config: configuration
592 @raise: VmError on error
593 """
594 # todo - add support for scheduling params?
595 self.config = config
596 try:
597 # Initial domain create.
598 self.setName(sxp.child_value(config, 'name'))
599 self.check_name(self.name)
600 self.init_image()
601 self.configure_cpus(config)
602 self.init_domain()
603 self.register_domain()
604 self.configure_bootloader()
606 # Create domain devices.
607 self.configure_backends()
608 self.configure_restart()
609 self.construct_image()
610 self.configure()
611 self.exportToDB(save=True)
612 except Exception, ex:
613 # Catch errors, cleanup and re-raise.
614 print 'Domain construction error:', ex
615 import traceback
616 traceback.print_exc()
617 self.destroy()
618 raise
620 def register_domain(self):
621 xd = get_component('xen.xend.XendDomain')
622 xd._add_domain(self)
623 self.exportToDB(save=True)
625 def configure_cpus(self, config):
626 try:
627 self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
628 except:
629 raise VmError('invalid cpu weight')
630 self.memory = int(sxp.child_value(config, 'memory'))
631 if self.memory is None:
632 raise VmError('missing memory size')
633 self.target = self.memory * (1 << 20)
634 self.ssidref = int(sxp.child_value(config, 'ssidref'))
635 cpu = sxp.child_value(config, 'cpu')
636 if self.recreate and self.id and cpu is not None and int(cpu) >= 0:
637 xc.domain_pincpu(self.id, 0, 1<<int(cpu))
638 try:
639 image = sxp.child_value(self.config, 'image')
640 vcpus = sxp.child_value(image, 'vcpus')
641 if vcpus:
642 self.vcpus = int(vcpus)
643 except:
644 raise VmError('invalid vcpus value')
646 def exportVCPUSToDB(self, vcpus):
647 for v in range(0,vcpus):
648 path = "/cpu/%d"%(v)
649 if not self.vcpusdb.has_key(path):
650 self.vcpusdb[path] = self.db.addChild(path)
651 db = self.vcpusdb[path]
652 log.debug("writing key availability=online to path %s in store"%(path))
653 db['availability'] = "online"
654 db.saveDB(save=True)
656 def init_image(self):
657 """Create boot image handler for the domain.
658 """
659 image = sxp.child_value(self.config, 'image')
660 if image is None:
661 raise VmError('missing image')
662 self.image = ImageHandler.create(self, image)
664 def construct_image(self):
665 """Construct the boot image for the domain.
666 """
667 self.create_channel()
668 self.image.createImage()
669 self.exportToDB()
670 if self.store_channel and self.store_mfn >= 0:
671 self.db.introduceDomain(self.id,
672 self.store_mfn,
673 self.store_channel)
674 # get the configured value of vcpus and update store
675 self.exportVCPUSToDB(self.vcpus)
677 def delete(self):
678 """Delete the vm's db.
679 """
680 if dom_get(self.id):
681 return
682 self.id = None
683 self.saveToDB(sync=True)
684 try:
685 # Todo: eventually will have to wait for devices to signal
686 # destruction before can delete the db.
687 if self.db:
688 self.db.delete()
689 except Exception, ex:
690 log.warning("error in domain db delete: %s", ex)
691 pass
693 def destroy_domain(self):
694 """Destroy the vm's domain.
695 The domain will not finally go away unless all vm
696 devices have been released.
697 """
698 if self.id is None:
699 return
700 try:
701 xc.domain_destroy(dom=self.id)
702 except Exception, err:
703 log.exception("Domain destroy failed: %s", self.name)
705 def cleanup(self):
706 """Cleanup vm resources: release devices.
707 """
708 self.state = STATE_VM_TERMINATED
709 self.release_devices()
710 if self.store_channel:
711 try:
712 self.store_channel.close()
713 self.store_channel = None
714 except:
715 pass
716 try:
717 self.db.releaseDomain(self.id)
718 except Exception, ex:
719 log.warning("error in domain release on xenstore: %s", ex)
720 pass
721 if self.console_channel:
722 # notify processes using this cosole?
723 try:
724 self.console_channel.close()
725 self.console_channel = None
726 except:
727 pass
728 if self.image:
729 try:
730 self.device_model_pid = 0
731 self.image.destroy()
732 self.image = None
733 except:
734 pass
736 def destroy(self):
737 """Clenup vm and destroy domain.
738 """
739 self.destroy_domain()
740 self.cleanup()
741 self.saveToDB()
742 return 0
744 def is_terminated(self):
745 """Check if a domain has been terminated.
746 """
747 return self.state == STATE_VM_TERMINATED
749 def release_devices(self):
750 """Release all vm devices.
751 """
752 reboot = self.restart_pending()
753 for ctrl in self.controllers.values():
754 if ctrl.isDestroyed(): continue
755 ctrl.destroyController(reboot=reboot)
756 t = xstransact("%s/device" % self.path)
757 for d in t.list("vbd"):
758 t.remove(d)
759 for d in t.list("vif"):
760 t.remove(d)
761 for d in t.list("vtpm"):
762 t.remove(d)
763 t.commit()
765 def show(self):
766 """Print virtual machine info.
767 """
768 print "[VM dom=%d name=%s memory=%d ssidref=%d" % (self.id, self.name, self.memory, self.ssidref)
769 print "image:"
770 sxp.show(self.image)
771 print "]"
773 def init_domain(self):
774 """Initialize the domain memory.
775 """
776 if self.recreate:
777 return
778 if self.start_time is None:
779 self.start_time = time.time()
780 try:
781 cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
782 except:
783 raise VmError('invalid cpu')
784 id = self.image.initDomain(self.id, self.memory, self.ssidref, cpu, self.cpu_weight)
785 log.debug('init_domain> Created domain=%d name=%s memory=%d',
786 id, self.name, self.memory)
787 self.setdom(id)
789 def eventChannelOld(self, key):
790 """Create an event channel to the domain.
791 If saved info is available recreate the channel.
793 @param key db key for the saved data (if any)
794 """
795 db = self.db.addChild(key)
796 return EventChannel.restoreFromDB(db, 0, self.id)
798 def eventChannel(self, path=None, key=None):
799 """Create an event channel to the domain.
801 @param path under which port is stored in db
802 """
803 port = 0
804 try:
805 if path and key:
806 if path:
807 db = self.db.addChild(path)
808 else:
809 db = self.db
810 port = int(db[key].getData())
811 except: pass
812 return EventChannel.interdomain(0, self.id, port1=port, port2=0)
814 def create_channel(self):
815 """Create the channels to the domain.
816 """
817 self.store_channel = self.eventChannelOld("store_channel")
818 self.console_channel = self.eventChannel("console", "port")
821 def create_configured_devices(self):
822 devices = sxp.children(self.config, 'device')
823 for d in devices:
824 dev_config = sxp.child0(d)
825 if dev_config is None:
826 raise VmError('invalid device')
827 dev_type = sxp.name(dev_config)
829 if not controller.isDevControllerClass(dev_type):
830 raise VmError('unknown device type: ' + dev_type)
832 self.createDevice(dev_type, dev_config)
835 def create_devices(self):
836 """Create the devices for a vm.
838 @raise: VmError for invalid devices
839 """
840 if self.rebooting():
841 for ctrl in self.controllers.values():
842 ctrl.initController(reboot=True)
843 else:
844 self.create_configured_devices()
845 if not self.device_model_pid:
846 self.device_model_pid = self.image.createDeviceModel()
848 def device_create(self, dev_config):
849 """Create a new device.
851 @param dev_config: device configuration
852 """
853 dev_type = sxp.name(dev_config)
854 dev = self.createDevice(dev_type, dev_config, change=True)
855 self.config.append(['device', dev.getConfig()])
856 return dev.sxpr()
858 def device_configure(self, dev_config, id):
859 """Configure an existing device.
861 @param dev_config: device configuration
862 @param id: device id
863 """
864 type = sxp.name(dev_config)
865 dev = self.getDevice(type, id)
866 old_config = dev.getConfig()
867 new_config = dev.configure(dev_config, change=True)
868 # Patch new config into vm config.
869 new_full_config = ['device', new_config]
870 old_full_config = ['device', old_config]
871 old_index = self.config.index(old_full_config)
872 self.config[old_index] = new_full_config
873 return new_config
875 def device_refresh(self, type, id):
876 """Refresh a device.
878 @param type: device type
879 @param id: device id
880 """
881 dev = self.getDevice(type, id)
882 dev.refresh()
884 def device_delete(self, type, id):
885 """Destroy and remove a device.
887 @param type: device type
888 @param id: device id
889 """
890 dev = self.getDevice(type, id)
891 dev_config = dev.getConfig()
892 if dev_config:
893 self.config.remove(['device', dev_config])
894 self.deleteDevice(type, dev.getId())
896 def configure_bootloader(self):
897 """Configure boot loader.
898 """
899 self.bootloader = sxp.child_value(self.config, "bootloader")
901 def configure_restart(self):
902 """Configure the vm restart mode.
903 """
904 r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT)
905 if r not in restart_modes:
906 raise VmError('invalid restart mode: ' + str(r))
907 self.restart_mode = r;
909 def restart_needed(self, reason):
910 """Determine if the vm needs to be restarted when shutdown
911 for the given reason.
913 @param reason: shutdown reason
914 @return True if needs restart, False otherwise
915 """
916 if self.restart_mode == RESTART_NEVER:
917 return False
918 if self.restart_mode == RESTART_ALWAYS:
919 return True
920 if self.restart_mode == RESTART_ONREBOOT:
921 return reason == 'reboot'
922 return False
924 def restart_cancel(self):
925 """Cancel a vm restart.
926 """
927 self.restart_state = None
929 def restarting(self):
930 """Put the vm into restart mode.
931 """
932 self.restart_state = STATE_RESTART_PENDING
934 def restart_pending(self):
935 """Test if the vm has a pending restart.
936 """
937 return self.restart_state == STATE_RESTART_PENDING
939 def rebooting(self):
940 return self.restart_state == STATE_RESTART_BOOTING
942 def restart_check(self):
943 """Check if domain restart is OK.
944 To prevent restart loops, raise an error if it is
945 less than MINIMUM_RESTART_TIME seconds since the last restart.
946 """
947 tnow = time.time()
948 if self.restart_time is not None:
949 tdelta = tnow - self.restart_time
950 if tdelta < self.MINIMUM_RESTART_TIME:
951 self.restart_cancel()
952 msg = 'VM %s restarting too fast' % self.name
953 log.error(msg)
954 raise VmError(msg)
955 self.restart_time = tnow
956 self.restart_count += 1
958 def restart(self):
959 """Restart the domain after it has exited.
960 Reuses the domain id
962 """
963 try:
964 self.clear_shutdown()
965 self.state = STATE_VM_OK
966 self.shutdown_pending = None
967 self.restart_check()
968 self.exportToDB()
969 self.restart_state = STATE_RESTART_BOOTING
970 if self.bootloader:
971 self.config = self.bootloader_config()
972 self.construct(self.config)
973 self.saveToDB()
974 finally:
975 self.restart_state = None
977 def bootloader_config(self):
978 # if we're restarting with a bootloader, we need to run it
979 # FIXME: this assumes the disk is the first device and
980 # that we're booting from the first disk
981 blcfg = None
982 # FIXME: this assumes that we want to use the first disk
983 dev = sxp.child_value(self.config, "device")
984 if dev:
985 disk = sxp.child_value(dev, "uname")
986 fn = blkdev_uname_to_file(disk)
987 blcfg = bootloader(self.bootloader, fn, 1, self.vcpus)
988 if blcfg is None:
989 msg = "Had a bootloader specified, but can't find disk"
990 log.error(msg)
991 raise VmError(msg)
992 config = sxp.merge(['vm', blcfg ], self.config)
993 return config
995 def configure_backends(self):
996 """Set configuration flags if the vm is a backend for netif or blkif.
997 Configure the backends to use for vbd and vif if specified.
998 """
999 for c in sxp.children(self.config, 'backend'):
1000 v = sxp.child0(c)
1001 name = sxp.name(v)
1002 if name == 'blkif':
1003 self.blkif_backend = True
1004 elif name == 'netif':
1005 self.netif_backend = True
1006 elif name == 'usbif':
1007 self.usbif_backend = True
1008 elif name == 'tpmif':
1009 self.tpmif_backend = True
1010 else:
1011 raise VmError('invalid backend type:' + str(name))
1013 def configure(self):
1014 """Configure a vm.
1016 """
1017 self.configure_fields()
1018 self.create_devices()
1019 self.create_blkif()
1021 def create_blkif(self):
1022 """Create the block device interface (blkif) for the vm.
1023 The vm needs a blkif even if it doesn't have any disks
1024 at creation time, for example when it uses NFS root.
1026 """
1027 return
1028 blkif = self.getDeviceController("vbd", error=False)
1029 if not blkif:
1030 blkif = self.createDeviceController("vbd")
1031 backend = blkif.getBackend(0)
1032 backend.connect(recreate=self.recreate)
1034 def configure_fields(self):
1035 """Process the vm configuration fields using the registered handlers.
1036 """
1037 index = {}
1038 for field in sxp.children(self.config):
1039 field_name = sxp.name(field)
1040 field_index = index.get(field_name, 0)
1041 field_handler = config_handlers.get(field_name)
1042 # Ignore unknown fields. Warn?
1043 if field_handler:
1044 v = field_handler(self, self.config, field, field_index)
1045 else:
1046 log.warning("Unknown config field %s", field_name)
1047 index[field_name] = field_index + 1
1049 def mem_target_set(self, target):
1050 """Set domain memory target in bytes.
1051 """
1052 if target:
1053 self.target = target * (1 << 20)
1054 # Commit to XenStore immediately
1055 self.exportToDB()
1057 def vcpu_hotplug(self, vcpu, state):
1058 """Disable or enable VCPU in domain.
1059 """
1060 db = ""
1061 try:
1062 db = self.vcpusdb['/cpu/%d'%(vcpu)]
1063 except:
1064 log.error("Invalid VCPU")
1065 return
1067 if self.store_channel:
1068 if int(state) == 0:
1069 db['availability'] = "offline"
1070 else:
1071 db['availability'] = "online"
1073 db.saveDB(save=True)
1075 def shutdown(self, reason):
1076 if not reason in shutdown_reasons.values():
1077 raise XendError('invalid reason:' + reason)
1078 db = self.db.addChild("/control");
1079 db['shutdown'] = reason;
1080 db.saveDB(save=True);
1081 if not reason in ['suspend']:
1082 self.shutdown_pending = {'start':time.time(), 'reason':reason}
1084 def clear_shutdown(self):
1085 db = self.db.addChild("/control")
1086 db['shutdown'] = ""
1087 db.saveDB(save=True)
1089 def send_sysrq(self, key=0):
1090 db = self.db.addChild("/control");
1091 db['sysrq'] = '%c' % key;
1092 db.saveDB(save=True);
1094 def shutdown_time_left(self, timeout):
1095 if not self.shutdown_pending:
1096 return 0
1097 return timeout - (time.time() - self.shutdown_pending['start'])
1099 def dom0_init_store(self):
1100 if not self.store_channel:
1101 self.store_channel = self.eventChannelOld("store_channel")
1102 self.store_mfn = xc.init_store(self.store_channel.port2)
1103 if self.store_mfn >= 0:
1104 self.db.introduceDomain(self.id, self.store_mfn,
1105 self.store_channel)
1106 self.exportToDB(save=True, sync=True)
1107 # get run-time value of vcpus and update store
1108 self.exportVCPUSToDB(dom_get(self.id)['vcpus'])
1111 def vm_field_ignore(_, _1, _2, _3):
1112 """Dummy config field handler used for fields with built-in handling.
1113 Matches the signature required by config_handlers.
1114 """
1115 pass
1118 def vm_field_maxmem(vm, _1, val, _2):
1119 """Config field handler to configure vm memory limit. Matches the
1120 signature required by config_handlers.
1121 """
1122 maxmem = sxp.child0(val)
1123 if maxmem is None:
1124 maxmem = vm.memory
1125 try:
1126 maxmem = int(maxmem)
1127 except:
1128 raise VmError("invalid maxmem: " + str(maxmem))
1129 xc.domain_setmaxmem(vm.id, maxmem_kb = maxmem * 1024)
1132 #============================================================================
1133 # Register image handlers.
1135 from image import \
1136 addImageHandlerClass, \
1137 ImageHandler, \
1138 LinuxImageHandler, \
1139 VmxImageHandler
1141 addImageHandlerClass(LinuxImageHandler)
1142 addImageHandlerClass(VmxImageHandler)
1145 """Table of handlers for field configuration.
1147 field_name[String]: fn(vm, config, field, index) -> value(ignored)
1148 """
1149 config_handlers = {
1151 # Ignore the fields we already handle.
1153 'name': vm_field_ignore,
1154 'memory': vm_field_ignore,
1155 'ssidref': vm_field_ignore,
1156 'cpu': vm_field_ignore,
1157 'cpu_weight': vm_field_ignore,
1158 'restart': vm_field_ignore,
1159 'image': vm_field_ignore,
1160 'device': vm_field_ignore,
1161 'backend': vm_field_ignore,
1162 'vcpus': vm_field_ignore,
1163 'bootloader': vm_field_ignore,
1165 # Register other config handlers.
1166 'maxmem': vm_field_maxmem
1170 #============================================================================
1171 # Register device controllers and their device config types.
1173 controller.addDevControllerClass("vbd", server.blkif.BlkifController)
1174 controller.addDevControllerClass("vif", server.netif.NetifController)
1175 controller.addDevControllerClass("vtpm", server.tpmif.TPMifController)
1176 controller.addDevControllerClass("pci", server.pciif.PciController)
1177 controller.addDevControllerClass("usb", server.usbif.UsbifController)