-}
module Ganeti.HTools.QC
- ( test_PeerMap
- , test_Container
- , test_Instance
- , test_Node
+ ( testPeerMap
+ , testContainer
+ , testInstance
+ , testNode
+ , testText
+ , testCluster
) where
import Test.QuickCheck
import qualified Ganeti.HTools.Loader as Loader
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.PeerMap as PeerMap
-import qualified Ganeti.HTools.Rapi as Rapi
import qualified Ganeti.HTools.Text as Text
import qualified Ganeti.HTools.Types as Types
import qualified Ganeti.HTools.Utils as Utils
+-- | Simple checker for whether OpResult is fail or pass
+isFailure :: Types.OpResult a -> Bool
+isFailure (Types.OpFail _) = True
+isFailure _ = False
+
-- copied from the introduction to quickcheck
instance Arbitrary Char where
arbitrary = choose ('\32', '\128')
name <- arbitrary
mem <- choose(0, 100)
dsk <- choose(0, 100)
- run_st <- arbitrary
+ run_st <- elements ["ERROR_up", "ERROR_down", "ADMIN_down"
+ , "ERROR_nodedown", "ERROR_nodeoffline"
+ , "running"
+ , "no_such_status1", "no_such_status2"]
pn <- arbitrary
sn <- arbitrary
- return $ Instance.create name mem dsk run_st pn sn
+ vcpus <- arbitrary
+ return $ Instance.create name mem dsk vcpus run_st [] pn sn
-- and a random node
instance Arbitrary Node.Node where
mem_n <- choose (0, mem_t - mem_f)
dsk_t <- arbitrary
dsk_f <- choose (0, dsk_t)
+ cpu_t <- arbitrary
offl <- arbitrary
let n = Node.create name (fromIntegral mem_t) mem_n mem_f
- (fromIntegral dsk_t) dsk_f offl
+ (fromIntegral dsk_t) dsk_f cpu_t offl
n' = Node.buildPeers n Container.empty
return n'
-- | Make sure add is idempotent
-prop_PeerMap_addIdempotent pmap key elem =
+prop_PeerMap_addIdempotent pmap key em =
fn puniq == fn (fn puniq)
where _types = (pmap::PeerMap.PeerMap,
- key::PeerMap.Key, elem::PeerMap.Elem)
- fn = PeerMap.add key elem
+ key::PeerMap.Key, em::PeerMap.Elem)
+ fn = PeerMap.add key em
puniq = PeerMap.accumArray const pmap
-- | Make sure remove is idempotent
puniq = PeerMap.accumArray const pmap
-- | Make sure an added item is found
-prop_PeerMap_addFind pmap key elem =
- PeerMap.find key (PeerMap.add key elem puniq) == elem
+prop_PeerMap_addFind pmap key em =
+ PeerMap.find key (PeerMap.add key em puniq) == em
where _types = (pmap::PeerMap.PeerMap,
- key::PeerMap.Key, elem::PeerMap.Elem)
+ key::PeerMap.Key, em::PeerMap.Elem)
puniq = PeerMap.accumArray const pmap
-- | Manual check that maxElem returns the maximum indeed, or 0 for null
where _types = pmap::PeerMap.PeerMap
puniq = PeerMap.accumArray const pmap
-test_PeerMap =
+testPeerMap =
[ run prop_PeerMap_addIdempotent
, run prop_PeerMap_removeIdempotent
, run prop_PeerMap_maxElem
cont = foldl (\c x -> Container.add x x c) Container.empty cdata
fn x1 x2 = Container.addTwo x1 x1 x2 x2
-test_Container =
+testContainer =
[ run prop_Container_addTwo ]
-- Simple instance tests, we only have setter/getters
where _types = (inst::Instance.Instance, name::String)
prop_Instance_setPri inst pdx =
- Instance.pnode (Instance.setPri inst pdx) == pdx
+ Instance.pNode (Instance.setPri inst pdx) == pdx
where _types = (inst::Instance.Instance, pdx::Types.Ndx)
prop_Instance_setSec inst sdx =
- Instance.snode (Instance.setSec inst sdx) == sdx
+ Instance.sNode (Instance.setSec inst sdx) == sdx
where _types = (inst::Instance.Instance, sdx::Types.Ndx)
prop_Instance_setBoth inst pdx sdx =
- Instance.pnode si == pdx && Instance.snode si == sdx
+ Instance.pNode si == pdx && Instance.sNode si == sdx
where _types = (inst::Instance.Instance, pdx::Types.Ndx, sdx::Types.Ndx)
si = Instance.setBoth inst pdx sdx
-test_Instance =
+prop_Instance_runStatus_True inst =
+ let run_st = Instance.running inst
+ run_tx = Instance.runSt inst
+ in
+ run_tx == "running" || run_tx == "ERROR_up" ==> run_st
+
+prop_Instance_runStatus_False inst =
+ let run_st = Instance.running inst
+ run_tx = Instance.runSt inst
+ in
+ run_tx /= "running" && run_tx /= "ERROR_up" ==> not run_st
+
+testInstance =
[ run prop_Instance_setIdx
, run prop_Instance_setName
, run prop_Instance_setPri
, run prop_Instance_setSec
, run prop_Instance_setBoth
+ , run prop_Instance_runStatus_True
+ , run prop_Instance_runStatus_False
+ ]
+
+-- Instance text loader tests
+
+prop_Text_Load_Instance name mem dsk vcpus status pnode snode pdx sdx =
+ let vcpus_s = show vcpus
+ dsk_s = show dsk
+ mem_s = show mem
+ rsnode = snode ++ "a" -- non-empty secondary node
+ rsdx = if pdx == sdx
+ then sdx + 1
+ else sdx
+ ndx = [(pnode, pdx), (rsnode, rsdx)]
+ tags = ""
+ inst = Text.loadInst ndx
+ [name, mem_s, dsk_s, vcpus_s, status, pnode, rsnode, tags]::
+ Maybe (String, Instance.Instance)
+ _types = ( name::String, mem::Int, dsk::Int
+ , vcpus::Int, status::String
+ , pnode::String, snode::String
+ , pdx::Types.Ndx, sdx::Types.Ndx)
+ in
+ case inst of
+ Nothing -> False
+ Just (_, i) ->
+ (Instance.name i == name &&
+ Instance.vcpus i == vcpus &&
+ Instance.mem i == mem &&
+ Instance.pNode i == pdx &&
+ Instance.sNode i == rsdx)
+
+testText =
+ [ run prop_Text_Load_Instance
]
-- Node tests
-- | Check that an instance add with too high memory or disk will be rejected
-prop_Node_addPri node inst = (Instance.mem inst >= Node.f_mem node ||
- Instance.dsk inst >= Node.f_dsk node) &&
- (not $ Node.failN1 node)
+prop_Node_addPri node inst = (Instance.mem inst >= Node.fMem node ||
+ Instance.dsk inst >= Node.fDsk node) &&
+ not (Node.failN1 node)
==>
- isNothing(Node.addPri node inst)
+ isFailure (Node.addPri node inst)
where _types = (node::Node.Node, inst::Instance.Instance)
-- | Check that an instance add with too high memory or disk will be rejected
prop_Node_addSec node inst pdx =
- (Instance.mem inst >= (Node.f_mem node - Node.r_mem node) ||
- Instance.dsk inst >= Node.f_dsk node) &&
- (not $ Node.failN1 node)
- ==> isNothing(Node.addSec node inst pdx)
+ (Instance.mem inst >= (Node.fMem node - Node.rMem node) ||
+ Instance.dsk inst >= Node.fDsk node) &&
+ not (Node.failN1 node)
+ ==> isFailure (Node.addSec node inst pdx)
where _types = (node::Node.Node, inst::Instance.Instance, pdx::Int)
-test_Node =
+testNode =
[ run prop_Node_addPri
, run prop_Node_addSec
]
+
+
+-- Cluster tests
+
+-- | Check that the cluster score is close to zero for a homogeneous cluster
+prop_Score_Zero node count =
+ (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.fromAssocList nlst
+ score = Cluster.compCV nl
+ -- 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-15
+
+testCluster =
+ [ run prop_Score_Zero
+ ]