aboutsummaryrefslogtreecommitdiffstats
path: root/tools/python/xen/xend/xenstore/xsobj.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/python/xen/xend/xenstore/xsobj.py')
-rw-r--r--tools/python/xen/xend/xenstore/xsobj.py522
1 files changed, 522 insertions, 0 deletions
diff --git a/tools/python/xen/xend/xenstore/xsobj.py b/tools/python/xen/xend/xenstore/xsobj.py
new file mode 100644
index 0000000000..b1c9a4f1d1
--- /dev/null
+++ b/tools/python/xen/xend/xenstore/xsobj.py
@@ -0,0 +1,522 @@
+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):
+ try:
+ data = getattr(db, self.path)
+ except AttributeError:
+ return None
+ 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)