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 from ganeti.serializer import Private
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
90 return "<%s %r>" % (self._text, self._fn)
91
99
102 """Builds wrapper class with description text.
103
104 @type text: string
105 @param text: Description text
106 @return: Callable class
107
108 """
109 assert text[0] == text[0].upper()
110
111 return compat.partial(_DescWrapper, text)
112
125
128 """Build description for combinating operator.
129
130 @type op: string
131 @param op: Operator as text (e.g. "and")
132 @type args: list
133 @param args: Operator arguments
134 @type fn: callable
135 @param fn: Wrapped function
136
137 """
138
139
140
141 if __debug__ and TNone in args and args.index(TNone) > 0:
142 raise Exception("TNone must be listed first")
143
144 if len(args) == 1:
145 descr = str(args[0])
146 else:
147 descr = (" %s " % op).join(Parens(i) for i in args)
148
149 return WithDesc(descr)(fn)
150
151
152
153
154
155 @WithDesc(str([]))
156 -def EmptyList():
157 """Returns an empty list.
158
159 """
160 return []
161
165 """Returns an empty dict.
166
167 """
168 return {}
169
170
171
172 NoDefault = object()
173
174
175
176 @WithDesc("Anything")
177 -def TAny(_):
178 """Accepts any value.
179
180 """
181 return True
182
183
184 @WithDesc("NotNone")
185 -def TNotNone(val):
186 """Checks if the given value is not None.
187
188 """
189 return val is not None
190
191
192 @WithDesc("None")
193 -def TNone(val):
194 """Checks if the given value is None.
195
196 """
197 return val is None
198
199
200 @WithDesc("ValueNone")
201 -def TValueNone(val):
202 """Checks if the given value is L{constants.VALUE_NONE}.
203
204 """
205 return val == constants.VALUE_NONE
206
207
208 @WithDesc("Boolean")
209 -def TBool(val):
210 """Checks if the given value is a boolean.
211
212 """
213 return isinstance(val, bool)
214
215
216 @WithDesc("Integer")
217 -def TInt(val):
218 """Checks if the given value is an integer.
219
220 """
221
222
223
224
225
226 return isinstance(val, (int, long)) and not isinstance(val, bool)
227
228
229 @WithDesc("Float")
230 -def TFloat(val):
231 """Checks if the given value is a float.
232
233 """
234 return isinstance(val, float)
235
236
237 @WithDesc("String")
238 -def TString(val):
239 """Checks if the given value is a string.
240
241 """
242 return isinstance(val, basestring)
243
244
245 @WithDesc("EvalToTrue")
246 -def TTrue(val):
247 """Checks if a given value evaluates to a boolean True value.
248
249 """
250 return bool(val)
251
254 """Builds a function that checks if a given value is a member of a list.
255
256 """
257 def fn(val):
258 return val in target_list
259
260 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
261
262
263
264 @WithDesc("List")
265 -def TList(val):
266 """Checks if the given value is a list.
267
268 """
269 return isinstance(val, list)
270
271
272 @WithDesc("Tuple")
273 -def TTuple(val):
274 """Checks if the given value is a tuple.
275
276 """
277 return isinstance(val, tuple)
278
279
280 @WithDesc("Dictionary")
281 -def TDict(val):
282 """Checks if the given value is a dictionary.
283
284 Note that L{PrivateDict}s subclass dict and pass this check.
285
286 """
287 return isinstance(val, dict)
288
291 """Check is the given container is of the given size.
292
293 """
294 def fn(container):
295 return len(container) == size
296
297 return WithDesc("Length %s" % (size, ))(fn)
298
299
300
301 -def TAnd(*args):
302 """Combine multiple functions using an AND operation.
303
304 """
305 def fn(val):
306 return compat.all(t(val) for t in args)
307
308 return CombinationDesc("and", args, fn)
309
312 """Combine multiple functions using an OR operation.
313
314 """
315 def fn(val):
316 return compat.any(t(val) for t in args)
317
318 return CombinationDesc("or", args, fn)
319
320
321 -def TMap(fn, test):
322 """Checks that a modified version of the argument passes the given test.
323
324 """
325 return WithDesc("Result of %s must be %s" %
326 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
327
330 """Checks whether a string matches a specific regular expression.
331
332 @param pobj: Compiled regular expression as returned by C{re.compile}
333
334 """
335 desc = WithDesc("String matching regex \"%s\"" %
336 pobj.pattern.encode("string_escape"))
337
338 return desc(TAnd(TString, pobj.match))
339
342 """Wrap a test in a TOr(TNone, test).
343
344 This makes it easier to define TMaybe* types.
345
346 """
347 return TOr(TNone, test)
348
355
356
357
358
359
360 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
361
362
363 TMaybeString = TMaybe(TNonEmptyString)
364
365
366 TMaybeBool = TMaybe(TBool)
367
368
369 TMaybeDict = TMaybe(TDict)
370
371
372 TMaybeList = TMaybe(TList)
378 return WithDesc("EqualOrGreaterThanZero")(TAnd(val_type, lambda v: v >= 0))
379
384 return WithDesc("GreaterThanZero")(TAnd(val_type, lambda v: v > 0))
385
386
387
388 TNonNegativeInt = TNonNegative(TInt)
389
390
391 TPositiveInt = TPositive(TInt)
392
393
394 TMaybePositiveInt = TMaybe(TPositiveInt)
395
396
397 TNegativeInt = \
398 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
399
400
401 TNonNegativeFloat = \
402 TAnd(TFloat, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0.0))
403
404
405 TJobId = WithDesc("JobId")(TOr(TNonNegativeInt,
406 TRegex(re.compile("^%s$" %
407 constants.JOB_ID_TEMPLATE))))
408
409
410 TDouble = TFloat
411
412
413 TNumber = TOr(TInt, TFloat)
414
415
416 TRelativeJobId = WithDesc("RelativeJobId")(TNegativeInt)
420 """Checks if a given value is an instance of C{cls}.
421
422 @type cls: class
423 @param cls: Class object
424
425 """
426 name = "%s.%s" % (cls.__module__, cls.__name__)
427
428 desc = WithDesc("Instance of %s" % (Parens(name), ))
429
430 return desc(lambda val: isinstance(val, cls))
431
434 """Checks if a given value is an instance of Private.
435
436 """
437 def fn(val):
438 return isinstance(val, Private) and val_type(val.Get())
439
440 desc = WithDesc("Private %s" % Parens(val_type))
441
442 return desc(fn)
443
446 """Checks if a given value is a list with all elements of the same type.
447
448 """
449 desc = WithDesc("List of %s" % (Parens(my_type), ))
450 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
451
452
453 TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
457 """Checks if a given value is a list with the proper size and its
458 elements match the given types.
459
460 """
461 desc = WithDesc("Tuple of %s" % (Parens(val_types), ))
462 return desc(TAnd(TOr(TTuple, TList), TIsLength(len(val_types)),
463 TItems(val_types)))
464
467 """Checks if a given value is a list with all elements of the same
468 type and eliminates duplicated elements.
469
470 """
471 desc = WithDesc("Set of %s" % (Parens(val_type), ))
472 return desc(lambda st: TListOf(val_type)(list(set(st))))
473
474
475 -def TDictOf(key_type, val_type):
476 """Checks a dict type for the type of its key/values.
477
478 """
479 desc = WithDesc("Dictionary with keys of %s and values of %s" %
480 (Parens(key_type), Parens(val_type)))
481
482 def fn(container):
483 return (compat.all(key_type(v) for v in container.keys()) and
484 compat.all(val_type(v) for v in container.values()))
485
486 return desc(TAnd(TDict, fn))
487
490 """Helper function for L{TStrictDict}.
491
492 """
493 notfound_fn = lambda _: not exclusive
494
495 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
496
497 return False
498
499 return compat.all(items.get(key, notfound_fn)(value)
500 for (key, value) in val.items())
501
504 """Strict dictionary check with specific keys.
505
506 @type require_all: boolean
507 @param require_all: Whether all keys in L{items} are required
508 @type exclusive: boolean
509 @param exclusive: Whether only keys listed in L{items} should be accepted
510 @type items: dictionary
511 @param items: Mapping from key (string) to verification function
512
513 """
514 descparts = ["Dictionary containing"]
515
516 if exclusive:
517 descparts.append(" none but the")
518
519 if require_all:
520 descparts.append(" required")
521
522 if len(items) == 1:
523 descparts.append(" key ")
524 else:
525 descparts.append(" keys ")
526
527 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
528 for (key, value) in items.items()))
529
530 desc = WithDesc("".join(descparts))
531
532 return desc(TAnd(TDict,
533 compat.partial(_TStrictDictCheck, require_all, exclusive,
534 items)))
535
538 """Checks individual items of a container.
539
540 If the verified value and the list of expected items differ in length, this
541 check considers only as many items as are contained in the shorter list. Use
542 L{TIsLength} to enforce a certain length.
543
544 @type items: list
545 @param items: List of checks
546
547 """
548 assert items, "Need items"
549
550 text = ["Item", "item"]
551 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
552 (text[int(idx > 0)], idx, Parens(check))
553 for (idx, check) in enumerate(items)))
554
555 return desc(lambda value: compat.all(check(i)
556 for (check, i) in zip(items, value)))
557
558
559 TAllocPolicy = TElemOf(constants.VALID_ALLOC_POLICIES)
560 TCVErrorCode = TElemOf(constants.CV_ALL_ECODES_STRINGS)
561 TQueryResultCode = TElemOf(constants.RS_ALL)
562 TExportTarget = TOr(TNonEmptyString, TList)
563 TExportMode = TElemOf(constants.EXPORT_MODES)
564 TDiskIndex = TAnd(TNonNegativeInt, lambda val: val < constants.MAX_DISKS)
565 TReplaceDisksMode = TElemOf(constants.REPLACE_MODES)
566 TDiskTemplate = TElemOf(constants.DISK_TEMPLATES)
567 TEvacMode = TElemOf(constants.NODE_EVAC_MODES)
568 TIAllocatorTestDir = TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)
569 TIAllocatorMode = TElemOf(constants.VALID_IALLOCATOR_MODES)
570 TImportExportCompression = TElemOf(constants.IEC_ALL)
571 TAdminStateSource = TElemOf(constants.ADMIN_STATE_SOURCES)
575 """Generates a check for modification lists.
576
577 """
578
579
580 old_mod_item_fn = \
581 TAnd(TIsLength(2),
582 TItems([TOr(TElemOf(constants.DDMS_VALUES), TNonNegativeInt), fn]))
583
584
585 mod_item_fn = \
586 TAnd(TIsLength(3), TItems([
587 TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
588 Comment("Device index, can be negative, e.g. -1 for last disk")
589 (TOr(TInt, TString)),
590 fn,
591 ]))
592
593 return TOr(Comment("Recommended")(TListOf(mod_item_fn)),
594 Comment("Deprecated")(TListOf(old_mod_item_fn)))
595
596
597 TINicParams = \
598 Comment("NIC parameters")(TDictOf(TElemOf(constants.INIC_PARAMS),
599 TMaybe(TString)))
600
601 TIDiskParams = \
602 Comment("Disk parameters")(TDictOf(TNonEmptyString,
603 TOr(TNonEmptyString, TInt)))
604
605 THypervisor = TElemOf(constants.HYPER_TYPES)
606 TMigrationMode = TElemOf(constants.HT_MIGRATION_MODES)
607 TNICMode = TElemOf(constants.NIC_VALID_MODES)
608 TInstCreateMode = TElemOf(constants.INSTANCE_CREATE_MODES)
609 TRebootType = TElemOf(constants.REBOOT_TYPES)
610 TFileDriver = TElemOf(constants.FILE_DRIVER)
611 TOobCommand = TElemOf(constants.OOB_COMMANDS)
612
613 TQueryTypeOp = TElemOf(set(constants.QR_VIA_OP)
614 .union(set(constants.QR_VIA_LUXI)))
615
616 TDiskParams = \
617 Comment("Disk parameters")(TDictOf(TNonEmptyString,
618 TOr(TNonEmptyString, TInt)))
619
620 TDiskChanges = \
621 TAnd(TIsLength(2),
622 TItems([Comment("Disk index")(TNonNegativeInt),
623 Comment("Parameters")(TDiskParams)]))
624
625 TRecreateDisksInfo = TOr(TListOf(TNonNegativeInt), TListOf(TDiskChanges))
629 """Builds a function that checks if a given value is a valid storage
630 type.
631
632 """
633 return (val in constants.STORAGE_TYPES)
634
635
636 TTagKind = TElemOf(constants.VALID_TAG_TYPES)
637 TDdmSimple = TElemOf(constants.DDMS_VALUES)
638 TVerifyOptionalChecks = TElemOf(constants.VERIFY_OPTIONAL_CHECKS)
643 """Ensure a given CIDR notation type is valid.
644
645 """
646 try:
647 ipaddr.IPv4Network(value)
648 except ipaddr.AddressValueError:
649 return False
650 return True
651
655 """Ensure a given CIDR notation type is valid.
656
657 """
658 try:
659 ipaddr.IPv4Address(value)
660 except ipaddr.AddressValueError:
661 return False
662 return True
663
667 """Ensure a given CIDR notation type is valid.
668
669 """
670 try:
671 ipaddr.IPv6Address(value)
672 except ipaddr.AddressValueError:
673 return False
674 return True
675
679 """Ensure a given CIDR notation type is valid.
680
681 """
682 try:
683 ipaddr.IPv6Network(value)
684 except ipaddr.AddressValueError:
685 return False
686 return True
687
688
689 TIPv4Address = TAnd(TString, _CheckCIDRAddrNotation)
690 TIPv6Address = TAnd(TString, _CheckCIDR6AddrNotation)
691 TIPv4Network = TAnd(TString, _CheckCIDRNetNotation)
692 TIPv6Network = TAnd(TString, _CheckCIDR6NetNotation)
697
700 """Helper to generate type checks for objects.
701
702 @param obj: The object to generate type checks
703 @param fields_types: The fields and their types as a dict
704 @return: A ht type check function
705
706 """
707 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
708 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
709 return TStrictDict(True, True, fields_types)
710
711
712 TQueryFieldDef = \
713 TObjectCheck(objects.QueryFieldDefinition, {
714 "name": TNonEmptyString,
715 "title": TNonEmptyString,
716 "kind": TElemOf(constants.QFT_ALL),
717 "doc": TNonEmptyString
718 })
719
720 TQueryRow = \
721 TListOf(TAnd(TIsLength(2),
722 TItems([TElemOf(constants.RS_ALL), TAny])))
723
724 TQueryResult = TListOf(TQueryRow)
725
726 TQueryResponse = \
727 TObjectCheck(objects.QueryResponse, {
728 "fields": TListOf(TQueryFieldDef),
729 "data": TQueryResult
730 })
731
732 TQueryFieldsResponse = \
733 TObjectCheck(objects.QueryFieldsResponse, {
734 "fields": TListOf(TQueryFieldDef)
735 })
736
737 TJobIdListItem = \
738 TAnd(TIsLength(2),
739 TItems([Comment("success")(TBool),
740 Comment("Job ID if successful, error message"
741 " otherwise")(TOr(TString, TJobId))]))
742
743 TJobIdList = TListOf(TJobIdListItem)
744
745 TJobIdListOnly = TStrictDict(True, True, {
746 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList)
747 })
748
749 TInstanceMultiAllocResponse = \
750 TStrictDict(True, True, {
751 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList),
752 constants.ALLOCATABLE_KEY: TListOf(TNonEmptyString),
753 constants.FAILED_KEY: TListOf(TNonEmptyString)
754 })
755