Revision 72cd5493

b/lib/cmdlib/instance.py
2387 2387
                           "hypervisor", "instance", "cluster")
2388 2388

  
2389 2389
    self.op.disks = self._UpgradeDiskNicMods(
2390
      "disk", self.op.disks, opcodes.OpInstanceSetParams.TestDiskModifications)
2390
      "disk", self.op.disks, ht.TSetParamsMods(ht.TIDiskParams))
2391 2391
    self.op.nics = self._UpgradeDiskNicMods(
2392
      "NIC", self.op.nics, opcodes.OpInstanceSetParams.TestNicModifications)
2392
      "NIC", self.op.nics, ht.TSetParamsMods(ht.TINicParams))
2393 2393

  
2394 2394
    if self.op.disks and self.op.disk_template is not None:
2395 2395
      raise errors.OpPrereqError("Disk template conversion and other disk"
b/lib/ht.py
23 23

  
24 24
import re
25 25
import operator
26
import ipaddr
26 27

  
27 28
from ganeti import compat
28 29
from ganeti import utils
29 30
from ganeti import constants
31
from ganeti import objects
30 32

  
31 33

  
32 34
_PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
......
359 361
#: Maybe a list (list or None)
360 362
TMaybeList = TMaybe(TList)
361 363

  
364

  
365
#: a non-negative number (value > 0)
366
# val_type should be TInt, TDouble (== TFloat), or TNumber
367
def TNonNegative(val_type):
368
  return WithDesc("EqualOrGreaterThanZero")(TAnd(val_type, lambda v: v >= 0))
369

  
370

  
371
#: a positive number (value >= 0)
372
# val_type should be TInt, TDouble (== TFloat), or TNumber
373
def TPositive(val_type):
374
  return WithDesc("GreaterThanZero")(TAnd(val_type, lambda v: v > 0))
375

  
376

  
362 377
#: a non-negative integer (value >= 0)
363
TNonNegativeInt = \
364
  TAnd(TInt, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0))
378
TNonNegativeInt = TNonNegative(TInt)
365 379

  
366 380
#: a positive integer (value > 0)
367
TPositiveInt = \
368
  TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
381
TPositiveInt = TPositive(TInt)
369 382

  
370 383
#: a maybe positive integer (positive integer or None)
371 384
TMaybePositiveInt = TMaybe(TPositiveInt)
......
383 396
                               TRegex(re.compile("^%s$" %
384 397
                                                 constants.JOB_ID_TEMPLATE))))
385 398

  
399
#: Double (== Float)
400
TDouble = TFloat
401

  
386 402
#: Number
387 403
TNumber = TOr(TInt, TFloat)
388 404

  
......
415 431
TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
416 432

  
417 433

  
434
def TTupleOf(*val_types):
435
  """Checks if a given value is a list with the proper size and its
436
     elements match the given types.
437

  
438
  """
439
  desc = WithDesc("Tuple of %s" % (Parens(val_types), ))
440
  return desc(TAnd(TIsLength(len(val_types)), TItems(val_types)))
441

  
442

  
443
def TSetOf(val_type):
444
  """Checks if a given value is a list with all elements of the same
445
     type and eliminates duplicated elements.
446

  
447
  """
448
  desc = WithDesc("Set of %s" % (Parens(val_type), ))
449
  return desc(lambda st: TListOf(val_type)(list(set(st))))
450

  
451

  
418 452
def TDictOf(key_type, val_type):
419 453
  """Checks a dict type for the type of its key/values.
420 454

  
......
497 531

  
498 532
  return desc(lambda value: compat.all(check(i)
499 533
                                       for (check, i) in zip(items, value)))
534

  
535

  
536
TAllocPolicy = TElemOf(constants.VALID_ALLOC_POLICIES)
537
TCVErrorCode = TElemOf(constants.CV_ALL_ECODES_STRINGS)
538
TQueryResultCode = TElemOf(constants.RS_ALL)
539
TExportTarget = TOr(TNonEmptyString, TList)
540
TExportMode = TElemOf(constants.EXPORT_MODES)
541
TDiskIndex = TAnd(TNonNegativeInt, lambda val: val < constants.MAX_DISKS)
542
TReplaceDisksMode = TElemOf(constants.REPLACE_MODES)
543
TDiskTemplate = TElemOf(constants.DISK_TEMPLATES)
544
TNodeEvacMode = TElemOf(constants.IALLOCATOR_NEVAC_MODES)
545
TIAllocatorTestDir = TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)
546
TIAllocatorMode = TElemOf(constants.VALID_IALLOCATOR_MODES)
547

  
548

  
549
def TSetParamsMods(fn):
550
  """Generates a check for modification lists.
551

  
552
  """
553
  # Old format
554
  # TODO: Remove in version 2.11 including support in LUInstanceSetParams
555
  old_mod_item_fn = \
556
    TAnd(TIsLength(2),
557
         TItems([TOr(TElemOf(constants.DDMS_VALUES), TNonNegativeInt), fn]))
558

  
559
  # New format, supporting adding/removing disks/NICs at arbitrary indices
560
  mod_item_fn = \
561
      TAnd(TIsLength(3), TItems([
562
        TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
563
        Comment("Device index, can be negative, e.g. -1 for last disk")
564
                 (TOr(TInt, TString)),
565
        fn,
566
        ]))
567

  
568
  return TOr(Comment("Recommended")(TListOf(mod_item_fn)),
569
             Comment("Deprecated")(TListOf(old_mod_item_fn)))
570

  
571

  
572
TINicParams = \
573
    Comment("NIC parameters")(TDictOf(TElemOf(constants.INIC_PARAMS),
574
                                      TMaybeString))
575

  
576
TIDiskParams = \
577
    Comment("Disk parameters")(TDictOf(TElemOf(constants.IDISK_PARAMS),
578
                                       TOr(TNonEmptyString, TInt)))
579

  
580
THypervisor = TElemOf(constants.HYPER_TYPES)
581
TMigrationMode = TElemOf(constants.HT_MIGRATION_MODES)
582
TNICMode = TElemOf(constants.NIC_VALID_MODES)
583
TInstCreateMode = TElemOf(constants.INSTANCE_CREATE_MODES)
584
TRebootType = TElemOf(constants.REBOOT_TYPES)
585
TFileDriver = TElemOf(constants.FILE_DRIVER)
586
TOobCommand = TElemOf(constants.OOB_COMMANDS)
587
TQueryTypeOp = TElemOf(constants.QR_VIA_OP)
588

  
589
TDiskParams = \
590
    Comment("Disk parameters")(TDictOf(TNonEmptyString,
591
                                       TOr(TNonEmptyString, TInt)))
592

  
593
TDiskChanges = \
594
    TAnd(TIsLength(2),
595
         TItems([Comment("Disk index")(TNonNegativeInt),
596
                 Comment("Parameters")(TDiskParams)]))
597

  
598
TRecreateDisksInfo = TOr(TListOf(TNonNegativeInt), TListOf(TDiskChanges))
599

  
600

  
601
def TStorageType(val):
602
  """Builds a function that checks if a given value is a valid storage
603
  type.
604

  
605
  """
606
  return (val in constants.STORAGE_TYPES)
607

  
608

  
609
TTagKind = TElemOf(constants.VALID_TAG_TYPES)
610
TDdmSimple = TElemOf(constants.DDMS_VALUES)
611
TVerifyOptionalChecks = TElemOf(constants.VERIFY_OPTIONAL_CHECKS)
612

  
613

  
614
@WithDesc("IPv4 network")
615
def _CheckCIDRNetNotation(value):
616
  """Ensure a given CIDR notation type is valid.
617

  
618
  """
619
  try:
620
    ipaddr.IPv4Network(value)
621
  except ipaddr.AddressValueError:
622
    return False
623
  return True
624

  
625

  
626
@WithDesc("IPv4 address")
627
def _CheckCIDRAddrNotation(value):
628
  """Ensure a given CIDR notation type is valid.
629

  
630
  """
631
  try:
632
    ipaddr.IPv4Address(value)
633
  except ipaddr.AddressValueError:
634
    return False
635
  return True
636

  
637

  
638
@WithDesc("IPv6 address")
639
def _CheckCIDR6AddrNotation(value):
640
  """Ensure a given CIDR notation type is valid.
641

  
642
  """
643
  try:
644
    ipaddr.IPv6Address(value)
645
  except ipaddr.AddressValueError:
646
    return False
647
  return True
648

  
649

  
650
@WithDesc("IPv6 network")
651
def _CheckCIDR6NetNotation(value):
652
  """Ensure a given CIDR notation type is valid.
653

  
654
  """
655
  try:
656
    ipaddr.IPv6Network(value)
657
  except ipaddr.AddressValueError:
658
    return False
659
  return True
660

  
661

  
662
TIPv4Address = TAnd(TString, _CheckCIDRAddrNotation)
663
TIPv6Address = TAnd(TString, _CheckCIDR6AddrNotation)
664
TIPv4Network = TAnd(TString, _CheckCIDRNetNotation)
665
TIPv6Network = TAnd(TString, _CheckCIDR6NetNotation)
666

  
667

  
668
def TObject(val_type):
669
  return TDictOf(TAny, val_type)
670

  
671

  
672
def TObjectCheck(obj, fields_types):
673
  """Helper to generate type checks for objects.
674

  
675
  @param obj: The object to generate type checks
676
  @param fields_types: The fields and their types as a dict
677
  @return: A ht type check function
678

  
679
  """
680
  assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
681
    "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
682
  return TStrictDict(True, True, fields_types)
683

  
684

  
685
TQueryFieldDef = \
686
    TObjectCheck(objects.QueryFieldDefinition, {
687
        "name": TNonEmptyString,
688
        "title": TNonEmptyString,
689
        "kind": TElemOf(constants.QFT_ALL),
690
        "doc": TNonEmptyString
691
    })
692

  
693
TQueryRow = \
694
    TListOf(TAnd(TIsLength(2),
695
                 TItems([TElemOf(constants.RS_ALL), TAny])))
696

  
697
TQueryResult = TListOf(TQueryRow)
698

  
699
TQueryResponse = \
700
    TObjectCheck(objects.QueryResponse, {
701
        "fields": TListOf(TQueryFieldDef),
702
        "data": TQueryResult
703
    })
704

  
705
TQueryFieldsResponse = \
706
    TObjectCheck(objects.QueryFieldsResponse, {
707
        "fields": TListOf(TQueryFieldDef)
708
    })
709

  
710
TJobIdListItem = \
711
    TAnd(TIsLength(2),
712
         TItems([Comment("success")(TBool),
713
                 Comment("Job ID if successful, error message"
714
                         " otherwise")(TOr(TString, TJobId))]))
715

  
716
TJobIdList = TListOf(TJobIdListItem)
717

  
718
TJobIdListOnly = TStrictDict(True, True, {
719
  constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList)
720
  })
721

  
722
TInstanceMultiAllocResponse = \
723
    TStrictDict(True, True, {
724
      constants.JOB_IDS_KEY: Comment("List of submitted jobs")(TJobIdList),
725
      ALLOCATABLE_KEY: TListOf(TNonEmptyString),
726
      FAILED_KEY: TListOf(TNonEmptyString)
727
    })
b/test/py/ganeti.opcodes_unittest.py
362 362
  def testJobIdList(self):
363 363
    for i in [[], [(False, "error")], [(False, "")],
364 364
              [(True, 123), (True, "999")]]:
365
      self.assertTrue(opcodes.TJobIdList(i))
365
      self.assertTrue(ht.TJobIdList(i))
366 366

  
367 367
    for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
368
      self.assertFalse(opcodes.TJobIdList(i))
368
      self.assertFalse(ht.TJobIdList(i))
369 369

  
370 370
  def testJobIdListOnly(self):
371
    self.assertTrue(opcodes.TJobIdListOnly({
371
    self.assertTrue(ht.TJobIdListOnly({
372 372
      constants.JOB_IDS_KEY: [],
373 373
      }))
374
    self.assertTrue(opcodes.TJobIdListOnly({
374
    self.assertTrue(ht.TJobIdListOnly({
375 375
      constants.JOB_IDS_KEY: [(True, "9282")],
376 376
      }))
377 377

  
378
    self.assertFalse(opcodes.TJobIdListOnly({
378
    self.assertFalse(ht.TJobIdListOnly({
379 379
      "x": None,
380 380
      }))
381
    self.assertFalse(opcodes.TJobIdListOnly({
381
    self.assertFalse(ht.TJobIdListOnly({
382 382
      constants.JOB_IDS_KEY: [],
383 383
      "x": None,
384 384
      }))
385
    self.assertFalse(opcodes.TJobIdListOnly({
385
    self.assertFalse(ht.TJobIdListOnly({
386 386
      constants.JOB_IDS_KEY: [("foo", "bar")],
387 387
      }))
388
    self.assertFalse(opcodes.TJobIdListOnly({
388
    self.assertFalse(ht.TJobIdListOnly({
389 389
      constants.JOB_IDS_KEY: [("one", "two", "three")],
390 390
      }))
391 391

  
......
433 433
    self.assertFalse(fn([[constants.DDM_ADD]]))
434 434

  
435 435
  def testNicModifications(self):
436
    fn = opcodes.OpInstanceSetParams.TestNicModifications
436
    fn = ht.TSetParamsMods(ht.TINicParams)
437 437
    self._GenericTests(fn)
438 438

  
439 439
    for param in constants.INIC_PARAMS:
......
441 441
      self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
442 442

  
443 443
  def testDiskModifications(self):
444
    fn = opcodes.OpInstanceSetParams.TestDiskModifications
444
    fn = ht.TSetParamsMods(ht.TIDiskParams)
445 445
    self._GenericTests(fn)
446 446

  
447 447
    for param in constants.IDISK_PARAMS:

Also available in: Unified diff