ia64/xen-unstable

view tools/python/xen/xend/xenstore/xsobj.py @ 6692:0c9c044fd00c

Disable sync dbmap saves -- they remove nodes added by other tools.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 21:13:46 2005 +0000 (2005-09-07)
parents dd668f7527cb
children 7bc32f4c67fb
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 introduceDomain(self, dom, page, evtchn, path=None):
323 db = self.__db__
324 if path is None:
325 path = db.relPath()
326 log.info("DBMap>introduceDomain> %d %d %s %s" %(dom, page, evtchn, path))
327 try:
328 db.introduceDomain(dom, page, evtchn, path)
329 except Exception, ex:
330 import traceback
331 traceback.print_exc()
332 log.info("DBMap>introduceDomain> %s" %ex)
333 pass # todo: don't ignore
335 def releaseDomain(self, dom):
336 db = self.__db__
337 log.info("DBMap>releaseDomain> %d" %dom)
338 try:
339 db.releaseDomain(dom)
340 except Exception, ex:
341 import traceback
342 traceback.print_exc()
343 log.info("DBMap>releaseDomain> %s" %ex)
344 pass # todo: don't ignore
346 def watch(self, fn, path=""):
347 return self.__db__.watch(fn, path=path)
349 def unwatch(self, sid):
350 return self.__db__.unwatch(sid)
352 def subscribe(self, event, fn):
353 return self.__db__.subscribe(event, fn)
355 def unsubscribe(self, sid):
356 return self.__db__.unsubscribe(sid)
358 def sendEvent(self, event, val):
359 return self.__db__.sendEvent(event, val)
361 def transactionBegin(self):
362 # Begin a transaction.
363 pass
365 def transactionCommit(self):
366 # Commit writes to db.
367 pass
369 def transactionFail(self):
370 # Fail a transaction.
371 # We have changed values, what do we do?
372 pass
374 def checkName(self, k):
375 if k == "":
376 raise ValueError("invalid key, empty string")
377 for c in k:
378 if c in VALID_KEY_CHARS: continue
379 raise ValueError("invalid key char '%s'" % c)
381 def _setData(self, v):
382 #print 'DBMap>_setData>', self.getPath(), 'data=', v
383 if v != self.__data__:
384 self.__dirty__ = True
385 self.__data__ = v
387 def setData(self, v):
388 if isinstance(v, dict):
389 for (key, val) in v.items():
390 self[key] = val
391 else:
392 self._setData(v)
394 def getData(self):
395 return self.__data__
397 def _set(self, k, v):
398 dict.__setitem__(self, k, v)
400 def _get(self, k):
401 try:
402 return dict.__getitem__(self, k)
403 except:
404 return None
406 def _del(self, k, v):
407 try:
408 dict.__delitem__(self, k)
409 except:
410 pass
412 def _contains(self, k):
413 return dict.__contains__(self, k)
415 def __setitem__(self, k, v, save=False):
416 node = self.addChild(k)
417 node.setData(v)
418 if save:
419 node.saveDB()
421 def __getitem__(self, k):
422 if self._contains(k):
423 v = self._get(k)
424 else:
425 v = self.readChildDB(k)
426 self._set(k, v)
427 return v
429 def __delitem__(self, k):
430 self._del(k)
431 self.deleteChildDB(k)
433 def __repr__(self):
434 if len(self):
435 return dict.__repr__(self)
436 else:
437 return repr(self.__data__)
439 def __setattr__(self, k, v):
440 if k.startswith("__"):
441 object.__setattr__(self, k, v)
442 else:
443 self.__setitem__(k, v, save=True)
444 return v
446 def __getattr__(self, k):
447 if k.startswith("__"):
448 v = object.__getattr__(self, k)
449 else:
450 try:
451 v = self.__getitem__(k).getData()
452 except LookupError, ex:
453 raise AttributeError(ex.args)
454 return v
456 def __delattr__(self, k):
457 return self.__delitem__(k)
459 def delete(self):
460 dict.clear(self)
461 self.__data__ = None
462 if self.__db__:
463 self.__db__.delete()
465 def clear(self):
466 dict.clear(self)
467 if self.__db__:
468 self.__db__.deleteChildren()
470 def getChild(self, k):
471 return self._get(k)
473 def getChildDB(self, k):
474 self.checkName(k)
475 return self.__db__ and self.__db__.getChild(k)
477 def deleteChildDB(self, k):
478 if self.__db__:
479 self.__db__.deleteChild(k)
481 def _addChild(self, k):
482 kid = self._get(k)
483 if kid is None:
484 kid = DBMap(parent=self, name=k, db=self.getChildDB(k))
485 self._set(k, kid)
486 return kid
488 def addChild(self, path):
489 l = path.split("/")
490 n = self
491 for x in l:
492 if x == "": continue
493 n = n._addChild(x)
494 return n
496 def getDB(self):
497 return self.__db__
499 def setDB(self, db):
500 if (db is not None) and not isinstance(db, XenNode):
501 raise ValueError("invalid db")
502 self.__db__ = db
503 for (k, v) in self.items():
504 if v is None: continue
505 if isinstance(v, DBMap):
506 v._setDB(self.addChild(k), restore)
508 def readDB(self):
509 if self.__db__ is None:
510 return
511 self.__data__ = self.__db__.getData()
512 for k in self.__db__.ls():
513 n = self.addChild(k)
514 n.readDB()
515 self.__dirty__ = False
517 def readChildDB(self, k):
518 if self.__db__ and (k in self.__db__.ls()):
519 n = self.addChild(k)
520 n.readDB()
521 raise LookupError("invalid key '%s'" % k)
523 def saveDB(self, sync=False, save=False):
524 """Save unsaved data to db.
525 If save or sync is true, saves whether dirty or not.
526 If sync is true, removes db entries not in the map.
527 """
529 if self.__db__ is None:
530 #print 'DBMap>saveDB>',self.getPath(), 'no db'
531 return
532 # Write data.
533 #print 'DBMap>saveDB>', self.getPath(), 'dirty=', self.__dirty__, 'data=', self.__data__
534 if ((self.__data__ is not None)
535 and (sync or save or self.__dirty__)):
536 self.__db__.setData(self.__data__)
537 self.__dirty__ = False
538 else:
539 #print 'DBMap>saveDB>', self.getPath(), 'not written'
540 pass
541 # Write children.
542 for (name, node) in self.items():
543 if not isinstance(node, DBMap): continue
544 node.saveDB(sync=sync, save=save)
545 # Remove db nodes not in children.
546 ###if sync:
547 ### for name in self.__db__.ls():
548 ### if name not in self:
549 ### self.__db__.delete(name)
551 def importFromDB(self, obj, fields):
552 """Set fields in obj from db fields.
553 """
554 for f in fields:
555 f.importFromDB(self, obj)
557 def exportToDB(self, obj, fields, save=False, sync=False):
558 """Set fields in db from obj fields.
559 """
560 for f in fields:
561 f.exportToDB(self, obj)
562 self.saveDB(save=save, sync=sync)