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 strictly positive integer 326 TStrictPositiveInt = \ 327 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0)) 328 329 #: a strictly negative integer (0 > value) 330 TStrictNegativeInt = \ 331 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0))) 332 333 #: a positive float 334 TPositiveFloat = \ 335 TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0)) 336 337 #: Job ID 338 TJobId = WithDesc("JobId")(TOr(TPositiveInt, 339 TRegex(re.compile("^%s$" % 340 constants.JOB_ID_TEMPLATE)))) 341 342 #: Number 343 TNumber = TOr(TInt, TFloat) 344 345 #: Relative job ID 346 TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
347 348 349 -def TListOf(my_type):
350 """Checks if a given value is a list with all elements of the same type. 351 352 """ 353 desc = WithDesc("List of %s" % (Parens(my_type), )) 354 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
355
356 357 -def TDictOf(key_type, val_type):
358 """Checks a dict type for the type of its key/values. 359 360 """ 361 desc = WithDesc("Dictionary with keys of %s and values of %s" % 362 (Parens(key_type), Parens(val_type))) 363 364 def fn(container): 365 return (compat.all(key_type(v) for v in container.keys()) and 366 compat.all(val_type(v) for v in container.values()))
367 368 return desc(TAnd(TDict, fn)) 369
370 371 -def _TStrictDictCheck(require_all, exclusive, items, val):
372 """Helper function for L{TStrictDict}. 373 374 """ 375 notfound_fn = lambda _: not exclusive 376 377 if require_all and not frozenset(val.keys()).issuperset(items.keys()): 378 # Requires items not found in value 379 return False 380 381 return compat.all(items.get(key, notfound_fn)(value) 382 for (key, value) in val.items())
383
384 385 -def TStrictDict(require_all, exclusive, items):
386 """Strict dictionary check with specific keys. 387 388 @type require_all: boolean 389 @param require_all: Whether all keys in L{items} are required 390 @type exclusive: boolean 391 @param exclusive: Whether only keys listed in L{items} should be accepted 392 @type items: dictionary 393 @param items: Mapping from key (string) to verification function 394 395 """ 396 descparts = ["Dictionary containing"] 397 398 if exclusive: 399 descparts.append(" none but the") 400 401 if require_all: 402 descparts.append(" required") 403 404 if len(items) == 1: 405 descparts.append(" key ") 406 else: 407 descparts.append(" keys ") 408 409 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value) 410 for (key, value) in items.items())) 411 412 desc = WithDesc("".join(descparts)) 413 414 return desc(TAnd(TDict, 415 compat.partial(_TStrictDictCheck, require_all, exclusive, 416 items)))
417
418 419 -def TItems(items):
420 """Checks individual items of a container. 421 422 If the verified value and the list of expected items differ in length, this 423 check considers only as many items as are contained in the shorter list. Use 424 L{TIsLength} to enforce a certain length. 425 426 @type items: list 427 @param items: List of checks 428 429 """ 430 assert items, "Need items" 431 432 text = ["Item", "item"] 433 desc = WithDesc(utils.CommaJoin("%s %s is %s" % 434 (text[int(idx > 0)], idx, Parens(check)) 435 for (idx, check) in enumerate(items))) 436 437 return desc(lambda value: compat.all(check(i) 438 for (check, i) in zip(items, value)))
439