From 41044e04c1fea3630a5129de68a1938b3e3d53d3 Mon Sep 17 00:00:00 2001 From: Bernardo Dal Seno <bdalseno@google.com> Date: Mon, 15 Apr 2013 11:03:38 +0200 Subject: [PATCH] Add multiple min/max specs in instance policy Now instance policies can contain more than one min/max specs. This is the main element of the "Constrained instance sizes" section in the "Partitioned Ganeti" design doc. This is a big patch, but changing the type of a configuration item requires to change all the code that handles it. Signed-off-by: Bernardo Dal Seno <bdalseno@google.com> Reviewed-by: Helga Velroyen <helgav@google.com> --- doc/rapi.rst | 2 +- lib/cli.py | 38 +++-- lib/cmdlib.py | 21 ++- lib/config.py | 3 +- lib/constants.py | 2 +- lib/objects.py | 32 ++-- man/htools.rst | 6 +- src/Ganeti/HTools/Backend/Text.hs | 48 +++++- src/Ganeti/HTools/Instance.hs | 20 ++- src/Ganeti/HTools/Program/Hspace.hs | 10 +- src/Ganeti/HTools/Types.hs | 34 ++--- src/Ganeti/Objects.hs | 4 +- test/data/htools/clean-nonzero-score.data | 4 +- test/data/htools/common-suffix.data | 4 +- test/data/htools/empty-cluster.data | 4 +- test/data/htools/hail-alloc-drbd.json | 74 ++++----- .../htools/hail-alloc-invalid-twodisks.json | 36 ++--- test/data/htools/hail-alloc-twodisks.json | 36 ++--- test/data/htools/hail-change-group.json | 112 +++++++------- test/data/htools/hail-node-evac.json | 38 ++--- test/data/htools/hail-reloc-drbd.json | 74 ++++----- test/data/htools/hbal-split-insts.data | 6 +- test/data/htools/invalid-node.data | 4 +- test/data/htools/missing-resources.data | 4 +- test/data/htools/multiple-master.data | 4 +- test/data/htools/n1-failure.data | 6 +- test/data/htools/rapi/groups.json | 38 ++--- test/data/htools/rapi/info.json | 36 ++--- test/data/htools/unique-reboot-order.data | 4 +- test/hs/Test/Ganeti/HTools/Backend/Text.hs | 10 +- test/hs/Test/Ganeti/HTools/Types.hs | 41 ++++- test/hs/Test/Ganeti/TestHTools.hs | 4 +- test/py/ganeti.cli_unittest.py | 73 +++++---- test/py/ganeti.cmdlib_unittest.py | 143 +++++++++++++++--- test/py/ganeti.config_unittest.py | 42 ++--- test/py/ganeti.objects_unittest.py | 132 +++++++++++++--- tools/cfgupgrade | 20 ++- 37 files changed, 767 insertions(+), 402 deletions(-) diff --git a/doc/rapi.rst b/doc/rapi.rst index eb39719f1..eb115dcf8 100644 --- a/doc/rapi.rst +++ b/doc/rapi.rst @@ -251,7 +251,7 @@ The instance policy specification is a dict with the following fields: :pyeval:`constants.ISPECS_MINMAX` - A dict with the following two fields: + A list of dictionaries, each with the following two fields: |ispec-min|, |ispec-max| A sub- `dict` with the following fields, which sets the limit of the diff --git a/lib/cli.py b/lib/cli.py index 7615c7358..b924e89a1 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -3736,13 +3736,24 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): if iscluster: eff_ipolicy = custom_ipolicy - custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX, {}) - ret = [ - (key, - FormatParamsDictInfo(custom_minmax.get(key, {}), - eff_ipolicy[constants.ISPECS_MINMAX][key])) - for key in constants.ISPECS_MINMAX_KEYS - ] + minmax_out = [] + custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX) + if custom_minmax: + for (k, minmax) in enumerate(custom_minmax): + minmax_out.append([ + ("%s/%s" % (key, k), + FormatParamsDictInfo(minmax[key], minmax[key])) + for key in constants.ISPECS_MINMAX_KEYS + ]) + else: + for (k, minmax) in enumerate(eff_ipolicy[constants.ISPECS_MINMAX]): + minmax_out.append([ + ("%s/%s" % (key, k), + FormatParamsDictInfo({}, minmax[key])) + for key in constants.ISPECS_MINMAX_KEYS + ]) + ret = [("bounds specs", minmax_out)] + if iscluster: stdspecs = custom_ipolicy[constants.ISPECS_STD] ret.append( @@ -3787,8 +3798,8 @@ def PrintIPolicyCommand(buf, ipolicy, isgroup): _PrintSpecsParameters(buf, stdspecs) minmax = ipolicy.get("minmax") if minmax: - minspecs = minmax.get("min") - maxspecs = minmax.get("max") + minspecs = minmax[0].get("min") + maxspecs = minmax[0].get("max") if minspecs and maxspecs: buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR) buf.write("min:") @@ -3892,13 +3903,14 @@ def _InitISpecsFromSplitOpts(ipolicy, ispecs_mem_size, ispecs_cpu_count, for key, val in specs.items(): # {min: .. ,max: .., std: ..} assert key in ispecs ispecs[key][name] = val - ipolicy[constants.ISPECS_MINMAX] = {} + minmax_out = {} for key in constants.ISPECS_MINMAX_KEYS: if fill_all: - ipolicy[constants.ISPECS_MINMAX][key] = \ + minmax_out[key] = \ objects.FillDict(constants.ISPECS_MINMAX_DEFAULTS[key], ispecs[key]) else: - ipolicy[constants.ISPECS_MINMAX][key] = ispecs[key] + minmax_out[key] = ispecs[key] + ipolicy[constants.ISPECS_MINMAX] = [minmax_out] if fill_all: ipolicy[constants.ISPECS_STD] = \ objects.FillDict(constants.IPOLICY_DEFAULTS[constants.ISPECS_STD], @@ -3953,7 +3965,7 @@ def _InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs, msg = "Invalid key in bounds instance specifications: %s" % key raise errors.OpPrereqError(msg, errors.ECODE_INVAL) minmax_out[key] = _ParseISpec(spec, key, True) - ipolicy_out[constants.ISPECS_MINMAX] = minmax_out + ipolicy_out[constants.ISPECS_MINMAX] = [minmax_out] if std_ispecs is not None: assert not group_ipolicy # This is not an option for gnt-group ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std", False) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 7ba55d3ed..2c6eccb53 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -828,7 +828,8 @@ def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False): if (not value or value == [constants.VALUE_DEFAULT] or value == constants.VALUE_DEFAULT): if group_policy: - del ipolicy[key] + if key in ipolicy: + del ipolicy[key] else: raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'" " on the cluster'" % key, @@ -843,8 +844,9 @@ def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False): " '%s': '%s', error: %s" % (key, value, err), errors.ECODE_INVAL) elif key == constants.ISPECS_MINMAX: - for k in value.keys(): - utils.ForceDictType(value[k], constants.ISPECS_PARAMETER_TYPES) + for minmax in value: + for k in minmax.keys(): + utils.ForceDictType(minmax[k], constants.ISPECS_PARAMETER_TYPES) ipolicy[key] = value elif key == constants.ISPECS_STD: if group_policy: @@ -1276,10 +1278,15 @@ def _ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, disk_count, ret.append("Disk template %s is not allowed (allowed templates: %s)" % (disk_template, utils.CommaJoin(allowed_dts))) - minmax = ipolicy[constants.ISPECS_MINMAX] - return ret + filter(None, - (_compute_fn(name, qualifier, minmax, value) - for (name, qualifier, value) in test_settings)) + min_errs = None + for minmax in ipolicy[constants.ISPECS_MINMAX]: + errs = filter(None, + (_compute_fn(name, qualifier, minmax, value) + for (name, qualifier, value) in test_settings)) + if min_errs is None or len(errs) < len(min_errs): + min_errs = errs + assert min_errs is not None + return ret + min_errs def _ComputeIPolicyInstanceViolation(ipolicy, instance, cfg, diff --git a/lib/config.py b/lib/config.py index 6c829917a..b968fcf00 100644 --- a/lib/config.py +++ b/lib/config.py @@ -633,7 +633,8 @@ class ConfigWriter: result.append("%s has invalid instance policy: %s" % (owner, err)) for key, value in ipolicy.items(): if key == constants.ISPECS_MINMAX: - _helper_ispecs(owner, "ipolicy/" + key, value) + for k in range(len(value)): + _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k]) elif key == constants.ISPECS_STD: _helper(owner, "ipolicy/" + key, value, constants.ISPECS_PARAMETER_TYPES) diff --git a/lib/constants.py b/lib/constants.py index e439ea826..0e49de634 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -2215,7 +2215,7 @@ ISPECS_MINMAX_DEFAULTS = { }, } IPOLICY_DEFAULTS = { - ISPECS_MINMAX: ISPECS_MINMAX_DEFAULTS, + ISPECS_MINMAX: [ISPECS_MINMAX_DEFAULTS], ISPECS_STD: { ISPEC_MEM_SIZE: 128, ISPEC_CPU_COUNT: 1, diff --git a/lib/objects.py b/lib/objects.py index c032c448d..8d809c48d 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -959,20 +959,24 @@ class InstancePolicy(ConfigObject): if check_std: InstancePolicy._CheckIncompleteSpec(stdspec, constants.ISPECS_STD) - minmaxspecs = ipolicy[constants.ISPECS_MINMAX] - missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys()) - if missing: - msg = "Missing instance specification: %s" % utils.CommaJoin(missing) - raise errors.ConfigurationError(msg) - for (key, spec) in minmaxspecs.items(): - InstancePolicy._CheckIncompleteSpec(spec, key) - - spec_std_ok = True - for param in constants.ISPECS_PARAMETERS: - par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec, - param, check_std) - spec_std_ok = spec_std_ok and par_std_ok - if not spec_std_ok: + if not ipolicy[constants.ISPECS_MINMAX]: + raise errors.ConfigurationError("Empty minmax specifications") + std_is_good = False + for minmaxspecs in ipolicy[constants.ISPECS_MINMAX]: + missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys()) + if missing: + msg = "Missing instance specification: %s" % utils.CommaJoin(missing) + raise errors.ConfigurationError(msg) + for (key, spec) in minmaxspecs.items(): + InstancePolicy._CheckIncompleteSpec(spec, key) + + spec_std_ok = True + for param in constants.ISPECS_PARAMETERS: + par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec, + param, check_std) + spec_std_ok = spec_std_ok and par_std_ok + std_is_good = std_is_good or spec_std_ok + if not std_is_good: raise errors.ConfigurationError("Invalid std specifications") @classmethod diff --git a/man/htools.rst b/man/htools.rst index b908863ef..22b67bbb7 100644 --- a/man/htools.rst +++ b/man/htools.rst @@ -198,8 +198,10 @@ support all options. Some common options are: groups, in the following format (separated by ``|``): - owner (empty if cluster, group name otherwise) - - standard, min, max instance specs, containing the following values - separated by commas: + - standard, min, max instance specs; min and max instance specs are + separated between them by a semicolon, and can be specified multiple + times (min;max;min;max...); each of the specs contains the following + values separated by commas: - memory size - cpu count - disk size diff --git a/src/Ganeti/HTools/Backend/Text.hs b/src/Ganeti/HTools/Backend/Text.hs index 579ce77bc..2370eb749 100644 --- a/src/Ganeti/HTools/Backend/Text.hs +++ b/src/Ganeti/HTools/Backend/Text.hs @@ -32,12 +32,14 @@ module Ganeti.HTools.Backend.Text , loadInst , loadNode , loadISpec + , loadMultipleMinMaxISpecs , loadIPolicy , serializeInstances , serializeNode , serializeNodes , serializeGroup , serializeISpec + , serializeMultipleMinMaxISpecs , serializeIPolicy , serializeCluster ) where @@ -117,6 +119,10 @@ serializeInstances :: Node.List -> Instance.List -> String serializeInstances nl = unlines . map (serializeInstance nl) . Container.elems +-- | Separator between ISpecs (in MinMaxISpecs). +iSpecsSeparator :: Char +iSpecsSeparator = ';' + -- | Generate a spec data from a given ISpec object. serializeISpec :: ISpec -> String serializeISpec ispec = @@ -130,15 +136,20 @@ serializeISpec ispec = serializeDiskTemplates :: [DiskTemplate] -> String serializeDiskTemplates = intercalate "," . map diskTemplateToRaw +-- | Generate min/max instance specs data. +serializeMultipleMinMaxISpecs :: [MinMaxISpecs] -> String +serializeMultipleMinMaxISpecs minmaxes = + intercalate [iSpecsSeparator] $ foldr serialpair [] minmaxes + where serialpair (MinMaxISpecs minspec maxspec) acc = + serializeISpec minspec : serializeISpec maxspec : acc + -- | Generate policy data from a given policy object. serializeIPolicy :: String -> IPolicy -> String serializeIPolicy owner ipol = let IPolicy minmax stdspec dts vcpu_ratio spindle_ratio = ipol - MinMaxISpecs minspec maxspec = minmax strings = [ owner , serializeISpec stdspec - , serializeISpec minspec - , serializeISpec maxspec + , serializeMultipleMinMaxISpecs minmax , serializeDiskTemplates dts , show vcpu_ratio , show spindle_ratio @@ -255,18 +266,41 @@ loadISpec owner [mem_s, cpu_c, dsk_s, dsk_c, nic_c, su] = do return $ ISpec xmem_s xcpu_c xdsk_s xdsk_c xnic_c xsu loadISpec owner s = fail $ "Invalid ispec data for " ++ owner ++ ": " ++ show s +-- | Load a single min/max ISpec pair +loadMinMaxISpecs :: String -> String -> String -> Result MinMaxISpecs +loadMinMaxISpecs owner minspec maxspec = do + xminspec <- loadISpec (owner ++ "/minspec") (commaSplit minspec) + xmaxspec <- loadISpec (owner ++ "/maxspec") (commaSplit maxspec) + return $ MinMaxISpecs xminspec xmaxspec + +-- | Break a list of ispecs strings into a list of (min/max) ispecs pairs +breakISpecsPairs :: String -> [String] -> Result [(String, String)] +breakISpecsPairs _ [] = + return [] +breakISpecsPairs owner (x:y:xs) = do + rest <- breakISpecsPairs owner xs + return $ (x, y) : rest +breakISpecsPairs owner _ = + fail $ "Odd number of min/max specs for " ++ owner + +-- | Load a list of min/max ispecs pairs +loadMultipleMinMaxISpecs :: String -> [String] -> Result [MinMaxISpecs] +loadMultipleMinMaxISpecs owner ispecs = do + pairs <- breakISpecsPairs owner ispecs + mapM (uncurry $ loadMinMaxISpecs owner) pairs + -- | Loads an ipolicy from a field list. loadIPolicy :: [String] -> Result (String, IPolicy) -loadIPolicy [owner, stdspec, minspec, maxspec, dtemplates, +loadIPolicy [owner, stdspec, minmaxspecs, dtemplates, vcpu_ratio, spindle_ratio] = do xstdspec <- loadISpec (owner ++ "/stdspec") (commaSplit stdspec) - xminspec <- loadISpec (owner ++ "/minspec") (commaSplit minspec) - xmaxspec <- loadISpec (owner ++ "/maxspec") (commaSplit maxspec) + xminmaxspecs <- loadMultipleMinMaxISpecs owner $ + sepSplit iSpecsSeparator minmaxspecs xdts <- mapM diskTemplateFromRaw $ commaSplit dtemplates xvcpu_ratio <- tryRead (owner ++ "/vcpu_ratio") vcpu_ratio xspindle_ratio <- tryRead (owner ++ "/spindle_ratio") spindle_ratio return (owner, - IPolicy (MinMaxISpecs xminspec xmaxspec) xstdspec + IPolicy xminmaxspecs xstdspec xdts xvcpu_ratio xspindle_ratio) loadIPolicy s = fail $ "Invalid ipolicy data: '" ++ show s ++ "'" diff --git a/src/Ganeti/HTools/Instance.hs b/src/Ganeti/HTools/Instance.hs index 8de100979..4ba3e91bb 100644 --- a/src/Ganeti/HTools/Instance.hs +++ b/src/Ganeti/HTools/Instance.hs @@ -279,12 +279,26 @@ instAboveISpec inst ispec | vcpus inst < T.iSpecCpuCount ispec = Bad T.FailCPU | otherwise = Ok () +-- | Checks if an instance matches a min/max specs pair +instMatchesMinMaxSpecs :: Instance -> T.MinMaxISpecs -> T.OpResult () +instMatchesMinMaxSpecs inst minmax = do + instAboveISpec inst (T.minMaxISpecsMinSpec minmax) + instBelowISpec inst (T.minMaxISpecsMaxSpec minmax) + +-- | Checks if an instance matches any specs of a policy +instMatchesSpecs :: Instance -> [T.MinMaxISpecs] -> T.OpResult () + -- Return Ok for no constraints, though this should never happen +instMatchesSpecs _ [] = Ok () +instMatchesSpecs inst (minmax:minmaxes) = + foldr eithermatch (instMatchesMinMaxSpecs inst minmax) minmaxes + where eithermatch mm (Bad _) = instMatchesMinMaxSpecs inst mm + eithermatch _ y@(Ok ()) = y +-- # See 04f231771 + -- | Checks if an instance matches a policy. instMatchesPolicy :: Instance -> T.IPolicy -> T.OpResult () instMatchesPolicy inst ipol = do - let minmax = T.iPolicyMinMaxISpecs ipol - instAboveISpec inst (T.minMaxISpecsMinSpec minmax) - instBelowISpec inst (T.minMaxISpecsMaxSpec minmax) + instMatchesSpecs inst $ T.iPolicyMinMaxISpecs ipol if diskTemplate inst `elem` T.iPolicyDiskTemplates ipol then Ok () else Bad T.FailDisk diff --git a/src/Ganeti/HTools/Program/Hspace.hs b/src/Ganeti/HTools/Program/Hspace.hs index f4b0836d9..d26d5fe9b 100644 --- a/src/Ganeti/HTools/Program/Hspace.hs +++ b/src/Ganeti/HTools/Program/Hspace.hs @@ -446,9 +446,13 @@ main opts args = do -- Run the tiered allocation - let minmax = iPolicyMinMaxISpecs ipol - let tspec = fromMaybe (rspecFromISpec (minMaxISpecsMaxSpec minmax)) - (optTieredSpec opts) + let minmaxes = iPolicyMinMaxISpecs ipol + -- TODO: Go through all min/max specs pairs + tspec <- case minmaxes of + [] -> exitErr "Empty list of specs received from the cluster" + minmax:_ -> return $ fromMaybe + (rspecFromISpec (minMaxISpecsMaxSpec minmax)) + (optTieredSpec opts) (treason, trl_nl, _, spec_map) <- runAllocation cdata stop_allocation diff --git a/src/Ganeti/HTools/Types.hs b/src/Ganeti/HTools/Types.hs index e579e3634..e9e9badb6 100644 --- a/src/Ganeti/HTools/Types.hs +++ b/src/Ganeti/HTools/Types.hs @@ -169,12 +169,12 @@ $(THH.buildObject "ISpec" "iSpec" -- | The default minimum ispec. defMinISpec :: ISpec -defMinISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinmaxMinMemorySize - , iSpecCpuCount = C.ipolicyDefaultsMinmaxMinCpuCount - , iSpecDiskSize = C.ipolicyDefaultsMinmaxMinDiskSize - , iSpecDiskCount = C.ipolicyDefaultsMinmaxMinDiskCount - , iSpecNicCount = C.ipolicyDefaultsMinmaxMinNicCount - , iSpecSpindleUse = C.ipolicyDefaultsMinmaxMinSpindleUse +defMinISpec = ISpec { iSpecMemorySize = C.ispecsMinmaxDefaultsMinMemorySize + , iSpecCpuCount = C.ispecsMinmaxDefaultsMinCpuCount + , iSpecDiskSize = C.ispecsMinmaxDefaultsMinDiskSize + , iSpecDiskCount = C.ispecsMinmaxDefaultsMinDiskCount + , iSpecNicCount = C.ispecsMinmaxDefaultsMinNicCount + , iSpecSpindleUse = C.ispecsMinmaxDefaultsMinSpindleUse } -- | The default standard ispec. @@ -189,12 +189,12 @@ defStdISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsStdMemorySize -- | The default max ispec. defMaxISpec :: ISpec -defMaxISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinmaxMaxMemorySize - , iSpecCpuCount = C.ipolicyDefaultsMinmaxMaxCpuCount - , iSpecDiskSize = C.ipolicyDefaultsMinmaxMaxDiskSize - , iSpecDiskCount = C.ipolicyDefaultsMinmaxMaxDiskCount - , iSpecNicCount = C.ipolicyDefaultsMinmaxMaxNicCount - , iSpecSpindleUse = C.ipolicyDefaultsMinmaxMaxSpindleUse +defMaxISpec = ISpec { iSpecMemorySize = C.ispecsMinmaxDefaultsMaxMemorySize + , iSpecCpuCount = C.ispecsMinmaxDefaultsMaxCpuCount + , iSpecDiskSize = C.ispecsMinmaxDefaultsMaxDiskSize + , iSpecDiskCount = C.ispecsMinmaxDefaultsMaxDiskCount + , iSpecNicCount = C.ispecsMinmaxDefaultsMaxNicCount + , iSpecSpindleUse = C.ispecsMinmaxDefaultsMaxSpindleUse } -- | Minimum and maximum instance specs type. @@ -204,15 +204,15 @@ $(THH.buildObject "MinMaxISpecs" "minMaxISpecs" ]) -- | Defult minimum and maximum instance specs. -defMinMaxISpecs :: MinMaxISpecs -defMinMaxISpecs = MinMaxISpecs { minMaxISpecsMinSpec = defMinISpec - , minMaxISpecsMaxSpec = defMaxISpec - } +defMinMaxISpecs :: [MinMaxISpecs] +defMinMaxISpecs = [MinMaxISpecs { minMaxISpecsMinSpec = defMinISpec + , minMaxISpecsMaxSpec = defMaxISpec + }] -- | Instance policy type. $(THH.buildObject "IPolicy" "iPolicy" [ THH.renameField "MinMaxISpecs" $ - THH.simpleField C.ispecsMinmax [t| MinMaxISpecs |] + THH.simpleField C.ispecsMinmax [t| [MinMaxISpecs] |] , THH.renameField "StdSpec" $ THH.simpleField C.ispecsStd [t| ISpec |] , THH.renameField "DiskTemplates" $ THH.simpleField C.ipolicyDts [t| [DiskTemplate] |] diff --git a/src/Ganeti/Objects.hs b/src/Ganeti/Objects.hs index 908daecf6..155030954 100644 --- a/src/Ganeti/Objects.hs +++ b/src/Ganeti/Objects.hs @@ -512,7 +512,7 @@ $(buildObject "MinMaxISpecs" "mmis" -- has a special 2-level inheritance mode. $(buildObject "PartialIPolicy" "ipolicy" [ optionalField . renameField "MinMaxISpecsP" - $ simpleField C.ispecsMinmax [t| MinMaxISpecs |] + $ simpleField C.ispecsMinmax [t| [MinMaxISpecs] |] , optionalField . renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |] , optionalField . renameField "SpindleRatioP" @@ -527,7 +527,7 @@ $(buildObject "PartialIPolicy" "ipolicy" -- has a special 2-level inheritance mode. $(buildObject "FilledIPolicy" "ipolicy" [ renameField "MinMaxISpecs" - $ simpleField C.ispecsMinmax [t| MinMaxISpecs |] + $ simpleField C.ispecsMinmax [t| [MinMaxISpecs] |] , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |] , simpleField "spindle-ratio" [t| Double |] , simpleField "vcpu-ratio" [t| Double |] diff --git a/test/data/htools/clean-nonzero-score.data b/test/data/htools/clean-nonzero-score.data index 72762fa91..28ebdf130 100644 --- a/test/data/htools/clean-nonzero-score.data +++ b/test/data/htools/clean-nonzero-score.data @@ -11,5 +11,5 @@ new-3|128|1024|1|running|Y|node-01-003||diskless||1 new-4|128|1024|1|running|Y|node-01-002||diskless||1 -|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 -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 +|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 +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 diff --git a/test/data/htools/common-suffix.data b/test/data/htools/common-suffix.data index f7410aed0..1e34c9437 100644 --- a/test/data/htools/common-suffix.data +++ b/test/data/htools/common-suffix.data @@ -6,5 +6,5 @@ node2.example.com|1024|0|896|95367|94343|4|N|fake-uuid-01|1 instance1.example.com|128|1024|1|running|Y|node2.example.com||plain| -|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 -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 +|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 +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 diff --git a/test/data/htools/empty-cluster.data b/test/data/htools/empty-cluster.data index d25526f5d..9e17bbb0c 100644 --- a/test/data/htools/empty-cluster.data +++ b/test/data/htools/empty-cluster.data @@ -3,5 +3,5 @@ group-01|fake-uuid-01|preferred| -|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 -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 +|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 +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 diff --git a/test/data/htools/hail-alloc-drbd.json b/test/data/htools/hail-alloc-drbd.json index f650fbe60..57e20c3c6 100644 --- a/test/data/htools/hail-alloc-drbd.json +++ b/test/data/htools/hail-alloc-drbd.json @@ -14,24 +14,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", @@ -58,22 +60,26 @@ "disk-count": 1, "spindle-use": 1 }, - "min": { - "nic-count": 1, - "disk-size": 1024, - "memory-size": 128, - "cpu-count": 1, - "disk-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "memory-size": 32768, - "cpu-count": 8, - "disk-count": 16, - "spindle-use": 8 - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 1024, + "memory-size": 128, + "cpu-count": 1, + "disk-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "memory-size": 32768, + "cpu-count": 8, + "disk-count": 16, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/hail-alloc-invalid-twodisks.json b/test/data/htools/hail-alloc-invalid-twodisks.json index 790de2a54..4f233c73e 100644 --- a/test/data/htools/hail-alloc-invalid-twodisks.json +++ b/test/data/htools/hail-alloc-invalid-twodisks.json @@ -16,24 +16,26 @@ "disk-templates": [ "file" ], - "minmax" : { - "max": { - "cpu-count": 2, - "disk-count": 8, - "disk-size": 2048, - "memory-size": 12800, - "nic-count": 8, - "spindle-use": 8 - }, - "min": { - "cpu-count": 1, - "disk-count": 1, - "disk-size": 1024, - "memory-size": 128, - "nic-count": 1, - "spindle-use": 1 + "minmax" : [ + { + "max": { + "cpu-count": 2, + "disk-count": 8, + "disk-size": 2048, + "memory-size": 12800, + "nic-count": 8, + "spindle-use": 8 + }, + "min": { + "cpu-count": 1, + "disk-count": 1, + "disk-size": 1024, + "memory-size": 128, + "nic-count": 1, + "spindle-use": 1 + } } - }, + ], "spindle-ratio": 32.0, "std": { "cpu-count": 1, diff --git a/test/data/htools/hail-alloc-twodisks.json b/test/data/htools/hail-alloc-twodisks.json index 2a4f63686..b4e72805c 100644 --- a/test/data/htools/hail-alloc-twodisks.json +++ b/test/data/htools/hail-alloc-twodisks.json @@ -16,24 +16,26 @@ "disk-templates": [ "file" ], - "minmax": { - "max": { - "cpu-count": 2, - "disk-count": 8, - "disk-size": 2048, - "memory-size": 12800, - "nic-count": 8, - "spindle-use": 8 - }, - "min": { - "cpu-count": 1, - "disk-count": 1, - "disk-size": 1024, - "memory-size": 128, - "nic-count": 1, - "spindle-use": 1 + "minmax": [ + { + "max": { + "cpu-count": 2, + "disk-count": 8, + "disk-size": 2048, + "memory-size": 12800, + "nic-count": 8, + "spindle-use": 8 + }, + "min": { + "cpu-count": 1, + "disk-count": 1, + "disk-size": 1024, + "memory-size": 128, + "nic-count": 1, + "spindle-use": 1 + } } - }, + ], "spindle-ratio": 32.0, "std": { "cpu-count": 1, diff --git a/test/data/htools/hail-change-group.json b/test/data/htools/hail-change-group.json index f698638e8..8cca142dc 100644 --- a/test/data/htools/hail-change-group.json +++ b/test/data/htools/hail-change-group.json @@ -14,24 +14,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", @@ -58,24 +60,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", @@ -102,24 +106,26 @@ "disk-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 1024, - "memory-size": 128, - "cpu-count": 1, - "disk-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "memory-size": 32768, - "cpu-count": 8, - "disk-count": 16, - "spindle-use": 8 + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 1024, + "memory-size": 128, + "cpu-count": 1, + "disk-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "memory-size": 32768, + "cpu-count": 8, + "disk-count": 16, + "spindle-use": 8 + } } - }, + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/hail-node-evac.json b/test/data/htools/hail-node-evac.json index 8b80e2534..8fed47789 100644 --- a/test/data/htools/hail-node-evac.json +++ b/test/data/htools/hail-node-evac.json @@ -14,24 +14,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/hail-reloc-drbd.json b/test/data/htools/hail-reloc-drbd.json index ce66041ce..944700e2e 100644 --- a/test/data/htools/hail-reloc-drbd.json +++ b/test/data/htools/hail-reloc-drbd.json @@ -14,24 +14,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", @@ -58,22 +60,26 @@ "disk-count": 1, "spindle-use": 1 }, - "min": { - "nic-count": 1, - "disk-size": 1024, - "memory-size": 128, - "cpu-count": 1, - "disk-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "memory-size": 32768, - "cpu-count": 8, - "disk-count": 16, - "spindle-use": 8 - }, + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 1024, + "memory-size": 128, + "cpu-count": 1, + "disk-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "memory-size": 32768, + "cpu-count": 8, + "disk-count": 16, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/hbal-split-insts.data b/test/data/htools/hbal-split-insts.data index 5c5761f31..fe3bb22cd 100644 --- a/test/data/htools/hbal-split-insts.data +++ b/test/data/htools/hbal-split-insts.data @@ -140,6 +140,6 @@ new-126|128|1024|1|running|Y|node-01-002|node-01-004|drbd||1 new-127|128|1024|1|running|Y|node-01-001|node-01-003|drbd||1 -|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 -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 -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 +|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 +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 +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 diff --git a/test/data/htools/invalid-node.data b/test/data/htools/invalid-node.data index f85223ea8..9655f72cd 100644 --- a/test/data/htools/invalid-node.data +++ b/test/data/htools/invalid-node.data @@ -6,5 +6,5 @@ node-01-002|1024|0|896|95367|94343|4|N|fake-uuid-01|1 new-0|128|1024|1|running|Y|no-such-node||plain| -|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 -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 +|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 +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 diff --git a/test/data/htools/missing-resources.data b/test/data/htools/missing-resources.data index b12eb4fc5..500576a5d 100644 --- a/test/data/htools/missing-resources.data +++ b/test/data/htools/missing-resources.data @@ -5,5 +5,5 @@ node2|1024|0|0|95367|0|4|N|fake-uuid-01|1 -|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 -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 +|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 +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 diff --git a/test/data/htools/multiple-master.data b/test/data/htools/multiple-master.data index 35aa1b7bd..5d4fc52f3 100644 --- a/test/data/htools/multiple-master.data +++ b/test/data/htools/multiple-master.data @@ -6,5 +6,5 @@ node-01-003|91552|0|91296|953674|953674|16|M|fake-uuid-01|1 -|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 -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 +|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 +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 diff --git a/test/data/htools/n1-failure.data b/test/data/htools/n1-failure.data index 8a90223b2..521875009 100644 --- a/test/data/htools/n1-failure.data +++ b/test/data/htools/n1-failure.data @@ -20,6 +20,6 @@ new-6|128|1024|1|running|Y|node-01-004|node-01-002|drbd||1 new-7|128|1024|1|running|Y|node-01-001|node-01-003|drbd||1 -|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 -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 -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 +|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 +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 +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 diff --git a/test/data/htools/rapi/groups.json b/test/data/htools/rapi/groups.json index dab65698f..8ac08cbfa 100644 --- a/test/data/htools/rapi/groups.json +++ b/test/data/htools/rapi/groups.json @@ -11,24 +11,26 @@ "disk-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "cpu-count": 1, - "nic-count": 1, - "disk-size": 1024, - "memory-size": 128, - "disk-count": 1, - "spindle-use": 1 - }, - "max": { - "cpu-count": 8, - "nic-count": 8, - "disk-size": 1048576, - "memory-size": 32768, - "disk-count": 16, - "spindle-use": 8 - } - }, + "minmax": [ + { + "min": { + "cpu-count": 1, + "nic-count": 1, + "disk-size": 1024, + "memory-size": 128, + "disk-count": 1, + "spindle-use": 1 + }, + "max": { + "cpu-count": 8, + "nic-count": 8, + "disk-size": 1048576, + "memory-size": 32768, + "disk-count": 16, + "spindle-use": 8 + } + } + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/rapi/info.json b/test/data/htools/rapi/info.json index a37d4e52e..20fc0af28 100644 --- a/test/data/htools/rapi/info.json +++ b/test/data/htools/rapi/info.json @@ -86,24 +86,26 @@ "cpu-count": 1, "spindle-use": 1 }, - "minmax": { - "min": { - "nic-count": 1, - "disk-size": 128, - "disk-count": 1, - "memory-size": 128, - "cpu-count": 1, - "spindle-use": 1 - }, - "max": { - "nic-count": 8, - "disk-size": 1048576, - "disk-count": 16, - "memory-size": 32768, - "cpu-count": 8, - "spindle-use": 8 + "minmax": [ + { + "min": { + "nic-count": 1, + "disk-size": 128, + "disk-count": 1, + "memory-size": 128, + "cpu-count": 1, + "spindle-use": 1 + }, + "max": { + "nic-count": 8, + "disk-size": 1048576, + "disk-count": 16, + "memory-size": 32768, + "cpu-count": 8, + "spindle-use": 8 + } } - }, + ], "vcpu-ratio": 4.0, "disk-templates": [ "sharedfile", diff --git a/test/data/htools/unique-reboot-order.data b/test/data/htools/unique-reboot-order.data index d4261c999..a756a05a5 100644 --- a/test/data/htools/unique-reboot-order.data +++ b/test/data/htools/unique-reboot-order.data @@ -8,5 +8,5 @@ new-0|128|1152|1|running|Y|node-01-001|node-01-002|drbd||1 new-1|128|1152|1|running|Y|node-01-002|node-01-003|drbd||1 -|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 -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 +|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 +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 diff --git a/test/hs/Test/Ganeti/HTools/Backend/Text.hs b/test/hs/Test/Ganeti/HTools/Backend/Text.hs index 5763fa0a6..088a53bbc 100644 --- a/test/hs/Test/Ganeti/HTools/Backend/Text.hs +++ b/test/hs/Test/Ganeti/HTools/Backend/Text.hs @@ -7,7 +7,7 @@ {- -Copyright (C) 2009, 2010, 2011, 2012 Google Inc. +Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -158,6 +158,13 @@ prop_ISpecIdempotent ispec = Bad msg -> failTest $ "Failed to load ispec: " ++ msg Ok ispec' -> ispec ==? ispec' +prop_MultipleMinMaxISpecsIdempotent :: [Types.MinMaxISpecs] -> Property +prop_MultipleMinMaxISpecsIdempotent minmaxes = + case Text.loadMultipleMinMaxISpecs "dummy" . Utils.sepSplit ';' . + Text.serializeMultipleMinMaxISpecs $ minmaxes of + Bad msg -> failTest $ "Failed to load min/max ispecs: " ++ msg + Ok minmaxes' -> minmaxes ==? minmaxes' + prop_IPolicyIdempotent :: Types.IPolicy -> Property prop_IPolicyIdempotent ipol = case Text.loadIPolicy . Utils.sepSplit '|' $ @@ -210,6 +217,7 @@ testSuite "HTools/Backend/Text" , 'prop_Load_NodeFail , 'prop_NodeLSIdempotent , 'prop_ISpecIdempotent + , 'prop_MultipleMinMaxISpecsIdempotent , 'prop_IPolicyIdempotent , 'prop_CreateSerialise ] diff --git a/test/hs/Test/Ganeti/HTools/Types.hs b/test/hs/Test/Ganeti/HTools/Types.hs index 3980edb86..1379878ed 100644 --- a/test/hs/Test/Ganeti/HTools/Types.hs +++ b/test/hs/Test/Ganeti/HTools/Types.hs @@ -42,6 +42,7 @@ import Test.HUnit import Control.Applicative import Data.List (sort) +import Control.Monad (replicateM) import Test.Ganeti.TestHelper import Test.Ganeti.TestCommon @@ -101,19 +102,45 @@ genBiggerISpec imin = do , Types.iSpecSpindleUse = fromIntegral su } +genMinMaxISpecs :: Gen Types.MinMaxISpecs +genMinMaxISpecs = do + imin <- arbitrary + imax <- genBiggerISpec imin + return Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin + , Types.minMaxISpecsMaxSpec = imax + } + +instance Arbitrary Types.MinMaxISpecs where + arbitrary = genMinMaxISpecs + +genMinMaxStdISpecs :: Gen (Types.MinMaxISpecs, Types.ISpec) +genMinMaxStdISpecs = do + imin <- arbitrary + istd <- genBiggerISpec imin + imax <- genBiggerISpec istd + return (Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = imin + , Types.minMaxISpecsMaxSpec = imax + }, + istd) + +genIPolicySpecs :: Gen ([Types.MinMaxISpecs], Types.ISpec) +genIPolicySpecs = do + num_mm <- choose (1, 6) -- 6 is just an arbitrary limit + std_compl <- choose (1, num_mm) + mm_head <- replicateM (std_compl - 1) genMinMaxISpecs + (mm_middle, istd) <- genMinMaxStdISpecs + mm_tail <- replicateM (num_mm - std_compl) genMinMaxISpecs + return (mm_head ++ (mm_middle : mm_tail), istd) + + instance Arbitrary Types.IPolicy where arbitrary = do - imin <- arbitrary - istd <- genBiggerISpec imin - imax <- genBiggerISpec istd + (iminmax, istd) <- genIPolicySpecs num_tmpl <- choose (0, length allDiskTemplates) dts <- genUniquesList num_tmpl arbitrary vcpu_ratio <- choose (1.0, maxVcpuRatio) spindle_ratio <- choose (1.0, maxSpindleRatio) - return Types.IPolicy { Types.iPolicyMinMaxISpecs = Types.MinMaxISpecs - { Types.minMaxISpecsMinSpec = imin - , Types.minMaxISpecsMaxSpec = imax - } + return Types.IPolicy { Types.iPolicyMinMaxISpecs = iminmax , Types.iPolicyStdSpec = istd , Types.iPolicyDiskTemplates = dts , Types.iPolicyVcpuRatio = vcpu_ratio diff --git a/test/hs/Test/Ganeti/TestHTools.hs b/test/hs/Test/Ganeti/TestHTools.hs index 3b0ac6a68..961d7cd0e 100644 --- a/test/hs/Test/Ganeti/TestHTools.hs +++ b/test/hs/Test/Ganeti/TestHTools.hs @@ -52,7 +52,7 @@ import qualified Ganeti.HTools.Types as Types -- | Null iPolicy, and by null we mean very liberal. nullIPolicy :: Types.IPolicy nullIPolicy = Types.IPolicy - { Types.iPolicyMinMaxISpecs = Types.MinMaxISpecs + { Types.iPolicyMinMaxISpecs = [Types.MinMaxISpecs { Types.minMaxISpecsMinSpec = Types.ISpec { Types.iSpecMemorySize = 0 , Types.iSpecCpuCount = 0 , Types.iSpecDiskSize = 0 @@ -68,7 +68,7 @@ nullIPolicy = Types.IPolicy , Types.iSpecNicCount = C.maxNics , Types.iSpecSpindleUse = maxBound } - } + }] , Types.iPolicyStdSpec = Types.ISpec { Types.iSpecMemorySize = Types.unitMem , Types.iSpecCpuCount = Types.unitCpu , Types.iSpecDiskSize = Types.unitDsk diff --git a/test/py/ganeti.cli_unittest.py b/test/py/ganeti.cli_unittest.py index 6ad63fcd9..4d89c262f 100755 --- a/test/py/ganeti.cli_unittest.py +++ b/test/py/ganeti.cli_unittest.py @@ -1184,7 +1184,8 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): # Policies are big, and we want to see the difference in case of an error self.maxDiff = None - def _RecursiveCheckMergedDicts(self, default_pol, diff_pol, merged_pol): + def _RecursiveCheckMergedDicts(self, default_pol, diff_pol, merged_pol, + merge_minmax=False): self.assertTrue(type(default_pol) is dict) self.assertTrue(type(diff_pol) is dict) self.assertTrue(type(merged_pol) is dict) @@ -1194,6 +1195,12 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): if key in diff_pol: if type(val) is dict: self._RecursiveCheckMergedDicts(default_pol[key], diff_pol[key], val) + elif (merge_minmax and key == "minmax" and type(val) is list and + len(val) == 1): + self.assertEqual(len(default_pol[key]), 1) + self.assertEqual(len(diff_pol[key]), 1) + self._RecursiveCheckMergedDicts(default_pol[key][0], + diff_pol[key][0], val[0]) else: self.assertEqual(val, diff_pol[key]) else: @@ -1214,16 +1221,18 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): self.assertEqual(pol0, constants.IPOLICY_DEFAULTS) exp_pol1 = { - constants.ISPECS_MINMAX: { - constants.ISPECS_MIN: { - constants.ISPEC_CPU_COUNT: 2, - constants.ISPEC_DISK_COUNT: 1, - }, - constants.ISPECS_MAX: { - constants.ISPEC_MEM_SIZE: 12*1024, - constants.ISPEC_DISK_COUNT: 2, + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: { + constants.ISPEC_CPU_COUNT: 2, + constants.ISPEC_DISK_COUNT: 1, + }, + constants.ISPECS_MAX: { + constants.ISPEC_MEM_SIZE: 12*1024, + constants.ISPEC_DISK_COUNT: 2, + }, }, - }, + ], constants.ISPECS_STD: { constants.ISPEC_CPU_COUNT: 2, constants.ISPEC_DISK_COUNT: 2, @@ -1242,18 +1251,20 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): fill_all=True ) self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS, - exp_pol1, pol1) + exp_pol1, pol1, merge_minmax=True) exp_pol2 = { - constants.ISPECS_MINMAX: { - constants.ISPECS_MIN: { - constants.ISPEC_DISK_SIZE: 512, - constants.ISPEC_NIC_COUNT: 2, + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: { + constants.ISPEC_DISK_SIZE: 512, + constants.ISPEC_NIC_COUNT: 2, + }, + constants.ISPECS_MAX: { + constants.ISPEC_NIC_COUNT: 3, + }, }, - constants.ISPECS_MAX: { - constants.ISPEC_NIC_COUNT: 3, - }, - }, + ], constants.ISPECS_STD: { constants.ISPEC_CPU_COUNT: 2, constants.ISPEC_NIC_COUNT: 3, @@ -1273,7 +1284,7 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): fill_all=True ) self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS, - exp_pol2, pol2) + exp_pol2, pol2, merge_minmax=True) for fill_all in [False, True]: exp_pol3 = { @@ -1294,7 +1305,7 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): ) if fill_all: self._RecursiveCheckMergedDicts(constants.IPOLICY_DEFAULTS, - exp_pol3, pol3) + exp_pol3, pol3, merge_minmax=True) else: self.assertEqual(pol3, exp_pol3) @@ -1384,6 +1395,9 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): broken_mmspecs[key]["invalid_key"] = None self._TestInvalidISpecs(broken_mmspecs, None) del broken_mmspecs[key]["invalid_key"] + broken_mmspecs["invalid_key"] = None + self._TestInvalidISpecs(broken_mmspecs, None) + del broken_mmspecs["invalid_key"] assert broken_mmspecs == good_mmspecs good_stdspecs = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD] @@ -1437,7 +1451,7 @@ class TestCreateIPolicyFromOpts(unittest.TestCase): minmax_ispecs = {} for (key, spec) in exp_minmax.items(): minmax_ispecs[key] = self._ConvertSpecToStrings(spec) - exp_ipol[constants.ISPECS_MINMAX] = exp_minmax + exp_ipol[constants.ISPECS_MINMAX] = [exp_minmax] else: minmax_ispecs = None if exp_std is not None: @@ -1527,15 +1541,16 @@ class TestPrintIPolicyCommand(unittest.TestCase): policies = [ {}, {"std": {}}, - {"minmax": {}}, - {"minmax": { + {"minmax": []}, + {"minmax": [{}]}, + {"minmax": [{ "min": {}, "max": {}, - }}, - {"minmax": { + }]}, + {"minmax": [{ "min": self._SPECS1, "max": {}, - }}, + }]}, ] for pol in policies: self._CheckPrintIPolicyCommand(pol, False, "") @@ -1544,10 +1559,10 @@ class TestPrintIPolicyCommand(unittest.TestCase): cases = [ ({"std": self._SPECS1}, " %s %s" % (cli.IPOLICY_STD_SPECS_STR, self._SPECS1_STR)), - ({"minmax": { + ({"minmax": [{ "min": self._SPECS1, "max": self._SPECS2, - }}, + }]}, " %s min:%s/max:%s" % (cli.IPOLICY_BOUNDS_SPECS_STR, self._SPECS1_STR, self._SPECS2_STR)), ] diff --git a/test/py/ganeti.cmdlib_unittest.py b/test/py/ganeti.cmdlib_unittest.py index 68ee95e27..bc4e7ee4f 100755 --- a/test/py/ganeti.cmdlib_unittest.py +++ b/test/py/ganeti.cmdlib_unittest.py @@ -673,7 +673,7 @@ class TestComputeIPolicySpecViolation(unittest.TestCase): # Minimal policy accepted by _ComputeIPolicySpecViolation() _MICRO_IPOL = { constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS], - constants.ISPECS_MINMAX: NotImplemented, + constants.ISPECS_MINMAX: [NotImplemented], } def test(self): @@ -719,6 +719,79 @@ class TestComputeIPolicySpecViolation(unittest.TestCase): self.assertEqual(ret, ["foo", "bar"]) self.assertFalse(spec.spec) + def testWithIPolicy(self): + mem_size = 2048 + cpu_count = 2 + disk_count = 1 + disk_sizes = [512] + nic_count = 1 + spindle_use = 4 + disk_template = "mytemplate" + ispec = { + constants.ISPEC_MEM_SIZE: mem_size, + constants.ISPEC_CPU_COUNT: cpu_count, + constants.ISPEC_DISK_COUNT: disk_count, + constants.ISPEC_DISK_SIZE: disk_sizes[0], + constants.ISPEC_NIC_COUNT: nic_count, + constants.ISPEC_SPINDLE_USE: spindle_use, + } + ipolicy1 = { + constants.ISPECS_MINMAX: [{ + constants.ISPECS_MIN: ispec, + constants.ISPECS_MAX: ispec, + }], + constants.IPOLICY_DTS: [disk_template], + } + ispec_copy = copy.deepcopy(ispec) + ipolicy2 = { + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: ispec_copy, + constants.ISPECS_MAX: ispec_copy, + }, + { + constants.ISPECS_MIN: ispec, + constants.ISPECS_MAX: ispec, + }, + ], + constants.IPOLICY_DTS: [disk_template], + } + ipolicy3 = { + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: ispec, + constants.ISPECS_MAX: ispec, + }, + { + constants.ISPECS_MIN: ispec_copy, + constants.ISPECS_MAX: ispec_copy, + }, + ], + constants.IPOLICY_DTS: [disk_template], + } + def AssertComputeViolation(ipolicy, violations): + ret = cmdlib._ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, + disk_count, nic_count, + disk_sizes, spindle_use, + disk_template) + self.assertEqual(len(ret), violations) + + AssertComputeViolation(ipolicy1, 0) + AssertComputeViolation(ipolicy2, 0) + AssertComputeViolation(ipolicy3, 0) + for par in constants.ISPECS_PARAMETERS: + ispec[par] += 1 + AssertComputeViolation(ipolicy1, 1) + AssertComputeViolation(ipolicy2, 0) + AssertComputeViolation(ipolicy3, 0) + ispec[par] -= 2 + AssertComputeViolation(ipolicy1, 1) + AssertComputeViolation(ipolicy2, 0) + AssertComputeViolation(ipolicy3, 0) + ispec[par] += 1 # Restore + ipolicy1[constants.IPOLICY_DTS] = ["another_template"] + AssertComputeViolation(ipolicy1, 1) + class _StubComputeIPolicySpecViolation: def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes, @@ -1733,12 +1806,32 @@ class TestGetUpdatedIPolicy(unittest.TestCase): """Tests for cmdlib._GetUpdatedIPolicy()""" _OLD_CLUSTER_POLICY = { constants.IPOLICY_VCPU_RATIO: 1.5, - constants.ISPECS_MINMAX: constants.ISPECS_MINMAX_DEFAULTS, + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: { + constants.ISPEC_MEM_SIZE: 32768, + constants.ISPEC_CPU_COUNT: 8, + constants.ISPEC_DISK_COUNT: 1, + constants.ISPEC_DISK_SIZE: 1024, + constants.ISPEC_NIC_COUNT: 1, + constants.ISPEC_SPINDLE_USE: 1, + }, + constants.ISPECS_MAX: { + constants.ISPEC_MEM_SIZE: 65536, + constants.ISPEC_CPU_COUNT: 10, + constants.ISPEC_DISK_COUNT: 5, + constants.ISPEC_DISK_SIZE: 1024 * 1024, + constants.ISPEC_NIC_COUNT: 3, + constants.ISPEC_SPINDLE_USE: 12, + }, + }, + constants.ISPECS_MINMAX_DEFAULTS, + ], constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD], } _OLD_GROUP_POLICY = { constants.IPOLICY_SPINDLE_RATIO: 2.5, - constants.ISPECS_MINMAX: { + constants.ISPECS_MINMAX: [{ constants.ISPECS_MIN: { constants.ISPEC_MEM_SIZE: 128, constants.ISPEC_CPU_COUNT: 1, @@ -1755,11 +1848,11 @@ class TestGetUpdatedIPolicy(unittest.TestCase): constants.ISPEC_NIC_COUNT: 3, constants.ISPEC_SPINDLE_USE: 12, }, - }, + }], } def _TestSetSpecs(self, old_policy, isgroup): - diff_minmax = { + diff_minmax = [{ constants.ISPECS_MIN: { constants.ISPEC_MEM_SIZE: 64, constants.ISPEC_CPU_COUNT: 1, @@ -1776,7 +1869,7 @@ class TestGetUpdatedIPolicy(unittest.TestCase): constants.ISPEC_NIC_COUNT: 9, constants.ISPEC_SPINDLE_USE: 18, }, - } + }] diff_std = { constants.ISPEC_DISK_COUNT: 10, constants.ISPEC_DISK_SIZE: 512, @@ -1847,6 +1940,16 @@ class TestGetUpdatedIPolicy(unittest.TestCase): self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy, old_policy, diff_policy, group_policy=False) + def testUnsetEmpty(self): + old_policy = {} + for key in constants.IPOLICY_ALL_KEYS: + diff_policy = { + key: constants.VALUE_DEFAULT, + } + new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy, + group_policy=True) + self.assertEqual(new_policy, old_policy) + def _TestInvalidKeys(self, old_policy, isgroup): INVALID_KEY = "this_key_shouldnt_be_allowed" INVALID_DICT = { @@ -1856,7 +1959,7 @@ class TestGetUpdatedIPolicy(unittest.TestCase): self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy, old_policy, invalid_policy, group_policy=isgroup) invalid_ispecs = { - constants.ISPECS_MINMAX: INVALID_DICT, + constants.ISPECS_MINMAX: [INVALID_DICT], } self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy, old_policy, invalid_ispecs, group_policy=isgroup) @@ -1871,19 +1974,21 @@ class TestGetUpdatedIPolicy(unittest.TestCase): invalid_policy = { constants.ISPECS_MINMAX: invalid_ispecs, } - for key in constants.ISPECS_MINMAX_KEYS: - ispec = invalid_ispecs[key] - ispec[INVALID_KEY] = None - self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy, - old_policy, invalid_policy, group_policy=isgroup) - del ispec[INVALID_KEY] - for par in constants.ISPECS_PARAMETERS: - oldv = ispec[par] - ispec[par] = "this_is_not_good" + for minmax in invalid_ispecs: + for key in constants.ISPECS_MINMAX_KEYS: + ispec = minmax[key] + ispec[INVALID_KEY] = None self.assertRaises(errors.TypeEnforcementError, - cmdlib._GetUpdatedIPolicy, - old_policy, invalid_policy, group_policy=isgroup) - ispec[par] = oldv + cmdlib._GetUpdatedIPolicy, old_policy, + invalid_policy, group_policy=isgroup) + del ispec[INVALID_KEY] + for par in constants.ISPECS_PARAMETERS: + oldv = ispec[par] + ispec[par] = "this_is_not_good" + self.assertRaises(errors.TypeEnforcementError, + cmdlib._GetUpdatedIPolicy, + old_policy, invalid_policy, group_policy=isgroup) + ispec[par] = oldv # This is to make sure that no two errors were present during the tests cmdlib._GetUpdatedIPolicy(old_policy, invalid_policy, group_policy=isgroup) diff --git a/test/py/ganeti.config_unittest.py b/test/py/ganeti.config_unittest.py index 7eff2e6d1..cbfa51022 100755 --- a/test/py/ganeti.config_unittest.py +++ b/test/py/ganeti.config_unittest.py @@ -461,12 +461,13 @@ class TestConfigRunner(unittest.TestCase): ispeclist = [] if constants.ISPECS_MINMAX in ipolicy: - ispeclist.extend([ - (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MIN], - "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MIN)), - (ipolicy[constants.ISPECS_MINMAX][constants.ISPECS_MAX], - "%s/%s" % (constants.ISPECS_MINMAX, constants.ISPECS_MAX)), - ]) + for k in range(len(ipolicy[constants.ISPECS_MINMAX])): + ispeclist.extend([ + (ipolicy[constants.ISPECS_MINMAX][k][constants.ISPECS_MIN], + "%s[%s]/%s" % (constants.ISPECS_MINMAX, k, constants.ISPECS_MIN)), + (ipolicy[constants.ISPECS_MINMAX][k][constants.ISPECS_MAX], + "%s[%s]/%s" % (constants.ISPECS_MINMAX, k, constants.ISPECS_MAX)), + ]) if constants.ISPECS_STD in ipolicy: ispeclist.append((ipolicy[constants.ISPECS_STD], constants.ISPECS_STD)) @@ -498,23 +499,24 @@ class TestConfigRunner(unittest.TestCase): if constants.ISPECS_MINMAX in ipolicy: # Test partial minmax specs - minmax = ipolicy[constants.ISPECS_MINMAX] - for key in constants.ISPECS_MINMAX_KEYS: - self.assertTrue(key in minmax) - ispec = minmax[key] - del minmax[key] - errs = cfg.VerifyConfig() - self.assertTrue(len(errs) >= 1) - self.assertTrue(_IsErrorInList("Missing instance specification", errs)) - minmax[key] = ispec - for par in constants.ISPECS_PARAMETERS: - oldv = ispec[par] - del ispec[par] + for minmax in ipolicy[constants.ISPECS_MINMAX]: + for key in constants.ISPECS_MINMAX_KEYS: + self.assertTrue(key in minmax) + ispec = minmax[key] + del minmax[key] errs = cfg.VerifyConfig() self.assertTrue(len(errs) >= 1) - self.assertTrue(_IsErrorInList("Missing instance specs parameters", + self.assertTrue(_IsErrorInList("Missing instance specification", errs)) - ispec[par] = oldv + minmax[key] = ispec + for par in constants.ISPECS_PARAMETERS: + oldv = ispec[par] + del ispec[par] + errs = cfg.VerifyConfig() + self.assertTrue(len(errs) >= 1) + self.assertTrue(_IsErrorInList("Missing instance specs parameters", + errs)) + ispec[par] = oldv errs = cfg.VerifyConfig() self.assertFalse(errs) diff --git a/test/py/ganeti.objects_unittest.py b/test/py/ganeti.objects_unittest.py index 318ef88aa..719a4a12a 100755 --- a/test/py/ganeti.objects_unittest.py +++ b/test/py/ganeti.objects_unittest.py @@ -22,6 +22,7 @@ """Script for unittesting the objects module""" +import copy import unittest from ganeti import constants @@ -414,11 +415,12 @@ class TestInstancePolicy(unittest.TestCase): def _AssertIPolicyIsFull(self, policy): self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS) - minmax = policy[constants.ISPECS_MINMAX] - self.assertEqual(frozenset(minmax.keys()), constants.ISPECS_MINMAX_KEYS) - for key in constants.ISPECS_MINMAX_KEYS: - self.assertEqual(frozenset(minmax[key].keys()), - constants.ISPECS_PARAMETERS) + self.assertTrue(len(policy[constants.ISPECS_MINMAX]) > 0) + for minmax in policy[constants.ISPECS_MINMAX]: + self.assertEqual(frozenset(minmax.keys()), constants.ISPECS_MINMAX_KEYS) + for key in constants.ISPECS_MINMAX_KEYS: + self.assertEqual(frozenset(minmax[key].keys()), + constants.ISPECS_PARAMETERS) self.assertEqual(frozenset(policy[constants.ISPECS_STD].keys()), constants.ISPECS_PARAMETERS) @@ -427,36 +429,132 @@ class TestInstancePolicy(unittest.TestCase): True) self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS) + def _AssertPolicyIsBad(self, ipolicy, do_check_std=None): + if do_check_std is None: + check_std_vals = [False, True] + else: + check_std_vals = [do_check_std] + for check_std in check_std_vals: + self.assertRaises(errors.ConfigurationError, + objects.InstancePolicy.CheckISpecSyntax, + ipolicy, check_std) + def testCheckISpecSyntax(self): default_stdspec = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD] incomplete_ipolicies = [ { - constants.ISPECS_MINMAX: {}, + constants.ISPECS_MINMAX: [], constants.ISPECS_STD: default_stdspec, }, { - constants.ISPECS_MINMAX: { + constants.ISPECS_MINMAX: [{}], + constants.ISPECS_STD: default_stdspec, + }, + { + constants.ISPECS_MINMAX: [{ constants.ISPECS_MIN: NotImplemented, - }, + }], constants.ISPECS_STD: default_stdspec, }, { - constants.ISPECS_MINMAX: { + constants.ISPECS_MINMAX: [{ constants.ISPECS_MAX: NotImplemented, - }, + }], constants.ISPECS_STD: default_stdspec, }, { - constants.ISPECS_MINMAX: { + constants.ISPECS_MINMAX: [{ constants.ISPECS_MIN: NotImplemented, constants.ISPECS_MAX: NotImplemented, - }, + }], }, ] for ipol in incomplete_ipolicies: self.assertRaises(errors.ConfigurationError, objects.InstancePolicy.CheckISpecSyntax, ipol, True) + oldminmax = ipol[constants.ISPECS_MINMAX] + if oldminmax: + # Prepending valid specs shouldn't change the error + ipol[constants.ISPECS_MINMAX] = ([constants.ISPECS_MINMAX_DEFAULTS] + + oldminmax) + self.assertRaises(errors.ConfigurationError, + objects.InstancePolicy.CheckISpecSyntax, + ipol, True) + + good_ipolicy = { + constants.ISPECS_MINMAX: [ + { + constants.ISPECS_MIN: { + constants.ISPEC_MEM_SIZE: 64, + constants.ISPEC_CPU_COUNT: 1, + constants.ISPEC_DISK_COUNT: 2, + constants.ISPEC_DISK_SIZE: 64, + constants.ISPEC_NIC_COUNT: 1, + constants.ISPEC_SPINDLE_USE: 1, + }, + constants.ISPECS_MAX: { + constants.ISPEC_MEM_SIZE: 16384, + constants.ISPEC_CPU_COUNT: 5, + constants.ISPEC_DISK_COUNT: 12, + constants.ISPEC_DISK_SIZE: 1024, + constants.ISPEC_NIC_COUNT: 9, + constants.ISPEC_SPINDLE_USE: 18, + }, + }, + { + constants.ISPECS_MIN: { + constants.ISPEC_MEM_SIZE: 32768, + constants.ISPEC_CPU_COUNT: 8, + constants.ISPEC_DISK_COUNT: 1, + constants.ISPEC_DISK_SIZE: 1024, + constants.ISPEC_NIC_COUNT: 1, + constants.ISPEC_SPINDLE_USE: 1, + }, + constants.ISPECS_MAX: { + constants.ISPEC_MEM_SIZE: 65536, + constants.ISPEC_CPU_COUNT: 10, + constants.ISPEC_DISK_COUNT: 5, + constants.ISPEC_DISK_SIZE: 1024 * 1024, + constants.ISPEC_NIC_COUNT: 3, + constants.ISPEC_SPINDLE_USE: 12, + }, + }, + ], + } + good_ipolicy[constants.ISPECS_STD] = copy.deepcopy( + good_ipolicy[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX]) + # Check that it's really good before making it bad + objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True) + + bad_ipolicy = copy.deepcopy(good_ipolicy) + for minmax in bad_ipolicy[constants.ISPECS_MINMAX]: + for (key, spec) in minmax.items(): + for param in spec: + oldv = spec[param] + del spec[param] + self._AssertPolicyIsBad(bad_ipolicy) + if key == constants.ISPECS_MIN: + spec[param] = minmax[constants.ISPECS_MAX][param] + 1 + self._AssertPolicyIsBad(bad_ipolicy) + spec[param] = oldv + assert bad_ipolicy == good_ipolicy + + stdspec = bad_ipolicy[constants.ISPECS_STD] + for param in stdspec: + oldv = stdspec[param] + del stdspec[param] + self._AssertPolicyIsBad(bad_ipolicy, True) + # Note that std spec is the same as a max spec + stdspec[param] = oldv + 1 + self._AssertPolicyIsBad(bad_ipolicy, True) + stdspec[param] = oldv + assert bad_ipolicy == good_ipolicy + + for minmax in good_ipolicy[constants.ISPECS_MINMAX]: + for spec in minmax.values(): + good_ipolicy[constants.ISPECS_STD] = spec + objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True) def testCheckISpecParamSyntax(self): par = "my_parameter" @@ -558,12 +656,7 @@ class TestInstancePolicy(unittest.TestCase): def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol): for (key, value) in merged_pol.items(): if key in diff_pol: - if key == constants.ISPECS_MINMAX: - self.assertEqual(frozenset(value), constants.ISPECS_MINMAX_KEYS) - for k in constants.ISPECS_MINMAX_KEYS: - self._AssertISpecsMerged(default_pol[key][k], diff_pol[key][k], - value[k]) - elif key == constants.ISPECS_STD: + if key == constants.ISPECS_STD: self._AssertISpecsMerged(default_pol[key], diff_pol[key], value) else: self.assertEqual(value, diff_pol[key]) @@ -575,6 +668,9 @@ class TestInstancePolicy(unittest.TestCase): {constants.IPOLICY_VCPU_RATIO: 3.14}, {constants.IPOLICY_SPINDLE_RATIO: 2.72}, {constants.IPOLICY_DTS: [constants.DT_FILE]}, + {constants.ISPECS_STD: {constants.ISPEC_DISK_COUNT: 3}}, + {constants.ISPECS_MINMAX: [constants.ISPECS_MINMAX_DEFAULTS, + constants.ISPECS_MINMAX_DEFAULTS]} ] for diff_pol in partial_policies: policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol) diff --git a/tools/cfgupgrade b/tools/cfgupgrade index 4d80b18ca..0526b1a17 100755 --- a/tools/cfgupgrade +++ b/tools/cfgupgrade @@ -104,8 +104,8 @@ def CheckHostname(path): def _FillIPolicySpecs(default_ipolicy, ipolicy): if "minmax" in ipolicy: - for (key, spec) in ipolicy["minmax"].items(): - for (par, val) in default_ipolicy["minmax"][key].items(): + for (key, spec) in ipolicy["minmax"][0].items(): + for (par, val) in default_ipolicy["minmax"][0][key].items(): if par not in spec: spec[par] = val @@ -120,7 +120,7 @@ def UpgradeIPolicy(ipolicy, default_ipolicy, isgroup): minmax[key] = ipolicy[key] del ipolicy[key] if minmax: - ipolicy["minmax"] = minmax + ipolicy["minmax"] = [minmax] if isgroup and "std" in ipolicy: del ipolicy["std"] _FillIPolicySpecs(default_ipolicy, ipolicy) @@ -260,13 +260,19 @@ def UpgradeAll(config_data): UpgradeInstances(config_data) -def DowngradeIPolicy(ipolicy): +def DowngradeIPolicy(ipolicy, owner): # Downgrade IPolicy to 2.7 (stable) minmax_keys = ["min", "max"] specs_is_split = any((k in ipolicy) for k in minmax_keys) if not specs_is_split: if "minmax" in ipolicy: - minmax = ipolicy["minmax"] + if type(ipolicy["minmax"]) is not list: + raise Error("Invalid minmax type in %s ipolicy: %s" % + (owner, type(ipolicy["minmax"]))) + if len(ipolicy["minmax"]) > 1: + logging.warning("Discarding some limit specs values from %s policy", + owner) + minmax = ipolicy["minmax"][0] del ipolicy["minmax"] else: minmax = {} @@ -281,7 +287,7 @@ def DowngradeGroups(config_data): for group in config_data["nodegroups"].values(): ipolicy = group.get("ipolicy", None) if ipolicy is not None: - DowngradeIPolicy(ipolicy) + DowngradeIPolicy(ipolicy, "group \"%s\"" % group.get("name")) def DowngradeEnabledTemplates(cluster): @@ -300,7 +306,7 @@ def DowngradeCluster(config_data): DowngradeEnabledTemplates(cluster) ipolicy = cluster.get("ipolicy", None) if ipolicy: - DowngradeIPolicy(ipolicy) + DowngradeIPolicy(ipolicy, "cluster") def DowngradeAll(config_data): -- GitLab