htools: return new state from new IAllocator modes
[ganeti-local] / htools / Ganeti / HTools / QC.hs
index 241caf8..56ba8be 100644 (file)
@@ -34,6 +34,7 @@ module Ganeti.HTools.QC
     , testJobs
     , testCluster
     , testLoader
+    , testTypes
     ) where
 
 import Test.QuickCheck
@@ -110,6 +111,7 @@ setInstanceSmallerThanNode node inst =
 -- | Create an instance given its spec.
 createInstance mem dsk vcpus =
     Instance.create "inst-unnamed" mem dsk vcpus "running" [] True (-1) (-1)
+                    Types.DTDrbd8
 
 -- | Create a small cluster by repeating a node spec.
 makeSmallCluster :: Node.Node -> Int -> Node.List
@@ -190,6 +192,7 @@ instance Arbitrary Instance.Instance where
       sn <- arbitrary
       vcpus <- choose (0, maxCpu)
       return $ Instance.create name mem dsk vcpus run_st [] True pn sn
+                               Types.DTDrbd8
 
 -- | Generas an arbitrary node based on sizing information.
 genNode :: Maybe Int -- ^ Minimum node size in terms of units
@@ -265,6 +268,12 @@ instance Arbitrary SmallRatio where
       v <- choose (0, 1)
       return $ SmallRatio v
 
+instance Arbitrary Types.AllocPolicy where
+  arbitrary = elements [minBound..maxBound]
+
+instance Arbitrary Types.DiskTemplate where
+  arbitrary = elements [minBound..maxBound]
+
 -- * Actual tests
 
 -- ** Utils tests
@@ -289,11 +298,65 @@ prop_Utils_fromObjWithDefault def_value random_key =
          random_key (def_value+1) == Just def_value
         where _types = def_value :: Integer
 
+-- | Test that functional if' behaves like the syntactic sugar if.
+prop_Utils_if'if :: Bool -> Int -> Int -> Bool
+prop_Utils_if'if cnd a b = Utils.if' cnd a b == if cnd then a else b
+
+-- | Test basic select functionality
+prop_Utils_select :: Int   -- ^ Default result
+                  -> [Int] -- ^ List of False values
+                  -> [Int] -- ^ List of True values
+                  -> Bool  -- ^ Test result
+prop_Utils_select def lst1 lst2 =
+  Utils.select def cndlist == expectedresult
+  where expectedresult = Utils.if' (null lst2) def (head lst2)
+        flist = map (\e -> (False, e)) lst1
+        tlist = map (\e -> (True, e)) lst2
+        cndlist = flist ++ tlist
+
+-- | Test basic select functionality with undefined default
+prop_Utils_select_undefd :: [Int] -- ^ List of False values
+                         -> NonEmptyList Int -- ^ List of True values
+                         -> Bool  -- ^ Test result
+prop_Utils_select_undefd lst1 (NonEmpty lst2) =
+  Utils.select undefined cndlist == head lst2
+  where flist = map (\e -> (False, e)) lst1
+        tlist = map (\e -> (True, e)) lst2
+        cndlist = flist ++ tlist
+
+-- | Test basic select functionality with undefined list values
+prop_Utils_select_undefv :: [Int] -- ^ List of False values
+                         -> NonEmptyList Int -- ^ List of True values
+                         -> Bool  -- ^ Test result
+prop_Utils_select_undefv lst1 (NonEmpty lst2) =
+  Utils.select undefined cndlist == head lst2
+  where flist = map (\e -> (False, e)) lst1
+        tlist = map (\e -> (True, e)) lst2
+        cndlist = flist ++ tlist ++ [undefined]
+
+prop_Utils_parseUnit (NonNegative n) =
+    Utils.parseUnit (show n) == Types.Ok n &&
+    Utils.parseUnit (show n ++ "m") == Types.Ok n &&
+    (case Utils.parseUnit (show n ++ "M") of
+      Types.Ok m -> if n > 0
+                    then m < n  -- for positive values, X MB is less than X MiB
+                    else m == 0 -- but for 0, 0 MB == 0 MiB
+      Types.Bad _ -> False) &&
+    Utils.parseUnit (show n ++ "g") == Types.Ok (n*1024) &&
+    Utils.parseUnit (show n ++ "t") == Types.Ok (n*1048576) &&
+    Types.isBad (Utils.parseUnit (show n ++ "x")::Types.Result Int)
+    where _types = (n::Int)
+
 -- | Test list for the Utils module.
 testUtils =
   [ run prop_Utils_commaJoinSplit
   , run prop_Utils_commaSplitJoin
   , run prop_Utils_fromObjWithDefault
+  , run prop_Utils_if'if
+  , run prop_Utils_select
+  , run prop_Utils_select_undefd
+  , run prop_Utils_select_undefv
+  , run prop_Utils_parseUnit
   ]
 
 -- ** PeerMap tests
@@ -497,7 +560,7 @@ testInstance =
 
 prop_Text_Load_Instance name mem dsk vcpus status
                         (NonEmpty pnode) snode
-                        (NonNegative pdx) (NonNegative sdx) autobal =
+                        (NonNegative pdx) (NonNegative sdx) autobal dt =
     pnode /= snode && pdx /= sdx ==>
     let vcpus_s = show vcpus
         dsk_s = show dsk
@@ -508,20 +571,23 @@ prop_Text_Load_Instance name mem dsk vcpus status
         nl = Data.Map.fromList ndx
         tags = ""
         sbal = if autobal then "Y" else "N"
+        sdt = Types.dtToString dt
         inst = Text.loadInst nl
                [name, mem_s, dsk_s, vcpus_s, status,
-                sbal, pnode, snode, tags]:: Maybe (String, Instance.Instance)
+                sbal, pnode, snode, sdt, tags]
         fail1 = Text.loadInst nl
                [name, mem_s, dsk_s, vcpus_s, status,
-                sbal, pnode, pnode, tags]:: Maybe (String, Instance.Instance)
+                sbal, pnode, pnode, tags]
         _types = ( name::String, mem::Int, dsk::Int
                  , vcpus::Int, status::String
                  , snode::String
                  , autobal::Bool)
     in
       case inst of
-        Nothing -> False
-        Just (_, i) ->
+        Types.Bad msg -> printTestCase ("Failed to load instance: " ++ msg)
+                         False
+        Types.Ok (_, i) -> printTestCase ("Mismatch in some field while\
+                                          \ loading the instance") $
             Instance.name i == name &&
             Instance.vcpus i == vcpus &&
             Instance.mem i == mem &&
@@ -530,13 +596,15 @@ prop_Text_Load_Instance name mem dsk vcpus status
                                  then Node.noSecondary
                                  else sdx) &&
             Instance.autoBalance i == autobal &&
-            isNothing fail1
+            Types.isBad fail1
 
 prop_Text_Load_InstanceFail ktn fields =
-    length fields /= 9 ==>
+    length fields /= 10 ==>
     case Text.loadInst nl fields of
-      Types.Ok _ -> False
-      Types.Bad msg -> "Invalid/incomplete instance data: '" `isPrefixOf` msg
+      Types.Ok _ -> printTestCase "Managed to load instance from invalid\
+                                  \ data" False
+      Types.Bad msg -> printTestCase ("Unrecognised error message: " ++ msg) $
+                       "Invalid/incomplete instance data: '" `isPrefixOf` msg
     where nl = Data.Map.fromList ktn
 
 prop_Text_Load_Node name tm nm fm td fd tc fo =
@@ -649,7 +717,7 @@ prop_Node_addSec node inst pdx =
 
 -- | Checks for memory reservation changes.
 prop_Node_rMem inst =
-    forAll (arbitrary `suchThat` ((> 0) . Node.fMem)) $ \node ->
+    forAll (arbitrary `suchThat` ((> Types.unitMem) . Node.fMem)) $ \node ->
     -- ab = auto_balance, nb = non-auto_balance
     -- we use -1 as the primary node of the instance
     let inst' = inst { Instance.pNode = -1, Instance.autoBalance = True }
@@ -742,9 +810,8 @@ prop_Score_Zero node =
     (not (Node.offline node) && not (Node.failN1 node) && (count > 0) &&
      (Node.tDsk node > 0) && (Node.tMem node > 0)) ==>
     let fn = Node.buildPeers node Container.empty
-        nlst = zip [1..] $ replicate count fn::[(Types.Ndx, Node.Node)]
-        nl = Container.fromList nlst
-        score = Cluster.compCV nl
+        nlst = replicate count fn
+        score = Cluster.compCVNodes nlst
     -- we can't say == 0 here as the floating point errors accumulate;
     -- this should be much lower than the default score in CLI.hs
     in score <= 1e-12
@@ -799,7 +866,7 @@ prop_ClusterCanTieredAlloc node inst =
         il = Container.empty
         allocnodes = Cluster.genAllocNodes defGroupList nl rqnodes True
     in case allocnodes >>= \allocnodes' ->
-        Cluster.tieredAlloc nl il inst allocnodes' [] [] of
+        Cluster.tieredAlloc nl il (Just 1) inst allocnodes' [] [] of
          Types.Bad _ -> False
          Types.Ok (_, _, il', ixes, cstats) -> not (null ixes) &&
                                       IntMap.size il' == length ixes &&
@@ -842,7 +909,7 @@ prop_ClusterAllocBalance =
         allocnodes = Cluster.genAllocNodes defGroupList nl' 2 True
         i_templ = createInstance Types.unitMem Types.unitDsk Types.unitCpu
     in case allocnodes >>= \allocnodes' ->
-        Cluster.iterateAlloc nl' il i_templ allocnodes' [] [] of
+        Cluster.iterateAlloc nl' il (Just 5) i_templ allocnodes' [] [] of
          Types.Bad _ -> False
          Types.Ok (_, xnl, il', _, _) ->
                    let ynl = Container.add (Node.idx hnode) hnode xnl
@@ -950,9 +1017,42 @@ prop_Loader_mergeData ns =
       in (sum . map (length . Node.pList)) nodes == 0 &&
          null instances
 
+-- | Check that compareNameComponent on equal strings works.
+prop_Loader_compareNameComponent_equal :: String -> Bool
+prop_Loader_compareNameComponent_equal s =
+  Loader.compareNameComponent s s ==
+    Loader.LookupResult Loader.ExactMatch s
+
+-- | Check that compareNameComponent on prefix strings works.
+prop_Loader_compareNameComponent_prefix :: NonEmptyList Char -> String -> Bool
+prop_Loader_compareNameComponent_prefix (NonEmpty s1) s2 =
+  Loader.compareNameComponent (s1 ++ "." ++ s2) s1 ==
+    Loader.LookupResult Loader.PartialMatch s1
+
 testLoader =
   [ run prop_Loader_lookupNode
   , run prop_Loader_lookupInstance
   , run prop_Loader_assignIndices
   , run prop_Loader_mergeData
+  , run prop_Loader_compareNameComponent_equal
+  , run prop_Loader_compareNameComponent_prefix
   ]
+
+-- ** Types tests
+
+prop_AllocPolicy_serialisation apol =
+    case Types.apolFromString (Types.apolToString apol) of
+      Types.Ok p -> printTestCase ("invalid deserialisation " ++ show p) $
+                    p == apol
+      Types.Bad s -> printTestCase ("failed to deserialise: " ++ s) False
+
+prop_DiskTemplate_serialisation dt =
+    case Types.dtFromString (Types.dtToString dt) of
+      Types.Ok p -> printTestCase ("invalid deserialisation " ++ show p) $
+                    p == dt
+      Types.Bad s -> printTestCase ("failed to deserialise: " ++ s) False
+
+testTypes =
+    [ run prop_AllocPolicy_serialisation
+    , run prop_DiskTemplate_serialisation
+    ]