debuggers.hg

annotate tools/python/xen/xend/xenstore/xstransact.py @ 16702:9fe92a88912b

Fix xend xenstore handling.

xend can get into a situation where two processes are attempting to
interact with the xenstore socket, with disastrous results. Fix the
two bad users of xstransact, add a big warning, and fix the destructor
so future mistakes will be detected earlier.

Signed-off-by: John Levon <john.levon@sun.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Dec 27 12:27:34 2007 +0000 (2007-12-27)
parents 1b501311c778
children
rev   line source
cl349@6739 1 # Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
emellor@7108 2 # Copyright (C) 2005 XenSource Ltd
cl349@6739 3
cl349@6739 4 # This file is subject to the terms and conditions of the GNU General
cl349@6739 5 # Public License. See the file "COPYING" in the main directory of
cl349@6739 6 # this archive for more details.
cl349@6739 7
cl349@6816 8 from xen.xend.xenstore.xsutil import xshandle
cl349@6739 9
keir@16702 10 class xstransact:
keir@16702 11 """WARNING: Be very careful if you're instantiating an xstransact object
keir@16702 12 yourself (i.e. not using the capitalized static helpers like .Read().
keir@16702 13 It is essential that you clean up the object in place via
keir@16702 14 t.commit/abort(): GC can happen at any time, including contexts where
keir@16702 15 it's not safe to to use the shared xenstore socket fd. In particular,
keir@16702 16 if xend forks, and GC occurs, we can have two processes trying to
keir@16702 17 use the same xenstore fd, and all hell breaks loose.
keir@16702 18 """
emellor@7108 19
cl349@6739 20
emellor@7813 21 def __init__(self, path = ""):
emellor@7670 22
emellor@7456 23 self.in_transaction = False # Set this temporarily -- if this
emellor@7456 24 # constructor fails, then we need to
emellor@7456 25 # protect __del__.
ewan@12653 26
ewan@12653 27 assert path is not None
cl349@6739 28 self.path = path.rstrip("/")
kaf24@7318 29 self.transaction = xshandle().transaction_start()
emellor@7072 30 self.in_transaction = True
cl349@6739 31
cl349@6739 32 def __del__(self):
keir@16702 33 # see above.
cl349@6739 34 if self.in_transaction:
keir@16702 35 raise RuntimeError("ERROR: GC of live transaction")
cl349@6739 36
cl349@6739 37 def commit(self):
cl349@6739 38 if not self.in_transaction:
cl349@6739 39 raise RuntimeError
cl349@6739 40 self.in_transaction = False
kaf24@7318 41 rc = xshandle().transaction_end(self.transaction, False)
kaf24@7318 42 self.transaction = "0"
kaf24@7318 43 return rc
cl349@6739 44
cl349@6739 45 def abort(self):
kaf24@7318 46 if not self.in_transaction:
kaf24@7318 47 return True
cl349@6739 48 self.in_transaction = False
kaf24@7318 49 rc = xshandle().transaction_end(self.transaction, True)
kaf24@7318 50 self.transaction = "0"
kaf24@7318 51 return rc
cl349@6739 52
cl349@6739 53 def _read(self, key):
emellor@7813 54 path = self.prependPath(key)
emellor@7017 55 try:
kaf24@7318 56 return xshandle().read(self.transaction, path)
emellor@7017 57 except RuntimeError, ex:
emellor@7017 58 raise RuntimeError(ex.args[0],
emellor@7017 59 '%s, while reading %s' % (ex.args[1], path))
cl349@6739 60
cl349@6739 61 def read(self, *args):
emellor@7116 62 """If no arguments are given, return the value at this transaction's
emellor@7116 63 path. If one argument is given, treat that argument as a subpath to
emellor@7116 64 this transaction's path, and return the value at that path.
emellor@7116 65 Otherwise, treat each argument as a subpath to this transaction's
emellor@7116 66 path, and return a list composed of the values at each of those
emellor@7116 67 instead.
emellor@7116 68 """
cl349@6739 69 if len(args) == 0:
kaf24@7318 70 return xshandle().read(self.transaction, self.path)
cl349@6739 71 if len(args) == 1:
cl349@6739 72 return self._read(args[0])
cl349@6739 73 ret = []
cl349@6739 74 for key in args:
cl349@6739 75 ret.append(self._read(key))
cl349@6739 76 return ret
cl349@6739 77
cl349@6983 78 def _write(self, key, data):
emellor@7813 79 path = self.prependPath(key)
emellor@7017 80 try:
kaf24@7318 81 xshandle().write(self.transaction, path, data)
emellor@7017 82 except RuntimeError, ex:
emellor@7017 83 raise RuntimeError(ex.args[0],
emellor@7017 84 ('%s, while writing %s : %s' %
emellor@7017 85 (ex.args[1], path, str(data))))
cl349@6739 86
emellor@7714 87 def write(self, *args):
cl349@6739 88 if len(args) == 0:
cl349@6739 89 raise TypeError
cl349@6739 90 if isinstance(args[0], dict):
cl349@6739 91 for d in args:
cl349@6739 92 if not isinstance(d, dict):
cl349@6739 93 raise TypeError
cl349@6739 94 for key in d.keys():
emellor@6951 95 try:
cl349@6983 96 self._write(key, d[key])
emellor@6951 97 except TypeError, msg:
emellor@6951 98 raise TypeError('Writing %s: %s: %s' %
emellor@6951 99 (key, str(d[key]), msg))
cl349@6739 100 elif isinstance(args[0], list):
cl349@6739 101 for l in args:
cl349@6739 102 if not len(l) == 2:
cl349@6739 103 raise TypeError
cl349@6983 104 self._write(l[0], l[1])
cl349@6739 105 elif len(args) % 2 == 0:
cl349@6739 106 for i in range(len(args) / 2):
cl349@6983 107 self._write(args[i * 2], args[i * 2 + 1])
cl349@6739 108 else:
cl349@6739 109 raise TypeError
cl349@6739 110
cl349@6749 111 def _remove(self, key):
emellor@7813 112 path = self.prependPath(key)
kaf24@7318 113 return xshandle().rm(self.transaction, path)
cl349@6739 114
cl349@6749 115 def remove(self, *args):
emellor@6962 116 """If no arguments are given, remove this transaction's path.
emellor@6962 117 Otherwise, treat each argument as a subpath to this transaction's
emellor@6962 118 path, and remove each of those instead.
emellor@6962 119 """
cl349@6749 120 if len(args) == 0:
kaf24@7318 121 xshandle().rm(self.transaction, self.path)
emellor@6962 122 else:
emellor@6962 123 for key in args:
emellor@6962 124 self._remove(key)
cl349@6739 125
cl349@6749 126 def _list(self, key):
emellor@7813 127 path = self.prependPath(key)
kaf24@7318 128 l = xshandle().ls(self.transaction, path)
cl349@6816 129 if l:
cl349@6816 130 return map(lambda x: key + "/" + x, l)
cl349@6816 131 return []
cl349@6739 132
cl349@6749 133 def list(self, *args):
emellor@7108 134 """If no arguments are given, list this transaction's path, returning
emellor@7137 135 the entries therein, or the empty list if no entries are found.
emellor@7137 136 Otherwise, treat each argument as a subpath to this transaction's
emellor@7137 137 path, and return the cumulative listing of each of those instead.
emellor@7108 138 """
cl349@6749 139 if len(args) == 0:
kaf24@7318 140 ret = xshandle().ls(self.transaction, self.path)
emellor@7137 141 if ret is None:
emellor@7137 142 return []
emellor@7137 143 else:
emellor@7137 144 return ret
emellor@7108 145 else:
emellor@7108 146 ret = []
emellor@7108 147 for key in args:
emellor@7108 148 ret.extend(self._list(key))
emellor@7108 149 return ret
emellor@7108 150
emellor@7108 151
emellor@7108 152 def list_recursive_(self, subdir, keys):
cl349@6749 153 ret = []
emellor@7108 154 for key in keys:
emellor@7108 155 new_subdir = subdir + "/" + key
kaf24@7318 156 l = xshandle().ls(self.transaction, new_subdir)
emellor@7108 157 if l:
emellor@7108 158 ret.append([key, self.list_recursive_(new_subdir, l)])
emellor@7108 159 else:
kaf24@7318 160 ret.append([key, xshandle().read(self.transaction, new_subdir)])
cl349@6749 161 return ret
cl349@6739 162
emellor@7108 163
emellor@7108 164 def list_recursive(self, *args):
emellor@7137 165 """If no arguments are given, list this transaction's path, returning
emellor@7137 166 the entries therein, or the empty list if no entries are found.
emellor@7137 167 Otherwise, treat each argument as a subpath to this transaction's
emellor@7137 168 path, and return the cumulative listing of each of those instead.
emellor@7137 169 """
emellor@7108 170 if len(args) == 0:
emellor@7108 171 args = self.list()
emellor@7137 172 if args is None or len(args) == 0:
emellor@7137 173 return []
emellor@7108 174
emellor@7108 175 return self.list_recursive_(self.path, args)
emellor@7108 176
emellor@7108 177
cl349@6856 178 def gather(self, *args):
cl349@6861 179 if len(args) and type(args[0]) != tuple:
cl349@6861 180 args = args,
cl349@6856 181 ret = []
cl349@6856 182 for tup in args:
cl349@6856 183 if len(tup) == 2:
cl349@6856 184 (key, fn) = tup
cl349@6856 185 defval = None
cl349@6856 186 else:
cl349@6856 187 (key, fn, defval) = tup
emellor@7017 188
emellor@7017 189 val = self._read(key)
emellor@7857 190 # If fn is str, then this will successfully convert None to 'None'
emellor@7857 191 # (which we don't want). If it is int or float, then it will
emellor@7857 192 # throw ValueError on any non-convertible value. We check
emellor@7857 193 # explicitly for None, using defval instead, but allow ValueError
emellor@7857 194 # to propagate.
emellor@7017 195 if val is None:
cl349@6856 196 val = defval
emellor@7017 197 else:
emellor@7857 198 val = fn(val)
cl349@6856 199 ret.append(val)
cl349@6856 200 if len(ret) == 1:
cl349@6856 201 return ret[0]
cl349@6856 202 return ret
cl349@6856 203
cl349@6858 204 def store(self, *args):
cl349@6861 205 if len(args) and type(args[0]) != tuple:
cl349@6861 206 args = args,
cl349@6858 207 for tup in args:
cl349@6858 208 if len(tup) == 2:
cl349@6858 209 (key, val) = tup
cl349@6858 210 try:
emellor@8161 211 fmt = { str : "%s",
emellor@8161 212 int : "%i",
emellor@8161 213 float : "%f",
emellor@8161 214 long : "%li",
cl349@6859 215 type(None) : None }[type(val)]
cl349@6858 216 except KeyError:
cl349@6858 217 raise TypeError
cl349@6858 218 else:
cl349@6858 219 (key, val, fmt) = tup
cl349@6862 220 if val is None:
cl349@6862 221 self._remove(key)
cl349@6862 222 else:
cl349@6859 223 self._write(key, fmt % val)
cl349@6858 224
cl349@6749 225
emellor@8209 226 def mkdir(self, *args):
emellor@8209 227 if len(args) == 0:
emellor@8209 228 xshandle().mkdir(self.transaction, self.path)
emellor@8209 229 else:
emellor@8209 230 for key in args:
emellor@8209 231 xshandle().mkdir(self.transaction, self.prependPath(key))
emellor@8209 232
emellor@8209 233
emellor@10179 234 def get_permissions(self, *args):
emellor@10179 235 """If no arguments are given, return the permissions at this
emellor@10179 236 transaction's path. If one argument is given, treat that argument as
emellor@10179 237 a subpath to this transaction's path, and return the permissions at
emellor@10179 238 that path. Otherwise, treat each argument as a subpath to this
emellor@10179 239 transaction's path, and return a list composed of the permissions at
emellor@10179 240 each of those instead.
emellor@10179 241 """
emellor@10179 242 if len(args) == 0:
emellor@10179 243 return xshandle().get_permissions(self.transaction, self.path)
emellor@10179 244 if len(args) == 1:
emellor@10179 245 return self._get_permissions(args[0])
emellor@10179 246 ret = []
emellor@10179 247 for key in args:
emellor@10179 248 ret.append(self._get_permissions(key))
emellor@10179 249 return ret
emellor@10179 250
emellor@10179 251
emellor@10179 252 def _get_permissions(self, key):
emellor@10179 253 path = self.prependPath(key)
emellor@10179 254 try:
emellor@10179 255 return xshandle().get_permissions(self.transaction, path)
emellor@10179 256 except RuntimeError, ex:
emellor@10179 257 raise RuntimeError(ex.args[0],
emellor@10179 258 '%s, while getting permissions from %s' %
emellor@10179 259 (ex.args[1], path))
emellor@10179 260
emellor@10179 261
emellor@8209 262 def set_permissions(self, *args):
emellor@8209 263 if len(args) == 0:
emellor@8209 264 raise TypeError
emellor@8209 265 elif isinstance(args[0], str):
emellor@8209 266 self.callRebased(args[0], self.set_permissions, *args[1:])
emellor@8209 267 else:
emellor@8209 268 if not self.path:
emellor@8209 269 raise RuntimeError('Cannot set permissions on the root')
emellor@8209 270
emellor@8209 271 xshandle().set_permissions(self.transaction, self.path,
emellor@8209 272 list(args))
emellor@8209 273
emellor@8209 274
emellor@7813 275 def remove2(self, middlePath, *args):
emellor@7813 276 self.callRebased(middlePath, self.remove, *args)
emellor@7813 277
emellor@7813 278
emellor@7813 279 def write2(self, middlePath, *args):
emellor@7813 280 self.callRebased(middlePath, self.write, *args)
emellor@7813 281
emellor@7813 282
emellor@7813 283 def callRebased(self, middlePath, func, *args):
emellor@7813 284 oldpath = self.path
emellor@7813 285 self.path = self.prependPath(middlePath)
emellor@7813 286 try:
emellor@7813 287 func(*args)
emellor@7813 288 finally:
emellor@7813 289 self.path = oldpath
emellor@7813 290
emellor@7813 291
emellor@7813 292 def prependPath(self, key):
emellor@7813 293 if self.path:
emellor@7813 294 return self.path + '/' + key
emellor@7813 295 else:
emellor@7813 296 return key
emellor@7813 297
emellor@7813 298
cl349@6739 299 def Read(cls, path, *args):
emellor@7116 300 """If only one argument is given (path), return the value stored at
emellor@7116 301 that path. If two arguments are given, treat the second argument as a
emellor@7116 302 subpath within the first, and return the value at the composed path.
emellor@7116 303 Otherwise, treat each argument after the first as a subpath to the
emellor@7116 304 given path, and return a list composed of the values at each of those
emellor@7116 305 instead. This operation is performed inside a transaction.
emellor@7116 306 """
emellor@8209 307 return complete(path, lambda t: t.read(*args))
cl349@6739 308 Read = classmethod(Read)
cl349@6739 309
emellor@7714 310 def Write(cls, path, *args):
emellor@8209 311 complete(path, lambda t: t.write(*args))
cl349@6739 312 Write = classmethod(Write)
cl349@6739 313
cl349@6816 314 def Remove(cls, path, *args):
emellor@6962 315 """If only one argument is given (path), remove it. Otherwise, treat
emellor@6962 316 each further argument as a subpath to the given path, and remove each
emellor@6962 317 of those instead. This operation is performed inside a transaction.
emellor@6962 318 """
emellor@8209 319 complete(path, lambda t: t.remove(*args))
cl349@6749 320 Remove = classmethod(Remove)
cl349@6739 321
cl349@6749 322 def List(cls, path, *args):
emellor@7137 323 """If only one argument is given (path), list its contents, returning
emellor@7137 324 the entries therein, or the empty list if no entries are found.
emellor@7137 325 Otherwise, treat each further argument as a subpath to the given path,
emellor@7137 326 and return the cumulative listing of each of those instead. This
emellor@7137 327 operation is performed inside a transaction.
emellor@7116 328 """
emellor@8209 329 return complete(path, lambda t: t.list(*args))
cl349@6749 330 List = classmethod(List)
cl349@6856 331
emellor@7137 332 def ListRecursive(cls, path, *args):
emellor@7137 333 """If only one argument is given (path), list its contents
emellor@7137 334 recursively, returning the entries therein, or the empty list if no
emellor@7137 335 entries are found. Otherwise, treat each further argument as a
emellor@7137 336 subpath to the given path, and return the cumulative listing of each
emellor@7137 337 of those instead. This operation is performed inside a transaction.
emellor@7137 338 """
emellor@8209 339 return complete(path, lambda t: t.list_recursive(*args))
emellor@7137 340 ListRecursive = classmethod(ListRecursive)
emellor@7137 341
cl349@6856 342 def Gather(cls, path, *args):
emellor@8209 343 return complete(path, lambda t: t.gather(*args))
cl349@6856 344 Gather = classmethod(Gather)
cl349@6858 345
cl349@6858 346 def Store(cls, path, *args):
emellor@8209 347 complete(path, lambda t: t.store(*args))
emellor@8209 348 Store = classmethod(Store)
emellor@8209 349
emellor@8209 350 def SetPermissions(cls, path, *args):
emellor@8209 351 complete(path, lambda t: t.set_permissions(*args))
emellor@8209 352 SetPermissions = classmethod(SetPermissions)
emellor@8209 353
emellor@8209 354 def Mkdir(cls, path, *args):
emellor@8209 355 complete(path, lambda t: t.mkdir(*args))
emellor@8209 356 Mkdir = classmethod(Mkdir)
cl349@6858 357
emellor@8209 358
emellor@8209 359 def complete(path, f):
emellor@8209 360 while True:
emellor@8209 361 t = xstransact(path)
emellor@8209 362 try:
emellor@8209 363 result = f(t)
emellor@8209 364 if t.commit():
emellor@8209 365 return result
emellor@8209 366 except:
emellor@8209 367 t.abort()
emellor@8209 368 raise