diff options
author | cl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk> | 2005-06-09 09:01:57 +0000 |
---|---|---|
committer | cl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk> | 2005-06-09 09:01:57 +0000 |
commit | 42d8ee3480d321fd2dfff4b8393152b94236bbfd (patch) | |
tree | f08d4c2a8c7e3ccde89dcb38194628200de7ce71 /tools | |
parent | 57c8cb2125af4c1db6381529b38cae3561b0436a (diff) | |
download | xen-42d8ee3480d321fd2dfff4b8393152b94236bbfd.tar.gz xen-42d8ee3480d321fd2dfff4b8393152b94236bbfd.tar.bz2 xen-42d8ee3480d321fd2dfff4b8393152b94236bbfd.zip |
bitkeeper revision 1.1662.1.21 (42a80585xAt7ZrRcqmCRm3HvTrL7Mg)
Many files:
Switch to xenstore for storing persistent information.
Signed-off-by: Mike Wray <mike.wray@hp.com>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/python/xen/xend/XendDomain.py | 83 | ||||
-rw-r--r-- | tools/python/xen/xend/XendDomainInfo.py | 212 | ||||
-rw-r--r-- | tools/python/xen/xend/XendRoot.py | 10 | ||||
-rw-r--r-- | tools/python/xen/xend/XendVnet.py | 22 | ||||
-rw-r--r-- | tools/python/xen/xend/image.py | 40 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/blkif.py | 23 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/channel.py | 79 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/console.py | 7 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/controller.py | 71 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/netif.py | 52 | ||||
-rw-r--r-- | tools/python/xen/xend/server/usbif.py | 6 | ||||
-rw-r--r-- | tools/python/xen/xend/xenstore/xsobj.py | 5 |
12 files changed, 420 insertions, 190 deletions
diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 5819a9e4bf..11f434a46d 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -23,7 +23,8 @@ from xen.xend.XendLogging import log from xen.xend import scheduler from xen.xend.server import channel from xen.xend.server import relocate -from xen.xend import XendDB +from xen.xend.uuid import getUuid +from xen.xend.xenstore import XenNode, DBMap __all__ = [ "XendDomain" ] @@ -40,9 +41,6 @@ class XendDomain: """Index of all domains. Singleton. """ - """Path to domain database.""" - dbpath = "domain" - """Dict of domain info indexed by domain id.""" domains = None @@ -53,7 +51,7 @@ class XendDomain: # So we stuff the XendDomain instance (self) into xroot's components. xroot.add_component("xen.xend.XendDomain", self) self.domains = XendDomainDict() - self.db = XendDB.XendDB(self.dbpath) + self.dbmap = DBMap(db=XenNode("/domain")) eserver.subscribe('xend.virq', self.onVirq) self.initial_refresh() @@ -96,11 +94,18 @@ class XendDomain: """Refresh initial domain info from db. """ doms = self.xen_domains() - for config in self.db.fetchall("").values(): - domid = int(sxp.child_value(config, 'id')) - if domid in doms: + self.dbmap.readDB() + for domdb in self.dbmap.values(): + try: + domid = int(domdb.id) + except: + domid = None + # XXX if domid in self.domains, then something went wrong + if (domid is None) or (domid in self.domains): + domdb.delete() + elif domid in doms: try: - self._new_domain(config, doms[domid]) + self._new_domain(domdb, doms[domid]) except Exception, ex: log.exception("Error recreating domain info: id=%d", domid) self._delete_domain(domid) @@ -108,27 +113,20 @@ class XendDomain: self._delete_domain(domid) self.refresh(cleanup=True) - def sync_domain(self, info): - """Sync info for a domain to disk. - - info domain info - """ - self.db.save(str(info.id), info.sxpr()) - def close(self): pass - def _new_domain(self, savedinfo, info): + def _new_domain(self, db, info): """Create a domain entry from saved info. - @param savedinfo: saved info from the db + @param db: saved info from the db @param info: domain info from xen @return: domain """ - uuid = sxp.child_value(savedinfo, 'uuid') - dominfo = XendDomainInfo.recreate(savedinfo, info, uuid) + log.error(db) + log.error(db.uuid) + dominfo = XendDomainInfo.recreate(db, info) self.domains[dominfo.id] = dominfo - self.sync_domain(dominfo) return dominfo def _add_domain(self, info, notify=True): @@ -141,11 +139,11 @@ class XendDomain: for i, d in self.domains.items(): if i != d.id: del self.domains[i] - self.db.delete(str(i)) + self.dbmap.delete(d.uuid) if info.id in self.domains: notify = False self.domains[info.id] = info - self.sync_domain(info) + info.exportToDB(save=True) if notify: eserver.inject('xend.domain.create', [info.name, info.id]) @@ -155,12 +153,26 @@ class XendDomain: @param id: domain id @param notify: send a domain died event if true """ + try: + if self.xen_domain(id): + return + except: + pass info = self.domains.get(id) if info: del self.domains[id] - self.db.delete(str(id)) + info.cleanup() + info.delete() if notify: eserver.inject('xend.domain.died', [info.name, info.id]) + # XXX this should not be needed + for domdb in self.dbmap.values(): + try: + domid = int(domdb.id) + except: + domid = None + if (domid is None) or (domid == id): + domdb.delete() def reap(self): """Look for domains that have crashed or stopped. @@ -263,8 +275,7 @@ class XendDomain: @param config: configuration @return: domain """ - dominfo = XendDomainInfo.create(config) - self._add_domain(dominfo) + dominfo = XendDomainInfo.create(self.dbmap, config) return dominfo def domain_restart(self, dominfo): @@ -277,7 +288,6 @@ class XendDomain: [dominfo.name, dominfo.id, "begin"]) try: dominfo.restart() - self._add_domain(dominfo) log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id) eserver.inject("xend.domain.restart", [dominfo.name, dominfo.id, "success"]) @@ -297,8 +307,7 @@ class XendDomain: """ config = sxp.child_value(vmconfig, 'config') uuid = sxp.child_value(vmconfig, 'uuid') - dominfo = XendDomainInfo.restore(config, uuid=uuid) - self._add_domain(dominfo) + dominfo = XendDomainInfo.restore(self.dbmap, config, uuid=uuid) return dominfo def domain_restore(self, src, progress=False): @@ -330,8 +339,12 @@ class XendDomain: try: info = self.xen_domain(id) if info: - log.info("Creating entry for unknown domain: id=%d", id) - dominfo = XendDomainInfo.recreate(None, info) + uuid = getUuid() + log.info( + "Creating entry for unknown domain: id=%d uuid=%s", + id, uuid) + db = self.dbmap.addChild(uuid) + dominfo = XendDomainInfo.recreate(db, info) self._add_domain(dominfo) except Exception, ex: log.exception("Error creating domain info: id=%d", id) @@ -593,7 +606,7 @@ class XendDomain: """ dominfo = self.domain_lookup(id) val = dominfo.device_create(devconfig) - self.sync_domain(dominfo) + dominfo.exportToDB() return val def domain_device_configure(self, id, devconfig, devid): @@ -606,7 +619,7 @@ class XendDomain: """ dominfo = self.domain_lookup(id) val = dominfo.device_configure(devconfig, devid) - self.sync_domain(dominfo) + dominfo.exportToDB() return val def domain_device_refresh(self, id, type, devid): @@ -618,7 +631,7 @@ class XendDomain: """ dominfo = self.domain_lookup(id) val = dominfo.device_refresh(type, devid) - self.sync_domain(dominfo) + dominfo.exportToDB() return val def domain_device_destroy(self, id, type, devid): @@ -630,7 +643,7 @@ class XendDomain: """ dominfo = self.domain_lookup(id) val = dominfo.device_destroy(type, devid) - self.sync_domain(dominfo) + dominfo.exportToDB() return val def domain_devtype_ls(self, id, type): diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 97d6dbbf31..16415d78a7 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -30,6 +30,7 @@ from XendError import XendError, VmError from xen.xend.XendRoot import get_component from xen.xend.uuid import getUuid +from xen.xend.xenstore import DBVar """Flag for a block device backend domain.""" SIF_BLK_BE_DOMAIN = (1<<4) @@ -145,94 +146,92 @@ class XendDomainInfo: """ MINIMUM_RESTART_TIME = 20 - def _create(cls, uuid=None): - """Create a vm object with a uuid. - - @param uuid uuid to use - @return vm - """ - if uuid is None: - uuid = getUuid() - vm = cls() - vm.uuid = uuid - return vm - - _create = classmethod(_create) - - def create(cls, config): + def create(cls, parentdb, config): """Create a VM from a configuration. - If a vm has been partially created and there is an error it - is destroyed. + @param parentdb: parent db @param config configuration @raise: VmError for invalid configuration """ - vm = cls._create() + uuid = getUuid() + db = parentdb.addChild(uuid) + vm = cls(db) vm.construct(config) + vm.saveDB(sync=True) return vm create = classmethod(create) - def recreate(cls, savedinfo, info, uuid=None): + def recreate(cls, db, info): """Create the VM object for an existing domain. - @param savedinfo: saved info from the domain DB + @param db: domain db @param info: domain info from xc - @param uuid: uuid to use - @type info: xc domain dict """ - vm = cls._create(uuid=uuid) - - log.debug('savedinfo=' + prettyprintstring(savedinfo)) + dom = info['dom'] + vm = cls(db) + db.readDB() + vm.importFromDB() + config = vm.config log.debug('info=' + str(info)) + log.debug('config=' + prettyprintstring(config)) - vm.recreate = True - vm.savedinfo = savedinfo - vm.setdom(info['dom']) + vm.setdom(dom) vm.memory = info['mem_kb']/1024 - start_time = sxp.child_value(savedinfo, 'start_time') - if start_time is not None: - vm.start_time = float(start_time) - vm.restart_state = sxp.child_value(savedinfo, 'restart_state') - vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0)) - restart_time = sxp.child_value(savedinfo, 'restart_time') - if restart_time is not None: - vm.restart_time = float(restart_time) - config = sxp.child_value(savedinfo, 'config') - if config: - vm.construct(config) + try: + vm.recreate = True + vm.construct(config) + finally: + vm.recreate = False else: - vm.setName(sxp.child_value(savedinfo, 'name', - "Domain-%d" % info['dom'])) - vm.recreate = False - vm.savedinfo = None + vm.setName("Domain-%d" % dom) + vm.exportToDB(save=True) return vm recreate = classmethod(recreate) - def restore(cls, config, uuid=None): + def restore(cls, parentdb, config, uuid=None): """Create a domain and a VM object to do a restore. + @param parentdb: parent db @param config: domain configuration @param uuid: uuid to use """ - vm = cls._create(uuid=uuid) + db = parentdb.addChild(uuid) + vm = cls(db) dom = xc.domain_create() vm.setdom(dom) vm.dom_construct(vm.id, config) + vm.saveDB(sync=True) return vm restore = classmethod(restore) - def __init__(self): + __exports__ = [ + DBVar('id', ty='str'), + DBVar('name', ty='str'), + DBVar('uuid', ty='str'), + DBVar('config', ty='sxpr'), + DBVar('start_time', ty='float'), + DBVar('state', ty='str'), + DBVar('store_mfn', ty='long'), + DBVar('restart_mode', ty='str'), + DBVar('restart_state', ty='str'), + DBVar('restart_time', ty='float'), + DBVar('restart_count', ty='int'), + ] + + def __init__(self, db): + self.db = db + self.uuid = db.getName() + self.recreate = 0 self.restore = 0 self.config = None - self.uuid = None self.id = None self.cpu_weight = 1 self.start_time = None @@ -262,23 +261,39 @@ class XendDomainInfo: self.restart_count = 0 self.console_port = None - self.savedinfo = None self.vcpus = 1 self.bootloader = None + def setDB(self, db): + self.db = db + + def saveDB(self, save=False, sync=False): + self.db.saveDB(save=save, sync=sync) + + def exportToDB(self, save=False, sync=False): + if self.channel: + self.channel.saveToDB(self.db.addChild("channel")) + if self.store_channel: + self.store_channel.saveToDB(self.db.addChild("store_channel")) + self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync) + + def importFromDB(self): + self.db.importFromDB(self, fields=self.__exports__) + def setdom(self, dom): """Set the domain id. @param dom: domain id """ self.id = int(dom) + #self.db.id = self.id def getDomain(self): return self.id def setName(self, name): self.name = name - #self.db.name = self.name + self.db.name = self.name def getName(self): return self.name @@ -301,6 +316,7 @@ class XendDomainInfo: self.state = state self.state_updated.notifyAll() self.state_updated.release() + self.saveDB() def state_wait(self, state): self.state_updated.acquire() @@ -484,6 +500,7 @@ class XendDomainInfo: self.configure_restart() self.construct_image() self.configure() + self.exportToDB() except Exception, ex: # Catch errors, cleanup and re-raise. print 'Domain construction error:', ex @@ -495,6 +512,7 @@ class XendDomainInfo: def register_domain(self): xd = get_component('xen.xend.XendDomain') xd._add_domain(self) + self.exportToDB() def configure_cpus(self, config): try: @@ -528,42 +546,26 @@ class XendDomainInfo: """ self.create_channel() self.image.createImage() - #self.image.exportToDB() + self.image.exportToDB() #if self.store_channel: # self.db.introduceDomain(self.id, # self.store_mfn, # self.store_channel) - def get_device_savedinfo(self, type, id): - val = None - if self.savedinfo is None: - return val - devices = sxp.child(self.savedinfo, 'devices') - if devices is None: - return val - for d in sxp.children(devices, type): - did = sxp.child_value(d, 'id') - if did is None: continue - if int(did) == id: - val = d - break - return val - - def get_device_recreate(self, type, id): - return self.get_device_savedinfo(type, id) or self.recreate - - def destroy(self): - """Completely destroy the vm. + def delete(self): + """Delete the vm's db. """ + if self.dom_get(self.id): + return + self.id = None + self.saveDB(sync=True) try: - self.cleanup() - except Exception, ex: - log.warning("error in domain cleanup: %s", ex) - pass - try: - self.destroy_domain() + # Todo: eventually will have to wait for devices to signal + # destruction before can delete the db. + if self.db: + self.db.delete() except Exception, ex: - log.warning("error in domain destroy: %s", ex) + log.warning("error in domain db delete: %s", ex) pass def destroy_domain(self): @@ -571,6 +573,18 @@ class XendDomainInfo: The domain will not finally go away unless all vm devices have been released. """ + if self.id is None: + return + try: + xc.domain_destroy(dom=self.id) + except Exception, err: + log.exception("Domain destroy failed: %s", self.name) + + def cleanup(self): + """Cleanup vm resources: release devices. + """ + self.state = STATE_VM_TERMINATED + self.release_devices() if self.channel: try: self.channel.close() @@ -583,23 +597,25 @@ class XendDomainInfo: self.store_channel = None except: pass + #try: + # self.db.releaseDomain(self.id) + #except Exception, ex: + # log.warning("error in domain release on xenstore: %s", ex) + # pass if self.image: try: self.image.destroy() self.image = None except: pass - if self.id is None: return 0 - try: - xc.domain_destroy(dom=self.id) - except Exception, err: - log.exception("Domain destroy failed: %s", self.name) - def cleanup(self): - """Cleanup vm resources: release devices. + def destroy(self): + """Clenup vm and destroy domain. """ - self.state = STATE_VM_TERMINATED - self.release_devices() + self.cleanup() + self.destroy_domain() + self.saveDB() + return 0 def is_terminated(self): """Check if a domain has been terminated. @@ -639,27 +655,24 @@ class XendDomainInfo: if not self.restore: self.setdom(dom) - def openChannel(self, name, local, remote): + def openChannel(self, key, local, remote): """Create a channel to the domain. If saved info is available recreate the channel. + @param key db key for the saved data (if any) @param local default local port @param remote default remote port """ - local = 0 - remote = 1 - if self.savedinfo: - info = sxp.child(self.savedinfo, name) - if info: - local = int(sxp.child_value(info, "local_port", 0)) - remote = int(sxp.child_value(info, "remote_port", 1)) - chan = channelFactory().openChannel(self.id, local_port=local, - remote_port=remote) + db = self.db.addChild(key) + chan = channelFactory().restoreFromDB(db, self.id, local, remote) + #todo: save here? + #chan.saveToDB(db) return chan - def eventChannel(self, name): - return EventChannel.interdomain(0, self.id) - + def eventChannel(self, key): + db = self.db.addChild(key) + return EventChannel.restoreFromDB(db, 0, self.id) + def create_channel(self): """Create the channels to the domain. """ @@ -823,6 +836,7 @@ class XendDomainInfo: if self.bootloader: self.config = self.bootloader_config() self.construct(self.config) + self.saveDB() finally: self.restart_state = None diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py index 10e560fbcc..045a5a5fa4 100644 --- a/tools/python/xen/xend/XendRoot.py +++ b/tools/python/xen/xend/XendRoot.py @@ -25,9 +25,6 @@ import sxp class XendRoot: """Root of the management classes.""" - """Default path to the root of the database.""" - dbroot_default = "/var/lib/xen/xend-db" - """Default path to the config file.""" config_default = "/etc/xen/xend-config.sxp" @@ -82,7 +79,6 @@ class XendRoot: components = {} def __init__(self): - self.dbroot = None self.config_path = None self.config = None self.logging = None @@ -171,7 +167,6 @@ class XendRoot: def configure(self): self.set_config() self.configure_logger() - self.dbroot = self.get_config_value("dbroot", self.dbroot_default) def configure_logger(self): logfile = self.get_config_value("logfile", self.logfile_default) @@ -192,11 +187,6 @@ class XendRoot: """ return self.logging and self.logging.getLogger() - def get_dbroot(self): - """Get the path to the database root. - """ - return self.dbroot - def set_config(self): """If the config file exists, read it. If not, ignore it. diff --git a/tools/python/xen/xend/XendVnet.py b/tools/python/xen/xend/XendVnet.py index d95fd204aa..3614127c49 100644 --- a/tools/python/xen/xend/XendVnet.py +++ b/tools/python/xen/xend/XendVnet.py @@ -4,11 +4,10 @@ """ from xen.util import Brctl - -import sxp -import XendDB -from XendError import XendError -from XendLogging import log +from xen.xend import sxp +from xen.xend.XendError import XendError +from xen.xend.XendLogging import log +from xen.xend.xenstore import XenNode, DBMap def vnet_cmd(cmd): out = None @@ -63,14 +62,15 @@ class XendVnet: """Index of all vnets. Singleton. """ - dbpath = "vnet" + dbpath = "/vnet" def __init__(self): # Table of vnet info indexed by vnet id. self.vnet = {} - self.db = XendDB.XendDB(self.dbpath) - vnets = self.db.fetchall("") - for config in vnets.values(): + self.dbmap = DBMap(db=XenNode(self.dbpath)) + self.dbmap.readDB() + for vnetdb in self.dbmap.values(): + config = vnetdb.config info = XendVnetInfo(config) self.vnet[info.id] = info try: @@ -115,7 +115,7 @@ class XendVnet: """ info = XendVnetInfo(config) self.vnet[info.id] = info - self.db.save(info.id, info.sxpr()) + self.dbmap["%s/config" % info.id] = info.sxpr() info.configure() def vnet_delete(self, id): @@ -126,7 +126,7 @@ class XendVnet: info = self.vnet_get(id) if info: del self.vnet[id] - self.db.delete(id) + self.dbmap.delete(id) info.delete() def instance(): diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index d7f6965767..e0d70581bf 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -4,7 +4,7 @@ import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() from xen.xend import sxp from xen.xend.XendError import VmError from xen.xend.XendLogging import log -#from xen.xend.xenstore import DBVar +from xen.xend.xenstore import DBVar class ImageHandler: """Abstract base class for image handlers. @@ -73,7 +73,7 @@ class ImageHandler: #====================================================================== # Instance vars and methods. - #db = None + db = None ostype = None config = None @@ -82,25 +82,25 @@ class ImageHandler: cmdline = None flags = 0 - #__exports__ = [ - # DBVar('ostype', ty='str'), - # DBVar('config', ty='sxpr'), - # DBVar('kernel', ty='str'), - # DBVar('ramdisk', ty='str'), - # DBVar('cmdline', ty='str'), - # DBVar('flags', ty='int'), - # ] + __exports__ = [ + DBVar('ostype', ty='str'), + DBVar('config', ty='sxpr'), + DBVar('kernel', ty='str'), + DBVar('ramdisk', ty='str'), + DBVar('cmdline', ty='str'), + DBVar('flags', ty='int'), + ] def __init__(self, vm, config): self.vm = vm - #self.db = vm.db.addChild('/image') + self.db = vm.db.addChild('/image') self.config = config - #def exportToDB(self, save=False): - # self.db.exportToDB(self, fields=self.__exports__, save=save) + def exportToDB(self, save=False): + self.db.exportToDB(self, fields=self.__exports__, save=save) - #def importFromDB(self): - # self.db.importFromDB(self, fields=self.__exports__) + def importFromDB(self): + self.db.importFromDB(self, fields=self.__exports__) def unlink(self, f): if not f: return @@ -234,11 +234,11 @@ class Plan9ImageHandler(ImageHandler): class VmxImageHandler(ImageHandler): - #__exports__ = ImageHandler.__exports__ + [ - # DBVar('memmap', ty='str'), - # DBVar('memmap_value', ty='sxpr'), - # # device channel? - # ] + __exports__ = ImageHandler.__exports__ + [ + DBVar('memmap', ty='str'), + DBVar('memmap_value', ty='sxpr'), + # device channel? + ] ostype = "vmx" memmap = None diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 602979fed0..75a76e8bda 100755 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -9,6 +9,7 @@ from xen.xend.XendRoot import get_component from xen.xend.XendLogging import log from xen.xend import sxp from xen.xend import Blkctl +from xen.xend.xenstore import DBVar from xen.xend.server import channel from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController @@ -158,6 +159,18 @@ class BlkDev(Dev): """Info record for a block device. """ + __exports__ = Dev.__exports__ + [ + DBVar('dev', ty='str'), + DBVar('vdev', ty='int'), + DBVar('mode', ty='str'), + DBVar('viftype', ty='str'), + DBVar('params', ty='str'), + DBVar('node', ty='str'), + DBVar('device', ty='long'), + DBVar('start_sector', ty='long'), + DBVar('nr_sectors', ty='long'), + ] + def __init__(self, controller, id, config, recreate=False): Dev.__init__(self, controller, id, config, recreate=recreate) self.dev = None @@ -215,8 +228,7 @@ class BlkDev(Dev): def attach(self, recreate=False, change=False): if recreate: - node = sxp.child_value(recreate, 'node') - self.setNode(node) + pass else: node = Blkctl.block('bind', self.type, self.params) self.setNode(node) @@ -299,7 +311,8 @@ class BlkDev(Dev): return self.controller.getBackend(self.backendDomain) def refresh(self): - log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, self.id) + log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, + self.id) self.interfaceChanged() def destroy(self, change=False, reboot=False): @@ -308,7 +321,8 @@ class BlkDev(Dev): @param change: change flag """ self.destroyed = True - log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, self.id) + log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, + self.id) self.send_be_vbd_destroy() if change: self.interfaceChanged() @@ -445,5 +459,4 @@ class BlkifController(DevController): log.error("Exception connecting backend: %s", ex) else: log.error('interface connect on unknown interface: id=%d', id) - diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py index e5b82b5330..00f451a7b8 100755 --- a/tools/python/xen/xend/server/channel.py +++ b/tools/python/xen/xend/server/channel.py @@ -31,6 +31,33 @@ class EventChannel(dict): interdomain = classmethod(interdomain) + def restoreFromDB(cls, db, dom1, dom2, port1=0, port2=0): + """Create an event channel using db info if available. + Inverse to saveToDB(). + + @param db db + @param dom1 + @param dom2 + @param port1 + @param port2 + """ + try: + dom1 = int(db['dom1']) + except: pass + try: + dom2 = int(db['dom2']) + except: pass + try: + port1 = int(db['port1']) + except: pass + try: + port2 = int(db['port2']) + except: pass + evtchn = cls.interdomain(dom1, dom2, port1=port1, port2=port2) + return evtchn + + restoreFromDB = classmethod(restoreFromDB) + def __init__(self, dom1, dom2, d): d['dom1'] = dom1 d['dom2'] = dom2 @@ -54,6 +81,18 @@ class EventChannel(dict): evtchn_close(self.dom1, self.port1) evtchn_close(self.dom2, self.port2) + def saveToDB(self, db): + """Save the event channel to the db so it can be restored later, + using restoreFromDB() on the class. + + @param db db + """ + db['dom1'] = str(self.dom1) + db['dom2'] = str(self.dom2) + db['port1'] = str(self.port1) + db['port2'] = str(self.port2) + db.saveDB() + def sxpr(self): return ['event-channel', ['dom1', self.dom1 ], @@ -63,7 +102,7 @@ class EventChannel(dict): ] def __repr__(self): - return ("<EventChannel dom1:%s:%s dom2:%s:%s>" + return ("<EventChannel dom1:%d:%d dom2:%d:%d>" % (self.dom1, self.port1, self.dom2, self.port2)) def eventChannel(dom1, dom2, port1=0, port2=0): @@ -241,8 +280,31 @@ class ChannelFactory: @type remote: int @return: port object """ - return xu.port(dom, local_port=int(local_port), - remote_port=int(remote_port)) + return xu.port(dom, local_port=local_port, remote_port=remote_port) + + def restoreFromDB(self, db, dom, local, remote): + """Create a channel using ports restored from the db (if available). + Otherwise use the given ports. This is the inverse operation to + saveToDB() on a channel. + + @param db db + @param dom domain the channel connects to + @param local default local port + @param remote default remote port + """ + try: + local_port = int(db['local_port']) + except: + local_port = local + try: + remote_port = int(db['remote_port']) + except: + remote_port = remote + try: + chan = self.openChannel(dom, local_port, remote_port) + except: + return None + return chan def channelFactory(): """Singleton constructor for the channel factory. @@ -277,6 +339,17 @@ class Channel: # Make sure the port will deliver all the messages. self.port.register(TYPE_WILDCARD) + def saveToDB(self, db): + """Save the channel ports to the db so the channel can be restored later, + using restoreFromDB() on the factory. + + @param db db + """ + if self.closed: return + db['local_port'] = str(self.getLocalPort()) + db['remote_port'] = str(self.getRemotePort()) + db.saveDB() + def getKey(self): """Get the channel key. """ diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py index 1d0489de7f..743ace4aec 100755 --- a/tools/python/xen/xend/server/console.py +++ b/tools/python/xen/xend/server/console.py @@ -13,6 +13,7 @@ from xen.xend import EventServer; eserver = EventServer.instance() from xen.xend.XendLogging import log from xen.xend import XendRoot; xroot = XendRoot.instance() from xen.xend import sxp +from xen.xend.xenstore import DBVar from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController from xen.xend.server.messages import * @@ -76,6 +77,12 @@ class ConsoleDev(Dev, protocol.ServerFactory): STATUS_CONNECTED = 'connected' STATUS_LISTENING = 'listening' + __exports__ = Dev.__exports__ + [ + DBVar('status', ty='str'), + #DBVar('listening', ty='str'), + DBVar('console_port', ty='int'), + ] + def __init__(self, controller, id, config, recreate=False): Dev.__init__(self, controller, id, config) self.lock = threading.RLock() diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py index 5f99545fb9..d1e19efee1 100755 --- a/tools/python/xen/xend/server/controller.py +++ b/tools/python/xen/xend/server/controller.py @@ -4,6 +4,7 @@ for a domain. """ from xen.xend.XendError import XendError +from xen.xend.xenstore import DBVar from xen.xend.server.messages import msgTypeName, printMsg, getMessageType DEBUG = 0 @@ -155,11 +156,16 @@ class DevController: """ + # State: + # controller/<type> : for controller + # device/<type>/<id> : for each device + def createDevController(cls, vm, recreate=False): """Class method to create a dev controller. """ ctrl = cls(vm, recreate=recreate) ctrl.initController(recreate=recreate) + ctrl.exportToDB() return ctrl createDevController = classmethod(createDevController) @@ -169,16 +175,38 @@ class DevController: getType = classmethod(getType) + __exports__ = [ + DBVar('type', 'str'), + DBVar('destroyed', 'bool'), + ] + # Set when registered. type = None def __init__(self, vm, recreate=False): self.destroyed = False self.vm = vm + self.db = self.getDB() self.deviceId = 0 self.devices = {} self.device_order = [] + def getDB(self): + """Get the db node to use for a controller. + """ + return self.vm.db.addChild("/controller/%s" % self.getType()) + + def getDevDB(self, id): + """Get the db node to use for a device. + """ + return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id)) + + def exportToDB(self, save=False): + self.db.exportToDB(self, fields=self.__exports__, save=save) + + def importFromDB(self): + self.db.importFromDB(self, fields=self.__exports__) + def getDevControllerType(self): return self.dctype @@ -229,15 +257,15 @@ class DevController: If change is true the device is a change to an existing domain, i.e. it is being added at runtime rather than when the domain is created. """ - # skanky hack: we use the device ids to maybe find the savedinfo - # of the device... - id = self.nextDeviceId() - if recreate: - recreate = self.vm.get_device_savedinfo(self.getType(), id) - dev = self.newDevice(id, config, recreate=recreate) + dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate) + if self.vm.recreate: + dev.importFromDB() dev.init(recreate=recreate) self.addDevice(dev) + if not recreate: + dev.exportToDB() dev.attach(recreate=recreate, change=change) + dev.exportToDB() def configureDevice(self, id, config, change=False): """Reconfigure an existing device. @@ -344,11 +372,42 @@ class Dev: @type controller: DevController """ + # ./status : need 2: actual and requested? + # down-down: initial. + # up-up: fully up. + # down-up: down requested, still up. Watch front and back, when both + # down go to down-down. But what if one (or both) is not connected? + # Still have front/back trees with status? Watch front/status, back/status? + # up-down: up requested, still down. + # Back-end watches ./status, front/status + # Front-end watches ./status, back/status + # i.e. each watches the other 2. + # Each is status/request status/actual? + # + # backend? + # frontend? + + __exports__ = [ + DBVar('id', ty='int'), + DBVar('type', ty='str'), + DBVar('config', ty='sxpr'), + DBVar('destroyed', ty='bool'), + ] + def __init__(self, controller, id, config, recreate=False): self.controller = controller self.id = id self.config = config self.destroyed = False + self.type = self.getType() + + self.db = controller.getDevDB(id) + + def exportToDB(self, save=False): + self.db.exportToDB(self, fields=self.__exports__, save=save) + + def importFromDB(self): + self.db.importFromDB(self, fields=self.__exports__) def getDomain(self): return self.controller.getDomain() diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index 157490f5a2..0a49842522 100755 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -12,6 +12,7 @@ from xen.xend.XendError import XendError, VmError from xen.xend.XendLogging import log from xen.xend import XendVnet from xen.xend.XendRoot import get_component +from xen.xend.xenstore import DBVar from xen.xend.server import channel from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController @@ -21,6 +22,57 @@ class NetDev(Dev): """A network device. """ + # State: + # inherited + + # ./config + # ./mac + # ./be_mac + # ./bridge + # ./script + # ./ipaddr ? + # + # ./credit + # ./period + # + # ./vifctl: up/down? + # ./vifname + # + # + # Poss should have no backend state here - except for ref to backend's own tree + # for the device? And a status - the one we want. + # ./back/dom + # ./back/devid - id for back-end (netif_handle) - same as front/devid + # ./back/id - backend id (if more than one b/e per domain) + # ./back/status + # ./back/tx_shmem_frame - actually these belong in back-end state + # ./back/rx_shmem_frame + # + # ./front/dom + # ./front/devid + # ./front/status - need 2: one for requested, one for actual? Or drive from dev status + # and this is front status only. + # ./front/tx_shmem_frame + # ./front/rx_shmem_frame + # + # ./evtchn/front - here or in front/back? + # ./evtchn/back + # ./evtchn/status ? + # At present created by dev: but should be created unbound by front/back + # separately and then bound (by back)? + + __exports__ = Dev.__exports__ + [ + DBVar('config', ty='sxpr'), + DBVar('mac', ty='mac'), + DBVar('be_mac', ty='mac'), + DBVar('bridge', ty='str'), + DBVar('script', ty='str'), + #DBVar('ipaddr'), + DBVar('credit', ty='int'), + DBVar('period', ty='int'), + DBVar('vifname', ty='str'), + DBVar('evtchn'), #todo: export fields (renamed) + ] + def __init__(self, controller, id, config, recreate=False): Dev.__init__(self, controller, id, config, recreate=recreate) self.vif = int(self.id) diff --git a/tools/python/xen/xend/server/usbif.py b/tools/python/xen/xend/server/usbif.py index 657a98e671..d366985740 100644 --- a/tools/python/xen/xend/server/usbif.py +++ b/tools/python/xen/xend/server/usbif.py @@ -7,6 +7,7 @@ from xen.xend import sxp from xen.xend.XendLogging import log from xen.xend.XendError import XendError +from xen.xend.xenstore import DBVar from xen.xend.server import channel from xen.xend.server.controller import Dev, DevController @@ -141,6 +142,11 @@ class UsbBackend: class UsbDev(Dev): + + __exports__ = Dev.__exports__ + [ + DBVar('port', ty='int'), + DBVar('path', ty='str'), + ] def __init__(self, controller, id, config, recreate=False): Dev.__init__(self, controller, id, config, recreate=recreate) diff --git a/tools/python/xen/xend/xenstore/xsobj.py b/tools/python/xen/xend/xenstore/xsobj.py index 62e18d07d9..b1c9a4f1d1 100644 --- a/tools/python/xen/xend/xenstore/xsobj.py +++ b/tools/python/xen/xend/xenstore/xsobj.py @@ -201,7 +201,10 @@ class DBVar: setAttr(o, self.attr, val) def getDB(self, db): - data = getattr(db, self.path) + try: + data = getattr(db, self.path) + except AttributeError: + return None return DBConverter.convertFromDB(data, ty=self.ty) def setDB(self, db, val): |