Revision 41044e04

b/doc/rapi.rst
251 251

  
252 252

  
253 253
:pyeval:`constants.ISPECS_MINMAX`
254
  A dict with the following two fields:
254
  A list of dictionaries, each with the following two fields:
255 255

  
256 256
  |ispec-min|, |ispec-max|
257 257
    A sub- `dict` with the following fields, which sets the limit of the
b/lib/cli.py
3736 3736
  if iscluster:
3737 3737
    eff_ipolicy = custom_ipolicy
3738 3738

  
3739
  custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX, {})
3740
  ret = [
3741
    (key,
3742
     FormatParamsDictInfo(custom_minmax.get(key, {}),
3743
                          eff_ipolicy[constants.ISPECS_MINMAX][key]))
3744
    for key in constants.ISPECS_MINMAX_KEYS
3745
    ]
3739
  minmax_out = []
3740
  custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX)
3741
  if custom_minmax:
3742
    for (k, minmax) in enumerate(custom_minmax):
3743
      minmax_out.append([
3744
        ("%s/%s" % (key, k),
3745
         FormatParamsDictInfo(minmax[key], minmax[key]))
3746
        for key in constants.ISPECS_MINMAX_KEYS
3747
        ])
3748
  else:
3749
    for (k, minmax) in enumerate(eff_ipolicy[constants.ISPECS_MINMAX]):
3750
      minmax_out.append([
3751
        ("%s/%s" % (key, k),
3752
         FormatParamsDictInfo({}, minmax[key]))
3753
        for key in constants.ISPECS_MINMAX_KEYS
3754
        ])
3755
  ret = [("bounds specs", minmax_out)]
3756

  
3746 3757
  if iscluster:
3747 3758
    stdspecs = custom_ipolicy[constants.ISPECS_STD]
3748 3759
    ret.append(
......
3787 3798
      _PrintSpecsParameters(buf, stdspecs)
3788 3799
  minmax = ipolicy.get("minmax")
3789 3800
  if minmax:
3790
    minspecs = minmax.get("min")
3791
    maxspecs = minmax.get("max")
3801
    minspecs = minmax[0].get("min")
3802
    maxspecs = minmax[0].get("max")
3792 3803
    if minspecs and maxspecs:
3793 3804
      buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR)
3794 3805
      buf.write("min:")
......
3892 3903
    for key, val in specs.items(): # {min: .. ,max: .., std: ..}
3893 3904
      assert key in ispecs
3894 3905
      ispecs[key][name] = val
3895
  ipolicy[constants.ISPECS_MINMAX] = {}
3906
  minmax_out = {}
3896 3907
  for key in constants.ISPECS_MINMAX_KEYS:
3897 3908
    if fill_all:
3898
      ipolicy[constants.ISPECS_MINMAX][key] = \
3909
      minmax_out[key] = \
3899 3910
        objects.FillDict(constants.ISPECS_MINMAX_DEFAULTS[key], ispecs[key])
3900 3911
    else:
3901
      ipolicy[constants.ISPECS_MINMAX][key] = ispecs[key]
3912
      minmax_out[key] = ispecs[key]
3913
  ipolicy[constants.ISPECS_MINMAX] = [minmax_out]
3902 3914
  if fill_all:
3903 3915
    ipolicy[constants.ISPECS_STD] = \
3904 3916
        objects.FillDict(constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
......
3953 3965
        msg = "Invalid key in bounds instance specifications: %s" % key
3954 3966
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3955 3967
      minmax_out[key] = _ParseISpec(spec, key, True)
3956
    ipolicy_out[constants.ISPECS_MINMAX] = minmax_out
3968
    ipolicy_out[constants.ISPECS_MINMAX] = [minmax_out]
3957 3969
  if std_ispecs is not None:
3958 3970
    assert not group_ipolicy # This is not an option for gnt-group
3959 3971
    ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std", False)
b/lib/cmdlib.py
828 828
    if (not value or value == [constants.VALUE_DEFAULT] or
829 829
        value == constants.VALUE_DEFAULT):
830 830
      if group_policy:
831
        del ipolicy[key]
831
        if key in ipolicy:
832
          del ipolicy[key]
832 833
      else:
833 834
        raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
834 835
                                   " on the cluster'" % key,
......
843 844
                                     " '%s': '%s', error: %s" %
844 845
                                     (key, value, err), errors.ECODE_INVAL)
845 846
      elif key == constants.ISPECS_MINMAX:
846
        for k in value.keys():
847
          utils.ForceDictType(value[k], constants.ISPECS_PARAMETER_TYPES)
847
        for minmax in value:
848
          for k in minmax.keys():
849
            utils.ForceDictType(minmax[k], constants.ISPECS_PARAMETER_TYPES)
848 850
        ipolicy[key] = value
849 851
      elif key == constants.ISPECS_STD:
850 852
        if group_policy:
......
1276 1278
    ret.append("Disk template %s is not allowed (allowed templates: %s)" %
1277 1279
               (disk_template, utils.CommaJoin(allowed_dts)))
1278 1280

  
1279
  minmax = ipolicy[constants.ISPECS_MINMAX]
1280
  return ret + filter(None,
1281
                      (_compute_fn(name, qualifier, minmax, value)
1282
                       for (name, qualifier, value) in test_settings))
1281
  min_errs = None
1282
  for minmax in ipolicy[constants.ISPECS_MINMAX]:
1283
    errs = filter(None,
1284
                  (_compute_fn(name, qualifier, minmax, value)
1285
                   for (name, qualifier, value) in test_settings))
1286
    if min_errs is None or len(errs) < len(min_errs):
1287
      min_errs = errs
1288
  assert min_errs is not None
1289
  return ret + min_errs
1283 1290

  
1284 1291

  
1285 1292
def _ComputeIPolicyInstanceViolation(ipolicy, instance, cfg,
b/lib/config.py
633 633
        result.append("%s has invalid instance policy: %s" % (owner, err))
634 634
      for key, value in ipolicy.items():
635 635
        if key == constants.ISPECS_MINMAX:
636
          _helper_ispecs(owner, "ipolicy/" + key, value)
636
          for k in range(len(value)):
637
            _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k])
637 638
        elif key == constants.ISPECS_STD:
638 639
          _helper(owner, "ipolicy/" + key, value,
639 640
                  constants.ISPECS_PARAMETER_TYPES)
b/lib/constants.py
2215 2215
    },
2216 2216
  }
2217 2217
IPOLICY_DEFAULTS = {
2218
  ISPECS_MINMAX: ISPECS_MINMAX_DEFAULTS,
2218
  ISPECS_MINMAX: [ISPECS_MINMAX_DEFAULTS],
2219 2219
  ISPECS_STD: {
2220 2220
    ISPEC_MEM_SIZE: 128,
2221 2221
    ISPEC_CPU_COUNT: 1,
b/lib/objects.py
959 959
    if check_std:
960 960
      InstancePolicy._CheckIncompleteSpec(stdspec, constants.ISPECS_STD)
961 961

  
962
    minmaxspecs = ipolicy[constants.ISPECS_MINMAX]
963
    missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys())
964
    if missing:
965
      msg = "Missing instance specification: %s" % utils.CommaJoin(missing)
966
      raise errors.ConfigurationError(msg)
967
    for (key, spec) in minmaxspecs.items():
968
      InstancePolicy._CheckIncompleteSpec(spec, key)
969

  
970
    spec_std_ok = True
971
    for param in constants.ISPECS_PARAMETERS:
972
      par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec,
973
                                                         param, check_std)
974
      spec_std_ok = spec_std_ok and par_std_ok
975
    if not spec_std_ok:
962
    if not ipolicy[constants.ISPECS_MINMAX]:
963
      raise errors.ConfigurationError("Empty minmax specifications")
964
    std_is_good = False
965
    for minmaxspecs in ipolicy[constants.ISPECS_MINMAX]:
966
      missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys())
967
      if missing:
968
        msg = "Missing instance specification: %s" % utils.CommaJoin(missing)
969
        raise errors.ConfigurationError(msg)
970
      for (key, spec) in minmaxspecs.items():
971
        InstancePolicy._CheckIncompleteSpec(spec, key)
972

  
973
      spec_std_ok = True
974
      for param in constants.ISPECS_PARAMETERS:
975
        par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec,
976
                                                           param, check_std)
977
        spec_std_ok = spec_std_ok and par_std_ok
978
      std_is_good = std_is_good or spec_std_ok
979
    if not std_is_good:
976 980
      raise errors.ConfigurationError("Invalid std specifications")
977 981

  
978 982
  @classmethod
b/man/htools.rst
198 198
  groups, in the following format (separated by ``|``):
199 199

  
200 200
  - owner (empty if cluster, group name otherwise)
201
  - standard, min, max instance specs, containing the following values
202
    separated by commas:
201
  - standard, min, max instance specs; min and max instance specs are
202
    separated between them by a semicolon, and can be specified multiple
203
    times (min;max;min;max...); each of the specs contains the following
204
    values separated by commas:
203 205
    - memory size
204 206
    - cpu count
205 207
    - disk size
b/src/Ganeti/HTools/Backend/Text.hs
32 32
  , loadInst
33 33
  , loadNode
34 34
  , loadISpec
35
  , loadMultipleMinMaxISpecs
35 36
  , loadIPolicy
36 37
  , serializeInstances
37 38
  , serializeNode
38 39
  , serializeNodes
39 40
  , serializeGroup
40 41
  , serializeISpec
42
  , serializeMultipleMinMaxISpecs
41 43
  , serializeIPolicy
42 44
  , serializeCluster
43 45
  ) where
......
117 119
serializeInstances nl =
118 120
  unlines . map (serializeInstance nl) . Container.elems
119 121

  
122
-- | Separator between ISpecs (in MinMaxISpecs).
123
iSpecsSeparator :: Char
124
iSpecsSeparator = ';'
125

  
120 126
-- | Generate a spec data from a given ISpec object.
121 127
serializeISpec :: ISpec -> String
122 128
serializeISpec ispec =
......
130 136
serializeDiskTemplates :: [DiskTemplate] -> String
131 137
serializeDiskTemplates = intercalate "," . map diskTemplateToRaw
132 138

  
139
-- | Generate min/max instance specs data.
140
serializeMultipleMinMaxISpecs :: [MinMaxISpecs] -> String
141
serializeMultipleMinMaxISpecs minmaxes =
142
  intercalate [iSpecsSeparator] $ foldr serialpair [] minmaxes
143
  where serialpair (MinMaxISpecs minspec maxspec) acc =
144
          serializeISpec minspec : serializeISpec maxspec : acc
145

  
133 146
-- | Generate policy data from a given policy object.
134 147
serializeIPolicy :: String -> IPolicy -> String
135 148
serializeIPolicy owner ipol =
136 149
  let IPolicy minmax stdspec dts vcpu_ratio spindle_ratio = ipol
137
      MinMaxISpecs minspec maxspec = minmax
138 150
      strings = [ owner
139 151
                , serializeISpec stdspec
140
                , serializeISpec minspec
141
                , serializeISpec maxspec
152
                , serializeMultipleMinMaxISpecs minmax
142 153
                , serializeDiskTemplates dts
143 154
                , show vcpu_ratio
144 155
                , show spindle_ratio
......
255 266
  return $ ISpec xmem_s xcpu_c xdsk_s xdsk_c xnic_c xsu
256 267
loadISpec owner s = fail $ "Invalid ispec data for " ++ owner ++ ": " ++ show s
257 268

  
269
-- | Load a single min/max ISpec pair
270
loadMinMaxISpecs :: String -> String -> String -> Result MinMaxISpecs
271
loadMinMaxISpecs owner minspec maxspec = do
272
  xminspec <- loadISpec (owner ++ "/minspec") (commaSplit minspec)
273
  xmaxspec <- loadISpec (owner ++ "/maxspec") (commaSplit maxspec)
274
  return $ MinMaxISpecs xminspec xmaxspec
275

  
276
-- | Break a list of ispecs strings into a list of (min/max) ispecs pairs
277
breakISpecsPairs :: String -> [String] -> Result [(String, String)]
278
breakISpecsPairs _ [] =
279
  return []
280
breakISpecsPairs owner (x:y:xs) = do
281
  rest <- breakISpecsPairs owner xs
282
  return $ (x, y) : rest
283
breakISpecsPairs owner _ =
284
  fail $ "Odd number of min/max specs for " ++ owner
285

  
286
-- | Load a list of min/max ispecs pairs
287
loadMultipleMinMaxISpecs :: String -> [String] -> Result [MinMaxISpecs]
288
loadMultipleMinMaxISpecs owner ispecs = do
289
  pairs <- breakISpecsPairs owner ispecs
290
  mapM (uncurry $ loadMinMaxISpecs owner) pairs
291

  
258 292
-- | Loads an ipolicy from a field list.
259 293
loadIPolicy :: [String] -> Result (String, IPolicy)
260
loadIPolicy [owner, stdspec, minspec, maxspec, dtemplates,
294
loadIPolicy [owner, stdspec, minmaxspecs, dtemplates,
261 295
             vcpu_ratio, spindle_ratio] = do
262 296
  xstdspec <- loadISpec (owner ++ "/stdspec") (commaSplit stdspec)
263
  xminspec <- loadISpec (owner ++ "/minspec") (commaSplit minspec)
264
  xmaxspec <- loadISpec (owner ++ "/maxspec") (commaSplit maxspec)
297
  xminmaxspecs <- loadMultipleMinMaxISpecs owner $
298
                  sepSplit iSpecsSeparator minmaxspecs
265 299
  xdts <- mapM diskTemplateFromRaw $ commaSplit dtemplates
266 300
  xvcpu_ratio <- tryRead (owner ++ "/vcpu_ratio") vcpu_ratio
267 301
  xspindle_ratio <- tryRead (owner ++ "/spindle_ratio") spindle_ratio
268 302
  return (owner,
269
          IPolicy (MinMaxISpecs xminspec xmaxspec) xstdspec
303
          IPolicy xminmaxspecs xstdspec
270 304
                xdts xvcpu_ratio xspindle_ratio)
271 305
loadIPolicy s = fail $ "Invalid ipolicy data: '" ++ show s ++ "'"
272 306

  
b/src/Ganeti/HTools/Instance.hs
279 279
  | vcpus inst < T.iSpecCpuCount ispec = Bad T.FailCPU
280 280
  | otherwise = Ok ()
281 281

  
282
-- | Checks if an instance matches a min/max specs pair
283
instMatchesMinMaxSpecs :: Instance -> T.MinMaxISpecs -> T.OpResult ()
284
instMatchesMinMaxSpecs inst minmax = do
285
  instAboveISpec inst (T.minMaxISpecsMinSpec minmax)
286
  instBelowISpec inst (T.minMaxISpecsMaxSpec minmax)
287

  
288
-- | Checks if an instance matches any specs of a policy
289
instMatchesSpecs :: Instance -> [T.MinMaxISpecs] -> T.OpResult ()
290
 -- Return Ok for no constraints, though this should never happen
291
instMatchesSpecs _ [] = Ok ()
292
instMatchesSpecs inst (minmax:minmaxes) =
293
  foldr eithermatch (instMatchesMinMaxSpecs inst minmax) minmaxes
294
  where eithermatch mm (Bad _) = instMatchesMinMaxSpecs inst mm
295
        eithermatch _ y@(Ok ()) = y
296
--  # See 04f231771
297

  
282 298
-- | Checks if an instance matches a policy.
283 299
instMatchesPolicy :: Instance -> T.IPolicy -> T.OpResult ()
284 300
instMatchesPolicy inst ipol = do
285
  let minmax = T.iPolicyMinMaxISpecs ipol
286
  instAboveISpec inst (T.minMaxISpecsMinSpec minmax)
287
  instBelowISpec inst (T.minMaxISpecsMaxSpec minmax)
301
  instMatchesSpecs inst $ T.iPolicyMinMaxISpecs ipol
288 302
  if diskTemplate inst `elem` T.iPolicyDiskTemplates ipol
289 303
    then Ok ()
290 304
    else Bad T.FailDisk
b/src/Ganeti/HTools/Program/Hspace.hs
446 446

  
447 447
  -- Run the tiered allocation
448 448

  
449
  let minmax = iPolicyMinMaxISpecs ipol
450
  let tspec = fromMaybe (rspecFromISpec (minMaxISpecsMaxSpec minmax))
451
              (optTieredSpec opts)
449
  let minmaxes = iPolicyMinMaxISpecs ipol
450
  -- TODO: Go through all min/max specs pairs
451
  tspec <- case minmaxes of
452
             [] -> exitErr "Empty list of specs received from the cluster"
453
             minmax:_ -> return $ fromMaybe
454
                         (rspecFromISpec (minMaxISpecsMaxSpec minmax))
455
                         (optTieredSpec opts)
452 456

  
453 457
  (treason, trl_nl, _, spec_map) <-
454 458
    runAllocation cdata stop_allocation
b/src/Ganeti/HTools/Types.hs
169 169

  
170 170
-- | The default minimum ispec.
171 171
defMinISpec :: ISpec
172
defMinISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinmaxMinMemorySize
173
                    , iSpecCpuCount   = C.ipolicyDefaultsMinmaxMinCpuCount
174
                    , iSpecDiskSize   = C.ipolicyDefaultsMinmaxMinDiskSize
175
                    , iSpecDiskCount  = C.ipolicyDefaultsMinmaxMinDiskCount
176
                    , iSpecNicCount   = C.ipolicyDefaultsMinmaxMinNicCount
177
                    , iSpecSpindleUse = C.ipolicyDefaultsMinmaxMinSpindleUse
172
defMinISpec = ISpec { iSpecMemorySize = C.ispecsMinmaxDefaultsMinMemorySize
173
                    , iSpecCpuCount   = C.ispecsMinmaxDefaultsMinCpuCount
174
                    , iSpecDiskSize   = C.ispecsMinmaxDefaultsMinDiskSize
175
                    , iSpecDiskCount  = C.ispecsMinmaxDefaultsMinDiskCount
176
                    , iSpecNicCount   = C.ispecsMinmaxDefaultsMinNicCount
177
                    , iSpecSpindleUse = C.ispecsMinmaxDefaultsMinSpindleUse
178 178
                    }
179 179

  
180 180
-- | The default standard ispec.
......
189 189

  
190 190
-- | The default max ispec.
191 191
defMaxISpec :: ISpec
192
defMaxISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinmaxMaxMemorySize
193
                    , iSpecCpuCount   = C.ipolicyDefaultsMinmaxMaxCpuCount
194
                    , iSpecDiskSize   = C.ipolicyDefaultsMinmaxMaxDiskSize
195
                    , iSpecDiskCount  = C.ipolicyDefaultsMinmaxMaxDiskCount
196
                    , iSpecNicCount   = C.ipolicyDefaultsMinmaxMaxNicCount
197
                    , iSpecSpindleUse = C.ipolicyDefaultsMinmaxMaxSpindleUse
192
defMaxISpec = ISpec { iSpecMemorySize = C.ispecsMinmaxDefaultsMaxMemorySize
193
                    , iSpecCpuCount   = C.ispecsMinmaxDefaultsMaxCpuCount
194
                    , iSpecDiskSize   = C.ispecsMinmaxDefaultsMaxDiskSize
195
                    , iSpecDiskCount  = C.ispecsMinmaxDefaultsMaxDiskCount
196
                    , iSpecNicCount   = C.ispecsMinmaxDefaultsMaxNicCount
197
                    , iSpecSpindleUse = C.ispecsMinmaxDefaultsMaxSpindleUse
198 198
                    }
199 199

  
200 200
-- | Minimum and maximum instance specs type.
......
204 204
  ])
205 205

  
206 206
-- | Defult minimum and maximum instance specs.
207
defMinMaxISpecs :: MinMaxISpecs
208
defMinMaxISpecs = MinMaxISpecs { minMaxISpecsMinSpec = defMinISpec
209
                               , minMaxISpecsMaxSpec = defMaxISpec
210
                               }
207
defMinMaxISpecs :: [MinMaxISpecs]
208
defMinMaxISpecs = [MinMaxISpecs { minMaxISpecsMinSpec = defMinISpec
209
                                , minMaxISpecsMaxSpec = defMaxISpec
210
                                }]
211 211

  
212 212
-- | Instance policy type.
213 213
$(THH.buildObject "IPolicy" "iPolicy"
214 214
  [ THH.renameField "MinMaxISpecs" $
215
      THH.simpleField C.ispecsMinmax [t| MinMaxISpecs |]
215
      THH.simpleField C.ispecsMinmax [t| [MinMaxISpecs] |]
216 216
  , THH.renameField "StdSpec" $ THH.simpleField C.ispecsStd [t| ISpec |]
217 217
  , THH.renameField "DiskTemplates" $
218 218
      THH.simpleField C.ipolicyDts [t| [DiskTemplate] |]
b/src/Ganeti/Objects.hs
512 512
-- has a special 2-level inheritance mode.
513 513
$(buildObject "PartialIPolicy" "ipolicy"
514 514
  [ optionalField . renameField "MinMaxISpecsP"
515
                    $ simpleField C.ispecsMinmax   [t| MinMaxISpecs |]
515
                    $ simpleField C.ispecsMinmax   [t| [MinMaxISpecs] |]
516 516
  , optionalField . renameField "StdSpecP"
517 517
                    $ simpleField "std"            [t| PartialISpecParams |]
518 518
  , optionalField . renameField "SpindleRatioP"
......
527 527
-- has a special 2-level inheritance mode.
528 528
$(buildObject "FilledIPolicy" "ipolicy"
529 529
  [ renameField "MinMaxISpecs"
530
    $ simpleField C.ispecsMinmax [t| MinMaxISpecs |]
530
    $ simpleField C.ispecsMinmax [t| [MinMaxISpecs] |]
531 531
  , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
532 532
  , simpleField "spindle-ratio"  [t| Double |]
533 533
  , simpleField "vcpu-ratio"     [t| Double |]
b/test/data/htools/clean-nonzero-score.data
11 11
new-4|128|1024|1|running|Y|node-01-002||diskless||1
12 12

  
13 13

  
14
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
15
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
14
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
15
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
b/test/data/htools/common-suffix.data
6 6
instance1.example.com|128|1024|1|running|Y|node2.example.com||plain|
7 7

  
8 8

  
9
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,1|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
10
default|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,1|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
9
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,1|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
10
default|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,1|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
b/test/data/htools/empty-cluster.data
3 3

  
4 4

  
5 5

  
6
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
7
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
6
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
7
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
b/test/data/htools/hail-alloc-drbd.json
14 14
          "cpu-count": 1,
15 15
          "spindle-use": 1
16 16
        },
17
        "minmax": {
18
	  "min": {
19
	    "nic-count": 1,
20
	    "disk-size": 128,
21
	    "disk-count": 1,
22
	    "memory-size": 128,
23
	    "cpu-count": 1,
24
	    "spindle-use": 1
25
	  },
26
	  "max": {
27
	    "nic-count": 8,
28
	    "disk-size": 1048576,
29
	    "disk-count": 16,
30
	    "memory-size": 32768,
31
	    "cpu-count": 8,
32
	    "spindle-use": 8
33
	  }
34
        },
17
        "minmax": [
18
          {
19
            "min": {
20
              "nic-count": 1,
21
              "disk-size": 128,
22
              "disk-count": 1,
23
              "memory-size": 128,
24
              "cpu-count": 1,
25
              "spindle-use": 1
26
            },
27
            "max": {
28
              "nic-count": 8,
29
              "disk-size": 1048576,
30
              "disk-count": 16,
31
              "memory-size": 32768,
32
              "cpu-count": 8,
33
              "spindle-use": 8
34
            }
35
          }
36
        ],
35 37
        "vcpu-ratio": 4.0,
36 38
        "disk-templates": [
37 39
          "sharedfile",
......
58 60
      "disk-count": 1,
59 61
      "spindle-use": 1
60 62
    },
61
    "min": {
62
      "nic-count": 1,
63
      "disk-size": 1024,
64
      "memory-size": 128,
65
      "cpu-count": 1,
66
      "disk-count": 1,
67
      "spindle-use": 1
68
    },
69
    "max": {
70
      "nic-count": 8,
71
      "disk-size": 1048576,
72
      "memory-size": 32768,
73
      "cpu-count": 8,
74
      "disk-count": 16,
75
      "spindle-use": 8
76
    },
63
    "minmax": [
64
      {
65
        "min": {
66
          "nic-count": 1,
67
          "disk-size": 1024,
68
          "memory-size": 128,
69
          "cpu-count": 1,
70
          "disk-count": 1,
71
          "spindle-use": 1
72
        },
73
        "max": {
74
          "nic-count": 8,
75
          "disk-size": 1048576,
76
          "memory-size": 32768,
77
          "cpu-count": 8,
78
          "disk-count": 16,
79
          "spindle-use": 8
80
        }
81
      }
82
    ],
77 83
    "vcpu-ratio": 4.0,
78 84
    "disk-templates": [
79 85
      "sharedfile",
b/test/data/htools/hail-alloc-invalid-twodisks.json
16 16
        "disk-templates": [
17 17
          "file"
18 18
        ],
19
        "minmax" : {
20
          "max": {
21
            "cpu-count": 2,
22
            "disk-count": 8,
23
            "disk-size": 2048,
24
            "memory-size": 12800,
25
            "nic-count": 8,
26
            "spindle-use": 8
27
          },
28
          "min": {
29
            "cpu-count": 1,
30
            "disk-count": 1,
31
            "disk-size": 1024,
32
            "memory-size": 128,
33
            "nic-count": 1,
34
            "spindle-use": 1
19
        "minmax" : [
20
          {
21
            "max": {
22
              "cpu-count": 2,
23
              "disk-count": 8,
24
              "disk-size": 2048,
25
              "memory-size": 12800,
26
              "nic-count": 8,
27
              "spindle-use": 8
28
            },
29
            "min": {
30
              "cpu-count": 1,
31
              "disk-count": 1,
32
              "disk-size": 1024,
33
              "memory-size": 128,
34
              "nic-count": 1,
35
              "spindle-use": 1
36
            }
35 37
          }
36
        },
38
        ],
37 39
        "spindle-ratio": 32.0,
38 40
        "std": {
39 41
          "cpu-count": 1,
b/test/data/htools/hail-alloc-twodisks.json
16 16
        "disk-templates": [
17 17
          "file"
18 18
        ],
19
        "minmax": {
20
          "max": {
21
            "cpu-count": 2,
22
            "disk-count": 8,
23
            "disk-size": 2048,
24
            "memory-size": 12800,
25
            "nic-count": 8,
26
            "spindle-use": 8
27
          },
28
          "min": {
29
            "cpu-count": 1,
30
            "disk-count": 1,
31
            "disk-size": 1024,
32
            "memory-size": 128,
33
            "nic-count": 1,
34
            "spindle-use": 1
19
        "minmax": [
20
          {
21
            "max": {
22
              "cpu-count": 2,
23
              "disk-count": 8,
24
              "disk-size": 2048,
25
              "memory-size": 12800,
26
              "nic-count": 8,
27
              "spindle-use": 8
28
            },
29
            "min": {
30
              "cpu-count": 1,
31
              "disk-count": 1,
32
              "disk-size": 1024,
33
              "memory-size": 128,
34
              "nic-count": 1,
35
              "spindle-use": 1
36
            }
35 37
          }
36
        },
38
        ],
37 39
        "spindle-ratio": 32.0,
38 40
        "std": {
39 41
          "cpu-count": 1,
b/test/data/htools/hail-change-group.json
14 14
          "cpu-count": 1,
15 15
          "spindle-use": 1
16 16
        },
17
        "minmax": {
18
	  "min": {
19
	    "nic-count": 1,
20
	    "disk-size": 128,
21
	    "disk-count": 1,
22
	    "memory-size": 128,
23
	    "cpu-count": 1,
24
	    "spindle-use": 1
25
	  },
26
	  "max": {
27
	    "nic-count": 8,
28
	    "disk-size": 1048576,
29
	    "disk-count": 16,
30
	    "memory-size": 32768,
31
	    "cpu-count": 8,
32
	    "spindle-use": 8
33
	  }
34
        },
17
        "minmax": [
18
          {
19
            "min": {
20
              "nic-count": 1,
21
              "disk-size": 128,
22
              "disk-count": 1,
23
              "memory-size": 128,
24
              "cpu-count": 1,
25
              "spindle-use": 1
26
            },
27
            "max": {
28
              "nic-count": 8,
29
              "disk-size": 1048576,
30
              "disk-count": 16,
31
              "memory-size": 32768,
32
              "cpu-count": 8,
33
              "spindle-use": 8
34
            }
35
          }
36
        ],
35 37
        "vcpu-ratio": 4.0,
36 38
        "disk-templates": [
37 39
          "sharedfile",
......
58 60
          "cpu-count": 1,
59 61
          "spindle-use": 1
60 62
        },
61
        "minmax": {
62
	  "min": {
63
	    "nic-count": 1,
64
	    "disk-size": 128,
65
	    "disk-count": 1,
66
	    "memory-size": 128,
67
	    "cpu-count": 1,
68
	    "spindle-use": 1
69
	  },
70
	  "max": {
71
	    "nic-count": 8,
72
	    "disk-size": 1048576,
73
	    "disk-count": 16,
74
	    "memory-size": 32768,
75
	    "cpu-count": 8,
76
	    "spindle-use": 8
77
	  }
78
        },
63
        "minmax": [
64
          {
65
            "min": {
66
              "nic-count": 1,
67
              "disk-size": 128,
68
              "disk-count": 1,
69
              "memory-size": 128,
70
              "cpu-count": 1,
71
              "spindle-use": 1
72
            },
73
            "max": {
74
              "nic-count": 8,
75
              "disk-size": 1048576,
76
              "disk-count": 16,
77
              "memory-size": 32768,
78
              "cpu-count": 8,
79
              "spindle-use": 8
80
            }
81
          }
82
        ],
79 83
        "vcpu-ratio": 4.0,
80 84
        "disk-templates": [
81 85
          "sharedfile",
......
102 106
      "disk-count": 1,
103 107
      "spindle-use": 1
104 108
    },
105
    "minmax": {
106
      "min": {
107
	"nic-count": 1,
108
	"disk-size": 1024,
109
	"memory-size": 128,
110
	"cpu-count": 1,
111
	"disk-count": 1,
112
	"spindle-use": 1
113
      },
114
      "max": {
115
	"nic-count": 8,
116
	"disk-size": 1048576,
117
	"memory-size": 32768,
118
	"cpu-count": 8,
119
	"disk-count": 16,
120
	"spindle-use": 8
109
    "minmax": [
110
      {
111
        "min": {
112
          "nic-count": 1,
113
          "disk-size": 1024,
114
          "memory-size": 128,
115
          "cpu-count": 1,
116
          "disk-count": 1,
117
          "spindle-use": 1
118
        },
119
        "max": {
120
          "nic-count": 8,
121
          "disk-size": 1048576,
122
          "memory-size": 32768,
123
          "cpu-count": 8,
124
          "disk-count": 16,
125
          "spindle-use": 8
126
        }
121 127
      }
122
    },
128
    ],
123 129
    "vcpu-ratio": 4.0,
124 130
    "disk-templates": [
125 131
      "sharedfile",
b/test/data/htools/hail-node-evac.json
14 14
          "cpu-count": 1,
15 15
          "spindle-use": 1
16 16
        },
17
        "minmax": {
18
	  "min": {
19
	    "nic-count": 1,
20
	    "disk-size": 128,
21
	    "disk-count": 1,
22
	    "memory-size": 128,
23
	    "cpu-count": 1,
24
	    "spindle-use": 1
25
	  },
26
	  "max": {
27
	    "nic-count": 8,
28
	    "disk-size": 1048576,
29
	    "disk-count": 16,
30
	    "memory-size": 32768,
31
	    "cpu-count": 8,
32
	    "spindle-use": 8
33
	  }
34
        },
17
        "minmax": [
18
          {
19
            "min": {
20
              "nic-count": 1,
21
              "disk-size": 128,
22
              "disk-count": 1,
23
              "memory-size": 128,
24
              "cpu-count": 1,
25
              "spindle-use": 1
26
            },
27
            "max": {
28
              "nic-count": 8,
29
              "disk-size": 1048576,
30
              "disk-count": 16,
31
              "memory-size": 32768,
32
              "cpu-count": 8,
33
              "spindle-use": 8
34
            }
35
          }
36
        ],
35 37
        "vcpu-ratio": 4.0,
36 38
        "disk-templates": [
37 39
          "sharedfile",
b/test/data/htools/hail-reloc-drbd.json
14 14
          "cpu-count": 1,
15 15
          "spindle-use": 1
16 16
        },
17
        "minmax": {
18
	  "min": {
19
	    "nic-count": 1,
20
	    "disk-size": 128,
21
	    "disk-count": 1,
22
	    "memory-size": 128,
23
	    "cpu-count": 1,
24
	    "spindle-use": 1
25
	  },
26
	  "max": {
27
	    "nic-count": 8,
28
	    "disk-size": 1048576,
29
	    "disk-count": 16,
30
	    "memory-size": 32768,
31
	    "cpu-count": 8,
32
	    "spindle-use": 8
33
	  }
34
        },
17
        "minmax": [
18
          {
19
            "min": {
20
              "nic-count": 1,
21
              "disk-size": 128,
22
              "disk-count": 1,
23
              "memory-size": 128,
24
              "cpu-count": 1,
25
              "spindle-use": 1
26
            },
27
            "max": {
28
              "nic-count": 8,
29
              "disk-size": 1048576,
30
              "disk-count": 16,
31
              "memory-size": 32768,
32
              "cpu-count": 8,
33
              "spindle-use": 8
34
            }
35
          }
36
        ],
35 37
        "vcpu-ratio": 4.0,
36 38
        "disk-templates": [
37 39
          "sharedfile",
......
58 60
      "disk-count": 1,
59 61
      "spindle-use": 1
60 62
    },
61
    "min": {
62
      "nic-count": 1,
63
      "disk-size": 1024,
64
      "memory-size": 128,
65
      "cpu-count": 1,
66
      "disk-count": 1,
67
      "spindle-use": 1
68
    },
69
    "max": {
70
      "nic-count": 8,
71
      "disk-size": 1048576,
72
      "memory-size": 32768,
73
      "cpu-count": 8,
74
      "disk-count": 16,
75
      "spindle-use": 8
76
    },
63
    "minmax": [
64
      {
65
	"min": {
66
	  "nic-count": 1,
67
	  "disk-size": 1024,
68
	  "memory-size": 128,
69
	  "cpu-count": 1,
70
	  "disk-count": 1,
71
	  "spindle-use": 1
72
	},
73
	"max": {
74
	  "nic-count": 8,
75
	  "disk-size": 1048576,
76
	  "memory-size": 32768,
77
	  "cpu-count": 8,
78
	  "disk-count": 16,
79
	  "spindle-use": 8
80
	}
81
      }
82
    ],
77 83
    "vcpu-ratio": 4.0,
78 84
    "disk-templates": [
79 85
      "sharedfile",
b/test/data/htools/hbal-split-insts.data
140 140
new-127|128|1024|1|running|Y|node-01-001|node-01-003|drbd||1
141 141

  
142 142

  
143
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
144
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
145
group-02|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
143
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
144
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
145
group-02|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
b/test/data/htools/invalid-node.data
6 6
new-0|128|1024|1|running|Y|no-such-node||plain|
7 7

  
8 8

  
9
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
10
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
9
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
10
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
b/test/data/htools/missing-resources.data
5 5

  
6 6

  
7 7

  
8
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
9
default|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
8
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
9
default|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,8|diskless,file,sharedfile,plain,blockdev,drbd,rbd|4.0|32.0
b/test/data/htools/multiple-master.data
6 6

  
7 7

  
8 8

  
9
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
10
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
9
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
10
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
b/test/data/htools/n1-failure.data
20 20
new-7|128|1024|1|running|Y|node-01-001|node-01-003|drbd||1
21 21

  
22 22

  
23
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
24
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
25
group-02|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
23
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
24
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
25
group-02|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
b/test/data/htools/rapi/groups.json
11 11
        "disk-count": 1,
12 12
        "spindle-use": 1
13 13
      },
14
      "minmax": {
15
	"min": {
16
	  "cpu-count": 1,
17
	  "nic-count": 1,
18
	  "disk-size": 1024,
19
	  "memory-size": 128,
20
	  "disk-count": 1,
21
	  "spindle-use": 1
22
	},
23
	"max": {
24
	  "cpu-count": 8,
25
	  "nic-count": 8,
26
	  "disk-size": 1048576,
27
	  "memory-size": 32768,
28
	  "disk-count": 16,
29
	  "spindle-use": 8
30
	}
31
      },
14
      "minmax": [
15
        {
16
          "min": {
17
            "cpu-count": 1,
18
            "nic-count": 1,
19
            "disk-size": 1024,
20
            "memory-size": 128,
21
            "disk-count": 1,
22
            "spindle-use": 1
23
          },
24
          "max": {
25
            "cpu-count": 8,
26
            "nic-count": 8,
27
            "disk-size": 1048576,
28
            "memory-size": 32768,
29
            "disk-count": 16,
30
            "spindle-use": 8
31
          }
32
        }
33
      ],
32 34
      "vcpu-ratio": 4.0,
33 35
      "disk-templates": [
34 36
        "sharedfile",
b/test/data/htools/rapi/info.json
86 86
      "cpu-count": 1,
87 87
      "spindle-use": 1
88 88
    },
89
    "minmax": {
90
      "min": {
91
	"nic-count": 1,
92
	"disk-size": 128,
93
	"disk-count": 1,
94
	"memory-size": 128,
95
	"cpu-count": 1,
96
	"spindle-use": 1
97
      },
98
      "max": {
99
	"nic-count": 8,
100
	"disk-size": 1048576,
101
	"disk-count": 16,
102
	"memory-size": 32768,
103
	"cpu-count": 8,
104
	"spindle-use": 8
89
    "minmax": [
90
      {
91
        "min": {
92
          "nic-count": 1,
93
          "disk-size": 128,
94
          "disk-count": 1,
95
          "memory-size": 128,
96
          "cpu-count": 1,
97
          "spindle-use": 1
98
        },
99
        "max": {
100
          "nic-count": 8,
101
          "disk-size": 1048576,
102
          "disk-count": 16,
103
          "memory-size": 32768,
104
          "cpu-count": 8,
105
          "spindle-use": 8
106
        }
105 107
      }
106
    },
108
    ],
107 109
    "vcpu-ratio": 4.0,
108 110
    "disk-templates": [
109 111
      "sharedfile",
b/test/data/htools/unique-reboot-order.data
8 8
new-1|128|1152|1|running|Y|node-01-002|node-01-003|drbd||1
9 9

  
10 10

  
11
|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
12
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1|32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
11
|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
12
group-01|128,1,1024,1,1,1|128,1,1024,1,1,1;32768,8,1048576,16,8,12|diskless,file,sharedfile,plain,blockdev,drbd,rbd,ext|4.0|32.0
b/test/hs/Test/Ganeti/HTools/Backend/Text.hs
7 7

  
8 8
{-
9 9

  
10
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
10
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
11 11

  
12 12
This program is free software; you can redistribute it and/or modify
13 13
it under the terms of the GNU General Public License as published by
......
158 158
    Bad msg -> failTest $ "Failed to load ispec: " ++ msg
159 159
    Ok ispec' -> ispec ==? ispec'
160 160

  
161
prop_MultipleMinMaxISpecsIdempotent :: [Types.MinMaxISpecs] -> Property
162
prop_MultipleMinMaxISpecsIdempotent minmaxes =
163
  case Text.loadMultipleMinMaxISpecs "dummy" . Utils.sepSplit ';' .
164
       Text.serializeMultipleMinMaxISpecs $ minmaxes of
165
    Bad msg -> failTest $ "Failed to load min/max ispecs: " ++ msg
166
    Ok minmaxes' -> minmaxes ==? minmaxes'
167

  
161 168
prop_IPolicyIdempotent :: Types.IPolicy -> Property
162 169
prop_IPolicyIdempotent ipol =
163 170
  case Text.loadIPolicy . Utils.sepSplit '|' $
......
210 217
            , 'prop_Load_NodeFail
211 218
            , 'prop_NodeLSIdempotent
212 219
            , 'prop_ISpecIdempotent
220
            , 'prop_MultipleMinMaxISpecsIdempotent
213 221
            , 'prop_IPolicyIdempotent
214 222
            , 'prop_CreateSerialise
215 223
            ]
b/test/hs/Test/Ganeti/HTools/Types.hs
42 42

  
43 43
import Control.Applicative
44 44
import Data.List (sort)
45
import Control.Monad (replicateM)
45 46

  
46 47
import Test.Ganeti.TestHelper
47 48
import Test.Ganeti.TestCommon
......
101 102
                     , Types.iSpecSpindleUse = fromIntegral su
102 103
                     }
103 104

  
105
genMinMaxISpecs :: Gen Types.MinMaxISpecs
106
genMinMaxISpecs = do
107
  imin <- arbitrary
108
  imax <- genBiggerISpec imin
109
  return Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin
110
                             , Types.minMaxISpecsMaxSpec = imax
111
                             }
112

  
113
instance Arbitrary Types.MinMaxISpecs where
114
  arbitrary = genMinMaxISpecs
115

  
116
genMinMaxStdISpecs :: Gen (Types.MinMaxISpecs, Types.ISpec)
117
genMinMaxStdISpecs = do
118
  imin <- arbitrary
119
  istd <- genBiggerISpec imin
120
  imax <- genBiggerISpec istd
121
  return (Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin
122
                             , Types.minMaxISpecsMaxSpec = imax
123
                             },
124
          istd)
125

  
126
genIPolicySpecs :: Gen ([Types.MinMaxISpecs], Types.ISpec)
127
genIPolicySpecs = do
128
  num_mm <- choose (1, 6) -- 6 is just an arbitrary limit
129
  std_compl <- choose (1, num_mm)
130
  mm_head <- replicateM (std_compl - 1) genMinMaxISpecs
131
  (mm_middle, istd) <- genMinMaxStdISpecs
132
  mm_tail <- replicateM (num_mm - std_compl) genMinMaxISpecs
133
  return (mm_head ++ (mm_middle : mm_tail), istd)
134

  
135

  
104 136
instance Arbitrary Types.IPolicy where
105 137
  arbitrary = do
106
    imin <- arbitrary
107
    istd <- genBiggerISpec imin
108
    imax <- genBiggerISpec istd
138
    (iminmax, istd) <- genIPolicySpecs
109 139
    num_tmpl <- choose (0, length allDiskTemplates)
110 140
    dts  <- genUniquesList num_tmpl arbitrary
111 141
    vcpu_ratio <- choose (1.0, maxVcpuRatio)
112 142
    spindle_ratio <- choose (1.0, maxSpindleRatio)
113
    return Types.IPolicy { Types.iPolicyMinMaxISpecs = Types.MinMaxISpecs
114
                           { Types.minMaxISpecsMinSpec = imin
115
                           , Types.minMaxISpecsMaxSpec = imax
116
                           }
143
    return Types.IPolicy { Types.iPolicyMinMaxISpecs = iminmax
117 144
                         , Types.iPolicyStdSpec = istd
118 145
                         , Types.iPolicyDiskTemplates = dts
119 146
                         , Types.iPolicyVcpuRatio = vcpu_ratio
b/test/hs/Test/Ganeti/TestHTools.hs
52 52
-- | Null iPolicy, and by null we mean very liberal.
53 53
nullIPolicy :: Types.IPolicy
54 54
nullIPolicy = Types.IPolicy
55
  { Types.iPolicyMinMaxISpecs = Types.MinMaxISpecs
55
  { Types.iPolicyMinMaxISpecs = [Types.MinMaxISpecs
56 56
    { Types.minMaxISpecsMinSpec = Types.ISpec { Types.iSpecMemorySize = 0
57 57
                                              , Types.iSpecCpuCount   = 0
58 58
                                              , Types.iSpecDiskSize   = 0
......
68 68
      , Types.iSpecNicCount   = C.maxNics
69 69
      , Types.iSpecSpindleUse = maxBound
70 70
      }
71
    }
71
    }]
72 72
  , Types.iPolicyStdSpec = Types.ISpec { Types.iSpecMemorySize = Types.unitMem
73 73
                                       , Types.iSpecCpuCount   = Types.unitCpu
74 74
                                       , Types.iSpecDiskSize   = Types.unitDsk
b/test/py/ganeti.cli_unittest.py
1184 1184
    # Policies are big, and we want to see the difference in case of an error
1185 1185
    self.maxDiff = None
1186 1186

  
1187
  def _RecursiveCheckMergedDicts(self, default_pol, diff_pol, merged_pol):
1187
  def _RecursiveCheckMergedDicts(self, default_pol, diff_pol, merged_pol,
1188
                                 merge_minmax=False):
1188 1189
    self.assertTrue(type(default_pol) is dict)
1189 1190
    self.assertTrue(type(diff_pol) is dict)
1190 1191
    self.assertTrue(type(merged_pol) is dict)
......
1194 1195
      if key in diff_pol:
1195 1196
        if type(val) is dict:
1196 1197
          self._RecursiveCheckMergedDicts(default_pol[key], diff_pol[key], val)
1198
        elif (merge_minmax and key == "minmax" and type(val) is list and
1199
              len(val) == 1):
1200
          self.assertEqual(len(default_pol[key]), 1)
1201
          self.assertEqual(len(diff_pol[key]), 1)
1202
          self._RecursiveCheckMergedDicts(default_pol[key][0],
1203
                                          diff_pol[key][0], val[0])
1197 1204
        else:
1198 1205
          self.assertEqual(val, diff_pol[key])
1199 1206
      else:
......
1214 1221
    self.assertEqual(pol0, constants.IPOLICY_DEFAULTS)
1215 1222

  
1216 1223
    exp_pol1 = {
1217
      constants.ISPECS_MINMAX: {
1218
        constants.ISPECS_MIN: {
1219
          constants.ISPEC_CPU_COUNT: 2,
1220
          constants.ISPEC_DISK_COUNT: 1,
1221
          },
1222
        constants.ISPECS_MAX: {
1223
          constants.ISPEC_MEM_SIZE: 12*1024,
1224
          constants.ISPEC_DISK_COUNT: 2,
1224
      constants.ISPECS_MINMAX: [
1225
        {
1226
          constants.ISPECS_MIN: {
1227
            constants.ISPEC_CPU_COUNT: 2,
1228
            constants.ISPEC_DISK_COUNT: 1,
1229
            },
1230
          constants.ISPECS_MAX: {
1231
            constants.ISPEC_MEM_SIZE: 12*1024,
1232
            constants.ISPEC_DISK_COUNT: 2,
1233
            },
1225 1234
          },
1226
        },
1235
        ],
1227 1236
      constants.ISPECS_STD: {
1228 1237
        constants.ISPEC_CPU_COUNT: 2,
1229 1238
        constants.ISPEC_DISK_COUNT: 2,
......
1242 1251
      fill_all=True
1243 1252
      )
1244 1253
    self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1245
                                    exp_pol1, pol1)
1254
                                    exp_pol1, pol1, merge_minmax=True)
1246 1255

  
1247 1256
    exp_pol2 = {
1248
      constants.ISPECS_MINMAX: {
1249
        constants.ISPECS_MIN: {
1250
          constants.ISPEC_DISK_SIZE: 512,
1251
          constants.ISPEC_NIC_COUNT: 2,
1257
      constants.ISPECS_MINMAX: [
1258
        {
1259
          constants.ISPECS_MIN: {
1260
            constants.ISPEC_DISK_SIZE: 512,
1261
            constants.ISPEC_NIC_COUNT: 2,
1262
            },
1263
          constants.ISPECS_MAX: {
1264
            constants.ISPEC_NIC_COUNT: 3,
1265
            },
1252 1266
          },
1253
        constants.ISPECS_MAX: {
1254
          constants.ISPEC_NIC_COUNT: 3,
1255
          },
1256
        },
1267
        ],
1257 1268
      constants.ISPECS_STD: {
1258 1269
        constants.ISPEC_CPU_COUNT: 2,
1259 1270
        constants.ISPEC_NIC_COUNT: 3,
......
1273 1284
      fill_all=True
1274 1285
      )
1275 1286
    self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1276
                                      exp_pol2, pol2)
1287
                                      exp_pol2, pol2, merge_minmax=True)
1277 1288

  
1278 1289
    for fill_all in [False, True]:
1279 1290
      exp_pol3 = {
......
1294 1305
        )
1295 1306
      if fill_all:
1296 1307
        self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS,
1297
                                        exp_pol3, pol3)
1308
                                        exp_pol3, pol3, merge_minmax=True)
1298 1309
      else:
1299 1310
        self.assertEqual(pol3, exp_pol3)
1300 1311

  
......
1384 1395
      broken_mmspecs[key]["invalid_key"] = None
1385 1396
      self._TestInvalidISpecs(broken_mmspecs, None)
1386 1397
      del broken_mmspecs[key]["invalid_key"]
1398
    broken_mmspecs["invalid_key"] = None
1399
    self._TestInvalidISpecs(broken_mmspecs, None)
1400
    del broken_mmspecs["invalid_key"]
1387 1401
    assert broken_mmspecs == good_mmspecs
1388 1402

  
1389 1403
    good_stdspecs = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD]
......
1437 1451
      minmax_ispecs = {}
1438 1452
      for (key, spec) in exp_minmax.items():
1439 1453
        minmax_ispecs[key] = self._ConvertSpecToStrings(spec)
1440
      exp_ipol[constants.ISPECS_MINMAX] = exp_minmax
1454
      exp_ipol[constants.ISPECS_MINMAX] = [exp_minmax]
1441 1455
    else:
1442 1456
      minmax_ispecs = None
1443 1457
    if exp_std is not None:
......
1527 1541
    policies = [
1528 1542
      {},
1529 1543
      {"std": {}},
1530
      {"minmax": {}},
1531
      {"minmax": {
1544
      {"minmax": []},
1545
      {"minmax": [{}]},
1546
      {"minmax": [{
1532 1547
        "min": {},
1533 1548
        "max": {},
1534
        }},
1535
      {"minmax": {
1549
        }]},
1550
      {"minmax": [{
1536 1551
        "min": self._SPECS1,
1537 1552
        "max": {},
1538
        }},
1553
        }]},
1539 1554
      ]
1540 1555
    for pol in policies:
1541 1556
      self._CheckPrintIPolicyCommand(pol, False, "")
......
1544 1559
    cases = [
1545 1560
      ({"std": self._SPECS1},
1546 1561
       " %s %s" % (cli.IPOLICY_STD_SPECS_STR, self._SPECS1_STR)),
1547
      ({"minmax": {
1562
      ({"minmax": [{
1548 1563
        "min": self._SPECS1,
1549 1564
        "max": self._SPECS2,
1550
        }},
1565
        }]},
1551 1566
       " %s min:%s/max:%s" % (cli.IPOLICY_BOUNDS_SPECS_STR,
1552 1567
                              self._SPECS1_STR, self._SPECS2_STR)),
1553 1568
      ]
b/test/py/ganeti.cmdlib_unittest.py
673 673
  # Minimal policy accepted by _ComputeIPolicySpecViolation()
674 674
  _MICRO_IPOL = {
675 675
    constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
676
    constants.ISPECS_MINMAX: NotImplemented,
676
    constants.ISPECS_MINMAX: [NotImplemented],
677 677
    }
678 678

  
679 679
  def test(self):
......
719 719
    self.assertEqual(ret, ["foo", "bar"])
720 720
    self.assertFalse(spec.spec)
721 721

  
722
  def testWithIPolicy(self):
723
    mem_size = 2048
724
    cpu_count = 2
725
    disk_count = 1
726
    disk_sizes = [512]
727
    nic_count = 1
728
    spindle_use = 4
729
    disk_template = "mytemplate"
730
    ispec = {
731
      constants.ISPEC_MEM_SIZE: mem_size,
732
      constants.ISPEC_CPU_COUNT: cpu_count,
733
      constants.ISPEC_DISK_COUNT: disk_count,
734
      constants.ISPEC_DISK_SIZE: disk_sizes[0],
735
      constants.ISPEC_NIC_COUNT: nic_count,
736
      constants.ISPEC_SPINDLE_USE: spindle_use,
737
      }
738
    ipolicy1 = {
739
      constants.ISPECS_MINMAX: [{
740
        constants.ISPECS_MIN: ispec,
741
        constants.ISPECS_MAX: ispec,
742
        }],
743
      constants.IPOLICY_DTS: [disk_template],
744
      }
745
    ispec_copy = copy.deepcopy(ispec)
746
    ipolicy2 = {
747
      constants.ISPECS_MINMAX: [
748
        {
749
          constants.ISPECS_MIN: ispec_copy,
750
          constants.ISPECS_MAX: ispec_copy,
751
          },
752
        {
753
          constants.ISPECS_MIN: ispec,
754
          constants.ISPECS_MAX: ispec,
755
          },
756
        ],
757
      constants.IPOLICY_DTS: [disk_template],
758
      }
759
    ipolicy3 = {
760
      constants.ISPECS_MINMAX: [
761
        {
762
          constants.ISPECS_MIN: ispec,
763
          constants.ISPECS_MAX: ispec,
764
          },
765
        {
766
          constants.ISPECS_MIN: ispec_copy,
767
          constants.ISPECS_MAX: ispec_copy,
768
          },
769
        ],
770
      constants.IPOLICY_DTS: [disk_template],
771
      }
772
    def AssertComputeViolation(ipolicy, violations):
773
      ret = cmdlib._ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
774
                                                disk_count, nic_count,
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff