1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """Module implementing the parameter types code."""
32
33 import re
34 import operator
35 import ipaddr
36
37 from ganeti import compat
38 from ganeti import utils
39 from ganeti import constants
40 from ganeti import objects
41
42
43 _PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
47 """Enclose text in parens if necessary.
48
49 @param text: Text
50
51 """
52 text = str(text)
53
54 if _PAREN_RE.match(text):
55 return text
56 else:
57 return "(%s)" % text
58
61 __slots__ = [
62 "_fn",
63 "_text",
64 ]
65
67 """Initializes this class.
68
69 @param text: Description
70 @param fn: Wrapped function
71
72 """
73 assert text.strip()
74
75 self._text = text
76 self._fn = fn
77
79 return self._fn(*args)
80
83 """Wrapper class for description text.
84
85 """
88
96
99 """Builds wrapper class with description text.
100
101 @type text: string
102 @param text: Description text
103 @return: Callable class
104
105 """
106 assert text[0] == text[0].upper()
107
108 return compat.partial(_DescWrapper, text)
109
122
125 """Build description for combinating operator.
126
127 @type op: string
128 @param op: Operator as text (e.g. "and")
129 @type args: list
130 @param args: Operator arguments
131 @type fn: callable
132 @param fn: Wrapped function
133
134 """
135
136
137
138 if __debug__ and TNone in args and args.index(TNone) > 0:
139 raise Exception("TNone must be listed first")
140
141 if len(args) == 1:
142 descr = str(args[0])
143 else:
144 descr = (" %s " % op).join(Parens(i) for i in args)
145
146 return WithDesc(descr)(fn)
147
148
149
150
151
152 @WithDesc(str([]))
153 -def EmptyList():
154 """Returns an empty list.
155
156 """
157 return []
158
162 """Returns an empty dict.
163
164 """
165 return {}
166
167
168
169 NoDefault = object()
170
171
172
173 @WithDesc("Anything")
174 -def TAny(_):
175 """Accepts any value.
176
177 """
178 return True
179
180
181 @WithDesc("NotNone")
182 -def TNotNone(val):
183 """Checks if the given value is not None.
184
185 """
186 return val is not None
187
188
189 @WithDesc("None")
190 -def TNone(val):
191 """Checks if the given value is None.
192
193 """
194 return val is None
195
196
197 @WithDesc("ValueNone")
198 -def TValueNone(val):
199 """Checks if the given value is L{constants.VALUE_NONE}.
200
201 """
202 return val == constants.VALUE_NONE
203
204
205 @WithDesc("Boolean")
206 -def TBool(val):
207 """Checks if the given value is a boolean.
208
209 """
210 return isinstance(val, bool)
211
212
213 @WithDesc("Integer")
214 -def TInt(val):
215 """Checks if the given value is an integer.
216
217 """
218
219
220
221
222
223 return isinstance(val, (int, long)) and not isinstance(val, bool)
224
225
226 @WithDesc("Float")
227 -def TFloat(val):
228 """Checks if the given value is a float.
229
230 """
231 return isinstance(val, float)
232
233
234 @WithDesc("String")
235 -def TString(val):
236 """Checks if the given value is a string.
237
238 """
239 return isinstance(val, basestring)
240
241
242 @WithDesc("EvalToTrue")
243 -def TTrue(val):
244 """Checks if a given value evaluates to a boolean True value.
245
246 """
247 return bool(val)
248
251 """Builds a function that checks if a given value is a member of a list.
252
253 """
254 def fn(val):
255 return val in target_list
256
257 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
258
259
260
261 @WithDesc("List")
262 -def TList(val):
263 """Checks if the given value is a list.
264
265 """
266 return isinstance(val, list)
267
268
269 @WithDesc("Tuple")
270 -def TTuple(val):
271 """Checks if the given value is a tuple.
272
273 """
274 return isinstance(val, tuple)
275
276
277 @WithDesc("Dictionary")
278 -def TDict(val):
279 """Checks if the given value is a dictionary.
280
281 """
282 return isinstance(val, dict)
283
286 """Check is the given container is of the given size.
287
288 """
289 def fn(container):
290 return len(container) == size
291
292 return WithDesc("Length %s" % (size, ))(fn)
293
294
295
296 -def TAnd(*args):
297 """Combine multiple functions using an AND operation.
298
299 """
300 def fn(val):
301 return compat.all(t(val) for t in args)
302
303 return CombinationDesc("and", args, fn)
304
307 """Combine multiple functions using an OR operation.
308
309 """
310 def fn(val):
311 return compat.any(t(val) for t in args)
312
313 return CombinationDesc("or", args, fn)
314
315
316 -def TMap(fn, test):
317 """Checks that a modified version of the argument passes the given test.
318
319 """
320 return WithDesc("Result of %s must be %s" %
321 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
322
325 """Checks whether a string matches a specific regular expression.
326
327 @param pobj: Compiled regular expression as returned by C{re.compile}
328
329 """
330 desc = WithDesc("String matching regex \"%s\"" %
331 pobj.pattern.encode("string_escape"))
332
333 return desc(TAnd(TString, pobj.match))
334
337 """Wrap a test in a TOr(TNone, test).
338
339 This makes it easier to define TMaybe* types.
340
341 """
342 return TOr(TNone, test)
343
350
351
352
353
354
355 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
356
357
358 TMaybeString = TMaybe(TNonEmptyString)
359
360
361 TMaybeBool = TMaybe(TBool)
362
363
364 TMaybeDict = TMaybe(TDict)
365
366
367 TMaybeList = TMaybe(TList)
373 return WithDesc("EqualOrGreaterThanZero")(TAnd(val_type, lambda v: v >= 0))
374
379 return WithDesc("GreaterThanZero")(TAnd(val_type, lambda v: v > 0))
380
381
382
383 TNonNegativeInt = TNonNegative(TInt)
384
385
386 TPositiveInt = TPositive(TInt)
387
388
389 TMaybePositiveInt = TMaybe(TPositiveInt)
390
391
392 TNegativeInt = \
393 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
394
395
396 TNonNegativeFloat = \
397 TAnd(TFloat, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0.0))
398
399
400 TJobId = WithDesc("JobId")(TOr(TNonNegativeInt,
401 TRegex(re.compile("^%s$" %
402 constants.JOB_ID_TEMPLATE))))
403
404
405 TDouble = TFloat
406
407
408 TNumber = TOr(TInt, TFloat)
409
410
411 TRelativeJobId = WithDesc("RelativeJobId")(TNegativeInt)
415 """Checks if a given value is an instance of C{cls}.
416
417 @type cls: class
418 @param cls: Class object
419
420 """
421 name = "%s.%s" % (cls.__module__, cls.__name__)
422
423 desc = WithDesc("Instance of %s" % (Parens(name), ))
424
425 return desc(lambda val: isinstance(val, cls))
426
429 """Checks if a given value is a list with all elements of the same type.
430
431 """
432 desc = WithDesc("List of %s" % (Parens(my_type), ))
433 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
434
435
436 TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
440 """Checks if a given value is a list with the proper size and its
441 elements match the given types.
442
443 """
444 desc = WithDesc("Tuple of %s" % (Parens(val_types), ))
445 return desc(TAnd(TOr(TTuple, TList), TIsLength(len(val_types)),
446 TItems(val_types)))
447
450 """Checks if a given value is a list with all elements of the same
451 type and eliminates duplicated elements.
452
453 """
454 desc = WithDesc("Set of %s" % (Parens(val_type), ))
455 return desc(lambda st: TListOf(val_type)(list(set(st))))
456
457
458 -def TDictOf(key_type, val_type):
459 """Checks a dict type for the type of its key/values.
460
461 """
462 desc = WithDesc("Dictionary with keys of %s and values of %s" %
463 (Parens(key_type), Parens(val_type)))
464
465 def fn(container):
466 return (compat.all(key_type(v) for v in container.keys()) and
467 compat.all(val_type(v) for v in container.values()))
468
469 return desc(TAnd(TDict, fn))
470
473 """Helper function for L{TStrictDict}.
474
475 """
476 notfound_fn = lambda _: not exclusive
477
478 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
479
480 return False
481
482 return compat.all(items.get(key, notfound_fn)(value)
483 for (key, value) in val.items())
484
487 """Strict dictionary check with specific keys.
488
489 @type require_all: boolean
490 @param require_all: Whether all keys in L{items} are required
491 @type exclusive: boolean
492 @param exclusive: Whether only keys listed in L{items} should be accepted
493 @type items: dictionary
494 @param items: Mapping from key (string) to verification function
495
496 """
497 descparts = ["Dictionary containing"]
498
499 if exclusive:
500 descparts.append(" none but the")
501
502 if require_all:
503 descparts.append(" required")
504
505 if len(items) == 1:
506 descparts.append(" key ")
507 else:
508 descparts.append(" keys ")
509
510 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
511 for (key, value) in items.items()))
512
513 desc = WithDesc("".join(descparts))
514
515 return desc(TAnd(TDict,
516 compat.partial(_TStrictDictCheck, require_all, exclusive,
517 items)))
518
521 """Checks individual items of a container.
522
523 If the verified value and the list of expected items differ in length, this
524 check considers only as many items as are contained in the shorter list. Use
525 L{TIsLength} to enforce a certain length.
526
527 @type items: list
528 @param items: List of checks
529
530 """
531 assert items, "Need items"
532
533 text = ["Item", "item"]
534 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
535 (text[int(idx > 0)], idx, Parens(check))
536 for (idx, check) in enumerate(items)))
537
538 return desc(lambda value: compat.all(check(i)
539 for (check, i) in zip(items, value)))
540
541
542 TAllocPolicy = TElemOf(constants.VALID_ALLOC_POLICIES)
543 TCVErrorCode = TElemOf(constants.CV_ALL_ECODES_STRINGS)
544 TQueryResultCode = TElemOf(constants.RS_ALL)
545 TExportTarget = TOr(TNonEmptyString, TList)
546 TExportMode = TElemOf(constants.EXPORT_MODES)
547 TDiskIndex = TAnd(TNonNegativeInt, lambda val: val < constants.MAX_DISKS)
548 TReplaceDisksMode = TElemOf(constants.REPLACE_MODES)
549 TDiskTemplate = TElemOf(constants.DISK_TEMPLATES)
550 TEvacMode = TElemOf(constants.NODE_EVAC_MODES)
551 TIAllocatorTestDir = TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)
552 TIAllocatorMode = TElemOf(constants.VALID_IALLOCATOR_MODES)
556 """Generates a check for modification lists.
557
558 """
559
560
561 old_mod_item_fn = \
562 TAnd(TIsLength(2),
563 TItems([TOr(TElemOf(constants.DDMS_VALUES), TNonNegativeInt), fn]))
564
565
566 mod_item_fn = \
567 TAnd(TIsLength(3), TItems([
568 TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
569 Comment("Device index, can be negative, e.g. -1 for last disk")
570 (TOr(TInt, TString)),
571 fn,
572 ]))
573
574 return TOr(Comment("Recommended")(TListOf(mod_item_fn)),
575 Comment("Deprecated")(TListOf(old_mod_item_fn)))
576
577
578 TINicParams = \
579 Comment("NIC parameters")(TDictOf(TElemOf(constants.INIC_PARAMS),
580 TMaybe(TString)))
581
582 TIDiskParams = \
583 Comment("Disk parameters")(TDictOf(TNonEmptyString,
584 TOr(TNonEmptyString, TInt)))
585
586 THypervisor = TElemOf(constants.HYPER_TYPES)
587 TMigrationMode = TElemOf(constants.HT_MIGRATION_MODES)
588 TNICMode = TElemOf(constants.NIC_VALID_MODES)
589 TInstCreateMode = TElemOf(constants.INSTANCE_CREATE_MODES)
590 TRebootType = TElemOf(constants.REBOOT_TYPES)
591 TFileDriver = TElemOf(constants.FILE_DRIVER)
592 TOobCommand = TElemOf(constants.OOB_COMMANDS)
593 TQueryTypeOp = TElemOf(constants.QR_VIA_OP)
594
595 TDiskParams = \
596 Comment("Disk parameters")(TDictOf(TNonEmptyString,
597 TOr(TNonEmptyString, TInt)))
598
599 TDiskChanges = \
600 TAnd(TIsLength(2),
601 TItems([Comment("Disk index")(TNonNegativeInt),
602 Comment("Parameters")(TDiskParams)]))
603
604 TRecreateDisksInfo = TOr(TListOf(TNonNegativeInt), TListOf(TDiskChanges))
608 """Builds a function that checks if a given value is a valid storage
609 type.
610
611 """
612 return (val in constants.STORAGE_TYPES)
613
614
615 TTagKind = TElemOf(constants.VALID_TAG_TYPES)
616 TDdmSimple = TElemOf(constants.DDMS_VALUES)
617 TVerifyOptionalChecks = TElemOf(constants.VERIFY_OPTIONAL_CHECKS)
622 """Ensure a given CIDR notation type is valid.
623
624 """
625 try:
626 ipaddr.IPv4Network(value)
627 except ipaddr.AddressValueError:
628 return False
629 return True
630
634 """Ensure a given CIDR notation type is valid.
635
636 """
637 try:
638 ipaddr.IPv4Address(value)
639 except ipaddr.AddressValueError:
640 return False
641 return True
642
646 """Ensure a given CIDR notation type is valid.
647
648 """
649 try:
650 ipaddr.IPv6Address(value)
651 except ipaddr.AddressValueError:
652 return False
653 return True
654
658 """Ensure a given CIDR notation type is valid.
659
660 """
661 try:
662 ipaddr.IPv6Network(value)
663 except ipaddr.AddressValueError:
664 return False
665 return True
666
667
668 TIPv4Address = TAnd(TString, _CheckCIDRAddrNotation)
669 TIPv6Address = TAnd(TString, _CheckCIDR6AddrNotation)
670 TIPv4Network = TAnd(TString, _CheckCIDRNetNotation)
671 TIPv6Network = TAnd(TString, _CheckCIDR6NetNotation)
676
679 """Helper to generate type checks for objects.
680
681 @param obj: The object to generate type checks
682 @param fields_types: The fields and their types as a dict
683 @return: A ht type check function
684
685 """
686 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
687 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
688 return TStrictDict(True, True, fields_types)
689
690
691 TQueryFieldDef = \
692 TObjectCheck(objects.QueryFieldDefinition, {
693 "name": TNonEmptyString,
694 "title": TNonEmptyString,
695 "kind": TElemOf(constants.QFT_ALL),
696 "doc": TNonEmptyString
697 })
698
699 TQueryRow = \
700 TListOf(TAnd(TIsLength(2),
701 TItems([TElemOf(constants.RS_ALL), TAny])))
702
703 TQueryResult = TListOf(TQueryRow)
704
705 TQueryResponse = \
706 TObjectCheck(objects.QueryResponse, {
707 "fields": TListOf(TQueryFieldDef),
708 "data": TQueryResult
709 })
710
711 TQueryFieldsResponse = \
712 TObjectCheck(objects.QueryFieldsResponse, {
713 "fields": TListOf(TQueryFieldDef)
714 })
715
716 TJobIdListItem = \
717 TAnd(TIsLength(2),
718 TItems([Comment("success")(TBool),
719 Comment("Job ID if successful, error message"
720 " otherwise")(TOr(TString, TJobId))]))
721
722 TJobIdList = TListOf(TJobIdListItem)
723
724 TJobIdListOnly = TStrictDict(True, True, {
725 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList)
726 })
727
728 TInstanceMultiAllocResponse = \
729 TStrictDict(True, True, {
730 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList),
731 constants.ALLOCATABLE_KEY: TListOf(TNonEmptyString),
732 constants.FAILED_KEY: TListOf(TNonEmptyString)
733 })
734