module Test.Ganeti.Objects
( testObjects
, Node(..)
, genEmptyCluster
, genValidNetwork
, genBitStringMaxLen
) where
import Test.QuickCheck
import qualified Test.HUnit as HUnit
import Control.Applicative
import Control.Monad
import Data.Char
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Text.JSON as J
import Test.Ganeti.TestHelper
import Test.Ganeti.TestCommon
import Test.Ganeti.Types ()
import qualified Ganeti.Constants as C
import Ganeti.Network
import Ganeti.Objects as Objects
import Ganeti.JSON
import Ganeti.Types
$(genArbitrary ''PartialNDParams)
instance Arbitrary Node where
arbitrary = Node <$> genFQDN <*> genFQDN <*> genFQDN
<*> arbitrary <*> arbitrary <*> arbitrary <*> genFQDN
<*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary <*> genFQDN <*> arbitrary
<*> (Set.fromList <$> genTags)
$(genArbitrary ''BlockDriver)
$(genArbitrary ''DiskMode)
instance Arbitrary DiskLogicalId where
arbitrary = oneof [ LIDPlain <$> arbitrary <*> arbitrary
, LIDDrbd8 <$> genFQDN <*> genFQDN <*> arbitrary
<*> arbitrary <*> arbitrary <*> arbitrary
, LIDFile <$> arbitrary <*> arbitrary
, LIDBlockDev <$> arbitrary <*> arbitrary
, LIDRados <$> arbitrary <*> arbitrary
]
instance Arbitrary Disk where
arbitrary = Disk <$> arbitrary <*> pure [] <*> arbitrary
<*> arbitrary <*> arbitrary
$(genArbitrary ''PartialBeParams)
$(genArbitrary ''AdminState)
$(genArbitrary ''PartialNicParams)
$(genArbitrary ''PartialNic)
instance Arbitrary Instance where
arbitrary =
Instance
<$> genFQDN <*> genFQDN <*> genFQDN
<*> arbitrary
<*> pure (GenericContainer Map.empty) <*> arbitrary
<*> pure (GenericContainer Map.empty) <*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
<*> arbitrary
<*> arbitrary
<*> (Set.fromList <$> genTags)
$(genArbitrary ''PartialISpecParams)
$(genArbitrary ''PartialIPolicy)
$(genArbitrary ''FilledISpecParams)
$(genArbitrary ''FilledIPolicy)
$(genArbitrary ''IpFamily)
$(genArbitrary ''FilledNDParams)
$(genArbitrary ''FilledNicParams)
$(genArbitrary ''FilledBeParams)
instance Arbitrary ClusterHvParams where
arbitrary = return $ GenericContainer Map.empty
instance Arbitrary OsHvParams where
arbitrary = return $ GenericContainer Map.empty
instance Arbitrary ClusterNicParams where
arbitrary = (GenericContainer . Map.singleton C.ppDefault) <$> arbitrary
instance Arbitrary OsParams where
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
instance Arbitrary ClusterOsParams where
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
instance Arbitrary ClusterBeParams where
arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
instance Arbitrary TagSet where
arbitrary = Set.fromList <$> genTags
$(genArbitrary ''Cluster)
instance Arbitrary Network where
arbitrary = genValidNetwork
genValidNetwork :: Gen Objects.Network
genValidNetwork = do
netmask <- choose (24::Int, 30)
name <- genName >>= mkNonEmpty
mac_prefix <- genMaybe genName
net <- genIp4NetWithNetmask netmask
net6 <- genMaybe genIp6Net
gateway <- genMaybe genIp4AddrStr
gateway6 <- genMaybe genIp6Addr
res <- liftM Just (genBitString $ netmask2NumHosts netmask)
ext_res <- liftM Just (genBitString $ netmask2NumHosts netmask)
ctime <- arbitrary
mtime <- arbitrary
let n = Network name mac_prefix net net6 gateway
gateway6 res ext_res ctime mtime 0 Set.empty
return n
genBitString :: Int -> Gen String
genBitString len = vectorOf len (elements "01")
genBitStringMaxLen :: Int -> Gen String
genBitStringMaxLen maxLen = choose (0, maxLen) >>= genBitString
genEmptyCluster :: Int -> Gen ConfigData
genEmptyCluster ncount = do
nodes <- vector ncount
version <- arbitrary
grp <- arbitrary
let guuid = groupUuid grp
nodes' = zipWith (\n idx ->
let newname = nodeName n ++ "-" ++ show idx
in (newname, n { nodeGroup = guuid,
nodeName = newname}))
nodes [(1::Int)..]
nodemap = Map.fromList nodes'
contnodes = if Map.size nodemap /= ncount
then error ("Inconsistent node map, duplicates in" ++
" node name list? Names: " ++
show (map fst nodes'))
else GenericContainer nodemap
continsts = GenericContainer Map.empty
let contgroups = GenericContainer $ Map.singleton guuid grp
serial <- arbitrary
cluster <- resize 8 arbitrary
let c = ConfigData version cluster contnodes contgroups continsts serial
return c
prop_fillDict :: [(Int, Int)] -> [(Int, Int)] -> Property
prop_fillDict defaults custom =
let d_map = Map.fromList defaults
d_keys = map fst defaults
c_map = Map.fromList custom
c_keys = map fst custom
in conjoin [ printTestCase "Empty custom filling"
(fillDict d_map Map.empty [] == d_map)
, printTestCase "Empty defaults filling"
(fillDict Map.empty c_map [] == c_map)
, printTestCase "Delete all keys"
(fillDict d_map c_map (d_keys++c_keys) == Map.empty)
]
prop_Disk_serialisation :: Disk -> Property
prop_Disk_serialisation = testSerialisation
prop_Node_serialisation :: Node -> Property
prop_Node_serialisation = testSerialisation
prop_Inst_serialisation :: Instance -> Property
prop_Inst_serialisation = testSerialisation
prop_Network_serialisation :: Network -> Property
prop_Network_serialisation = testSerialisation
prop_Config_serialisation :: Property
prop_Config_serialisation =
forAll (choose (0, maxNodes `div` 4) >>= genEmptyCluster) testSerialisation
case_py_compat_networks :: HUnit.Assertion
case_py_compat_networks = do
let num_networks = 500::Int
networks <- genSample (vectorOf num_networks genValidNetwork)
let networks_with_properties = map getNetworkProperties networks
serialized = J.encode networks
mapM_ (\net -> when (any (not . isAscii) (J.encode net)) .
HUnit.assertFailure $
"Network has non-ASCII fields: " ++ show net
) networks
py_stdout <-
runPython "from ganeti import network\n\
\from ganeti import objects\n\
\from ganeti import serializer\n\
\import sys\n\
\net_data = serializer.Load(sys.stdin.read())\n\
\decoded = [objects.Network.FromDict(n) for n in net_data]\n\
\encoded = []\n\
\for net in decoded:\n\
\ a = network.AddressPool(net)\n\
\ encoded.append((a.GetFreeCount(), a.GetReservedCount(), \\\n\
\ net.ToDict()))\n\
\print serializer.Dump(encoded)" serialized
>>= checkPythonResult
let deserialised = J.decode py_stdout::J.Result [(Int, Int, Network)]
decoded <- case deserialised of
J.Ok ops -> return ops
J.Error msg ->
HUnit.assertFailure ("Unable to decode networks: " ++ msg)
>> fail "Unable to decode networks"
HUnit.assertEqual "Mismatch in number of returned networks"
(length decoded) (length networks_with_properties)
mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
) $ zip decoded networks_with_properties
getNetworkProperties :: Network -> (Int, Int, Network)
getNetworkProperties net =
let maybePool = createAddressPool net
in case maybePool of
(Just pool) -> (getFreeCount pool, getReservedCount pool, net)
Nothing -> (1, 1, net)
case_py_compat_nodegroups :: HUnit.Assertion
case_py_compat_nodegroups = do
let num_groups = 500::Int
groups <- genSample (vectorOf num_groups genNodeGroup)
let serialized = J.encode groups
mapM_ (\group -> when (any (not . isAscii) (J.encode group)) .
HUnit.assertFailure $
"Node group has non-ASCII fields: " ++ show group
) groups
py_stdout <-
runPython "from ganeti import objects\n\
\from ganeti import serializer\n\
\import sys\n\
\group_data = serializer.Load(sys.stdin.read())\n\
\decoded = [objects.NodeGroup.FromDict(g) for g in group_data]\n\
\encoded = [g.ToDict() for g in decoded]\n\
\print serializer.Dump(encoded)" serialized
>>= checkPythonResult
let deserialised = J.decode py_stdout::J.Result [NodeGroup]
decoded <- case deserialised of
J.Ok ops -> return ops
J.Error msg ->
HUnit.assertFailure ("Unable to decode node groups: " ++ msg)
>> fail "Unable to decode node groups"
HUnit.assertEqual "Mismatch in number of returned node groups"
(length decoded) (length groups)
mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
) $ zip decoded groups
genNodeGroup :: Gen NodeGroup
genNodeGroup = do
name <- genFQDN
members <- pure []
ndparams <- arbitrary
alloc_policy <- arbitrary
ipolicy <- arbitrary
diskparams <- pure (GenericContainer Map.empty)
num_networks <- choose (0, 3)
net_uuid_list <- vectorOf num_networks (arbitrary::Gen String)
nic_param_list <- vectorOf num_networks (arbitrary::Gen PartialNicParams)
net_map <- pure (GenericContainer . Map.fromList $
zip net_uuid_list nic_param_list)
ctime <- arbitrary
mtime <- arbitrary
uuid <- genFQDN `suchThat` (/= name)
serial <- arbitrary
tags <- Set.fromList <$> genTags
let group = NodeGroup name members ndparams alloc_policy ipolicy diskparams
net_map ctime mtime uuid serial tags
return group
instance Arbitrary NodeGroup where
arbitrary = genNodeGroup
testSuite "Objects"
[ 'prop_fillDict
, 'prop_Disk_serialisation
, 'prop_Inst_serialisation
, 'prop_Network_serialisation
, 'prop_Node_serialisation
, 'prop_Config_serialisation
, 'case_py_compat_networks
, 'case_py_compat_nodegroups
]