mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
Move XendVnet from using DBMap etc to using xstransact. This is the last
dependency upon xsnode, xsobj, and xsresource, so these can all go. Signed-off-by: Ewan Mellor <ewan@xensource.com>
This commit is contained in:
1 parent
ec7bb10f8e
commit
c8a89eae6b
5 files changed
+29
-1192
No files matched your search
@@ -13,6 +13,7 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2005 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
"""Handler for vnet operations.
|
||||
@@ -22,7 +23,8 @@ from xen.util import Brctl
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.xenstore import XenNode, DBMap, DBVar
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
|
||||
|
||||
def vnet_cmd(cmd):
|
||||
out = None
|
||||
@@ -39,39 +41,40 @@ class XendVnetInfo:
|
||||
|
||||
vifctl_ops = {'up': 'vif.add', 'down': 'vif.del'}
|
||||
|
||||
__exports__ = [
|
||||
DBVar('id', ty='str'),
|
||||
DBVar('dbid', ty='str'),
|
||||
DBVar('config', ty='sxpr'),
|
||||
]
|
||||
|
||||
def __init__(self, db, config=None):
|
||||
def __init__(self, dbpath, config=None):
|
||||
if config:
|
||||
self.id = sxp.child_value(config, 'id')
|
||||
self.id = str(self.id)
|
||||
self.id = str(sxp.child_value(config, 'id'))
|
||||
self.dbid = self.id.replace(':', '-')
|
||||
self.db = db.addChild(self.dbid)
|
||||
self.dbpath = dbpath + '/' + self.dbid
|
||||
self.config = config
|
||||
else:
|
||||
self.db = db
|
||||
self.dbpath = dbpath
|
||||
self.importFromDB()
|
||||
config = self.config
|
||||
|
||||
self.bridge = sxp.child_value(config, 'bridge')
|
||||
self.bridge = sxp.child_value(self.config, 'bridge')
|
||||
if not self.bridge:
|
||||
self.bridge = "vnet%s" % self.id
|
||||
self.vnetif = sxp.child_value(config, 'vnetif')
|
||||
self.vnetif = sxp.child_value(self.config, 'vnetif')
|
||||
if not self.vnetif:
|
||||
self.vnetif = "vnif%s" % self.id
|
||||
|
||||
def saveToDB(self, save=False, sync=False):
|
||||
self.db.saveDB(save=save, sync=sync)
|
||||
|
||||
def exportToDB(self, save=False, sync=False):
|
||||
self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
|
||||
to_store = {
|
||||
'id' : self.id,
|
||||
'dbid' : self.dbid,
|
||||
'config' : sxp.to_string
|
||||
}
|
||||
xstransact.Write(self.dbpath, to_store)
|
||||
|
||||
|
||||
def importFromDB(self):
|
||||
self.db.importFromDB(self, fields=self.__exports__)
|
||||
(self.id, self.dbid, c) = xstransact.Gather(self.dbpath,
|
||||
('id', str),
|
||||
('dbid', str),
|
||||
('config', str))
|
||||
self.config = sxp.from_string(c)
|
||||
|
||||
|
||||
def sxpr(self):
|
||||
return self.config
|
||||
@@ -88,7 +91,7 @@ class XendVnetInfo:
|
||||
Brctl.vif_bridge_rem({'bridge': self.bridge, 'vif': self.vnetif})
|
||||
Brctl.bridge_del(self.bridge)
|
||||
val = vnet_cmd(['vnet.del', self.id])
|
||||
self.db.delete()
|
||||
xstransact.Remove(self.dbpath)
|
||||
return val
|
||||
|
||||
def vifctl(self, op, vif, vmac):
|
||||
@@ -107,18 +110,17 @@ class XendVnet:
|
||||
def __init__(self):
|
||||
# Table of vnet info indexed by vnet id.
|
||||
self.vnet = {}
|
||||
self.db = DBMap(db=XenNode(self.dbpath))
|
||||
self.db.readDB()
|
||||
for vnetdb in self.db.values():
|
||||
listing = xstransact.List(self.dbpath)
|
||||
for entry in listing:
|
||||
try:
|
||||
info = XendVnetInfo(vnetdb)
|
||||
info = XendVnetInfo(self.dbpath + '/' + entry)
|
||||
self.vnet[info.id] = info
|
||||
info.configure()
|
||||
except XendError, ex:
|
||||
log.warning("Failed to configure vnet %s: %s", str(info.id), str(ex))
|
||||
except Exception, ex:
|
||||
log.exception("Vnet error")
|
||||
vnetdb.delete()
|
||||
xstransact.Remove(self.dbpath + '/' + entry)
|
||||
|
||||
def vnet_of_bridge(self, bridge):
|
||||
"""Get the vnet for a bridge (if any).
|
||||
@@ -155,9 +157,9 @@ class XendVnet:
|
||||
|
||||
@param config: config
|
||||
"""
|
||||
info = XendVnetInfo(self.db, config=config)
|
||||
info = XendVnetInfo(self.dbpath, config=config)
|
||||
self.vnet[info.id] = info
|
||||
info.saveToDB()
|
||||
info.exportToDB()
|
||||
info.configure()
|
||||
|
||||
def vnet_delete(self, id):
|
||||
|
||||
@@ -14,5 +14,3 @@
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
from xsnode import *
|
||||
from xsobj import *
|
||||
@@ -1,477 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
import errno
|
||||
import os
|
||||
import os.path
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from xen.lowlevel import xs
|
||||
from xen.xend import sxp
|
||||
from xen.xend.PrettyPrint import prettyprint
|
||||
|
||||
SELECT_TIMEOUT = 2.0
|
||||
|
||||
def getEventPath(event):
|
||||
if event and event.startswith("/"):
|
||||
event = event[1:]
|
||||
return os.path.join("/event", event)
|
||||
|
||||
def getEventIdPath(event):
|
||||
return os.path.join(getEventPath(event), "@eid")
|
||||
|
||||
class Subscription:
|
||||
|
||||
def __init__(self, path, fn, sid):
|
||||
self.path = path
|
||||
self.watcher = None
|
||||
self.fn = fn
|
||||
self.sid = sid
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def getSid(self):
|
||||
return self.sid
|
||||
|
||||
def watch(self, watcher):
|
||||
self.watcher = watcher
|
||||
watcher.addSubs(self)
|
||||
|
||||
def unwatch(self):
|
||||
watcher = self.watcher
|
||||
if watcher:
|
||||
self.watcher = None
|
||||
watcher.delSubs(self)
|
||||
return watcher
|
||||
|
||||
def notify(self, token, path, val):
|
||||
try:
|
||||
self.fn(self, token, path, val)
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
pass
|
||||
|
||||
class Watcher:
|
||||
|
||||
def __init__(self, store, path):
|
||||
self.path = path
|
||||
store.mkdirs(self.path)
|
||||
self.xs = None
|
||||
self.subscriptions = []
|
||||
|
||||
def fileno(self):
|
||||
if self.xs:
|
||||
return self.xs.fileno()
|
||||
else:
|
||||
return -1
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def getToken(self):
|
||||
return self.path
|
||||
|
||||
def addSubs(self, subs):
|
||||
self.subscriptions.append(subs)
|
||||
self.watch()
|
||||
|
||||
def delSubs(self, subs):
|
||||
self.subscriptions.remove(subs)
|
||||
if len(self.subscriptions) == 0:
|
||||
self.unwatch()
|
||||
|
||||
def watch(self):
|
||||
if self.xs: return
|
||||
self.xs = xs.open()
|
||||
self.xs.watch(path=self.getPath(), token=self.getToken())
|
||||
|
||||
def unwatch(self):
|
||||
if self.xs:
|
||||
## Possibly crashes xenstored.
|
||||
## try:
|
||||
## self.xs.unwatch(path=self.getPath(), token=self.getToken())
|
||||
## except Exception, ex:
|
||||
## print 'Watcher>unwatch>', ex
|
||||
try:
|
||||
self.xs.close()
|
||||
except Exception, ex:
|
||||
pass
|
||||
self.xs = None
|
||||
|
||||
def watching(self):
|
||||
return self.xs is not None
|
||||
|
||||
def getNotification(self):
|
||||
p = self.xs.read_watch()
|
||||
self.xs.acknowledge_watch(p[1])
|
||||
return p
|
||||
|
||||
def notify(self):
|
||||
try:
|
||||
(path, token) = self.getNotification()
|
||||
if path.endswith("@eid"):
|
||||
pass
|
||||
else:
|
||||
val = self.xs.read(path)
|
||||
for subs in self.subscriptions:
|
||||
subs.notify(token, path, val)
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
raise
|
||||
|
||||
class EventWatcher(Watcher):
|
||||
|
||||
def __init__(self, store, path, event):
|
||||
Watcher.__init__(self, store, path)
|
||||
self.event = event
|
||||
self.eidPath = getEventIdPath(event)
|
||||
if not store.exists(self.eidPath):
|
||||
store.write(self.eidPath, str(0))
|
||||
|
||||
def getEvent(self):
|
||||
return self.event
|
||||
|
||||
def getToken(self):
|
||||
return self.event
|
||||
|
||||
class XenStore:
|
||||
|
||||
xs = None
|
||||
watchThread = None
|
||||
subscription_id = 1
|
||||
|
||||
def __init__(self):
|
||||
self.subscriptions = {}
|
||||
self.watchers = {}
|
||||
self.write("/", "")
|
||||
|
||||
def getxs(self):
|
||||
if self.xs is None:
|
||||
ex = None
|
||||
for i in range(0,20):
|
||||
try:
|
||||
self.xs = xs.open()
|
||||
ex = None
|
||||
break
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception, ex:
|
||||
print >>sys.stderr, "Exception connecting to xenstored:", ex
|
||||
print >>sys.stderr, "Trying again..."
|
||||
time.sleep(1)
|
||||
else:
|
||||
raise ex
|
||||
|
||||
#todo would like to reconnect if xs conn closes (e.g. daemon restart).
|
||||
return self.xs
|
||||
|
||||
def dump(self, path="/", out=sys.stdout):
|
||||
print 'dump>', path
|
||||
val = ['node']
|
||||
val.append(['path', path])
|
||||
## perms = ['perms']
|
||||
## for p in self.getPerms(path):
|
||||
## l = ['perm']
|
||||
## l.append('dom', p.get['dom'])
|
||||
## for k in ['read', 'write', 'create', 'owner']:
|
||||
## v = p.get(k)
|
||||
## l.append([k, v])
|
||||
## perms.append(l)
|
||||
## val.append(perms)
|
||||
data = self.read(path)
|
||||
if data:
|
||||
val.append(['data', data])
|
||||
children = ['children']
|
||||
for x in self.lsPaths(path):
|
||||
print 'dump>', 'child=', x
|
||||
children.append(self.dump(x))
|
||||
if len(children) > 1:
|
||||
val.append(children)
|
||||
prettyprint(val, out=out)
|
||||
return val
|
||||
|
||||
def getPerms(self, path):
|
||||
return self.getxs().get_permissions(path)
|
||||
|
||||
def ls(self, path="/"):
|
||||
return self.getxs().ls(path)
|
||||
|
||||
def lsPaths(self, path="/"):
|
||||
return [ os.path.join(path, x) for x in self.ls(path) ]
|
||||
|
||||
def lsr(self, path="/", list=None):
|
||||
if list is None:
|
||||
list = []
|
||||
list.append(path)
|
||||
for x in self.lsPaths(path):
|
||||
list.append(x)
|
||||
self.lsr(x, list=list)
|
||||
return list
|
||||
|
||||
def rm(self, path):
|
||||
try:
|
||||
#for x in self.lsPaths():
|
||||
# self.getxs().rm(x)
|
||||
self.getxs().rm(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def exists(self, path):
|
||||
try:
|
||||
self.getxs().ls(path)
|
||||
return True
|
||||
except RuntimeError, ex:
|
||||
if ex.args[0] == errno.ENOENT:
|
||||
return False
|
||||
else:
|
||||
raise RuntimeError(ex.args[0],
|
||||
ex.args[1] +
|
||||
(', in exists(%s)' % (str(path))))
|
||||
|
||||
def mkdirs(self, path):
|
||||
if self.exists(path):
|
||||
return
|
||||
elts = path.split("/")
|
||||
p = "/"
|
||||
for x in elts:
|
||||
if x == "": continue
|
||||
p = os.path.join(p, x)
|
||||
if not self.exists(p):
|
||||
self.getxs().write(p, "")
|
||||
|
||||
def read(self, path):
|
||||
try:
|
||||
return self.getxs().read(path)
|
||||
except RuntimeError, ex:
|
||||
if ex.args[0] == errno.EISDIR:
|
||||
return None
|
||||
else:
|
||||
raise
|
||||
|
||||
def create(self, path):
|
||||
self.write(path, "")
|
||||
|
||||
def write(self, path, data):
|
||||
try:
|
||||
self.getxs().write(path, data)
|
||||
except RuntimeError, ex:
|
||||
raise RuntimeError(ex.args[0],
|
||||
ex.args[1] +
|
||||
(', while writing %s : %s' % (str(path),
|
||||
str(data))))
|
||||
|
||||
def begin(self):
|
||||
self.getxs().transaction_start()
|
||||
|
||||
def commit(self, abandon=False):
|
||||
self.getxs().transaction_end(abort=abandon)
|
||||
|
||||
def watch(self, path, fn):
|
||||
watcher = self.watchers.get(path)
|
||||
if not watcher:
|
||||
watcher = self.addWatcher(Watcher(self, path))
|
||||
return self.addSubscription(watcher, fn)
|
||||
|
||||
def unwatch(self, sid):
|
||||
s = self.subscriptions.get(sid)
|
||||
if not s: return
|
||||
del self.subscriptions[s.sid]
|
||||
watcher = s.unwatch()
|
||||
if watcher and not watcher.watching():
|
||||
try:
|
||||
del self.watchers[watcher.getPath()]
|
||||
except:
|
||||
pass
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
path = getEventPath(event)
|
||||
watcher = self.watchers.get(path)
|
||||
if not watcher:
|
||||
watcher = self.addWatcher(EventWatcher(self, path, event))
|
||||
return self.addSubscription(watcher, fn)
|
||||
|
||||
unsubscribe = unwatch
|
||||
|
||||
def sendEvent(self, event, data):
|
||||
eventPath = getEventPath(event)
|
||||
eidPath = getEventIdPath(event)
|
||||
try:
|
||||
#self.begin(eventPath)
|
||||
self.mkdirs(eventPath)
|
||||
eid = 1
|
||||
if self.exists(eidPath):
|
||||
try:
|
||||
eid = int(self.read(eidPath))
|
||||
eid += 1
|
||||
except Exception, ex:
|
||||
pass
|
||||
self.write(eidPath, str(eid))
|
||||
self.write(os.path.join(eventPath, str(eid)), data)
|
||||
finally:
|
||||
#self.commit()
|
||||
pass
|
||||
|
||||
def addWatcher(self, watcher):
|
||||
self.watchers[watcher.getPath()] = watcher
|
||||
self.watchStart()
|
||||
return watcher
|
||||
|
||||
def addSubscription(self, watcher, fn):
|
||||
self.subscription_id += 1
|
||||
subs = Subscription(watcher.getPath(), fn, self.subscription_id)
|
||||
self.subscriptions[subs.sid] = subs
|
||||
subs.watch(watcher)
|
||||
return subs.sid
|
||||
|
||||
def watchStart(self):
|
||||
if self.watchThread: return
|
||||
self.watchThread = threading.Thread(name="Watcher",
|
||||
target=self.watchMain)
|
||||
self.watchThread.setDaemon(True)
|
||||
self.watchThread.start()
|
||||
|
||||
def watchMain(self):
|
||||
try:
|
||||
while True:
|
||||
if self.watchThread is None: return
|
||||
if not self.watchers:
|
||||
return
|
||||
rd = self.watchers.values()
|
||||
try:
|
||||
(srd, swr, ser) = select.select(rd, [], [], SELECT_TIMEOUT)
|
||||
for watcher in srd:
|
||||
watcher.notify()
|
||||
except socket.error, ex:
|
||||
if ex.args[0] in (EAGAIN, EINTR):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
self.watchThread = None
|
||||
|
||||
def getXenStore():
|
||||
global xenstore
|
||||
try:
|
||||
return xenstore
|
||||
except:
|
||||
xenstore = XenStore()
|
||||
return xenstore
|
||||
|
||||
def sendEvent(event, val):
|
||||
getXenStore.sendEvent(event, val)
|
||||
|
||||
def subscribe(event, fn):
|
||||
return getXenStore().subscribe(event, fn)
|
||||
|
||||
def unsubscribe(sid):
|
||||
getXenStore().unsubscribe(sid)
|
||||
|
||||
class XenNode:
|
||||
|
||||
def __init__(self, path="/", create=True):
|
||||
self.store = getXenStore()
|
||||
self.path = path
|
||||
if not self.store.exists(path):
|
||||
if create:
|
||||
self.store.create(path)
|
||||
else:
|
||||
raise ValueError("path does not exist: '%s'" % path)
|
||||
|
||||
def getStore(self):
|
||||
return self.store
|
||||
|
||||
def relPath(self, path=""):
|
||||
if not path:
|
||||
return self.path
|
||||
if path and path.startswith("/"):
|
||||
path = path[1:]
|
||||
return os.path.join(self.path, path)
|
||||
|
||||
def delete(self, path=""):
|
||||
self.store.rm(self.relPath(path))
|
||||
|
||||
def exists(self, path=""):
|
||||
return self.store.exists(self.relPath(path))
|
||||
|
||||
def getNode(self, path="", create=True):
|
||||
if path == "":
|
||||
return self
|
||||
else:
|
||||
return XenNode(self.relPath(path=path), create=create)
|
||||
|
||||
getChild = getNode
|
||||
|
||||
def getData(self, path=""):
|
||||
path = self.relPath(path)
|
||||
try:
|
||||
return self.store.read(path)
|
||||
except:
|
||||
return None
|
||||
|
||||
def setData(self, data, path=""):
|
||||
return self.store.write(self.relPath(path), data)
|
||||
|
||||
def getLock(self):
|
||||
return None
|
||||
|
||||
def lock(self, lockid):
|
||||
return None
|
||||
|
||||
def unlock(self, lockid):
|
||||
return None
|
||||
|
||||
def deleteChild(self, name):
|
||||
self.delete(name)
|
||||
|
||||
def deleteChildren(self):
|
||||
for name in self.ls():
|
||||
self.deleteChild(name)
|
||||
|
||||
def getChildren(self):
|
||||
return [ self.getNode(name) for name in self.ls() ]
|
||||
|
||||
def ls(self):
|
||||
return self.store.ls(self.path)
|
||||
|
||||
def watch(self, fn, path=""):
|
||||
"""Watch a path for changes. The path is relative
|
||||
to the node and defaults to the node itself.
|
||||
"""
|
||||
return self.store.watch(self.relPath(path), fn)
|
||||
|
||||
def unwatch(self, sid):
|
||||
return self.store.unwatch(sid)
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
return self.store.subscribe(event, fn)
|
||||
|
||||
def unsubscribe(self, sid):
|
||||
self.store.unsubscribe(sid)
|
||||
|
||||
def sendEvent(self, event, data):
|
||||
return self.store.sendEvent(event, data)
|
||||
|
||||
def __repr__(self):
|
||||
return "<XenNode %s>" % self.path
|
||||
|
||||
|
||||
@@ -1,537 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
import string
|
||||
import types
|
||||
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
from xen.xend import sxp
|
||||
from xsnode import XenNode
|
||||
from xen.util.mac import macToString, macFromString
|
||||
|
||||
VALID_KEY_CHARS = string.ascii_letters + string.digits + "_-@"
|
||||
|
||||
def hasAttr(obj, attr):
|
||||
if isinstance(obj, dict):
|
||||
return obj.contains(attr)
|
||||
else:
|
||||
return hasattr(obj, attr)
|
||||
|
||||
def getAttr(obj, attr):
|
||||
try:
|
||||
if isinstance(obj, dict):
|
||||
return obj.get(attr)
|
||||
else:
|
||||
return getattr(obj, attr, None)
|
||||
except AttributeError:
|
||||
return None
|
||||
except LookupError:
|
||||
return None
|
||||
|
||||
def setAttr(obj, attr, val):
|
||||
if isinstance(obj, dict):
|
||||
obj[attr] = val
|
||||
else:
|
||||
setattr(obj, attr, val)
|
||||
|
||||
class DBConverter:
|
||||
"""Conversion of values to and from strings in xenstore.
|
||||
"""
|
||||
|
||||
converters = {}
|
||||
|
||||
def checkType(cls, ty):
|
||||
if ty is None or ty in cls.converters:
|
||||
return
|
||||
raise ValueError("invalid converter type: '%s'" % ty)
|
||||
|
||||
checkType = classmethod(checkType)
|
||||
|
||||
def getConverter(cls, ty=None):
|
||||
if ty is None:
|
||||
ty = "str"
|
||||
conv = cls.converters.get(ty)
|
||||
if not conv:
|
||||
raise ValueError("no converter for type: '%s'" % ty)
|
||||
return conv
|
||||
|
||||
getConverter = classmethod(getConverter)
|
||||
|
||||
def exportTypeToDB(cls, db, path, val, ty=None):
|
||||
return cls.getConverter(ty).exportToDB(db, path, val)
|
||||
|
||||
exportTypeToDB = classmethod(exportTypeToDB)
|
||||
|
||||
def importTypeFromDB(cls, db, path, ty=None):
|
||||
return cls.getConverter(ty).importFromDB(db, path)
|
||||
|
||||
importTypeFromDB = classmethod(importTypeFromDB)
|
||||
|
||||
# Must define in subclass.
|
||||
name = None
|
||||
|
||||
def __init__(self):
|
||||
self.register()
|
||||
|
||||
def register(self):
|
||||
if not self.name:
|
||||
raise ValueError("invalid converter name: '%s'" % self.name)
|
||||
self.converters[self.name] = self
|
||||
|
||||
def exportToDB(self, db, path, val):
|
||||
if val is None:
|
||||
return
|
||||
try:
|
||||
data = self.toDB(val)
|
||||
except Exception, ex:
|
||||
raise
|
||||
setattr(db, path, data)
|
||||
|
||||
def importFromDB(self, db, path):
|
||||
data = getAttr(db, path)
|
||||
if data is None:
|
||||
val = None
|
||||
else:
|
||||
try:
|
||||
val = self.fromDB(data.getData())
|
||||
except Exception, ex:
|
||||
raise
|
||||
return val
|
||||
|
||||
def toDB(self, val):
|
||||
raise NotImplementedError()
|
||||
|
||||
def fromDB(self, val):
|
||||
raise NotImplementedError()
|
||||
|
||||
class StrConverter(DBConverter):
|
||||
|
||||
name = "str"
|
||||
|
||||
def toDB(self, val):
|
||||
# Convert True/False to 1/0, otherwise they convert to
|
||||
# 'True' and 'False' rather than '1' and '0', even though
|
||||
# isinstance(True/False, int) is true.
|
||||
if isinstance(val, bool):
|
||||
val = int(val)
|
||||
return str(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return data
|
||||
|
||||
StrConverter()
|
||||
|
||||
class BoolConverter(DBConverter):
|
||||
|
||||
name = "bool"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(int(bool(val)))
|
||||
|
||||
def fromDB(self, data):
|
||||
return bool(int(data))
|
||||
|
||||
BoolConverter()
|
||||
|
||||
class SxprConverter(DBConverter):
|
||||
|
||||
name = "sxpr"
|
||||
|
||||
def toDB(self, val):
|
||||
return sxp.to_string(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return sxp.from_string(data)
|
||||
|
||||
SxprConverter()
|
||||
|
||||
class IntConverter(DBConverter):
|
||||
|
||||
name = "int"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(int(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return int(data)
|
||||
|
||||
IntConverter()
|
||||
|
||||
class FloatConverter(DBConverter):
|
||||
|
||||
name = "float"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(float(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return float(data)
|
||||
|
||||
FloatConverter()
|
||||
|
||||
class LongConverter(DBConverter):
|
||||
|
||||
name = "long"
|
||||
|
||||
def toDB(self, val):
|
||||
return str(long(val))
|
||||
|
||||
def fromDB(self, data):
|
||||
return long(data)
|
||||
|
||||
LongConverter()
|
||||
|
||||
class MacConverter(DBConverter):
|
||||
|
||||
name = "mac"
|
||||
|
||||
def toDB(self, val):
|
||||
return macToString(val)
|
||||
|
||||
def fromDB(self, data):
|
||||
return macFromString(data)
|
||||
|
||||
MacConverter()
|
||||
|
||||
class DBVar:
|
||||
|
||||
def __init__(self, var, ty=None, path=None):
|
||||
DBConverter.checkType(ty)
|
||||
if path is None:
|
||||
path = var
|
||||
self.var = var
|
||||
self.ty = ty
|
||||
self.path = path
|
||||
varpath = filter(bool, self.var.split())
|
||||
self.attrpath = varpath[:-1]
|
||||
self.attr = varpath[-1]
|
||||
|
||||
def exportToDB(self, db, obj):
|
||||
val = self.getObj(obj)
|
||||
DBConverter.exportTypeToDB(db, self.path, val, ty=self.ty)
|
||||
|
||||
def importFromDB(self, db, obj):
|
||||
val = DBConverter.importTypeFromDB(db, self.path, ty=self.ty)
|
||||
self.setObj(obj, val)
|
||||
|
||||
def getObj(self, obj):
|
||||
o = obj
|
||||
for x in self.attrpath:
|
||||
o = getAttr(o, x)
|
||||
if o is None:
|
||||
return None
|
||||
return getAttr(o, self.attr)
|
||||
|
||||
def setObj(self, obj, val):
|
||||
o = obj
|
||||
for x in self.attrpath:
|
||||
o = getAttr(o, x)
|
||||
# Don't set obj attr if val is None.
|
||||
if val is None and hasAttr(o, self.attr):
|
||||
return
|
||||
setAttr(o, self.attr, val)
|
||||
|
||||
class DBMap(dict):
|
||||
"""A persistent map. Extends dict with persistence.
|
||||
Set and get values using the usual map syntax:
|
||||
|
||||
m[k], m.get(k)
|
||||
m[k] = v
|
||||
|
||||
Also supports being treated as an object with attributes.
|
||||
When 'k' is a legal identifier you may also use
|
||||
|
||||
m.k, getattr(m, k)
|
||||
m.k = v, setattr(m, k)
|
||||
k in m, hasattr(m, k)
|
||||
|
||||
When setting you can pass in a normal value, for example
|
||||
|
||||
m.x = 3
|
||||
|
||||
Getting works too:
|
||||
|
||||
m.x ==> 3
|
||||
|
||||
while m['x'] will return the map for x.
|
||||
|
||||
m['x'].getData() ==> 3
|
||||
|
||||
To get values from subdirs use get() to get the subdir first:
|
||||
|
||||
get(m, 'foo').x
|
||||
m['foo'].x
|
||||
|
||||
instead of m.foo.x, because m.foo will return the data for field foo,
|
||||
not the directory.
|
||||
|
||||
You can assign values into a subdir by passing a map:
|
||||
|
||||
m.foo = {'x': 1, 'y':2 }
|
||||
|
||||
You can also use paths as keys:
|
||||
|
||||
m['foo/x'] = 1
|
||||
|
||||
sets field x in subdir foo.
|
||||
|
||||
"""
|
||||
|
||||
__db__ = None
|
||||
__data__ = None
|
||||
__perms__ = None
|
||||
__parent__ = None
|
||||
__name__ = ""
|
||||
|
||||
__transaction__ = False
|
||||
|
||||
# True if value set since saved (or never saved).
|
||||
__dirty__ = True
|
||||
|
||||
def __init__(self, parent=None, name="", db=None):
|
||||
if parent is None:
|
||||
self.__name__ = name
|
||||
else:
|
||||
if not isinstance(parent, DBMap):
|
||||
raise ValueError("invalid parent")
|
||||
self.__parent__ = parent
|
||||
self.__name__ = name
|
||||
db = self.__parent__.getChildDB(name)
|
||||
self.setDB(db)
|
||||
|
||||
def getName(self):
|
||||
return self.__name__
|
||||
|
||||
def getPath(self):
|
||||
return self.__db__ and self.__db__.relPath()
|
||||
|
||||
def watch(self, fn, path=""):
|
||||
return self.__db__.watch(fn, path=path)
|
||||
|
||||
def unwatch(self, sid):
|
||||
return self.__db__.unwatch(sid)
|
||||
|
||||
def subscribe(self, event, fn):
|
||||
return self.__db__.subscribe(event, fn)
|
||||
|
||||
def unsubscribe(self, sid):
|
||||
return self.__db__.unsubscribe(sid)
|
||||
|
||||
def sendEvent(self, event, val):
|
||||
return self.__db__.sendEvent(event, val)
|
||||
|
||||
def transactionBegin(self):
|
||||
# Begin a transaction.
|
||||
pass
|
||||
|
||||
def transactionCommit(self):
|
||||
# Commit writes to db.
|
||||
pass
|
||||
|
||||
def transactionFail(self):
|
||||
# Fail a transaction.
|
||||
# We have changed values, what do we do?
|
||||
pass
|
||||
|
||||
def checkName(self, k):
|
||||
if k == "":
|
||||
raise ValueError("invalid key, empty string")
|
||||
for c in k:
|
||||
if c in VALID_KEY_CHARS: continue
|
||||
raise ValueError("invalid key char '%s'" % c)
|
||||
|
||||
def _setData(self, v):
|
||||
#print 'DBMap>_setData>', self.getPath(), 'data=', v
|
||||
if v != self.__data__:
|
||||
self.__dirty__ = True
|
||||
self.__data__ = v
|
||||
|
||||
def setData(self, v):
|
||||
if isinstance(v, dict):
|
||||
for (key, val) in v.items():
|
||||
self[key] = val
|
||||
else:
|
||||
self._setData(v)
|
||||
|
||||
def getData(self):
|
||||
return self.__data__
|
||||
|
||||
def _set(self, k, v):
|
||||
dict.__setitem__(self, k, v)
|
||||
|
||||
def _get(self, k):
|
||||
try:
|
||||
return dict.__getitem__(self, k)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _del(self, k, v):
|
||||
try:
|
||||
dict.__delitem__(self, k)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _contains(self, k):
|
||||
return dict.__contains__(self, k)
|
||||
|
||||
def __setitem__(self, k, v, save=False):
|
||||
node = self.addChild(k)
|
||||
node.setData(v)
|
||||
if save:
|
||||
node.saveDB()
|
||||
|
||||
def __getitem__(self, k):
|
||||
if self._contains(k):
|
||||
v = self._get(k)
|
||||
else:
|
||||
v = self.readChildDB(k)
|
||||
self._set(k, v)
|
||||
return v
|
||||
|
||||
def __delitem__(self, k):
|
||||
self._del(k)
|
||||
self.deleteChildDB(k)
|
||||
|
||||
def __repr__(self):
|
||||
if len(self):
|
||||
return dict.__repr__(self)
|
||||
else:
|
||||
return repr(self.__data__)
|
||||
|
||||
def __setattr__(self, k, v):
|
||||
if k.startswith("__"):
|
||||
object.__setattr__(self, k, v)
|
||||
else:
|
||||
self.__setitem__(k, v, save=True)
|
||||
return v
|
||||
|
||||
def __getattr__(self, k):
|
||||
if k.startswith("__"):
|
||||
v = object.__getattr__(self, k)
|
||||
else:
|
||||
try:
|
||||
v = self.__getitem__(k).getData()
|
||||
except LookupError, ex:
|
||||
raise AttributeError(ex.args)
|
||||
return v
|
||||
|
||||
def __delattr__(self, k):
|
||||
return self.__delitem__(k)
|
||||
|
||||
def delete(self):
|
||||
dict.clear(self)
|
||||
self.__data__ = None
|
||||
if self.__db__:
|
||||
self.__db__.delete()
|
||||
|
||||
def clear(self):
|
||||
dict.clear(self)
|
||||
if self.__db__:
|
||||
self.__db__.deleteChildren()
|
||||
|
||||
def getChild(self, k):
|
||||
return self._get(k)
|
||||
|
||||
def getChildDB(self, k):
|
||||
self.checkName(k)
|
||||
return self.__db__ and self.__db__.getChild(k)
|
||||
|
||||
def deleteChildDB(self, k):
|
||||
if self.__db__:
|
||||
self.__db__.deleteChild(k)
|
||||
|
||||
def _addChild(self, k):
|
||||
kid = self._get(k)
|
||||
if kid is None:
|
||||
kid = DBMap(parent=self, name=k, db=self.getChildDB(k))
|
||||
self._set(k, kid)
|
||||
return kid
|
||||
|
||||
def addChild(self, path):
|
||||
l = path.split("/")
|
||||
n = self
|
||||
for x in l:
|
||||
if x == "": continue
|
||||
n = n._addChild(x)
|
||||
return n
|
||||
|
||||
def setDB(self, db):
|
||||
if (db is not None) and not isinstance(db, XenNode):
|
||||
raise ValueError("invalid db")
|
||||
self.__db__ = db
|
||||
for (k, v) in self.items():
|
||||
if v is None: continue
|
||||
if isinstance(v, DBMap):
|
||||
v._setDB(self.addChild(k), restore)
|
||||
|
||||
def readDB(self):
|
||||
if self.__db__ is None:
|
||||
return
|
||||
self.__data__ = self.__db__.getData()
|
||||
l = self.__db__.ls()
|
||||
if l:
|
||||
for k in l:
|
||||
n = self.addChild(k)
|
||||
n.readDB()
|
||||
self.__dirty__ = False
|
||||
|
||||
def readChildDB(self, k):
|
||||
if self.__db__ and (k in self.__db__.ls()):
|
||||
n = self.addChild(k)
|
||||
n.readDB()
|
||||
raise LookupError("invalid key '%s'" % k)
|
||||
|
||||
def saveDB(self, sync=False, save=False):
|
||||
"""Save unsaved data to db.
|
||||
If save or sync is true, saves whether dirty or not.
|
||||
If sync is true, removes db entries not in the map.
|
||||
"""
|
||||
|
||||
if self.__db__ is None:
|
||||
#print 'DBMap>saveDB>',self.getPath(), 'no db'
|
||||
return
|
||||
# Write data.
|
||||
#print 'DBMap>saveDB>', self.getPath(), 'dirty=', self.__dirty__, 'data=', self.__data__
|
||||
if ((self.__data__ is not None)
|
||||
and (sync or save or self.__dirty__)):
|
||||
self.__db__.setData(self.__data__)
|
||||
self.__dirty__ = False
|
||||
else:
|
||||
#print 'DBMap>saveDB>', self.getPath(), 'not written'
|
||||
pass
|
||||
# Write children.
|
||||
for (name, node) in self.items():
|
||||
if not isinstance(node, DBMap): continue
|
||||
node.saveDB(sync=sync, save=save)
|
||||
# Remove db nodes not in children.
|
||||
###if sync:
|
||||
### for name in self.__db__.ls():
|
||||
### if name not in self:
|
||||
### self.__db__.delete(name)
|
||||
|
||||
def importFromDB(self, obj, fields):
|
||||
"""Set fields in obj from db fields.
|
||||
"""
|
||||
for f in fields:
|
||||
f.importFromDB(self, obj)
|
||||
|
||||
def exportToDB(self, obj, fields, save=False, sync=False):
|
||||
"""Set fields in db from obj fields.
|
||||
"""
|
||||
for f in fields:
|
||||
f.exportToDB(self, obj)
|
||||
self.saveDB(save=save, sync=sync)
|
||||
@@ -1,149 +0,0 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
|
||||
#============================================================================
|
||||
# HTTP interface onto xenstore (read-only).
|
||||
# Mainly intended for testing.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from xen.web.httpserver import HttpServer, UnixHttpServer
|
||||
from xen.web.SrvBase import SrvBase
|
||||
from xen.web.SrvDir import SrvDir
|
||||
from xen.xend.Args import FormFn
|
||||
from xen.xend.xenstore import XenNode
|
||||
|
||||
def pathurl(req):
|
||||
url = req.prePathURL()
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
return url
|
||||
|
||||
def writelist(req, l):
|
||||
req.write('(')
|
||||
for k in l:
|
||||
req.write(' ' + k)
|
||||
req.write(')')
|
||||
|
||||
def lsData(dbnode, req, url):
|
||||
v = dbnode.getData()
|
||||
if v is None:
|
||||
req.write('<p>No data')
|
||||
else:
|
||||
req.write('<p>Data: <pre>')
|
||||
req.write(str(v))
|
||||
req.write('</pre>')
|
||||
v = dbnode.getLock()
|
||||
if v is None:
|
||||
req.write("<p>Unlocked")
|
||||
else:
|
||||
req.write("<p>Lock = %s" % v)
|
||||
|
||||
def lsChildren(dbnode, req, url):
|
||||
l = dbnode.ls()
|
||||
if l:
|
||||
req.write('<p>Children: <ul>')
|
||||
for key in l:
|
||||
child = dbnode.getChild(key)
|
||||
data = child.getData()
|
||||
if data is None: data = ""
|
||||
req.write('<li><a href="%(url)s%(key)s">%(key)s</a> %(data)s</li>'
|
||||
% { "url": url, "key": key, "data": data })
|
||||
req.write('</ul>')
|
||||
else:
|
||||
req.write('<p>No children')
|
||||
|
||||
|
||||
class DBDataResource(SrvBase):
|
||||
"""Resource for the node data.
|
||||
"""
|
||||
|
||||
def __init__(self, dbnode):
|
||||
SrvBase.__init__(self)
|
||||
self.dbnode = dbnode
|
||||
|
||||
def render_GET(self, req):
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
req.write("<pre>")
|
||||
req.write(self.getData() or self.getNoData())
|
||||
req.write("</pre>")
|
||||
req.write('</body></html>')
|
||||
|
||||
def getContentType(self):
|
||||
# Use content-type from metadata.
|
||||
return "text/plain"
|
||||
|
||||
def getData(self):
|
||||
v = self.dbnode.getData()
|
||||
if v is None: return v
|
||||
return str(v)
|
||||
|
||||
def getNoData(self):
|
||||
return ""
|
||||
|
||||
class DBNodeResource(SrvDir):
|
||||
"""Resource for a DB node.
|
||||
"""
|
||||
|
||||
def __init__(self, dbnode):
|
||||
SrvDir.__init__(self)
|
||||
self.dbnode = dbnode
|
||||
|
||||
def get(self, x):
|
||||
val = None
|
||||
if x == "__data__":
|
||||
val = DBDataResource(self.dbnode)
|
||||
else:
|
||||
if self.dbnode.exists(x):
|
||||
child = self.dbnode.getChild(x, create=False)
|
||||
else:
|
||||
child = None
|
||||
if child is not None:
|
||||
val = DBNodeResource(child)
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def ls(self, req, use_sxp=0):
|
||||
if use_sxp:
|
||||
writelist(req, self.dbnode.getChildren())
|
||||
else:
|
||||
url = pathurl(req)
|
||||
req.write("<fieldset>")
|
||||
lsData(self.dbnode, req, url)
|
||||
lsChildren(self.dbnode, req, url)
|
||||
req.write("</fieldset>")
|
||||
|
||||
def form(self, req):
|
||||
url = req.prePathURL()
|
||||
pass
|
||||
|
||||
class DBRootResource(DBNodeResource):
|
||||
"""Resource for the root of a DB.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
DBNodeResource.__init__(self, XenNode())
|
||||
|
||||
def main(argv):
|
||||
root = SrvDir()
|
||||
root.putChild('xenstore', DBRootResource())
|
||||
interface = ''
|
||||
port = 8003
|
||||
server = HttpServer(root=root, interface=interface, port=port)
|
||||
server.run()
|
||||