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)
553 TImportExportCompression = TElemOf(constants.IEC_ALL)
554 TAdminStateSource = TElemOf(constants.ADMIN_STATE_SOURCES)
558 """Generates a check for modification lists.
559
560 """
561
562
563 old_mod_item_fn = \
564 TAnd(TIsLength(2),
565 TItems([TOr(TElemOf(constants.DDMS_VALUES), TNonNegativeInt), fn]))
566
567
568 mod_item_fn = \
569 TAnd(TIsLength(3), TItems([
570 TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
571 Comment("Device index, can be negative, e.g. -1 for last disk")
572 (TOr(TInt, TString)),
573 fn,
574 ]))
575
576 return TOr(Comment("Recommended")(TListOf(mod_item_fn)),
577 Comment("Deprecated")(TListOf(old_mod_item_fn)))
578
579
580 TINicParams = \
581 Comment("NIC parameters")(TDictOf(TElemOf(constants.INIC_PARAMS),
582 TMaybe(TString)))
583
584 TIDiskParams = \
585 Comment("Disk parameters")(TDictOf(TNonEmptyString,
586 TOr(TNonEmptyString, TInt)))
587
588 THypervisor = TElemOf(constants.HYPER_TYPES)
589 TMigrationMode = TElemOf(constants.HT_MIGRATION_MODES)
590 TNICMode = TElemOf(constants.NIC_VALID_MODES)
591 TInstCreateMode = TElemOf(constants.INSTANCE_CREATE_MODES)
592 TRebootType = TElemOf(constants.REBOOT_TYPES)
593 TFileDriver = TElemOf(constants.FILE_DRIVER)
594 TOobCommand = TElemOf(constants.OOB_COMMANDS)
595
596 TQueryTypeOp = TElemOf(set(constants.QR_VIA_OP)
597 .union(set(constants.QR_VIA_LUXI)))
598
599 TDiskParams = \
600 Comment("Disk parameters")(TDictOf(TNonEmptyString,
601 TOr(TNonEmptyString, TInt)))
602
603 TDiskChanges = \
604 TAnd(TIsLength(2),
605 TItems([Comment("Disk index")(TNonNegativeInt),
606 Comment("Parameters")(TDiskParams)]))
607
608 TRecreateDisksInfo = TOr(TListOf(TNonNegativeInt), TListOf(TDiskChanges))
612 """Builds a function that checks if a given value is a valid storage
613 type.
614
615 """
616 return (val in constants.STORAGE_TYPES)
617
618
619 TTagKind = TElemOf(constants.VALID_TAG_TYPES)
620 TDdmSimple = TElemOf(constants.DDMS_VALUES)
621 TVerifyOptionalChecks = TElemOf(constants.VERIFY_OPTIONAL_CHECKS)
626 """Ensure a given CIDR notation type is valid.
627
628 """
629 try:
630 ipaddr.IPv4Network(value)
631 except ipaddr.AddressValueError:
632 return False
633 return True
634
638 """Ensure a given CIDR notation type is valid.
639
640 """
641 try:
642 ipaddr.IPv4Address(value)
643 except ipaddr.AddressValueError:
644 return False
645 return True
646
650 """Ensure a given CIDR notation type is valid.
651
652 """
653 try:
654 ipaddr.IPv6Address(value)
655 except ipaddr.AddressValueError:
656 return False
657 return True
658
662 """Ensure a given CIDR notation type is valid.
663
664 """
665 try:
666 ipaddr.IPv6Network(value)
667 except ipaddr.AddressValueError:
668 return False
669 return True
670
671
672 TIPv4Address = TAnd(TString, _CheckCIDRAddrNotation)
673 TIPv6Address = TAnd(TString, _CheckCIDR6AddrNotation)
674 TIPv4Network = TAnd(TString, _CheckCIDRNetNotation)
675 TIPv6Network = TAnd(TString, _CheckCIDR6NetNotation)
680
683 """Helper to generate type checks for objects.
684
685 @param obj: The object to generate type checks
686 @param fields_types: The fields and their types as a dict
687 @return: A ht type check function
688
689 """
690 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
691 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
692 return TStrictDict(True, True, fields_types)
693
694
695 TQueryFieldDef = \
696 TObjectCheck(objects.QueryFieldDefinition, {
697 "name": TNonEmptyString,
698 "title": TNonEmptyString,
699 "kind": TElemOf(constants.QFT_ALL),
700 "doc": TNonEmptyString
701 })
702
703 TQueryRow = \
704 TListOf(TAnd(TIsLength(2),
705 TItems([TElemOf(constants.RS_ALL), TAny])))
706
707 TQueryResult = TListOf(TQueryRow)
708
709 TQueryResponse = \
710 TObjectCheck(objects.QueryResponse, {
711 "fields": TListOf(TQueryFieldDef),
712 "data": TQueryResult
713 })
714
715 TQueryFieldsResponse = \
716 TObjectCheck(objects.QueryFieldsResponse, {
717 "fields": TListOf(TQueryFieldDef)
718 })
719
720 TJobIdListItem = \
721 TAnd(TIsLength(2),
722 TItems([Comment("success")(TBool),
723 Comment("Job ID if successful, error message"
724 " otherwise")(TOr(TString, TJobId))]))
725
726 TJobIdList = TListOf(TJobIdListItem)
727
728 TJobIdListOnly = TStrictDict(True, True, {
729 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList)
730 })
731
732 TInstanceMultiAllocResponse = \
733 TStrictDict(True, True, {
734 constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList),
735 constants.ALLOCATABLE_KEY: TListOf(TNonEmptyString),
736 constants.FAILED_KEY: TListOf(TNonEmptyString)
737 })
738