Revision 0f511c8a

b/lib/cli.py
3731 3731
  if iscluster:
3732 3732
    eff_ipolicy = custom_ipolicy
3733 3733

  
3734
  custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX)
3734
  custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX, {})
3735 3735
  ret = [
3736 3736
    (key,
3737 3737
     FormatParamsDictInfo(custom_minmax.get(key, {}),
b/lib/cmdlib.py
813 813
  return params_copy
814 814

  
815 815

  
816
def _UpdateMinMaxISpecs(ipolicy, new_minmax, group_policy):
817
  use_none = use_default = group_policy
818
  minmax = ipolicy.setdefault(constants.ISPECS_MINMAX, {})
819
  for (key, value) in new_minmax.items():
820
    if key not in constants.ISPECS_MINMAX_KEYS:
821
      raise errors.OpPrereqError("Invalid key in new ipolicy/%s: %s" %
822
                                 (constants.ISPECS_MINMAX, key),
823
                                 errors.ECODE_INVAL)
824
    old_spec = minmax.get(key, {})
825
    minmax[key] = _GetUpdatedParams(old_spec, value, use_none=use_none,
826
                                    use_default=use_default)
827
    utils.ForceDictType(minmax[key], constants.ISPECS_PARAMETER_TYPES)
828

  
829

  
830 816
def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
831 817
  """Return the new version of an instance policy.
832 818

  
......
834 820
    we should support removal of policy entries
835 821

  
836 822
  """
837
  use_none = use_default = group_policy
838 823
  ipolicy = copy.deepcopy(old_ipolicy)
839 824
  for key, value in new_ipolicy.items():
840 825
    if key not in constants.IPOLICY_ALL_KEYS:
841 826
      raise errors.OpPrereqError("Invalid key in new ipolicy: %s" % key,
842 827
                                 errors.ECODE_INVAL)
843
    if key == constants.ISPECS_MINMAX:
844
      _UpdateMinMaxISpecs(ipolicy, value, group_policy)
845
    elif key == constants.ISPECS_STD:
846
      ipolicy[key] = _GetUpdatedParams(old_ipolicy.get(key, {}), value,
847
                                       use_none=use_none,
848
                                       use_default=use_default)
849
      utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
828
    if (not value or value == [constants.VALUE_DEFAULT] or
829
        value == constants.VALUE_DEFAULT):
830
      if group_policy:
831
        del ipolicy[key]
832
      else:
833
        raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
834
                                   " on the cluster'" % key,
835
                                   errors.ECODE_INVAL)
850 836
    else:
851
      if (not value or value == [constants.VALUE_DEFAULT] or
852
          value == constants.VALUE_DEFAULT):
837
      if key in constants.IPOLICY_PARAMETERS:
838
        # FIXME: we assume all such values are float
839
        try:
840
          ipolicy[key] = float(value)
841
        except (TypeError, ValueError), err:
842
          raise errors.OpPrereqError("Invalid value for attribute"
843
                                     " '%s': '%s', error: %s" %
844
                                     (key, value, err), errors.ECODE_INVAL)
845
      elif key == constants.ISPECS_MINMAX:
846
        for k in value.keys():
847
          utils.ForceDictType(value[k], constants.ISPECS_PARAMETER_TYPES)
848
        ipolicy[key] = value
849
      elif key == constants.ISPECS_STD:
853 850
        if group_policy:
854
          del ipolicy[key]
855
        else:
856
          raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
857
                                     " on the cluster'" % key,
858
                                     errors.ECODE_INVAL)
851
          msg = "%s cannot appear in group instance specs" % key
852
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
853
        ipolicy[key] = _GetUpdatedParams(old_ipolicy.get(key, {}), value,
854
                                         use_none=False, use_default=False)
855
        utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
859 856
      else:
860
        if key in constants.IPOLICY_PARAMETERS:
861
          # FIXME: we assume all such values are float
862
          try:
863
            ipolicy[key] = float(value)
864
          except (TypeError, ValueError), err:
865
            raise errors.OpPrereqError("Invalid value for attribute"
866
                                       " '%s': '%s', error: %s" %
867
                                       (key, value, err), errors.ECODE_INVAL)
868
        else:
869
          # FIXME: we assume all others are lists; this should be redone
870
          # in a nicer way
871
          ipolicy[key] = list(value)
857
        # FIXME: we assume all others are lists; this should be redone
858
        # in a nicer way
859
        ipolicy[key] = list(value)
872 860
  try:
873 861
    objects.InstancePolicy.CheckParameterSyntax(ipolicy, not group_policy)
874 862
  except errors.ConfigurationError, err:
b/lib/objects.py
82 82
  return ret_dict
83 83

  
84 84

  
85
def _FillMinMaxISpecs(default_specs, custom_specs):
86
  assert frozenset(default_specs.keys()) == constants.ISPECS_MINMAX_KEYS
87
  ret_specs = {}
88
  for key in constants.ISPECS_MINMAX_KEYS:
89
    ret_specs[key] = FillDict(default_specs[key],
90
                              custom_specs.get(key, {}))
91
  return ret_specs
92

  
93

  
94 85
def FillIPolicy(default_ipolicy, custom_ipolicy):
95 86
  """Fills an instance policy with defaults.
96 87

  
97 88
  """
98 89
  assert frozenset(default_ipolicy.keys()) == constants.IPOLICY_ALL_KEYS
99
  ret_dict = {}
100
  # Instance specs
101
  new_mm = _FillMinMaxISpecs(default_ipolicy[constants.ISPECS_MINMAX],
102
                             custom_ipolicy.get(constants.ISPECS_MINMAX, {}))
103
  ret_dict[constants.ISPECS_MINMAX] = new_mm
104
  new_std = FillDict(default_ipolicy[constants.ISPECS_STD],
105
                     custom_ipolicy.get(constants.ISPECS_STD, {}))
106
  ret_dict[constants.ISPECS_STD] = new_std
107
  # list items
108
  for key in [constants.IPOLICY_DTS]:
109
    ret_dict[key] = list(custom_ipolicy.get(key, default_ipolicy[key]))
110
  # other items which we know we can directly copy (immutables)
111
  for key in constants.IPOLICY_PARAMETERS:
112
    ret_dict[key] = custom_ipolicy.get(key, default_ipolicy[key])
113

  
90
  ret_dict = copy.deepcopy(custom_ipolicy)
91
  for key in default_ipolicy:
92
    if key not in ret_dict:
93
      ret_dict[key] = copy.deepcopy(default_ipolicy[key])
94
    elif key == constants.ISPECS_STD:
95
      ret_dict[key] = FillDict(default_ipolicy[key], ret_dict[key])
114 96
  return ret_dict
115 97

  
116 98

  
......
198 180
  """Create empty IPolicy dictionary.
199 181

  
200 182
  """
201
  return {
202
    constants.ISPECS_MINMAX: {
203
      constants.ISPECS_MIN: {},
204
      constants.ISPECS_MAX: {},
205
      },
206
    constants.ISPECS_STD: {},
207
    }
183
  return {}
208 184

  
209 185

  
210 186
class ConfigObject(outils.ValidatedSlots):
......
954 930
                                      utils.CommaJoin(wrong_keys))
955 931

  
956 932
  @classmethod
933
  def _CheckIncompleteSpec(cls, spec, keyname):
934
    missing_params = constants.ISPECS_PARAMETERS - frozenset(spec.keys())
935
    if missing_params:
936
      msg = ("Missing instance specs parameters for %s: %s" %
937
             (keyname, utils.CommaJoin(missing_params)))
938
      raise errors.ConfigurationError(msg)
939

  
940
  @classmethod
957 941
  def CheckISpecSyntax(cls, ipolicy, check_std):
958 942
    """Check the instance policy specs for validity.
959 943

  
......
977 961
    if missing:
978 962
      msg = "Missing instance specification: %s" % utils.CommaJoin(missing)
979 963
      raise errors.ConfigurationError(msg)
964
    for (key, spec) in minmaxspecs.items():
965
      InstancePolicy._CheckIncompleteSpec(spec, key)
966
    if check_std:
967
      InstancePolicy._CheckIncompleteSpec(stdspec, constants.ISPECS_STD)
980 968
    for param in constants.ISPECS_PARAMETERS:
981 969
      InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec, param,
982 970
                                            check_std)
......
1002 990
    """
1003 991
    minspec = minmaxspecs[constants.ISPECS_MIN]
1004 992
    maxspec = minmaxspecs[constants.ISPECS_MAX]
1005
    min_v = minspec.get(name, 0)
993
    min_v = minspec[name]
1006 994

  
1007 995
    if check_std:
1008 996
      std_v = stdspec.get(name, min_v)
......
1011 999
      std_v = min_v
1012 1000
      std_msg = "-"
1013 1001

  
1014
    max_v = maxspec.get(name, std_v)
1002
    max_v = maxspec[name]
1015 1003
    if min_v > std_v or std_v > max_v:
1016 1004
      err = ("Invalid specification of min/max/std values for %s: %s/%s/%s" %
1017
             (name,
1018
              minspec.get(name, "-"),
1019
              maxspec.get(name, "-"),
1020
              std_msg))
1005
             (name, min_v, max_v, std_msg))
1021 1006
      raise errors.ConfigurationError(err)
1022 1007

  
1023 1008
  @classmethod
b/src/Ganeti/Objects.hs
66 66
  , PartialISpecParams(..)
67 67
  , fillISpecParams
68 68
  , allISpecParamFields
69
  , FilledMinMaxISpecs(..)
70
  , PartialMinMaxISpecs(..)
71
  , fillMinMaxISpecs
69
  , MinMaxISpecs(..)
72 70
  , FilledIPolicy(..)
73 71
  , PartialIPolicy(..)
74 72
  , fillIPolicy
......
505 503
  , simpleField C.ispecSpindleUse  [t| Int |]
506 504
  ])
507 505

  
508
-- | Partial min-max instance specs. These is not built via buildParam since
509
-- it has a special 2-level inheritance mode.
510
$(buildObject "PartialMinMaxISpecs" "mmis"
511
  [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
512
  , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
513
  ])
514

  
515
-- | Filled min-max instance specs. This is not built via buildParam since
516
-- it has a special 2-level inheritance mode.
517
$(buildObject "FilledMinMaxISpecs" "mmis"
506
$(buildObject "MinMaxISpecs" "mmis"
518 507
  [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
519 508
  , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
520 509
  ])
......
523 512
-- has a special 2-level inheritance mode.
524 513
$(buildObject "PartialIPolicy" "ipolicy"
525 514
  [ optionalField . renameField "MinMaxISpecsP"
526
                    $ simpleField C.ispecsMinmax [t| PartialMinMaxISpecs |]
527
  , renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |]
515
                    $ simpleField C.ispecsMinmax   [t| MinMaxISpecs |]
516
  , optionalField . renameField "StdSpecP"
517
                    $ simpleField "std"            [t| PartialISpecParams |]
528 518
  , optionalField . renameField "SpindleRatioP"
529 519
                    $ simpleField "spindle-ratio"  [t| Double |]
530 520
  , optionalField . renameField "VcpuRatioP"
......
537 527
-- has a special 2-level inheritance mode.
538 528
$(buildObject "FilledIPolicy" "ipolicy"
539 529
  [ renameField "MinMaxISpecs"
540
    $ simpleField C.ispecsMinmax [t| FilledMinMaxISpecs |]
530
    $ simpleField C.ispecsMinmax [t| MinMaxISpecs |]
541 531
  , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
542 532
  , simpleField "spindle-ratio"  [t| Double |]
543 533
  , simpleField "vcpu-ratio"     [t| Double |]
544 534
  , simpleField "disk-templates" [t| [DiskTemplate] |]
545 535
  ])
546 536

  
547
-- | Custom filler for the min-max instance specs.
548
fillMinMaxISpecs :: FilledMinMaxISpecs -> Maybe PartialMinMaxISpecs ->
549
                    FilledMinMaxISpecs
550
fillMinMaxISpecs fminmax Nothing = fminmax
551
fillMinMaxISpecs (FilledMinMaxISpecs { mmisMinSpec = fmin
552
                                     , mmisMaxSpec = fmax })
553
                 (Just PartialMinMaxISpecs { mmisMinSpecP = pmin
554
                                           , mmisMaxSpecP = pmax }) =
555
  FilledMinMaxISpecs { mmisMinSpec = fillISpecParams fmin pmin
556
                     , mmisMaxSpec = fillISpecParams fmax pmax }
557

  
558 537
-- | Custom filler for the ipolicy types.
559 538
fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
560 539
fillIPolicy (FilledIPolicy { ipolicyMinMaxISpecs  = fminmax
......
567 546
                            , ipolicySpindleRatioP  = pspindleRatio
568 547
                            , ipolicyVcpuRatioP     = pvcpuRatio
569 548
                            , ipolicyDiskTemplatesP = pdiskTemplates}) =
570
  FilledIPolicy { ipolicyMinMaxISpecs  = fillMinMaxISpecs fminmax pminmax
571
                , ipolicyStdSpec       = fillISpecParams fstd pstd
549
  FilledIPolicy { ipolicyMinMaxISpecs  = fromMaybe fminmax pminmax
550
                , ipolicyStdSpec       = case pstd of
551
                                         Nothing -> fstd
552
                                         Just p -> fillISpecParams fstd p
572 553
                , ipolicySpindleRatio  = fromMaybe fspindleRatio pspindleRatio
573 554
                , ipolicyVcpuRatio     = fromMaybe fvcpuRatio pvcpuRatio
574 555
                , ipolicyDiskTemplates = fromMaybe fdiskTemplates
b/test/hs/Test/Ganeti/Objects.hs
143 143
-- | FIXME: This generates completely random data, without normal
144 144
-- validation rules.
145 145
$(genArbitrary ''PartialISpecParams)
146
$(genArbitrary ''PartialMinMaxISpecs)
147 146

  
148 147
-- | FIXME: This generates completely random data, without normal
149 148
-- validation rules.
150 149
$(genArbitrary ''PartialIPolicy)
151 150

  
152 151
$(genArbitrary ''FilledISpecParams)
153
$(genArbitrary ''FilledMinMaxISpecs)
152
$(genArbitrary ''MinMaxISpecs)
154 153
$(genArbitrary ''FilledIPolicy)
155 154
$(genArbitrary ''IpFamily)
156 155
$(genArbitrary ''FilledNDParams)
b/test/py/ganeti.cmdlib_unittest.py
1733 1733
  """Tests for cmdlib._GetUpdatedIPolicy()"""
1734 1734
  _OLD_CLUSTER_POLICY = {
1735 1735
    constants.IPOLICY_VCPU_RATIO: 1.5,
1736
    constants.ISPECS_MINMAX: {
1737
      constants.ISPECS_MIN: {
1738
        constants.ISPEC_MEM_SIZE: 20,
1739
        constants.ISPEC_CPU_COUNT: 2,
1740
        },
1741
      constants.ISPECS_MAX: {},
1742
      },
1743
    constants.ISPECS_STD: {},
1736
    constants.ISPECS_MINMAX: constants.ISPECS_MINMAX_DEFAULTS,
1737
    constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1744 1738
    }
1745 1739
  _OLD_GROUP_POLICY = {
1746 1740
    constants.IPOLICY_SPINDLE_RATIO: 2.5,
1747 1741
    constants.ISPECS_MINMAX: {
1748 1742
      constants.ISPECS_MIN: {
1749
        constants.ISPEC_DISK_SIZE: 20,
1750
        constants.ISPEC_NIC_COUNT: 2,
1743
        constants.ISPEC_MEM_SIZE: 128,
1744
        constants.ISPEC_CPU_COUNT: 1,
1745
        constants.ISPEC_DISK_COUNT: 1,
1746
        constants.ISPEC_DISK_SIZE: 1024,
1747
        constants.ISPEC_NIC_COUNT: 1,
1748
        constants.ISPEC_SPINDLE_USE: 1,
1749
        },
1750
      constants.ISPECS_MAX: {
1751
        constants.ISPEC_MEM_SIZE: 32768,
1752
        constants.ISPEC_CPU_COUNT: 8,
1753
        constants.ISPEC_DISK_COUNT: 5,
1754
        constants.ISPEC_DISK_SIZE: 1024 * 1024,
1755
        constants.ISPEC_NIC_COUNT: 3,
1756
        constants.ISPEC_SPINDLE_USE: 12,
1751 1757
        },
1752
      constants.ISPECS_MAX: {},
1753 1758
      },
1754 1759
    }
1755 1760

  
1756 1761
  def _TestSetSpecs(self, old_policy, isgroup):
1757
    ispec_key = constants.ISPECS_MIN
1758
    diff_ispec = {
1759
      constants.ISPEC_MEM_SIZE: 50,
1760
      constants.ISPEC_DISK_SIZE: 30,
1762
    diff_minmax = {
1763
      constants.ISPECS_MIN: {
1764
        constants.ISPEC_MEM_SIZE: 64,
1765
        constants.ISPEC_CPU_COUNT: 1,
1766
        constants.ISPEC_DISK_COUNT: 2,
1767
        constants.ISPEC_DISK_SIZE: 64,
1768
        constants.ISPEC_NIC_COUNT: 1,
1769
        constants.ISPEC_SPINDLE_USE: 1,
1770
        },
1771
      constants.ISPECS_MAX: {
1772
        constants.ISPEC_MEM_SIZE: 16384,
1773
        constants.ISPEC_CPU_COUNT: 10,
1774
        constants.ISPEC_DISK_COUNT: 12,
1775
        constants.ISPEC_DISK_SIZE: 1024,
1776
        constants.ISPEC_NIC_COUNT: 9,
1777
        constants.ISPEC_SPINDLE_USE: 18,
1778
        },
1761 1779
      }
1780
    diff_std = {
1781
        constants.ISPEC_DISK_COUNT: 10,
1782
        constants.ISPEC_DISK_SIZE: 512,
1783
        }
1762 1784
    diff_policy = {
1763
      constants.ISPECS_MINMAX: {
1764
        ispec_key: diff_ispec,
1765
        },
1785
      constants.ISPECS_MINMAX: diff_minmax
1766 1786
      }
1767 1787
    if not isgroup:
1768
      diff_std = {
1769
        constants.ISPEC_CPU_COUNT: 3,
1770
        constants.ISPEC_DISK_COUNT: 3,
1771
        }
1772 1788
      diff_policy[constants.ISPECS_STD] = diff_std
1773 1789
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1774 1790
                                           group_policy=isgroup)
1775 1791

  
1776 1792
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1777
    new_ispec = new_policy[constants.ISPECS_MINMAX][ispec_key]
1778
    for key in diff_ispec:
1779
      self.assertTrue(key in new_ispec)
1780
      self.assertEqual(new_ispec[key], diff_ispec[key])
1793
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1781 1794
    for key in old_policy:
1782 1795
      if not key in diff_policy:
1783 1796
        self.assertTrue(key in new_policy)
1784 1797
        self.assertEqual(new_policy[key], old_policy[key])
1785 1798

  
1786
    if constants.ISPECS_MINMAX in old_policy:
1787
      old_minmax = old_policy[constants.ISPECS_MINMAX]
1788
      for key in old_minmax:
1789
        if key != ispec_key:
1790
          self.assertTrue(key in new_policy[constants.ISPECS_MINMAX])
1791
          self.assertEqual(new_policy[constants.ISPECS_MINMAX][key],
1792
                           old_minmax[key])
1793
      old_ispec = old_policy[constants.ISPECS_MINMAX][ispec_key]
1794
      for key in old_ispec:
1795
        if not key in diff_ispec:
1796
          self.assertTrue(key in new_ispec)
1797
          self.assertEqual(new_ispec[key], old_ispec[key])
1798

  
1799 1799
    if not isgroup:
1800 1800
      new_std = new_policy[constants.ISPECS_STD]
1801 1801
      for key in diff_std:
1802 1802
        self.assertTrue(key in new_std)
1803 1803
        self.assertEqual(new_std[key], diff_std[key])
1804
      old_std = old_policy.get(constants.ISPECS_STD, {})
1805
      for key in old_std:
1806
        self.assertTrue(key in new_std)
1807
        if key not in diff_std:
1808
          self.assertEqual(new_std[key], old_std[key])
1804 1809

  
1805

  
1806
  def _TestSet(self, old_policy, isgroup):
1807
    diff_policy = {
1808
      constants.IPOLICY_VCPU_RATIO: 3,
1809
      constants.IPOLICY_SPINDLE_RATIO: 1.9,
1810
      }
1810
  def _TestSet(self, old_policy, diff_policy, isgroup):
1811 1811
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1812 1812
                                           group_policy=isgroup)
1813 1813
    for key in diff_policy:
......
1819 1819
        self.assertEqual(new_policy[key], old_policy[key])
1820 1820

  
1821 1821
  def testSet(self):
1822
    self._TestSet(self._OLD_GROUP_POLICY, True)
1822
    diff_policy = {
1823
      constants.IPOLICY_VCPU_RATIO: 3,
1824
      constants.IPOLICY_DTS: [constants.DT_FILE],
1825
      }
1826
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1823 1827
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1824
    self._TestSet(self._OLD_CLUSTER_POLICY, False)
1828
    self._TestSet({}, diff_policy, True)
1829
    self._TestSetSpecs({}, True)
1830
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1825 1831
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1826 1832

  
1827 1833
  def testUnset(self):
......
1838 1844
        self.assertTrue(key in new_policy)
1839 1845
        self.assertEqual(new_policy[key], old_policy[key])
1840 1846

  
1847
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1848
                      old_policy, diff_policy, group_policy=False)
1849

  
1841 1850
  def _TestInvalidKeys(self, old_policy, isgroup):
1851
    INVALID_KEY = "this_key_shouldnt_be_allowed"
1842 1852
    INVALID_DICT = {
1843
      "this_key_shouldnt_be_allowed": 3,
1853
      INVALID_KEY: 3,
1844 1854
      }
1845 1855
    invalid_policy = INVALID_DICT
1846 1856
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
......
1848 1858
    invalid_ispecs = {
1849 1859
      constants.ISPECS_MINMAX: INVALID_DICT,
1850 1860
      }
1851
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1861
    self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy,
1852 1862
                      old_policy, invalid_ispecs, group_policy=isgroup)
1853
    for key in constants.ISPECS_MINMAX_KEYS:
1854
      invalid_ispec = {
1855
        constants.ISPECS_MINMAX: {
1856
          key: INVALID_DICT,
1857
          },
1863
    if isgroup:
1864
      invalid_for_group = {
1865
        constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1858 1866
        }
1867
      self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1868
                        old_policy, invalid_for_group, group_policy=isgroup)
1869
    good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
1870
    invalid_ispecs = copy.deepcopy(good_ispecs)
1871
    invalid_policy = {
1872
      constants.ISPECS_MINMAX: invalid_ispecs,
1873
      }
1874
    for key in constants.ISPECS_MINMAX_KEYS:
1875
      ispec = invalid_ispecs[key]
1876
      ispec[INVALID_KEY] = None
1859 1877
      self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy,
1860
                        old_policy, invalid_ispec, group_policy=isgroup)
1878
                        old_policy, invalid_policy, group_policy=isgroup)
1879
      del ispec[INVALID_KEY]
1880
      for par in constants.ISPECS_PARAMETERS:
1881
        oldv = ispec[par]
1882
        ispec[par] = "this_is_not_good"
1883
        self.assertRaises(errors.TypeEnforcementError,
1884
                          cmdlib._GetUpdatedIPolicy,
1885
                          old_policy, invalid_policy, group_policy=isgroup)
1886
        ispec[par] = oldv
1887
    # This is to make sure that no two errors were present during the tests
1888
    cmdlib._GetUpdatedIPolicy(old_policy, invalid_policy, group_policy=isgroup)
1861 1889

  
1862 1890
  def testInvalidKeys(self):
1863 1891
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1864 1892
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1865 1893

  
1894
  def testInvalidValues(self):
1895
    for par in (constants.IPOLICY_PARAMETERS |
1896
                frozenset([constants.IPOLICY_DTS])):
1897
      bad_policy = {
1898
        par: "invalid_value",
1899
        }
1900
      self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy, {},
1901
                        bad_policy, group_policy=True)
1866 1902

  
1867 1903
if __name__ == "__main__":
1868 1904
  testutils.GanetiTestProgram()
b/test/py/ganeti.config_unittest.py
436 436
    # depending on the owner (cluster or group)
437 437
    if isgroup:
438 438
      errs = cfg.VerifyConfig()
439
      # FIXME: A bug in FillIPolicy (issue 401) makes this test fail, so we
440
      # invert the assertions for the time being
441
      self.assertFalse(len(errs) >= 1)
439
      self.assertTrue(len(errs) >= 1)
442 440
      errstr = "%s has invalid instance policy" % ipowner
443
      self.assertFalse(_IsErrorInList(errstr, errs))
441
      self.assertTrue(_IsErrorInList(errstr, errs))
444 442
    else:
445 443
      self.assertRaises(AssertionError, cfg.VerifyConfig)
446 444
    del ipolicy[INVALID_KEY]
......
461 459
    else:
462 460
      del ipolicy[key]
463 461

  
464
    ispeclist = [
465
      (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MIN],
466
       "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MIN)),
467
      (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MAX],
468
       "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MAX)),
469
      (ipolicy[constants.ISPECS_STD], constants.ISPECS_STD),
470
      ]
462
    ispeclist = []
463
    if constants.ISPECS_MINMAX in ipolicy:
464
      ispeclist.extend([
465
        (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MIN],
466
         "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MIN)),
467
        (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MAX],
468
         "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MAX)),
469
        ])
470
    if constants.ISPECS_STD in ipolicy:
471
      ispeclist.append((ipolicy[constants.ISPECS_STD], constants.ISPECS_STD))
472

  
471 473
    for (ispec, ispecpath) in ispeclist:
472 474
      ispec[INVALID_KEY] = None
473 475
      errs = cfg.VerifyConfig()
......
494 496
        errs = cfg.VerifyConfig()
495 497
        self.assertFalse(errs)
496 498

  
499
    if constants.ISPECS_MINMAX in ipolicy:
500
      # Test partial minmax specs
501
      minmax = ipolicy[constants.ISPECS_MINMAX]
502
      for key in constants.ISPECS_MINMAX_KEYS:
503
        self.assertTrue(key in minmax)
504
        ispec = minmax[key]
505
        del minmax[key]
506
        errs = cfg.VerifyConfig()
507
        self.assertTrue(len(errs) >= 1)
508
        self.assertTrue(_IsErrorInList("Missing instance specification", errs))
509
        minmax[key] = ispec
510
        for par in constants.ISPECS_PARAMETERS:
511
          oldv = ispec[par]
512
          del ispec[par]
513
          errs = cfg.VerifyConfig()
514
          self.assertTrue(len(errs) >= 1)
515
          self.assertTrue(_IsErrorInList("Missing instance specs parameters",
516
                                         errs))
517
          ispec[par] = oldv
518
      errs = cfg.VerifyConfig()
519
      self.assertFalse(errs)
520

  
497 521
  def _TestVerifyConfigGroupIPolicy(self, groupinfo, cfg):
498 522
    old_ipolicy = groupinfo.ipolicy
499 523
    ipolicy = cfg.GetClusterInfo().SimpleFillIPolicy({})
......
506 530
      errs = cfg.VerifyConfig()
507 531
      self.assertFalse(errs)
508 532
      ipolicy[key] = oldv
509
    # Test partial minmax specs
510
    minmax = ipolicy[constants.ISPECS_MINMAX]
511
    for ispec_key in minmax.keys():
512
      ispec = minmax[ispec_key]
513
      for par in constants.ISPECS_PARAMETERS:
514
        oldv = ispec[par]
515
        del ispec[par]
516
        errs = cfg.VerifyConfig()
517
        self.assertFalse(errs)
518
        ispec[par] = oldv
519 533
    groupinfo.ipolicy = old_ipolicy
520 534

  
521 535
  def _TestVerifyConfigClusterIPolicy(self, ipolicy, cfg):
......
526 540
      del ipolicy[key]
527 541
      self.assertRaises(AssertionError, cfg.VerifyConfig)
528 542
      ipolicy[key] = oldv
529
    # Test partial minmax specs
530
    minmax = ipolicy[constants.ISPECS_MINMAX]
531
    for key in constants.ISPECS_MINMAX_KEYS:
532
      self.assertTrue(key in minmax)
533
      oldv = minmax[key]
534
      del minmax[key]
535
      self.assertRaises(AssertionError, cfg.VerifyConfig)
536
      minmax[key] = oldv
543
    errs = cfg.VerifyConfig()
544
    self.assertFalse(errs)
545
    # Partial standard specs
546
    ispec = ipolicy[constants.ISPECS_STD]
547
    for par in constants.ISPECS_PARAMETERS:
548
      oldv = ispec[par]
549
      del ispec[par]
550
      errs = cfg.VerifyConfig()
551
      self.assertTrue(len(errs) >= 1)
552
      self.assertTrue(_IsErrorInList("Missing instance specs parameters",
553
                                     errs))
554
      ispec[par] = oldv
537 555
    errs = cfg.VerifyConfig()
538 556
    self.assertFalse(errs)
539 557

  
b/test/py/ganeti.objects_unittest.py
460 460
  def testCheckISpecParamSyntax(self):
461 461
    par = "my_parameter"
462 462
    for check_std in [True, False]:
463
      # Only one policy limit
464
      for key in constants.ISPECS_MINMAX_KEYS:
465
        minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
466
        minmax[key][par] = 11
467
        objects.InstancePolicy._CheckISpecParamSyntax(minmax, {}, par,
468
                                                      check_std)
469
      if check_std:
470
        minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
471
        stdspec = {par: 11}
472
        objects.InstancePolicy._CheckISpecParamSyntax(minmax, stdspec, par,
473
                                                      check_std)
474

  
475 463
      # Min and max only
476 464
      good_values = [(11, 11), (11, 40), (0, 0)]
477 465
      for (mn, mx) in good_values:
......
588 576
      self._AssertIPolicyIsFull(policy)
589 577
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
590 578

  
591
  def testFillIPolicySpecs(self):
592
    partial_ipolicies = [
593
      {
594
        constants.ISPECS_MINMAX: {
595
          constants.ISPECS_MIN: {constants.ISPEC_MEM_SIZE: 32},
596
          constants.ISPECS_MAX: {constants.ISPEC_CPU_COUNT: 1024}
597
          },
598
        },
599
      {
600
        constants.ISPECS_MINMAX: {
601
          constants.ISPECS_MAX: {
602
            constants.ISPEC_DISK_COUNT: constants.MAX_DISKS - 1,
603
            constants.ISPEC_NIC_COUNT: constants.MAX_NICS - 1,
604
            },
605
          constants.ISPECS_MIN: {},
606
          },
607
          constants.ISPECS_STD: {constants.ISPEC_DISK_SIZE: 2048},
608
        },
609
      {
610
        constants.ISPECS_STD: {constants.ISPEC_SPINDLE_USE: 3},
611
        },
612
      ]
613
    for diff_pol in partial_ipolicies:
614
      policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
615
      objects.InstancePolicy.CheckParameterSyntax(policy, True)
616
      self._AssertIPolicyIsFull(policy)
617
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
618

  
619 579

  
620 580
if __name__ == "__main__":
621 581
  testutils.GanetiTestProgram()

Also available in: Unified diff