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(', '.join(str(v) for v in 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 TMaxValue = lambda max: WithDesc('Less than %s' % max)(lambda val: val < max)
560 TAllocPolicy = TElemOf(constants.VALID_ALLOC_POLICIES)
561 TCVErrorCode = TElemOf(constants.CV_ALL_ECODES_STRINGS)
562 TQueryResultCode = TElemOf(constants.RS_ALL)
563 TExportTarget = TOr(TNonEmptyString, TList)
564 TExportMode = TElemOf(constants.EXPORT_MODES)
565 TDiskIndex = TAnd(TNonNegativeInt, TMaxValue(constants.MAX_DISKS))
566 TReplaceDisksMode = TElemOf(constants.REPLACE_MODES)
567 TDiskTemplate = TElemOf(constants.DISK_TEMPLATES)
568 TEvacMode = TElemOf(constants.NODE_EVAC_MODES)
569 TIAllocatorTestDir = TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)
570 TIAllocatorMode = TElemOf(constants.VALID_IALLOCATOR_MODES)
571 TImportExportCompression = TElemOf(constants.IEC_ALL)
572 TAdminStateSource = TElemOf(constants.ADMIN_STATE_SOURCES)
576 """Generates a check for modification lists.
577
578 """
579
580
581 old_mod_item_fn = \
582 TAnd(TIsLength(2),
583 TItems([TOr(TElemOf(constants.DDMS_VALUES), TNonNegativeInt), fn]))
584
585
586 mod_item_fn = \
587 TAnd(TIsLength(3), TItems([
588 TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
589 Comment("Device index, can be negative, e.g. -1 for last disk")
590 (TOr(TInt, TString)),
591 fn,
592 ]))
593
594 return TOr(Comment("Recommended")(TListOf(mod_item_fn)),
595 Comment("Deprecated")(TListOf(old_mod_item_fn)))
596
597
598 TINicParams = \
599 Comment("NIC parameters")(TDictOf(TElemOf(constants.INIC_PARAMS),
600 TMaybe(TString)))
601
602 TIDiskParams = \
603 Comment("Disk parameters")(TDictOf(TNonEmptyString,
604 TOr(TNonEmptyString, TInt)))
605
606 THypervisor = TElemOf(constants.HYPER_TYPES)
607 TMigrationMode = TElemOf(constants.HT_MIGRATION_MODES)
608 TNICMode = TElemOf(constants.NIC_VALID_MODES)
609 TInstCreateMode = TElemOf(constants.INSTANCE_CREATE_MODES)
610 TRebootType = TElemOf(constants.REBOOT_TYPES)
611 TFileDriver = TElemOf(constants.FILE_DRIVER)
612 TOobCommand = TElemOf(constants.OOB_COMMANDS)
613
614 TQueryTypeOp = TElemOf(set(constants.QR_VIA_OP)
615 .union(set(constants.QR_VIA_LUXI)))
616
617 TDiskParams = \
618 Comment("Disk parameters")(TDictOf(TNonEmptyString,
619 TOr(TNonEmptyString, TInt)))
620
621 TDiskChanges = \
622 TAnd(TIsLength(2),
623 TItems([Comment("Disk index")(TNonNegativeInt),
624 Comment("Parameters")(TDiskParams)]))
625
626 TRecreateDisksInfo = TOr(TListOf(TNonNegativeInt), TListOf(TDiskChanges))
630 """Builds a function that checks if a given value is a valid storage
631 type.
632
633 """
634 return (val in constants.STORAGE_TYPES)
635
636
637 TTagKind = TElemOf(constants.VALID_TAG_TYPES)
638 TDdmSimple = TElemOf(constants.DDMS_VALUES)
639 TVerifyOptionalChecks = TElemOf(constants.VERIFY_OPTIONAL_CHECKS)
644 """Ensure a given CIDR notation type is valid.
645
646 """
647 try:
648 ipaddr.IPv4Network(value)
649 except ipaddr.AddressValueError:
650 return False
651 return True
652
656 """Ensure a given CIDR notation type is valid.
657
658 """
659 try:
660 ipaddr.IPv4Address(value)
661 except ipaddr.AddressValueError:
662 return False
663 return True
664
668 """Ensure a given CIDR notation type is valid.
669
670 """
671 try:
672 ipaddr.IPv6Address(value)
673 except ipaddr.AddressValueError:
674 return False
675 return True
676
680 """Ensure a given CIDR notation type is valid.
681
682 """
683 try:
684 ipaddr.IPv6Network(value)
685 except ipaddr.AddressValueError:
686 return False
687 return True
688
689
690 TIPv4Address = TAnd(TString, _CheckCIDRAddrNotation)
691 TIPv6Address = TAnd(TString, _CheckCIDR6AddrNotation)
692 TIPv4Network = TAnd(TString, _CheckCIDRNetNotation)
693 TIPv6Network = TAnd(TString, _CheckCIDR6NetNotation)
698
701 """Helper to generate type checks for objects.
702
703 @param obj: The object to generate type checks
704 @param fields_types: The fields and their types as a dict
705 @return: A ht type check function
706
707 """
708 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
709 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
710 return TStrictDict(True, True, fields_types)
711
712
713 TQueryFieldDef = \
714 TObjectCheck(objects.QueryFieldDefinition, {
715 "name": TNonEmptyString,
716 "title": TNonEmptyString,
717 "kind": TElemOf(constants.QFT_ALL),
718 "doc": TNonEmptyString
719 })
720
721 TQueryRow = \
722 TListOf(TAnd(TIsLength(2),
723 TItems([TElemOf(constants.RS_ALL), TAny])))
724
725 TQueryResult = TListOf(TQueryRow)
726
727 TQueryResponse = \
728 TObjectCheck(objects.QueryResponse, {
729 "fields": TListOf(TQueryFieldDef),
730 "data": TQueryResult
731 })
732
733 TQueryFieldsResponse = \
734 TObjectCheck(objects.QueryFieldsResponse, {
735 "fields": TListOf(TQueryFieldDef)
736 })
737
738 TJobIdListItem = \
739 TAnd(TIsLength(2),
740 TItems([Comment("success")(TBool),
741 Comment("Job ID if successful, error message"
742 " otherwise")(TOr(TString, TJobId))]))
743
744 TJobIdList = TListOf(TJobIdListItem)
745
746 TJobIdListOnly = TStrictDict(True, True, {
747 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList)
748 })
749
750 TInstanceMultiAllocResponse = \
751 TStrictDict(True, True, {
752 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList),
753 constants.ALLOCATABLE_KEY: TListOf(TNonEmptyString),
754 constants.FAILED_KEY: TListOf(TNonEmptyString)
755 })
756