Statistics
| Branch: | Tag: | Revision:

root / lib / ht.py @ b99b607f

History | View | Annotate | Download (8.4 kB)

1 62e0e880 Iustin Pop
#
2 62e0e880 Iustin Pop
#
3 62e0e880 Iustin Pop
4 8c9ee749 Michael Hanselmann
# Copyright (C) 2010, 2011 Google Inc.
5 62e0e880 Iustin Pop
#
6 62e0e880 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 62e0e880 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 62e0e880 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 62e0e880 Iustin Pop
# (at your option) any later version.
10 62e0e880 Iustin Pop
#
11 62e0e880 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 62e0e880 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 62e0e880 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 62e0e880 Iustin Pop
# General Public License for more details.
15 62e0e880 Iustin Pop
#
16 62e0e880 Iustin Pop
# You should have received a copy of the GNU General Public License
17 62e0e880 Iustin Pop
# along with this program; if not, write to the Free Software
18 62e0e880 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 62e0e880 Iustin Pop
# 02110-1301, USA.
20 62e0e880 Iustin Pop
21 62e0e880 Iustin Pop
22 62e0e880 Iustin Pop
"""Module implementing the parameter types code."""
23 62e0e880 Iustin Pop
24 8c9ee749 Michael Hanselmann
import re
25 8c9ee749 Michael Hanselmann
26 62e0e880 Iustin Pop
from ganeti import compat
27 8c9ee749 Michael Hanselmann
from ganeti import utils
28 8620f50e Michael Hanselmann
from ganeti import constants
29 8c9ee749 Michael Hanselmann
30 8c9ee749 Michael Hanselmann
31 8c9ee749 Michael Hanselmann
_PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
32 8c9ee749 Michael Hanselmann
33 8c9ee749 Michael Hanselmann
34 8c9ee749 Michael Hanselmann
def Parens(text):
35 8c9ee749 Michael Hanselmann
  """Enclose text in parens if necessary.
36 8c9ee749 Michael Hanselmann

37 8c9ee749 Michael Hanselmann
  @param text: Text
38 8c9ee749 Michael Hanselmann

39 8c9ee749 Michael Hanselmann
  """
40 8c9ee749 Michael Hanselmann
  text = str(text)
41 8c9ee749 Michael Hanselmann
42 8c9ee749 Michael Hanselmann
  if _PAREN_RE.match(text):
43 8c9ee749 Michael Hanselmann
    return text
44 8c9ee749 Michael Hanselmann
  else:
45 8c9ee749 Michael Hanselmann
    return "(%s)" % text
46 8c9ee749 Michael Hanselmann
47 8c9ee749 Michael Hanselmann
48 8c9ee749 Michael Hanselmann
def WithDesc(text):
49 8c9ee749 Michael Hanselmann
  """Builds wrapper class with description text.
50 8c9ee749 Michael Hanselmann

51 8c9ee749 Michael Hanselmann
  @type text: string
52 8c9ee749 Michael Hanselmann
  @param text: Description text
53 8c9ee749 Michael Hanselmann
  @return: Callable class
54 8c9ee749 Michael Hanselmann

55 8c9ee749 Michael Hanselmann
  """
56 8c9ee749 Michael Hanselmann
  assert text[0] == text[0].upper()
57 8c9ee749 Michael Hanselmann
58 8c9ee749 Michael Hanselmann
  class wrapper(object): # pylint: disable-msg=C0103
59 8c9ee749 Michael Hanselmann
    __slots__ = ["__call__"]
60 8c9ee749 Michael Hanselmann
61 8c9ee749 Michael Hanselmann
    def __init__(self, fn):
62 8c9ee749 Michael Hanselmann
      """Initializes this class.
63 8c9ee749 Michael Hanselmann

64 8c9ee749 Michael Hanselmann
      @param fn: Wrapped function
65 8c9ee749 Michael Hanselmann

66 8c9ee749 Michael Hanselmann
      """
67 8c9ee749 Michael Hanselmann
      self.__call__ = fn
68 8c9ee749 Michael Hanselmann
69 8c9ee749 Michael Hanselmann
    def __str__(self):
70 8c9ee749 Michael Hanselmann
      return text
71 8c9ee749 Michael Hanselmann
72 8c9ee749 Michael Hanselmann
  return wrapper
73 8c9ee749 Michael Hanselmann
74 8c9ee749 Michael Hanselmann
75 8c9ee749 Michael Hanselmann
def CombinationDesc(op, args, fn):
76 8c9ee749 Michael Hanselmann
  """Build description for combinating operator.
77 8c9ee749 Michael Hanselmann

78 8c9ee749 Michael Hanselmann
  @type op: string
79 8c9ee749 Michael Hanselmann
  @param op: Operator as text (e.g. "and")
80 8c9ee749 Michael Hanselmann
  @type args: list
81 8c9ee749 Michael Hanselmann
  @param args: Operator arguments
82 8c9ee749 Michael Hanselmann
  @type fn: callable
83 8c9ee749 Michael Hanselmann
  @param fn: Wrapped function
84 8c9ee749 Michael Hanselmann

85 8c9ee749 Michael Hanselmann
  """
86 8c9ee749 Michael Hanselmann
  if len(args) == 1:
87 8c9ee749 Michael Hanselmann
    descr = str(args[0])
88 8c9ee749 Michael Hanselmann
  else:
89 8c9ee749 Michael Hanselmann
    descr = (" %s " % op).join(Parens(i) for i in args)
90 8c9ee749 Michael Hanselmann
91 8c9ee749 Michael Hanselmann
  return WithDesc(descr)(fn)
92 8c9ee749 Michael Hanselmann
93 62e0e880 Iustin Pop
94 62e0e880 Iustin Pop
# Modifiable default values; need to define these here before the
95 62e0e880 Iustin Pop
# actual LUs
96 62e0e880 Iustin Pop
97 8c9ee749 Michael Hanselmann
@WithDesc(str([]))
98 62e0e880 Iustin Pop
def EmptyList():
99 62e0e880 Iustin Pop
  """Returns an empty list.
100 62e0e880 Iustin Pop

101 62e0e880 Iustin Pop
  """
102 62e0e880 Iustin Pop
  return []
103 62e0e880 Iustin Pop
104 62e0e880 Iustin Pop
105 8c9ee749 Michael Hanselmann
@WithDesc(str({}))
106 62e0e880 Iustin Pop
def EmptyDict():
107 62e0e880 Iustin Pop
  """Returns an empty dict.
108 62e0e880 Iustin Pop

109 62e0e880 Iustin Pop
  """
110 62e0e880 Iustin Pop
  return {}
111 62e0e880 Iustin Pop
112 62e0e880 Iustin Pop
113 62e0e880 Iustin Pop
#: The without-default default value
114 62e0e880 Iustin Pop
NoDefault = object()
115 62e0e880 Iustin Pop
116 62e0e880 Iustin Pop
117 8c9ee749 Michael Hanselmann
#: The no-type (value too complex to check it in the type system)
118 62e0e880 Iustin Pop
NoType = object()
119 62e0e880 Iustin Pop
120 62e0e880 Iustin Pop
121 62e0e880 Iustin Pop
# Some basic types
122 8620f50e Michael Hanselmann
@WithDesc("Anything")
123 8620f50e Michael Hanselmann
def TAny(_):
124 8620f50e Michael Hanselmann
  """Accepts any value.
125 8620f50e Michael Hanselmann

126 8620f50e Michael Hanselmann
  """
127 8620f50e Michael Hanselmann
  return True
128 8620f50e Michael Hanselmann
129 8620f50e Michael Hanselmann
130 8c9ee749 Michael Hanselmann
@WithDesc("NotNone")
131 62e0e880 Iustin Pop
def TNotNone(val):
132 62e0e880 Iustin Pop
  """Checks if the given value is not None.
133 62e0e880 Iustin Pop

134 62e0e880 Iustin Pop
  """
135 62e0e880 Iustin Pop
  return val is not None
136 62e0e880 Iustin Pop
137 62e0e880 Iustin Pop
138 8c9ee749 Michael Hanselmann
@WithDesc("None")
139 62e0e880 Iustin Pop
def TNone(val):
140 62e0e880 Iustin Pop
  """Checks if the given value is None.
141 62e0e880 Iustin Pop

142 62e0e880 Iustin Pop
  """
143 62e0e880 Iustin Pop
  return val is None
144 62e0e880 Iustin Pop
145 62e0e880 Iustin Pop
146 8c9ee749 Michael Hanselmann
@WithDesc("Boolean")
147 62e0e880 Iustin Pop
def TBool(val):
148 62e0e880 Iustin Pop
  """Checks if the given value is a boolean.
149 62e0e880 Iustin Pop

150 62e0e880 Iustin Pop
  """
151 62e0e880 Iustin Pop
  return isinstance(val, bool)
152 62e0e880 Iustin Pop
153 62e0e880 Iustin Pop
154 8c9ee749 Michael Hanselmann
@WithDesc("Integer")
155 62e0e880 Iustin Pop
def TInt(val):
156 62e0e880 Iustin Pop
  """Checks if the given value is an integer.
157 62e0e880 Iustin Pop

158 62e0e880 Iustin Pop
  """
159 8568de9e Michael Hanselmann
  # For backwards compatibility with older Python versions, boolean values are
160 8568de9e Michael Hanselmann
  # also integers and should be excluded in this test.
161 8568de9e Michael Hanselmann
  #
162 8568de9e Michael Hanselmann
  # >>> (isinstance(False, int), isinstance(True, int))
163 8568de9e Michael Hanselmann
  # (True, True)
164 b99b607f Michael Hanselmann
  return isinstance(val, (int, long)) and not isinstance(val, bool)
165 62e0e880 Iustin Pop
166 62e0e880 Iustin Pop
167 8c9ee749 Michael Hanselmann
@WithDesc("Float")
168 62e0e880 Iustin Pop
def TFloat(val):
169 62e0e880 Iustin Pop
  """Checks if the given value is a float.
170 62e0e880 Iustin Pop

171 62e0e880 Iustin Pop
  """
172 62e0e880 Iustin Pop
  return isinstance(val, float)
173 62e0e880 Iustin Pop
174 62e0e880 Iustin Pop
175 8c9ee749 Michael Hanselmann
@WithDesc("String")
176 62e0e880 Iustin Pop
def TString(val):
177 62e0e880 Iustin Pop
  """Checks if the given value is a string.
178 62e0e880 Iustin Pop

179 62e0e880 Iustin Pop
  """
180 62e0e880 Iustin Pop
  return isinstance(val, basestring)
181 62e0e880 Iustin Pop
182 62e0e880 Iustin Pop
183 8c9ee749 Michael Hanselmann
@WithDesc("EvalToTrue")
184 62e0e880 Iustin Pop
def TTrue(val):
185 62e0e880 Iustin Pop
  """Checks if a given value evaluates to a boolean True value.
186 62e0e880 Iustin Pop

187 62e0e880 Iustin Pop
  """
188 62e0e880 Iustin Pop
  return bool(val)
189 62e0e880 Iustin Pop
190 62e0e880 Iustin Pop
191 62e0e880 Iustin Pop
def TElemOf(target_list):
192 62e0e880 Iustin Pop
  """Builds a function that checks if a given value is a member of a list.
193 62e0e880 Iustin Pop

194 62e0e880 Iustin Pop
  """
195 8c9ee749 Michael Hanselmann
  def fn(val):
196 8c9ee749 Michael Hanselmann
    return val in target_list
197 8c9ee749 Michael Hanselmann
198 8c9ee749 Michael Hanselmann
  return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
199 62e0e880 Iustin Pop
200 62e0e880 Iustin Pop
201 62e0e880 Iustin Pop
# Container types
202 8c9ee749 Michael Hanselmann
@WithDesc("List")
203 62e0e880 Iustin Pop
def TList(val):
204 62e0e880 Iustin Pop
  """Checks if the given value is a list.
205 62e0e880 Iustin Pop

206 62e0e880 Iustin Pop
  """
207 62e0e880 Iustin Pop
  return isinstance(val, list)
208 62e0e880 Iustin Pop
209 62e0e880 Iustin Pop
210 8c9ee749 Michael Hanselmann
@WithDesc("Dictionary")
211 62e0e880 Iustin Pop
def TDict(val):
212 62e0e880 Iustin Pop
  """Checks if the given value is a dictionary.
213 62e0e880 Iustin Pop

214 62e0e880 Iustin Pop
  """
215 62e0e880 Iustin Pop
  return isinstance(val, dict)
216 62e0e880 Iustin Pop
217 62e0e880 Iustin Pop
218 62e0e880 Iustin Pop
def TIsLength(size):
219 62e0e880 Iustin Pop
  """Check is the given container is of the given size.
220 62e0e880 Iustin Pop

221 62e0e880 Iustin Pop
  """
222 8c9ee749 Michael Hanselmann
  def fn(container):
223 8c9ee749 Michael Hanselmann
    return len(container) == size
224 8c9ee749 Michael Hanselmann
225 8c9ee749 Michael Hanselmann
  return WithDesc("Length %s" % (size, ))(fn)
226 62e0e880 Iustin Pop
227 62e0e880 Iustin Pop
228 62e0e880 Iustin Pop
# Combinator types
229 62e0e880 Iustin Pop
def TAnd(*args):
230 62e0e880 Iustin Pop
  """Combine multiple functions using an AND operation.
231 62e0e880 Iustin Pop

232 62e0e880 Iustin Pop
  """
233 62e0e880 Iustin Pop
  def fn(val):
234 62e0e880 Iustin Pop
    return compat.all(t(val) for t in args)
235 8c9ee749 Michael Hanselmann
236 8c9ee749 Michael Hanselmann
  return CombinationDesc("and", args, fn)
237 62e0e880 Iustin Pop
238 62e0e880 Iustin Pop
239 62e0e880 Iustin Pop
def TOr(*args):
240 62e0e880 Iustin Pop
  """Combine multiple functions using an AND operation.
241 62e0e880 Iustin Pop

242 62e0e880 Iustin Pop
  """
243 62e0e880 Iustin Pop
  def fn(val):
244 62e0e880 Iustin Pop
    return compat.any(t(val) for t in args)
245 8c9ee749 Michael Hanselmann
246 8c9ee749 Michael Hanselmann
  return CombinationDesc("or", args, fn)
247 62e0e880 Iustin Pop
248 62e0e880 Iustin Pop
249 62e0e880 Iustin Pop
def TMap(fn, test):
250 62e0e880 Iustin Pop
  """Checks that a modified version of the argument passes the given test.
251 62e0e880 Iustin Pop

252 62e0e880 Iustin Pop
  """
253 8c9ee749 Michael Hanselmann
  return WithDesc("Result of %s must be %s" %
254 8c9ee749 Michael Hanselmann
                  (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
255 62e0e880 Iustin Pop
256 62e0e880 Iustin Pop
257 8620f50e Michael Hanselmann
def TRegex(pobj):
258 8620f50e Michael Hanselmann
  """Checks whether a string matches a specific regular expression.
259 8620f50e Michael Hanselmann

260 8620f50e Michael Hanselmann
  @param pobj: Compiled regular expression as returned by C{re.compile}
261 8620f50e Michael Hanselmann

262 8620f50e Michael Hanselmann
  """
263 8620f50e Michael Hanselmann
  desc = WithDesc("String matching regex \"%s\"" %
264 8620f50e Michael Hanselmann
                  pobj.pattern.encode("string_escape"))
265 8620f50e Michael Hanselmann
266 8620f50e Michael Hanselmann
  return desc(TAnd(TString, pobj.match))
267 8620f50e Michael Hanselmann
268 8620f50e Michael Hanselmann
269 62e0e880 Iustin Pop
# Type aliases
270 62e0e880 Iustin Pop
271 62e0e880 Iustin Pop
#: a non-empty string
272 8c9ee749 Michael Hanselmann
TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
273 62e0e880 Iustin Pop
274 62e0e880 Iustin Pop
#: a maybe non-empty string
275 62e0e880 Iustin Pop
TMaybeString = TOr(TNonEmptyString, TNone)
276 62e0e880 Iustin Pop
277 62e0e880 Iustin Pop
#: a maybe boolean (bool or none)
278 62e0e880 Iustin Pop
TMaybeBool = TOr(TBool, TNone)
279 62e0e880 Iustin Pop
280 5f074973 Michael Hanselmann
#: Maybe a dictionary (dict or None)
281 5f074973 Michael Hanselmann
TMaybeDict = TOr(TDict, TNone)
282 62e0e880 Iustin Pop
283 62e0e880 Iustin Pop
#: a positive integer
284 8c9ee749 Michael Hanselmann
TPositiveInt = \
285 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0))
286 62e0e880 Iustin Pop
287 62e0e880 Iustin Pop
#: a strictly positive integer
288 8c9ee749 Michael Hanselmann
TStrictPositiveInt = \
289 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
290 62e0e880 Iustin Pop
291 beff3779 René Nussbaumer
#: a positive float
292 beff3779 René Nussbaumer
TPositiveFloat = \
293 beff3779 René Nussbaumer
  TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
294 beff3779 René Nussbaumer
295 8620f50e Michael Hanselmann
#: Job ID
296 8620f50e Michael Hanselmann
TJobId = TOr(TPositiveInt,
297 8620f50e Michael Hanselmann
             TRegex(re.compile("^%s$" % constants.JOB_ID_TEMPLATE)))
298 8620f50e Michael Hanselmann
299 62e0e880 Iustin Pop
300 62e0e880 Iustin Pop
def TListOf(my_type):
301 62e0e880 Iustin Pop
  """Checks if a given value is a list with all elements of the same type.
302 62e0e880 Iustin Pop

303 62e0e880 Iustin Pop
  """
304 8c9ee749 Michael Hanselmann
  desc = WithDesc("List of %s" % (Parens(my_type), ))
305 8c9ee749 Michael Hanselmann
  return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
306 62e0e880 Iustin Pop
307 62e0e880 Iustin Pop
308 62e0e880 Iustin Pop
def TDictOf(key_type, val_type):
309 62e0e880 Iustin Pop
  """Checks a dict type for the type of its key/values.
310 62e0e880 Iustin Pop

311 62e0e880 Iustin Pop
  """
312 8c9ee749 Michael Hanselmann
  desc = WithDesc("Dictionary with keys of %s and values of %s" %
313 8c9ee749 Michael Hanselmann
                  (Parens(key_type), Parens(val_type)))
314 8c9ee749 Michael Hanselmann
315 8c9ee749 Michael Hanselmann
  def fn(container):
316 8c9ee749 Michael Hanselmann
    return (compat.all(key_type(v) for v in container.keys()) and
317 8c9ee749 Michael Hanselmann
            compat.all(val_type(v) for v in container.values()))
318 8c9ee749 Michael Hanselmann
319 8c9ee749 Michael Hanselmann
  return desc(TAnd(TDict, fn))
320 a464ce71 Michael Hanselmann
321 a464ce71 Michael Hanselmann
322 a464ce71 Michael Hanselmann
def _TStrictDictCheck(require_all, exclusive, items, val):
323 a464ce71 Michael Hanselmann
  """Helper function for L{TStrictDict}.
324 a464ce71 Michael Hanselmann

325 a464ce71 Michael Hanselmann
  """
326 a464ce71 Michael Hanselmann
  notfound_fn = lambda _: not exclusive
327 a464ce71 Michael Hanselmann
328 a464ce71 Michael Hanselmann
  if require_all and not frozenset(val.keys()).issuperset(items.keys()):
329 a464ce71 Michael Hanselmann
    # Requires items not found in value
330 a464ce71 Michael Hanselmann
    return False
331 a464ce71 Michael Hanselmann
332 a464ce71 Michael Hanselmann
  return compat.all(items.get(key, notfound_fn)(value)
333 a464ce71 Michael Hanselmann
                    for (key, value) in val.items())
334 a464ce71 Michael Hanselmann
335 a464ce71 Michael Hanselmann
336 a464ce71 Michael Hanselmann
def TStrictDict(require_all, exclusive, items):
337 a464ce71 Michael Hanselmann
  """Strict dictionary check with specific keys.
338 a464ce71 Michael Hanselmann

339 a464ce71 Michael Hanselmann
  @type require_all: boolean
340 a464ce71 Michael Hanselmann
  @param require_all: Whether all keys in L{items} are required
341 a464ce71 Michael Hanselmann
  @type exclusive: boolean
342 a464ce71 Michael Hanselmann
  @param exclusive: Whether only keys listed in L{items} should be accepted
343 a464ce71 Michael Hanselmann
  @type items: dictionary
344 a464ce71 Michael Hanselmann
  @param items: Mapping from key (string) to verification function
345 a464ce71 Michael Hanselmann

346 a464ce71 Michael Hanselmann
  """
347 a464ce71 Michael Hanselmann
  descparts = ["Dictionary containing"]
348 a464ce71 Michael Hanselmann
349 a464ce71 Michael Hanselmann
  if exclusive:
350 a464ce71 Michael Hanselmann
    descparts.append(" none but the")
351 a464ce71 Michael Hanselmann
352 a464ce71 Michael Hanselmann
  if require_all:
353 a464ce71 Michael Hanselmann
    descparts.append(" required")
354 a464ce71 Michael Hanselmann
355 a464ce71 Michael Hanselmann
  if len(items) == 1:
356 a464ce71 Michael Hanselmann
    descparts.append(" key ")
357 a464ce71 Michael Hanselmann
  else:
358 a464ce71 Michael Hanselmann
    descparts.append(" keys ")
359 a464ce71 Michael Hanselmann
360 a464ce71 Michael Hanselmann
  descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
361 a464ce71 Michael Hanselmann
                                   for (key, value) in items.items()))
362 a464ce71 Michael Hanselmann
363 a464ce71 Michael Hanselmann
  desc = WithDesc("".join(descparts))
364 a464ce71 Michael Hanselmann
365 a464ce71 Michael Hanselmann
  return desc(TAnd(TDict,
366 a464ce71 Michael Hanselmann
                   compat.partial(_TStrictDictCheck, require_all, exclusive,
367 a464ce71 Michael Hanselmann
                                  items)))
368 8620f50e Michael Hanselmann
369 8620f50e Michael Hanselmann
370 8620f50e Michael Hanselmann
def TItems(items):
371 8620f50e Michael Hanselmann
  """Checks individual items of a container.
372 8620f50e Michael Hanselmann

373 8620f50e Michael Hanselmann
  If the verified value and the list of expected items differ in length, this
374 8620f50e Michael Hanselmann
  check considers only as many items as are contained in the shorter list. Use
375 8620f50e Michael Hanselmann
  L{TIsLength} to enforce a certain length.
376 8620f50e Michael Hanselmann

377 8620f50e Michael Hanselmann
  @type items: list
378 8620f50e Michael Hanselmann
  @param items: List of checks
379 8620f50e Michael Hanselmann

380 8620f50e Michael Hanselmann
  """
381 8620f50e Michael Hanselmann
  assert items, "Need items"
382 8620f50e Michael Hanselmann
383 8620f50e Michael Hanselmann
  text = ["Item", "item"]
384 8620f50e Michael Hanselmann
  desc = WithDesc(utils.CommaJoin("%s %s is %s" %
385 8620f50e Michael Hanselmann
                                  (text[int(idx > 0)], idx, Parens(check))
386 8620f50e Michael Hanselmann
                                  for (idx, check) in enumerate(items)))
387 8620f50e Michael Hanselmann
388 8620f50e Michael Hanselmann
  return desc(lambda value: compat.all(check(i)
389 8620f50e Michael Hanselmann
                                       for (check, i) in zip(items, value)))