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

Source Code for Module ganeti.ht

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2010, 2011 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 if len(args) == 1: 125 descr = str(args[0]) 126 else: 127 descr = (" %s " % op).join(Parens(i) for i in args) 128 129 return WithDesc(descr)(fn)
130
131 132 # Modifiable default values; need to define these here before the 133 # actual LUs 134 135 @WithDesc(str([])) 136 -def EmptyList():
137 """Returns an empty list. 138 139 """ 140 return []
141
142 143 @WithDesc(str({})) 144 -def EmptyDict():
145 """Returns an empty dict. 146 147 """ 148 return {}
149 150 151 #: The without-default default value 152 NoDefault = object() 153 154 155 #: The no-type (value too complex to check it in the type system) 156 NoType = object()
157 158 159 # Some basic types 160 @WithDesc("Anything") 161 -def TAny(_):
162 """Accepts any value. 163 164 """ 165 return True
166
167 168 @WithDesc("NotNone") 169 -def TNotNone(val):
170 """Checks if the given value is not None. 171 172 """ 173 return val is not None
174
175 176 @WithDesc("None") 177 -def TNone(val):
178 """Checks if the given value is None. 179 180 """ 181 return val is None
182
183 184 @WithDesc("Boolean") 185 -def TBool(val):
186 """Checks if the given value is a boolean. 187 188 """ 189 return isinstance(val, bool)
190
191 192 @WithDesc("Integer") 193 -def TInt(val):
194 """Checks if the given value is an integer. 195 196 """ 197 # For backwards compatibility with older Python versions, boolean values are 198 # also integers and should be excluded in this test. 199 # 200 # >>> (isinstance(False, int), isinstance(True, int)) 201 # (True, True) 202 return isinstance(val, (int, long)) and not isinstance(val, bool)
203
204 205 @WithDesc("Float") 206 -def TFloat(val):
207 """Checks if the given value is a float. 208 209 """ 210 return isinstance(val, float)
211
212 213 @WithDesc("String") 214 -def TString(val):
215 """Checks if the given value is a string. 216 217 """ 218 return isinstance(val, basestring)
219
220 221 @WithDesc("EvalToTrue") 222 -def TTrue(val):
223 """Checks if a given value evaluates to a boolean True value. 224 225 """ 226 return bool(val)
227
228 229 -def TElemOf(target_list):
230 """Builds a function that checks if a given value is a member of a list. 231 232 """ 233 def fn(val): 234 return val in target_list
235 236 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn) 237
238 239 # Container types 240 @WithDesc("List") 241 -def TList(val):
242 """Checks if the given value is a list. 243 244 """ 245 return isinstance(val, list)
246
247 248 @WithDesc("Dictionary") 249 -def TDict(val):
250 """Checks if the given value is a dictionary. 251 252 """ 253 return isinstance(val, dict)
254
255 256 -def TIsLength(size):
257 """Check is the given container is of the given size. 258 259 """ 260 def fn(container): 261 return len(container) == size
262 263 return WithDesc("Length %s" % (size, ))(fn) 264
265 266 # Combinator types 267 -def TAnd(*args):
268 """Combine multiple functions using an AND operation. 269 270 """ 271 def fn(val): 272 return compat.all(t(val) for t in args)
273 274 return CombinationDesc("and", args, fn) 275
276 277 -def TOr(*args):
278 """Combine multiple functions using an AND operation. 279 280 """ 281 def fn(val): 282 return compat.any(t(val) for t in args)
283 284 return CombinationDesc("or", args, fn) 285
286 287 -def TMap(fn, test):
288 """Checks that a modified version of the argument passes the given test. 289 290 """ 291 return WithDesc("Result of %s must be %s" % 292 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
293
294 295 -def TRegex(pobj):
296 """Checks whether a string matches a specific regular expression. 297 298 @param pobj: Compiled regular expression as returned by C{re.compile} 299 300 """ 301 desc = WithDesc("String matching regex \"%s\"" % 302 pobj.pattern.encode("string_escape")) 303 304 return desc(TAnd(TString, pobj.match))
305 306 307 # Type aliases 308 309 #: a non-empty string 310 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue)) 311 312 #: a maybe non-empty string 313 TMaybeString = TOr(TNonEmptyString, TNone) 314 315 #: a maybe boolean (bool or none) 316 TMaybeBool = TOr(TBool, TNone) 317 318 #: Maybe a dictionary (dict or None) 319 TMaybeDict = TOr(TDict, TNone) 320 321 #: a positive integer 322 TPositiveInt = \ 323 TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0)) 324 325 #: a maybe positive integer (positive integer or None) 326 TMaybePositiveInt = TOr(TPositiveInt, TNone) 327 328 #: a strictly positive integer 329 TStrictPositiveInt = \ 330 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0)) 331 332 #: a maybe strictly positive integer (strictly positive integer or None) 333 TMaybeStrictPositiveInt = TOr(TStrictPositiveInt, TNone) 334 335 #: a strictly negative integer (0 > value) 336 TStrictNegativeInt = \ 337 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0))) 338 339 #: a positive float 340 TPositiveFloat = \ 341 TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0)) 342 343 #: Job ID 344 TJobId = WithDesc("JobId")(TOr(TPositiveInt, 345 TRegex(re.compile("^%s$" % 346 constants.JOB_ID_TEMPLATE)))) 347 348 #: Number 349 TNumber = TOr(TInt, TFloat) 350 351 #: Relative job ID 352 TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
353 354 355 -def TListOf(my_type):
356 """Checks if a given value is a list with all elements of the same type. 357 358 """ 359 desc = WithDesc("List of %s" % (Parens(my_type), )) 360 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
361 362 363 TMaybeListOf = lambda item_type: TOr(TNone, TListOf(item_type))
364 365 366 -def TDictOf(key_type, val_type):
367 """Checks a dict type for the type of its key/values. 368 369 """ 370 desc = WithDesc("Dictionary with keys of %s and values of %s" % 371 (Parens(key_type), Parens(val_type))) 372 373 def fn(container): 374 return (compat.all(key_type(v) for v in container.keys()) and 375 compat.all(val_type(v) for v in container.values()))
376 377 return desc(TAnd(TDict, fn)) 378
379 380 -def _TStrictDictCheck(require_all, exclusive, items, val):
381 """Helper function for L{TStrictDict}. 382 383 """ 384 notfound_fn = lambda _: not exclusive 385 386 if require_all and not frozenset(val.keys()).issuperset(items.keys()): 387 # Requires items not found in value 388 return False 389 390 return compat.all(items.get(key, notfound_fn)(value) 391 for (key, value) in val.items())
392
393 394 -def TStrictDict(require_all, exclusive, items):
395 """Strict dictionary check with specific keys. 396 397 @type require_all: boolean 398 @param require_all: Whether all keys in L{items} are required 399 @type exclusive: boolean 400 @param exclusive: Whether only keys listed in L{items} should be accepted 401 @type items: dictionary 402 @param items: Mapping from key (string) to verification function 403 404 """ 405 descparts = ["Dictionary containing"] 406 407 if exclusive: 408 descparts.append(" none but the") 409 410 if require_all: 411 descparts.append(" required") 412 413 if len(items) == 1: 414 descparts.append(" key ") 415 else: 416 descparts.append(" keys ") 417 418 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value) 419 for (key, value) in items.items())) 420 421 desc = WithDesc("".join(descparts)) 422 423 return desc(TAnd(TDict, 424 compat.partial(_TStrictDictCheck, require_all, exclusive, 425 items)))
426
427 428 -def TItems(items):
429 """Checks individual items of a container. 430 431 If the verified value and the list of expected items differ in length, this 432 check considers only as many items as are contained in the shorter list. Use 433 L{TIsLength} to enforce a certain length. 434 435 @type items: list 436 @param items: List of checks 437 438 """ 439 assert items, "Need items" 440 441 text = ["Item", "item"] 442 desc = WithDesc(utils.CommaJoin("%s %s is %s" % 443 (text[int(idx > 0)], idx, Parens(check)) 444 for (idx, check) in enumerate(items))) 445 446 return desc(lambda value: compat.all(check(i) 447 for (check, i) in zip(items, value)))
448