Package ganeti :: Module ht
[hide private]
[frames] | no frames]

Source Code for Module ganeti.ht

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2010, 2011, 2012 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Module implementing the parameter types code.""" 
 23   
 24  import re 
 25  import operator 
 26   
 27  from ganeti import compat 
 28  from ganeti import utils 
 29  from ganeti import constants 
 30   
 31   
 32  _PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$") 
33 34 35 -def Parens(text):
36 """Enclose text in parens if necessary. 37 38 @param text: Text 39 40 """ 41 text = str(text) 42 43 if _PAREN_RE.match(text): 44 return text 45 else: 46 return "(%s)" % text
47
48 49 -class _WrapperBase(object):
50 __slots__ = [ 51 "_fn", 52 "_text", 53 ] 54
55 - def __init__(self, text, fn):
56 """Initializes this class. 57 58 @param text: Description 59 @param fn: Wrapped function 60 61 """ 62 assert text.strip() 63 64 self._text = text 65 self._fn = fn
66
67 - def __call__(self, *args):
68 return self._fn(*args)
69
70 71 -class _DescWrapper(_WrapperBase):
72 """Wrapper class for description text. 73 74 """
75 - def __str__(self):
76 return self._text
77
78 79 -class _CommentWrapper(_WrapperBase):
80 """Wrapper class for comment. 81 82 """
83 - def __str__(self):
84 return "%s [%s]" % (self._fn, self._text)
85
86 87 -def WithDesc(text):
88 """Builds wrapper class with description text. 89 90 @type text: string 91 @param text: Description text 92 @return: Callable class 93 94 """ 95 assert text[0] == text[0].upper() 96 97 return compat.partial(_DescWrapper, text)
98
99 100 -def Comment(text):
101 """Builds wrapper for adding comment to description text. 102 103 @type text: string 104 @param text: Comment text 105 @return: Callable class 106 107 """ 108 assert not frozenset(text).intersection("[]") 109 110 return compat.partial(_CommentWrapper, text)
111
112 113 -def CombinationDesc(op, args, fn):
114 """Build description for combinating operator. 115 116 @type op: string 117 @param op: Operator as text (e.g. "and") 118 @type args: list 119 @param args: Operator arguments 120 @type fn: callable 121 @param fn: Wrapped function 122 123 """ 124 # Some type descriptions are rather long. If "None" is listed at the 125 # end or somewhere in between it is easily missed. Therefore it should 126 # be at the beginning, e.g. "None or (long description)". 127 if __debug__ and TNone in args and args.index(TNone) > 0: 128 raise Exception("TNone must be listed first") 129 130 if len(args) == 1: 131 descr = str(args[0]) 132 else: 133 descr = (" %s " % op).join(Parens(i) for i in args) 134 135 return WithDesc(descr)(fn)
136
137 138 # Modifiable default values; need to define these here before the 139 # actual LUs 140 141 @WithDesc(str([])) 142 -def EmptyList():
143 """Returns an empty list. 144 145 """ 146 return []
147
148 149 @WithDesc(str({})) 150 -def EmptyDict():
151 """Returns an empty dict. 152 153 """ 154 return {}
155 156 157 #: The without-default default value 158 NoDefault = object() 159 160 161 #: The no-type (value too complex to check it in the type system) 162 NoType = object()
163 164 165 # Some basic types 166 @WithDesc("Anything") 167 -def TAny(_):
168 """Accepts any value. 169 170 """ 171 return True
172
173 174 @WithDesc("NotNone") 175 -def TNotNone(val):
176 """Checks if the given value is not None. 177 178 """ 179 return val is not None
180
181 182 @WithDesc("None") 183 -def TNone(val):
184 """Checks if the given value is None. 185 186 """ 187 return val is None
188
189 190 @WithDesc("ValueNone") 191 -def TValueNone(val):
192 """Checks if the given value is L{constants.VALUE_NONE}. 193 194 """ 195 return val == constants.VALUE_NONE
196
197 198 @WithDesc("Boolean") 199 -def TBool(val):
200 """Checks if the given value is a boolean. 201 202 """ 203 return isinstance(val, bool)
204
205 206 @WithDesc("Integer") 207 -def TInt(val):
208 """Checks if the given value is an integer. 209 210 """ 211 # For backwards compatibility with older Python versions, boolean values are 212 # also integers and should be excluded in this test. 213 # 214 # >>> (isinstance(False, int), isinstance(True, int)) 215 # (True, True) 216 return isinstance(val, (int, long)) and not isinstance(val, bool)
217
218 219 @WithDesc("Float") 220 -def TFloat(val):
221 """Checks if the given value is a float. 222 223 """ 224 return isinstance(val, float)
225
226 227 @WithDesc("String") 228 -def TString(val):
229 """Checks if the given value is a string. 230 231 """ 232 return isinstance(val, basestring)
233
234 235 @WithDesc("EvalToTrue") 236 -def TTrue(val):
237 """Checks if a given value evaluates to a boolean True value. 238 239 """ 240 return bool(val)
241
242 243 -def TElemOf(target_list):
244 """Builds a function that checks if a given value is a member of a list. 245 246 """ 247 def fn(val): 248 return val in target_list
249 250 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn) 251
252 253 # Container types 254 @WithDesc("List") 255 -def TList(val):
256 """Checks if the given value is a list. 257 258 """ 259 return isinstance(val, list)
260
261 262 @WithDesc("Tuple") 263 -def TTuple(val):
264 """Checks if the given value is a tuple. 265 266 """ 267 return isinstance(val, tuple)
268
269 270 @WithDesc("Dictionary") 271 -def TDict(val):
272 """Checks if the given value is a dictionary. 273 274 """ 275 return isinstance(val, dict)
276
277 278 -def TIsLength(size):
279 """Check is the given container is of the given size. 280 281 """ 282 def fn(container): 283 return len(container) == size
284 285 return WithDesc("Length %s" % (size, ))(fn) 286
287 288 # Combinator types 289 -def TAnd(*args):
290 """Combine multiple functions using an AND operation. 291 292 """ 293 def fn(val): 294 return compat.all(t(val) for t in args)
295 296 return CombinationDesc("and", args, fn) 297
298 299 -def TOr(*args):
300 """Combine multiple functions using an OR operation. 301 302 """ 303 def fn(val): 304 return compat.any(t(val) for t in args)
305 306 return CombinationDesc("or", args, fn) 307
308 309 -def TMap(fn, test):
310 """Checks that a modified version of the argument passes the given test. 311 312 """ 313 return WithDesc("Result of %s must be %s" % 314 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
315
316 317 -def TRegex(pobj):
318 """Checks whether a string matches a specific regular expression. 319 320 @param pobj: Compiled regular expression as returned by C{re.compile} 321 322 """ 323 desc = WithDesc("String matching regex \"%s\"" % 324 pobj.pattern.encode("string_escape")) 325 326 return desc(TAnd(TString, pobj.match))
327
328 329 -def TMaybe(test):
330 """Wrap a test in a TOr(TNone, test). 331 332 This makes it easier to define TMaybe* types. 333 334 """ 335 return TOr(TNone, test)
336
337 338 -def TMaybeValueNone(test):
339 """Used for unsetting values. 340 341 """ 342 return TMaybe(TOr(TValueNone, test))
343 344 345 # Type aliases 346 347 #: a non-empty string 348 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue)) 349 350 #: a maybe non-empty string 351 TMaybeString = TMaybe(TNonEmptyString) 352 353 #: a maybe boolean (bool or none) 354 TMaybeBool = TMaybe(TBool) 355 356 #: Maybe a dictionary (dict or None) 357 TMaybeDict = TMaybe(TDict) 358 359 #: Maybe a list (list or None) 360 TMaybeList = TMaybe(TList) 361 362 #: a non-negative integer (value >= 0) 363 TNonNegativeInt = \ 364 TAnd(TInt, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0)) 365 366 #: a positive integer (value > 0) 367 TPositiveInt = \ 368 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0)) 369 370 #: a maybe positive integer (positive integer or None) 371 TMaybePositiveInt = TMaybe(TPositiveInt) 372 373 #: a negative integer (value < 0) 374 TNegativeInt = \ 375 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0))) 376 377 #: a positive float 378 TNonNegativeFloat = \ 379 TAnd(TFloat, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0.0)) 380 381 #: Job ID 382 TJobId = WithDesc("JobId")(TOr(TNonNegativeInt, 383 TRegex(re.compile("^%s$" % 384 constants.JOB_ID_TEMPLATE)))) 385 386 #: Number 387 TNumber = TOr(TInt, TFloat) 388 389 #: Relative job ID 390 TRelativeJobId = WithDesc("RelativeJobId")(TNegativeInt)
391 392 393 -def TInstanceOf(cls):
394 """Checks if a given value is an instance of C{cls}. 395 396 @type cls: class 397 @param cls: Class object 398 399 """ 400 name = "%s.%s" % (cls.__module__, cls.__name__) 401 402 desc = WithDesc("Instance of %s" % (Parens(name), )) 403 404 return desc(lambda val: isinstance(val, cls))
405
406 407 -def TListOf(my_type):
408 """Checks if a given value is a list with all elements of the same type. 409 410 """ 411 desc = WithDesc("List of %s" % (Parens(my_type), )) 412 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
413 414 415 TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
416 417 418 -def TDictOf(key_type, val_type):
419 """Checks a dict type for the type of its key/values. 420 421 """ 422 desc = WithDesc("Dictionary with keys of %s and values of %s" % 423 (Parens(key_type), Parens(val_type))) 424 425 def fn(container): 426 return (compat.all(key_type(v) for v in container.keys()) and 427 compat.all(val_type(v) for v in container.values()))
428 429 return desc(TAnd(TDict, fn)) 430
431 432 -def _TStrictDictCheck(require_all, exclusive, items, val):
433 """Helper function for L{TStrictDict}. 434 435 """ 436 notfound_fn = lambda _: not exclusive 437 438 if require_all and not frozenset(val.keys()).issuperset(items.keys()): 439 # Requires items not found in value 440 return False 441 442 return compat.all(items.get(key, notfound_fn)(value) 443 for (key, value) in val.items())
444
445 446 -def TStrictDict(require_all, exclusive, items):
447 """Strict dictionary check with specific keys. 448 449 @type require_all: boolean 450 @param require_all: Whether all keys in L{items} are required 451 @type exclusive: boolean 452 @param exclusive: Whether only keys listed in L{items} should be accepted 453 @type items: dictionary 454 @param items: Mapping from key (string) to verification function 455 456 """ 457 descparts = ["Dictionary containing"] 458 459 if exclusive: 460 descparts.append(" none but the") 461 462 if require_all: 463 descparts.append(" required") 464 465 if len(items) == 1: 466 descparts.append(" key ") 467 else: 468 descparts.append(" keys ") 469 470 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value) 471 for (key, value) in items.items())) 472 473 desc = WithDesc("".join(descparts)) 474 475 return desc(TAnd(TDict, 476 compat.partial(_TStrictDictCheck, require_all, exclusive, 477 items)))
478
479 480 -def TItems(items):
481 """Checks individual items of a container. 482 483 If the verified value and the list of expected items differ in length, this 484 check considers only as many items as are contained in the shorter list. Use 485 L{TIsLength} to enforce a certain length. 486 487 @type items: list 488 @param items: List of checks 489 490 """ 491 assert items, "Need items" 492 493 text = ["Item", "item"] 494 desc = WithDesc(utils.CommaJoin("%s %s is %s" % 495 (text[int(idx > 0)], idx, Parens(check)) 496 for (idx, check) in enumerate(items))) 497 498 return desc(lambda value: compat.all(check(i) 499 for (check, i) in zip(items, value)))
500