mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
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>
This commit is contained in:
1 parent
57c8cb2125
commit
42d8ee3480
12 files changed
+420
-190
No files matched your search
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
# 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 cleanup: %s", ex)
|
||||
pass
|
||||
try:
|
||||
self.destroy_domain()
|
||||
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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||