ia64/xen-unstable

view tools/python/xen/xend/xenstore/xsobj.py @ 6813:c1450b657ede

g/c introduceDomain and releaseDomain bindings in xsnode and xsobj.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 17:32:59 2005 +0000 (2005-09-13)
parents 7bc32f4c67fb
children 3eea03342466
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
17 import string
18 import types
20 from xen.xend.XendLogging import log
22 from xen.xend import sxp
23 from xsnode import XenNode
24 from xen.util.mac import macToString, macFromString
26 VALID_KEY_CHARS = string.ascii_letters + string.digits + "_-@"
28 def hasAttr(obj, attr):
29 if isinstance(obj, dict):
30 return obj.contains(attr)
31 else:
32 return hasattr(obj, attr)
34 def getAttr(obj, attr):
35 try:
36 if isinstance(obj, dict):
37 return obj.get(attr)
38 else:
39 return getattr(obj, attr, None)
40 except AttributeError:
41 return None
42 except LookupError:
43 return None
45 def setAttr(obj, attr, val):
46 if isinstance(obj, dict):
47 obj[attr] = val
48 else:
49 setattr(obj, attr, val)
51 class DBConverter:
52 """Conversion of values to and from strings in xenstore.
53 """
55 converters = {}
57 def checkType(cls, ty):
58 if ty is None or ty in cls.converters:
59 return
60 raise ValueError("invalid converter type: '%s'" % ty)
62 checkType = classmethod(checkType)
64 def getConverter(cls, ty=None):
65 if ty is None:
66 ty = "str"
67 conv = cls.converters.get(ty)
68 if not conv:
69 raise ValueError("no converter for type: '%s'" % ty)
70 return conv
72 getConverter = classmethod(getConverter)
74 def exportTypeToDB(cls, db, path, val, ty=None):
75 return cls.getConverter(ty).exportToDB(db, path, val)
77 exportTypeToDB = classmethod(exportTypeToDB)
79 def importTypeFromDB(cls, db, path, ty=None):
80 return cls.getConverter(ty).importFromDB(db, path)
82 importTypeFromDB = classmethod(importTypeFromDB)
84 # Must define in subclass.
85 name = None
87 def __init__(self):
88 self.register()
90 def register(self):
91 if not self.name:
92 raise ValueError("invalid converter name: '%s'" % self.name)
93 self.converters[self.name] = self
95 def exportToDB(self, db, path, val):
96 if val is None:
97 return
98 try:
99 data = self.toDB(val)
100 except Exception, ex:
101 raise
102 setattr(db, path, data)
104 def importFromDB(self, db, path):
105 data = getAttr(db, path)
106 if data is None:
107 val = None
108 else:
109 try:
110 val = self.fromDB(data.getData())
111 except Exception, ex:
112 raise
113 return val
115 def toDB(self, val):
116 raise NotImplementedError()
118 def fromDB(self, val):
119 raise NotImplementedError()
121 class StrConverter(DBConverter):
123 name = "str"
125 def toDB(self, val):
126 # Convert True/False to 1/0, otherwise they convert to
127 # 'True' and 'False' rather than '1' and '0', even though
128 # isinstance(True/False, int) is true.
129 if isinstance(val, bool):
130 val = int(val)
131 return str(val)
133 def fromDB(self, data):
134 return data
136 StrConverter()
138 class BoolConverter(DBConverter):
140 name = "bool"
142 def toDB(self, val):
143 return str(int(bool(val)))
145 def fromDB(self, data):
146 return bool(int(data))
148 BoolConverter()
150 class SxprConverter(DBConverter):
152 name = "sxpr"
154 def toDB(self, val):
155 return sxp.to_string(val)
157 def fromDB(self, data):
158 return sxp.from_string(data)
160 SxprConverter()
162 class IntConverter(DBConverter):
164 name = "int"
166 def toDB(self, val):
167 return str(int(val))
169 def fromDB(self, data):
170 return int(data)
172 IntConverter()
174 class FloatConverter(DBConverter):
176 name = "float"
178 def toDB(self, val):
179 return str(float(val))
181 def fromDB(self, data):
182 return float(data)
184 FloatConverter()
186 class LongConverter(DBConverter):
188 name = "long"
190 def toDB(self, val):
191 return str(long(val))
193 def fromDB(self, data):
194 return long(data)
196 LongConverter()
198 class MacConverter(DBConverter):
200 name = "mac"
202 def toDB(self, val):
203 return macToString(val)
205 def fromDB(self, data):
206 return macFromString(data)
208 MacConverter()
210 class DBVar:
212 def __init__(self, var, ty=None, path=None):
213 DBConverter.checkType(ty)
214 if path is None:
215 path = var
216 self.var = var
217 self.ty = ty
218 self.path = path
219 varpath = filter(bool, self.var.split())
220 self.attrpath = varpath[:-1]
221 self.attr = varpath[-1]
223 def exportToDB(self, db, obj):
224 val = self.getObj(obj)
225 DBConverter.exportTypeToDB(db, self.path, val, ty=self.ty)
227 def importFromDB(self, db, obj):
228 val = DBConverter.importTypeFromDB(db, self.path, ty=self.ty)
229 self.setObj(obj, val)
231 def getObj(self, obj):
232 o = obj
233 for x in self.attrpath:
234 o = getAttr(o, x)
235 if o is None:
236 return None
237 return getAttr(o, self.attr)
239 def setObj(self, obj, val):
240 o = obj
241 for x in self.attrpath:
242 o = getAttr(o, x)
243 # Don't set obj attr if val is None.
244 if val is None and hasAttr(o, self.attr):
245 return
246 setAttr(o, self.attr, val)
248 class DBMap(dict):
249 """A persistent map. Extends dict with persistence.
250 Set and get values using the usual map syntax:
252 m[k], m.get(k)
253 m[k] = v
255 Also supports being treated as an object with attributes.
256 When 'k' is a legal identifier you may also use
258 m.k, getattr(m, k)
259 m.k = v, setattr(m, k)
260 k in m, hasattr(m, k)
262 When setting you can pass in a normal value, for example
264 m.x = 3
266 Getting works too:
268 m.x ==> 3
270 while m['x'] will return the map for x.
272 m['x'].getData() ==> 3
274 To get values from subdirs use get() to get the subdir first:
276 get(m, 'foo').x
277 m['foo'].x
279 instead of m.foo.x, because m.foo will return the data for field foo,
280 not the directory.
282 You can assign values into a subdir by passing a map:
284 m.foo = {'x': 1, 'y':2 }
286 You can also use paths as keys:
288 m['foo/x'] = 1
290 sets field x in subdir foo.
292 """
294 __db__ = None
295 __data__ = None
296 __perms__ = None
297 __parent__ = None
298 __name__ = ""
300 __transaction__ = False
302 # True if value set since saved (or never saved).
303 __dirty__ = True
305 def __init__(self, parent=None, name="", db=None):
306 if parent is None:
307 self.__name__ = name
308 else:
309 if not isinstance(parent, DBMap):
310 raise ValueError("invalid parent")
311 self.__parent__ = parent
312 self.__name__ = name
313 db = self.__parent__.getChildDB(name)
314 self.setDB(db)
316 def getName(self):
317 return self.__name__
319 def getPath(self):
320 return self.__db__ and self.__db__.relPath()
322 def watch(self, fn, path=""):
323 return self.__db__.watch(fn, path=path)
325 def unwatch(self, sid):
326 return self.__db__.unwatch(sid)
328 def subscribe(self, event, fn):
329 return self.__db__.subscribe(event, fn)
331 def unsubscribe(self, sid):
332 return self.__db__.unsubscribe(sid)
334 def sendEvent(self, event, val):
335 return self.__db__.sendEvent(event, val)
337 def transactionBegin(self):
338 # Begin a transaction.
339 pass
341 def transactionCommit(self):
342 # Commit writes to db.
343 pass
345 def transactionFail(self):
346 # Fail a transaction.
347 # We have changed values, what do we do?
348 pass
350 def checkName(self, k):
351 if k == "":
352 raise ValueError("invalid key, empty string")
353 for c in k:
354 if c in VALID_KEY_CHARS: continue
355 raise ValueError("invalid key char '%s'" % c)
357 def _setData(self, v):
358 #print 'DBMap>_setData>', self.getPath(), 'data=', v
359 if v != self.__data__:
360 self.__dirty__ = True
361 self.__data__ = v
363 def setData(self, v):
364 if isinstance(v, dict):
365 for (key, val) in v.items():
366 self[key] = val
367 else:
368 self._setData(v)
370 def getData(self):
371 return self.__data__
373 def _set(self, k, v):
374 dict.__setitem__(self, k, v)
376 def _get(self, k):
377 try:
378 return dict.__getitem__(self, k)
379 except:
380 return None
382 def _del(self, k, v):
383 try:
384 dict.__delitem__(self, k)
385 except:
386 pass
388 def _contains(self, k):
389 return dict.__contains__(self, k)
391 def __setitem__(self, k, v, save=False):
392 node = self.addChild(k)
393 node.setData(v)
394 if save:
395 node.saveDB()
397 def __getitem__(self, k):
398 if self._contains(k):
399 v = self._get(k)
400 else:
401 v = self.readChildDB(k)
402 self._set(k, v)
403 return v
405 def __delitem__(self, k):
406 self._del(k)
407 self.deleteChildDB(k)
409 def __repr__(self):
410 if len(self):
411 return dict.__repr__(self)
412 else:
413 return repr(self.__data__)
415 def __setattr__(self, k, v):
416 if k.startswith("__"):
417 object.__setattr__(self, k, v)
418 else:
419 self.__setitem__(k, v, save=True)
420 return v
422 def __getattr__(self, k):
423 if k.startswith("__"):
424 v = object.__getattr__(self, k)
425 else:
426 try:
427 v = self.__getitem__(k).getData()
428 except LookupError, ex:
429 raise AttributeError(ex.args)
430 return v
432 def __delattr__(self, k):
433 return self.__delitem__(k)
435 def delete(self):
436 dict.clear(self)
437 self.__data__ = None
438 if self.__db__:
439 self.__db__.delete()
441 def clear(self):
442 dict.clear(self)
443 if self.__db__:
444 self.__db__.deleteChildren()
446 def getChild(self, k):
447 return self._get(k)
449 def getChildDB(self, k):
450 self.checkName(k)
451 return self.__db__ and self.__db__.getChild(k)
453 def deleteChildDB(self, k):
454 if self.__db__:
455 self.__db__.deleteChild(k)
457 def _addChild(self, k):
458 kid = self._get(k)
459 if kid is None:
460 kid = DBMap(parent=self, name=k, db=self.getChildDB(k))
461 self._set(k, kid)
462 return kid
464 def addChild(self, path):
465 l = path.split("/")
466 n = self
467 for x in l:
468 if x == "": continue
469 n = n._addChild(x)
470 return n
472 def getDB(self):
473 return self.__db__
475 def setDB(self, db):
476 if (db is not None) and not isinstance(db, XenNode):
477 raise ValueError("invalid db")
478 self.__db__ = db
479 for (k, v) in self.items():
480 if v is None: continue
481 if isinstance(v, DBMap):
482 v._setDB(self.addChild(k), restore)
484 def readDB(self):
485 if self.__db__ is None:
486 return
487 self.__data__ = self.__db__.getData()
488 for k in self.__db__.ls():
489 n = self.addChild(k)
490 n.readDB()
491 self.__dirty__ = False
493 def readChildDB(self, k):
494 if self.__db__ and (k in self.__db__.ls()):
495 n = self.addChild(k)
496 n.readDB()
497 raise LookupError("invalid key '%s'" % k)
499 def saveDB(self, sync=False, save=False):
500 """Save unsaved data to db.
501 If save or sync is true, saves whether dirty or not.
502 If sync is true, removes db entries not in the map.
503 """
505 if self.__db__ is None:
506 #print 'DBMap>saveDB>',self.getPath(), 'no db'
507 return
508 # Write data.
509 #print 'DBMap>saveDB>', self.getPath(), 'dirty=', self.__dirty__, 'data=', self.__data__
510 if ((self.__data__ is not None)
511 and (sync or save or self.__dirty__)):
512 self.__db__.setData(self.__data__)
513 self.__dirty__ = False
514 else:
515 #print 'DBMap>saveDB>', self.getPath(), 'not written'
516 pass
517 # Write children.
518 for (name, node) in self.items():
519 if not isinstance(node, DBMap): continue
520 node.saveDB(sync=sync, save=save)
521 # Remove db nodes not in children.
522 ###if sync:
523 ### for name in self.__db__.ls():
524 ### if name not in self:
525 ### self.__db__.delete(name)
527 def importFromDB(self, obj, fields):
528 """Set fields in obj from db fields.
529 """
530 for f in fields:
531 f.importFromDB(self, obj)
533 def exportToDB(self, obj, fields, save=False, sync=False):
534 """Set fields in db from obj fields.
535 """
536 for f in fields:
537 f.exportToDB(self, obj)
538 self.saveDB(save=save, sync=sync)