diff options
-rw-r--r-- | .rootkeys | 4 | ||||
-rw-r--r-- | tools/python/setup.py | 1 | ||||
-rw-r--r-- | tools/python/xen/xend/xenstore/__init__.py | 2 | ||||
-rw-r--r-- | tools/python/xen/xend/xenstore/xsnode.py | 382 | ||||
-rw-r--r-- | tools/python/xen/xend/xenstore/xsobj.py | 519 | ||||
-rw-r--r-- | tools/python/xen/xend/xenstore/xsresource.py | 136 |
6 files changed, 1044 insertions, 0 deletions
@@ -878,6 +878,10 @@ 41ee5e8dq9NtihbL4nWKjuSLOhXPUg tools/python/xen/xend/server/usbif.py 40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/python/xen/xend/sxp.py 42a48d152jkT7ykQT_LWKnS-ojV_ZA tools/python/xen/xend/uuid.py +42a5a2c0ik9zrQvwjTUKDVVEQmvO2Q tools/python/xen/xend/xenstore/__init__.py +42a5a2c04xNCYAUXD0b9IDf4XekXRg tools/python/xen/xend/xenstore/xsnode.py +42a5a2c0-aP98db2PJIDxQJfTEMZ-A tools/python/xen/xend/xenstore/xsobj.py +42a5a2c0gxfQiAH_oVTShNPeG0LG2Q tools/python/xen/xend/xenstore/xsresource.py 40d05079aFRp6NQdo5wIh5Ly31c0cg tools/python/xen/xm/__init__.py 40cf2937gKQcATgXKGtNeWb1PDH5nA tools/python/xen/xm/create.py 40f552eariuUSB9TWqCPnDLz5zvxMw tools/python/xen/xm/destroy.py diff --git a/tools/python/setup.py b/tools/python/setup.py index 5902d4151f..fabe80bd8b 100644 --- a/tools/python/setup.py +++ b/tools/python/setup.py @@ -48,6 +48,7 @@ setup(name = 'xen', 'xen.util', 'xen.xend', 'xen.xend.server', + 'xen.xend.xenstore', 'xen.xm', 'xen.web', ], diff --git a/tools/python/xen/xend/xenstore/__init__.py b/tools/python/xen/xend/xenstore/__init__.py new file mode 100644 index 0000000000..6772d2ceca --- /dev/null +++ b/tools/python/xen/xend/xenstore/__init__.py @@ -0,0 +1,2 @@ +from xsnode import * +from xsobj import * diff --git a/tools/python/xen/xend/xenstore/xsnode.py b/tools/python/xen/xend/xenstore/xsnode.py new file mode 100644 index 0000000000..ae770219ab --- /dev/null +++ b/tools/python/xen/xend/xenstore/xsnode.py @@ -0,0 +1,382 @@ +import errno +import os +import os.path +import select +import sys +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): + return os.path.join("/_event", event) + +def getEventIdPath(event): + return os.path.join(eventPath(event), "@eid") + +class Subscription: + + def __init__(self, event, fn, id): + self.event = event + self.watcher = None + self.fn = fn + self.id = id + + def watch(self, watcher): + self.watcher = watcher + watcher.addSubs(self) + + def unwatch(self): + watcher = self.watcher + if watcher: + self.watcher = None + watcher.delSubs(self) + + def notify(self, event): + try: + self.fn(event, id) + except SystemExitException: + raise + except: + pass + +class Watcher: + + def __init__(self, store, event): + self.path = getEventPath(event) + self.eidPath = getEventIdPath(event) + store.mkdirs(self.path) + if not store.exists(self.eidPath): + store.writeInt(self.eidPath, 0) + self.xs = None + self.subs = [] + + def __getattr__(self, k, v): + if k == "fileno": + if self.xs: + return self.xs.fileno + else: + return -1 + else: + return self.__dict__.get(k, v) + + def addSubs(self, subs): + self.subs.append(subs) + self.watch() + + def delSubs(self, subs): + self.subs.remove(subs) + if len(self.subs) == 0: + self.unwatch() + + def getEvent(self): + return self.event + + def watch(self): + if self.xs: return + self.xs = xs.open() + self.xs.watch(path) + + def unwatch(self): + if self.xs: + self.xs.unwatch(self.path) + self.xs.close() + self.xs = None + + def watching(self): + return self.xs is not None + + def getNotification(self): + p = self.xs.read_watch() + self.xs.acknowledge_watch() + eid = self.xs.readInt(self.eidPath) + return p + + def notify(self, subs): + p = self.getNotification() + for s in subs: + s.notify(p) + +class XenStore: + + def __init__(self): + self.xs = None + #self.xs = xs.open() + self.subscription = {} + self.subscription_id = 0 + self.events = {} + 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 Exception, ex: + print >>stderr, "Exception connecting to xsdaemon:", ex + print >>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 + + 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, "", create=True) + + 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, excl=False): + self.write(path, "", create=True, excl=excl) + + def write(self, path, data, create=True, excl=False): + self.mkdirs(path) + self.getxs().write(path, data, create=create, excl=excl) + + def begin(self, path): + self.getxs().begin_transaction(path) + + def commit(self, abandon=False): + self.getxs().end_transaction(abort=abandon) + + def subscribe(self, event, fn): + watcher = self.watchEvent(event) + self.subscription_id += 1 + subs = Subscription(event, fn, self.subscription_id) + self.subscription[subs.id] = subs + subs.watch(watcher) + return subs.id + + def unsubscribe(self, sid): + s = self.subscription.get(sid) + if not s: return + del self.subscription[s.id] + s.unwatch() + unwatchEvent(s.event) + + def sendEvent(self, event, data): + eventPath = getEventPath(event) + eidPath = getEventIdPath(event) + try: + self.begin(eventPath) + self.mkdirs(eventPath) + if self.exists(eidPath): + eid = self.readInt(eidPath) + eid += 1 + else: + eid = 1 + self.writeInt(eidPath, eid) + self.write(os.path.join(eventPath, str(eid)), data) + finally: + self.commit() + + def watchEvent(self, event): + if event in self.events: + return + watcher = Watcher(event) + self.watchers[watcher.getEvent()] = watcher + self.watchStart() + return watcher + + def unwatchEvent(self, event): + watcher = self.watchers.get(event) + if not watcher: + return + if not watcher.watching(): + del self.watchers[event] + + def watchStart(self): + if self.watchThread: return + + def watchMain(self): + try: + while True: + if self.watchThread is None: return + if not self.events: + return + rd = self.watchers.values() + try: + (rd, wr, er) = select.select(rd, [], [], SELECT_TIMEOUT) + for watcher in rd: + watcher.notify() + except socket.error, ex: + if ex.args[0] in (EAGAIN, EINTR): + pass + else: + raise + finally: + self.watchThread = None + + def introduceDomain(self, dom, page, evtchn, path): + self.getxs().introduce_domain(dom, page, evtchn.port1, path) + + def releaseDomain(self, dom): + self.getxs().release_domain(dom) + +def getXenStore(): + global xenstore + try: + return xenstore + except: + xenstore = XenStore() + return xenstore + +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 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=""): + path = self.relPath(path) + #print 'XenNode>setData>', 'path=', path, 'data=', data + return self.store.write(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 introduceDomain(self, dom, page, evtchn, path): + self.store.introduceDomain(dom, page, evtchn, path) + + def releaseDomain(self, dom): + self.store.releaseDomain(dom) + + def __repr__(self): + return "<XenNode %s>" % self.path + + diff --git a/tools/python/xen/xend/xenstore/xsobj.py b/tools/python/xen/xend/xenstore/xsobj.py new file mode 100644 index 0000000000..62e18d07d9 --- /dev/null +++ b/tools/python/xen/xend/xenstore/xsobj.py @@ -0,0 +1,519 @@ +import string +import types + +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): + if isinstance(obj, dict): + return dict.get(attr) + else: + return getattr(obj, attr, None) + +def setAttr(obj, attr, val): + if isinstance(obj, dict): + dict[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 convertToDB(cls, val, ty=None): + return cls.getConverter(ty).toDB(val) + + convertToDB = classmethod(convertToDB) + + def convertFromDB(cls, val, ty=None): + return cls.getConverter(ty).fromDB(val) + + convertFromDB = classmethod(convertFromDB) + + # 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 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): + self.setDB(db, self.getObj(obj)) + + def importFromDB(self, db, obj): + self.setObj(obj, self.getDB(db)) + + 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) + + def getDB(self, db): + data = getattr(db, self.path) + return DBConverter.convertFromDB(data, ty=self.ty) + + def setDB(self, db, val): + # Don't set in db if val is None. + #print 'DBVar>setDB>', self.path, 'val=', val + if val is None: + return + data = DBConverter.convertToDB(val, ty=self.ty) + #print 'DBVar>setDB>', self.path, 'data=', data + setattr(db, self.path, data) + + +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 introduceDomain(self, dom, page, evtchn, path=None): + db = self.__db__ + if path is None: + path = db.relPath() + print 'DBMap>introduceDomain>', dom, page, evtchn, path + try: + db.introduceDomain(dom, page, evtchn, path) + except Exception, ex: + import traceback + traceback.print_exc() + print 'DBMap>introduceDomain>', ex + pass # todo: don't ignore + + def releaseDomain(self, dom): + db = self.__db__ + print 'DBMap>releaseDomain>', dom + try: + db.releaseDomain(dom) + except Exception, ex: + import traceback + traceback.print_exc() + print 'DBMap>releaseDomain>', ex + pass # todo: don't ignore + + 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 watch(self, fn): + pass + + def unwatch(self, watch): + 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() + for k in self.__db__.ls(): + 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) diff --git a/tools/python/xen/xend/xenstore/xsresource.py b/tools/python/xen/xend/xenstore/xsresource.py new file mode 100644 index 0000000000..37011bdea3 --- /dev/null +++ b/tools/python/xen/xend/xenstore/xsresource.py @@ -0,0 +1,136 @@ +#============================================================================ +# 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() |