Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / QC.hs @ 6429e8d8

History | View | Annotate | Download (34.8 kB)

1 525bfb36 Iustin Pop
{-| Unittests for ganeti-htools.
2 e2fa2baf Iustin Pop
3 e2fa2baf Iustin Pop
-}
4 e2fa2baf Iustin Pop
5 e2fa2baf Iustin Pop
{-
6 e2fa2baf Iustin Pop
7 1bc47d38 Iustin Pop
Copyright (C) 2009, 2010, 2011 Google Inc.
8 e2fa2baf Iustin Pop
9 e2fa2baf Iustin Pop
This program is free software; you can redistribute it and/or modify
10 e2fa2baf Iustin Pop
it under the terms of the GNU General Public License as published by
11 e2fa2baf Iustin Pop
the Free Software Foundation; either version 2 of the License, or
12 e2fa2baf Iustin Pop
(at your option) any later version.
13 e2fa2baf Iustin Pop
14 e2fa2baf Iustin Pop
This program is distributed in the hope that it will be useful, but
15 e2fa2baf Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
16 e2fa2baf Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 e2fa2baf Iustin Pop
General Public License for more details.
18 e2fa2baf Iustin Pop
19 e2fa2baf Iustin Pop
You should have received a copy of the GNU General Public License
20 e2fa2baf Iustin Pop
along with this program; if not, write to the Free Software
21 e2fa2baf Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 e2fa2baf Iustin Pop
02110-1301, USA.
23 e2fa2baf Iustin Pop
24 e2fa2baf Iustin Pop
-}
25 e2fa2baf Iustin Pop
26 15f4c8ca Iustin Pop
module Ganeti.HTools.QC
27 691dcd2a Iustin Pop
    ( testUtils
28 691dcd2a Iustin Pop
    , testPeerMap
29 c15f7183 Iustin Pop
    , testContainer
30 c15f7183 Iustin Pop
    , testInstance
31 c15f7183 Iustin Pop
    , testNode
32 c15f7183 Iustin Pop
    , testText
33 88f25dd0 Iustin Pop
    , testOpCodes
34 db079755 Iustin Pop
    , testJobs
35 c15f7183 Iustin Pop
    , testCluster
36 c088674b Iustin Pop
    , testLoader
37 3c002a13 Iustin Pop
    , testTypes
38 7dd5ee6c Iustin Pop
    ) where
39 15f4c8ca Iustin Pop
40 15f4c8ca Iustin Pop
import Test.QuickCheck
41 bc782180 Iustin Pop
import Data.List (findIndex, intercalate, nub, isPrefixOf)
42 15f4c8ca Iustin Pop
import Data.Maybe
43 88f25dd0 Iustin Pop
import Control.Monad
44 88f25dd0 Iustin Pop
import qualified Text.JSON as J
45 8fcf251f Iustin Pop
import qualified Data.Map
46 3fea6959 Iustin Pop
import qualified Data.IntMap as IntMap
47 88f25dd0 Iustin Pop
import qualified Ganeti.OpCodes as OpCodes
48 db079755 Iustin Pop
import qualified Ganeti.Jobs as Jobs
49 223dbe53 Iustin Pop
import qualified Ganeti.Luxi
50 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.CLI as CLI
51 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Cluster as Cluster
52 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Container as Container
53 223dbe53 Iustin Pop
import qualified Ganeti.HTools.ExtLoader
54 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.IAlloc as IAlloc
55 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Instance as Instance
56 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Loader as Loader
57 223dbe53 Iustin Pop
import qualified Ganeti.HTools.Luxi
58 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Node as Node
59 10ef6b4e Iustin Pop
import qualified Ganeti.HTools.Group as Group
60 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.PeerMap as PeerMap
61 c478f837 Iustin Pop
import qualified Ganeti.HTools.Rapi
62 223dbe53 Iustin Pop
import qualified Ganeti.HTools.Simu
63 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Text as Text
64 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Types as Types
65 15f4c8ca Iustin Pop
import qualified Ganeti.HTools.Utils as Utils
66 223dbe53 Iustin Pop
import qualified Ganeti.HTools.Version
67 e82271f8 Iustin Pop
import qualified Ganeti.Constants as C
68 15f4c8ca Iustin Pop
69 8e4f6d56 Iustin Pop
run :: Testable prop => prop -> Args -> IO Result
70 8e4f6d56 Iustin Pop
run = flip quickCheckWithResult
71 8e4f6d56 Iustin Pop
72 3fea6959 Iustin Pop
-- * Constants
73 3fea6959 Iustin Pop
74 525bfb36 Iustin Pop
-- | Maximum memory (1TiB, somewhat random value).
75 8fcf251f Iustin Pop
maxMem :: Int
76 8fcf251f Iustin Pop
maxMem = 1024 * 1024
77 8fcf251f Iustin Pop
78 525bfb36 Iustin Pop
-- | Maximum disk (8TiB, somewhat random value).
79 8fcf251f Iustin Pop
maxDsk :: Int
80 49f9627a Iustin Pop
maxDsk = 1024 * 1024 * 8
81 8fcf251f Iustin Pop
82 525bfb36 Iustin Pop
-- | Max CPUs (1024, somewhat random value).
83 8fcf251f Iustin Pop
maxCpu :: Int
84 8fcf251f Iustin Pop
maxCpu = 1024
85 8fcf251f Iustin Pop
86 10ef6b4e Iustin Pop
defGroup :: Group.Group
87 10ef6b4e Iustin Pop
defGroup = flip Group.setIdx 0 $
88 10ef6b4e Iustin Pop
               Group.create "default" Utils.defaultGroupID
89 10ef6b4e Iustin Pop
                    Types.AllocPreferred
90 10ef6b4e Iustin Pop
91 10ef6b4e Iustin Pop
defGroupList :: Group.List
92 cb0c77ff Iustin Pop
defGroupList = Container.fromList [(Group.idx defGroup, defGroup)]
93 10ef6b4e Iustin Pop
94 10ef6b4e Iustin Pop
defGroupAssoc :: Data.Map.Map String Types.Gdx
95 10ef6b4e Iustin Pop
defGroupAssoc = Data.Map.singleton (Group.uuid defGroup) (Group.idx defGroup)
96 10ef6b4e Iustin Pop
97 3fea6959 Iustin Pop
-- * Helper functions
98 3fea6959 Iustin Pop
99 525bfb36 Iustin Pop
-- | Simple checker for whether OpResult is fail or pass.
100 79a72ce7 Iustin Pop
isFailure :: Types.OpResult a -> Bool
101 79a72ce7 Iustin Pop
isFailure (Types.OpFail _) = True
102 79a72ce7 Iustin Pop
isFailure _ = False
103 79a72ce7 Iustin Pop
104 525bfb36 Iustin Pop
-- | Update an instance to be smaller than a node.
105 3fea6959 Iustin Pop
setInstanceSmallerThanNode node inst =
106 4a007641 Iustin Pop
    inst { Instance.mem = Node.availMem node `div` 2
107 4a007641 Iustin Pop
         , Instance.dsk = Node.availDisk node `div` 2
108 4a007641 Iustin Pop
         , Instance.vcpus = Node.availCpu node `div` 2
109 3fea6959 Iustin Pop
         }
110 3fea6959 Iustin Pop
111 525bfb36 Iustin Pop
-- | Create an instance given its spec.
112 3fea6959 Iustin Pop
createInstance mem dsk vcpus =
113 c352b0a9 Iustin Pop
    Instance.create "inst-unnamed" mem dsk vcpus "running" [] True (-1) (-1)
114 d25643d1 Iustin Pop
                    Types.DTDrbd8
115 3fea6959 Iustin Pop
116 525bfb36 Iustin Pop
-- | Create a small cluster by repeating a node spec.
117 3fea6959 Iustin Pop
makeSmallCluster :: Node.Node -> Int -> Node.List
118 3fea6959 Iustin Pop
makeSmallCluster node count =
119 3fea6959 Iustin Pop
    let fn = Node.buildPeers node Container.empty
120 3fea6959 Iustin Pop
        namelst = map (\n -> (Node.name n, n)) (replicate count fn)
121 3fea6959 Iustin Pop
        (_, nlst) = Loader.assignIndices namelst
122 99b63608 Iustin Pop
    in nlst
123 3fea6959 Iustin Pop
124 525bfb36 Iustin Pop
-- | Checks if a node is "big" enough.
125 3fea6959 Iustin Pop
isNodeBig :: Node.Node -> Int -> Bool
126 3fea6959 Iustin Pop
isNodeBig node size = Node.availDisk node > size * Types.unitDsk
127 3fea6959 Iustin Pop
                      && Node.availMem node > size * Types.unitMem
128 3fea6959 Iustin Pop
                      && Node.availCpu node > size * Types.unitCpu
129 3fea6959 Iustin Pop
130 e08424a8 Guido Trotter
canBalance :: Cluster.Table -> Bool -> Bool -> Bool -> Bool
131 e08424a8 Guido Trotter
canBalance tbl dm im evac = isJust $ Cluster.tryBalance tbl dm im evac 0 0
132 3fea6959 Iustin Pop
133 f4161783 Iustin Pop
-- | Assigns a new fresh instance to a cluster; this is not
134 525bfb36 Iustin Pop
-- allocation, so no resource checks are done.
135 f4161783 Iustin Pop
assignInstance :: Node.List -> Instance.List -> Instance.Instance ->
136 f4161783 Iustin Pop
                  Types.Idx -> Types.Idx ->
137 f4161783 Iustin Pop
                  (Node.List, Instance.List)
138 f4161783 Iustin Pop
assignInstance nl il inst pdx sdx =
139 f4161783 Iustin Pop
  let pnode = Container.find pdx nl
140 f4161783 Iustin Pop
      snode = Container.find sdx nl
141 f4161783 Iustin Pop
      maxiidx = if Container.null il
142 f4161783 Iustin Pop
                then 0
143 f4161783 Iustin Pop
                else fst (Container.findMax il) + 1
144 f4161783 Iustin Pop
      inst' = inst { Instance.idx = maxiidx,
145 f4161783 Iustin Pop
                     Instance.pNode = pdx, Instance.sNode = sdx }
146 f4161783 Iustin Pop
      pnode' = Node.setPri pnode inst'
147 f4161783 Iustin Pop
      snode' = Node.setSec snode inst'
148 f4161783 Iustin Pop
      nl' = Container.addTwo pdx pnode' sdx snode' nl
149 f4161783 Iustin Pop
      il' = Container.add maxiidx inst' il
150 f4161783 Iustin Pop
  in (nl', il')
151 f4161783 Iustin Pop
152 3fea6959 Iustin Pop
-- * Arbitrary instances
153 3fea6959 Iustin Pop
154 525bfb36 Iustin Pop
-- | Defines a DNS name.
155 a070c426 Iustin Pop
newtype DNSChar = DNSChar { dnsGetChar::Char }
156 525bfb36 Iustin Pop
157 a070c426 Iustin Pop
instance Arbitrary DNSChar where
158 a070c426 Iustin Pop
    arbitrary = do
159 a070c426 Iustin Pop
      x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
160 a070c426 Iustin Pop
      return (DNSChar x)
161 a070c426 Iustin Pop
162 a070c426 Iustin Pop
getName :: Gen String
163 a070c426 Iustin Pop
getName = do
164 a070c426 Iustin Pop
  n <- choose (1, 64)
165 a070c426 Iustin Pop
  dn <- vector n::Gen [DNSChar]
166 a070c426 Iustin Pop
  return (map dnsGetChar dn)
167 a070c426 Iustin Pop
168 a070c426 Iustin Pop
169 a070c426 Iustin Pop
getFQDN :: Gen String
170 a070c426 Iustin Pop
getFQDN = do
171 a070c426 Iustin Pop
  felem <- getName
172 a070c426 Iustin Pop
  ncomps <- choose (1, 4)
173 a070c426 Iustin Pop
  frest <- vector ncomps::Gen [[DNSChar]]
174 a070c426 Iustin Pop
  let frest' = map (map dnsGetChar) frest
175 a070c426 Iustin Pop
  return (felem ++ "." ++ intercalate "." frest')
176 a070c426 Iustin Pop
177 15f4c8ca Iustin Pop
-- let's generate a random instance
178 15f4c8ca Iustin Pop
instance Arbitrary Instance.Instance where
179 15f4c8ca Iustin Pop
    arbitrary = do
180 a070c426 Iustin Pop
      name <- getFQDN
181 8fcf251f Iustin Pop
      mem <- choose (0, maxMem)
182 8fcf251f Iustin Pop
      dsk <- choose (0, maxDsk)
183 e82271f8 Iustin Pop
      run_st <- elements [ C.inststErrorup
184 e82271f8 Iustin Pop
                         , C.inststErrordown
185 e82271f8 Iustin Pop
                         , C.inststAdmindown
186 e82271f8 Iustin Pop
                         , C.inststNodedown
187 e82271f8 Iustin Pop
                         , C.inststNodeoffline
188 e82271f8 Iustin Pop
                         , C.inststRunning
189 e82271f8 Iustin Pop
                         , "no_such_status1"
190 e82271f8 Iustin Pop
                         , "no_such_status2"]
191 15f4c8ca Iustin Pop
      pn <- arbitrary
192 15f4c8ca Iustin Pop
      sn <- arbitrary
193 8fcf251f Iustin Pop
      vcpus <- choose (0, maxCpu)
194 c352b0a9 Iustin Pop
      return $ Instance.create name mem dsk vcpus run_st [] True pn sn
195 d25643d1 Iustin Pop
                               Types.DTDrbd8
196 15f4c8ca Iustin Pop
197 525bfb36 Iustin Pop
-- | Generas an arbitrary node based on sizing information.
198 525bfb36 Iustin Pop
genNode :: Maybe Int -- ^ Minimum node size in terms of units
199 525bfb36 Iustin Pop
        -> Maybe Int -- ^ Maximum node size (when Nothing, bounded
200 525bfb36 Iustin Pop
                     -- just by the max... constants)
201 525bfb36 Iustin Pop
        -> Gen Node.Node
202 00c75986 Iustin Pop
genNode min_multiplier max_multiplier = do
203 00c75986 Iustin Pop
  let (base_mem, base_dsk, base_cpu) =
204 00c75986 Iustin Pop
          case min_multiplier of
205 00c75986 Iustin Pop
            Just mm -> (mm * Types.unitMem,
206 00c75986 Iustin Pop
                        mm * Types.unitDsk,
207 00c75986 Iustin Pop
                        mm * Types.unitCpu)
208 00c75986 Iustin Pop
            Nothing -> (0, 0, 0)
209 00c75986 Iustin Pop
      (top_mem, top_dsk, top_cpu)  =
210 00c75986 Iustin Pop
          case max_multiplier of
211 00c75986 Iustin Pop
            Just mm -> (mm * Types.unitMem,
212 00c75986 Iustin Pop
                        mm * Types.unitDsk,
213 00c75986 Iustin Pop
                        mm * Types.unitCpu)
214 00c75986 Iustin Pop
            Nothing -> (maxMem, maxDsk, maxCpu)
215 00c75986 Iustin Pop
  name  <- getFQDN
216 00c75986 Iustin Pop
  mem_t <- choose (base_mem, top_mem)
217 00c75986 Iustin Pop
  mem_f <- choose (base_mem, mem_t)
218 00c75986 Iustin Pop
  mem_n <- choose (0, mem_t - mem_f)
219 00c75986 Iustin Pop
  dsk_t <- choose (base_dsk, top_dsk)
220 00c75986 Iustin Pop
  dsk_f <- choose (base_dsk, dsk_t)
221 00c75986 Iustin Pop
  cpu_t <- choose (base_cpu, top_cpu)
222 00c75986 Iustin Pop
  offl  <- arbitrary
223 00c75986 Iustin Pop
  let n = Node.create name (fromIntegral mem_t) mem_n mem_f
224 00c75986 Iustin Pop
          (fromIntegral dsk_t) dsk_f (fromIntegral cpu_t) offl 0
225 00c75986 Iustin Pop
  return $ Node.buildPeers n Container.empty
226 00c75986 Iustin Pop
227 15f4c8ca Iustin Pop
-- and a random node
228 15f4c8ca Iustin Pop
instance Arbitrary Node.Node where
229 00c75986 Iustin Pop
    arbitrary = genNode Nothing Nothing
230 15f4c8ca Iustin Pop
231 88f25dd0 Iustin Pop
-- replace disks
232 88f25dd0 Iustin Pop
instance Arbitrary OpCodes.ReplaceDisksMode where
233 88f25dd0 Iustin Pop
  arbitrary = elements [ OpCodes.ReplaceOnPrimary
234 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceOnSecondary
235 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceNewSecondary
236 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceAuto
237 88f25dd0 Iustin Pop
                       ]
238 88f25dd0 Iustin Pop
239 88f25dd0 Iustin Pop
instance Arbitrary OpCodes.OpCode where
240 88f25dd0 Iustin Pop
  arbitrary = do
241 88f25dd0 Iustin Pop
    op_id <- elements [ "OP_TEST_DELAY"
242 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_REPLACE_DISKS"
243 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_FAILOVER"
244 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_MIGRATE"
245 88f25dd0 Iustin Pop
                      ]
246 88f25dd0 Iustin Pop
    (case op_id of
247 88f25dd0 Iustin Pop
        "OP_TEST_DELAY" ->
248 88f25dd0 Iustin Pop
          liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary
249 88f25dd0 Iustin Pop
        "OP_INSTANCE_REPLACE_DISKS" ->
250 10028866 René Nussbaumer
          liftM5 OpCodes.OpInstanceReplaceDisks arbitrary arbitrary
251 88f25dd0 Iustin Pop
          arbitrary arbitrary arbitrary
252 88f25dd0 Iustin Pop
        "OP_INSTANCE_FAILOVER" ->
253 10028866 René Nussbaumer
          liftM2 OpCodes.OpInstanceFailover arbitrary arbitrary
254 88f25dd0 Iustin Pop
        "OP_INSTANCE_MIGRATE" ->
255 10028866 René Nussbaumer
          liftM4 OpCodes.OpInstanceMigrate arbitrary arbitrary arbitrary
256 8d66f58a René Nussbaumer
          arbitrary
257 88f25dd0 Iustin Pop
        _ -> fail "Wrong opcode")
258 88f25dd0 Iustin Pop
259 db079755 Iustin Pop
instance Arbitrary Jobs.OpStatus where
260 db079755 Iustin Pop
  arbitrary = elements [minBound..maxBound]
261 db079755 Iustin Pop
262 db079755 Iustin Pop
instance Arbitrary Jobs.JobStatus where
263 db079755 Iustin Pop
  arbitrary = elements [minBound..maxBound]
264 db079755 Iustin Pop
265 525bfb36 Iustin Pop
newtype SmallRatio = SmallRatio Double deriving Show
266 525bfb36 Iustin Pop
instance Arbitrary SmallRatio where
267 525bfb36 Iustin Pop
    arbitrary = do
268 525bfb36 Iustin Pop
      v <- choose (0, 1)
269 525bfb36 Iustin Pop
      return $ SmallRatio v
270 525bfb36 Iustin Pop
271 3c002a13 Iustin Pop
instance Arbitrary Types.AllocPolicy where
272 3c002a13 Iustin Pop
  arbitrary = elements [minBound..maxBound]
273 3c002a13 Iustin Pop
274 3c002a13 Iustin Pop
instance Arbitrary Types.DiskTemplate where
275 3c002a13 Iustin Pop
  arbitrary = elements [minBound..maxBound]
276 3c002a13 Iustin Pop
277 3fea6959 Iustin Pop
-- * Actual tests
278 8fcf251f Iustin Pop
279 525bfb36 Iustin Pop
-- ** Utils tests
280 525bfb36 Iustin Pop
281 525bfb36 Iustin Pop
-- | If the list is not just an empty element, and if the elements do
282 525bfb36 Iustin Pop
-- not contain commas, then join+split should be idempotent.
283 a1cd7c1e Iustin Pop
prop_Utils_commaJoinSplit =
284 a1cd7c1e Iustin Pop
    forAll (arbitrary `suchThat`
285 a1cd7c1e Iustin Pop
            (\l -> l /= [""] && all (not . elem ',') l )) $ \lst ->
286 a1cd7c1e Iustin Pop
    Utils.sepSplit ',' (Utils.commaJoin lst) == lst
287 a1cd7c1e Iustin Pop
288 525bfb36 Iustin Pop
-- | Split and join should always be idempotent.
289 691dcd2a Iustin Pop
prop_Utils_commaSplitJoin s = Utils.commaJoin (Utils.sepSplit ',' s) == s
290 691dcd2a Iustin Pop
291 a810ad21 Iustin Pop
-- | fromObjWithDefault, we test using the Maybe monad and an integer
292 525bfb36 Iustin Pop
-- value.
293 a810ad21 Iustin Pop
prop_Utils_fromObjWithDefault def_value random_key =
294 a810ad21 Iustin Pop
    -- a missing key will be returned with the default
295 a810ad21 Iustin Pop
    Utils.fromObjWithDefault [] random_key def_value == Just def_value &&
296 a810ad21 Iustin Pop
    -- a found key will be returned as is, not with default
297 a810ad21 Iustin Pop
    Utils.fromObjWithDefault [(random_key, J.showJSON def_value)]
298 a810ad21 Iustin Pop
         random_key (def_value+1) == Just def_value
299 cc532bdd Iustin Pop
        where _types = def_value :: Integer
300 a810ad21 Iustin Pop
301 525bfb36 Iustin Pop
-- | Test list for the Utils module.
302 691dcd2a Iustin Pop
testUtils =
303 691dcd2a Iustin Pop
  [ run prop_Utils_commaJoinSplit
304 691dcd2a Iustin Pop
  , run prop_Utils_commaSplitJoin
305 a810ad21 Iustin Pop
  , run prop_Utils_fromObjWithDefault
306 691dcd2a Iustin Pop
  ]
307 691dcd2a Iustin Pop
308 525bfb36 Iustin Pop
-- ** PeerMap tests
309 525bfb36 Iustin Pop
310 525bfb36 Iustin Pop
-- | Make sure add is idempotent.
311 fbb95f28 Iustin Pop
prop_PeerMap_addIdempotent pmap key em =
312 15f4c8ca Iustin Pop
    fn puniq == fn (fn puniq)
313 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap,
314 fbb95f28 Iustin Pop
                    key::PeerMap.Key, em::PeerMap.Elem)
315 fbb95f28 Iustin Pop
          fn = PeerMap.add key em
316 7bc82927 Iustin Pop
          puniq = PeerMap.accumArray const pmap
317 15f4c8ca Iustin Pop
318 525bfb36 Iustin Pop
-- | Make sure remove is idempotent.
319 15f4c8ca Iustin Pop
prop_PeerMap_removeIdempotent pmap key =
320 15f4c8ca Iustin Pop
    fn puniq == fn (fn puniq)
321 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
322 7bc82927 Iustin Pop
          fn = PeerMap.remove key
323 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
324 15f4c8ca Iustin Pop
325 525bfb36 Iustin Pop
-- | Make sure a missing item returns 0.
326 15f4c8ca Iustin Pop
prop_PeerMap_findMissing pmap key =
327 15f4c8ca Iustin Pop
    PeerMap.find key (PeerMap.remove key puniq) == 0
328 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
329 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
330 15f4c8ca Iustin Pop
331 525bfb36 Iustin Pop
-- | Make sure an added item is found.
332 fbb95f28 Iustin Pop
prop_PeerMap_addFind pmap key em =
333 fbb95f28 Iustin Pop
    PeerMap.find key (PeerMap.add key em puniq) == em
334 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap,
335 fbb95f28 Iustin Pop
                    key::PeerMap.Key, em::PeerMap.Elem)
336 7bc82927 Iustin Pop
          puniq = PeerMap.accumArray const pmap
337 15f4c8ca Iustin Pop
338 525bfb36 Iustin Pop
-- | Manual check that maxElem returns the maximum indeed, or 0 for null.
339 15f4c8ca Iustin Pop
prop_PeerMap_maxElem pmap =
340 15f4c8ca Iustin Pop
    PeerMap.maxElem puniq == if null puniq then 0
341 15f4c8ca Iustin Pop
                             else (maximum . snd . unzip) puniq
342 7bc82927 Iustin Pop
    where _types = pmap::PeerMap.PeerMap
343 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
344 15f4c8ca Iustin Pop
345 525bfb36 Iustin Pop
-- | List of tests for the PeerMap module.
346 c15f7183 Iustin Pop
testPeerMap =
347 7dd5ee6c Iustin Pop
    [ run prop_PeerMap_addIdempotent
348 7dd5ee6c Iustin Pop
    , run prop_PeerMap_removeIdempotent
349 7dd5ee6c Iustin Pop
    , run prop_PeerMap_maxElem
350 7dd5ee6c Iustin Pop
    , run prop_PeerMap_addFind
351 7dd5ee6c Iustin Pop
    , run prop_PeerMap_findMissing
352 7dd5ee6c Iustin Pop
    ]
353 7dd5ee6c Iustin Pop
354 525bfb36 Iustin Pop
-- ** Container tests
355 095d7ac0 Iustin Pop
356 095d7ac0 Iustin Pop
prop_Container_addTwo cdata i1 i2 =
357 095d7ac0 Iustin Pop
    fn i1 i2 cont == fn i2 i1 cont &&
358 095d7ac0 Iustin Pop
       fn i1 i2 cont == fn i1 i2 (fn i1 i2 cont)
359 095d7ac0 Iustin Pop
    where _types = (cdata::[Int],
360 095d7ac0 Iustin Pop
                    i1::Int, i2::Int)
361 095d7ac0 Iustin Pop
          cont = foldl (\c x -> Container.add x x c) Container.empty cdata
362 095d7ac0 Iustin Pop
          fn x1 x2 = Container.addTwo x1 x1 x2 x2
363 095d7ac0 Iustin Pop
364 5ef78537 Iustin Pop
prop_Container_nameOf node =
365 5ef78537 Iustin Pop
  let nl = makeSmallCluster node 1
366 5ef78537 Iustin Pop
      fnode = head (Container.elems nl)
367 5ef78537 Iustin Pop
  in Container.nameOf nl (Node.idx fnode) == Node.name fnode
368 5ef78537 Iustin Pop
369 525bfb36 Iustin Pop
-- | We test that in a cluster, given a random node, we can find it by
370 5ef78537 Iustin Pop
-- its name and alias, as long as all names and aliases are unique,
371 525bfb36 Iustin Pop
-- and that we fail to find a non-existing name.
372 5ef78537 Iustin Pop
prop_Container_findByName node othername =
373 5ef78537 Iustin Pop
  forAll (choose (1, 20)) $ \ cnt ->
374 5ef78537 Iustin Pop
  forAll (choose (0, cnt - 1)) $ \ fidx ->
375 5ef78537 Iustin Pop
  forAll (vector cnt) $ \ names ->
376 5ef78537 Iustin Pop
  (length . nub) (map fst names ++ map snd names) ==
377 5ef78537 Iustin Pop
  length names * 2 &&
378 5ef78537 Iustin Pop
  not (othername `elem` (map fst names ++ map snd names)) ==>
379 5ef78537 Iustin Pop
  let nl = makeSmallCluster node cnt
380 5ef78537 Iustin Pop
      nodes = Container.elems nl
381 5ef78537 Iustin Pop
      nodes' = map (\((name, alias), nn) -> (Node.idx nn,
382 5ef78537 Iustin Pop
                                             nn { Node.name = name,
383 5ef78537 Iustin Pop
                                                  Node.alias = alias }))
384 5ef78537 Iustin Pop
               $ zip names nodes
385 cb0c77ff Iustin Pop
      nl' = Container.fromList nodes'
386 5ef78537 Iustin Pop
      target = snd (nodes' !! fidx)
387 5ef78537 Iustin Pop
  in Container.findByName nl' (Node.name target) == Just target &&
388 5ef78537 Iustin Pop
     Container.findByName nl' (Node.alias target) == Just target &&
389 5ef78537 Iustin Pop
     Container.findByName nl' othername == Nothing
390 5ef78537 Iustin Pop
391 c15f7183 Iustin Pop
testContainer =
392 5ef78537 Iustin Pop
    [ run prop_Container_addTwo
393 5ef78537 Iustin Pop
    , run prop_Container_nameOf
394 5ef78537 Iustin Pop
    , run prop_Container_findByName
395 5ef78537 Iustin Pop
    ]
396 095d7ac0 Iustin Pop
397 525bfb36 Iustin Pop
-- ** Instance tests
398 525bfb36 Iustin Pop
399 7bc82927 Iustin Pop
-- Simple instance tests, we only have setter/getters
400 7bc82927 Iustin Pop
401 39d11971 Iustin Pop
prop_Instance_creat inst =
402 39d11971 Iustin Pop
    Instance.name inst == Instance.alias inst
403 39d11971 Iustin Pop
404 7bc82927 Iustin Pop
prop_Instance_setIdx inst idx =
405 7bc82927 Iustin Pop
    Instance.idx (Instance.setIdx inst idx) == idx
406 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, idx::Types.Idx)
407 7bc82927 Iustin Pop
408 7bc82927 Iustin Pop
prop_Instance_setName inst name =
409 39d11971 Iustin Pop
    Instance.name newinst == name &&
410 39d11971 Iustin Pop
    Instance.alias newinst == name
411 39d11971 Iustin Pop
    where _types = (inst::Instance.Instance, name::String)
412 39d11971 Iustin Pop
          newinst = Instance.setName inst name
413 39d11971 Iustin Pop
414 39d11971 Iustin Pop
prop_Instance_setAlias inst name =
415 39d11971 Iustin Pop
    Instance.name newinst == Instance.name inst &&
416 39d11971 Iustin Pop
    Instance.alias newinst == name
417 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, name::String)
418 39d11971 Iustin Pop
          newinst = Instance.setAlias inst name
419 7bc82927 Iustin Pop
420 7bc82927 Iustin Pop
prop_Instance_setPri inst pdx =
421 2060348b Iustin Pop
    Instance.pNode (Instance.setPri inst pdx) == pdx
422 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, pdx::Types.Ndx)
423 7bc82927 Iustin Pop
424 7bc82927 Iustin Pop
prop_Instance_setSec inst sdx =
425 2060348b Iustin Pop
    Instance.sNode (Instance.setSec inst sdx) == sdx
426 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, sdx::Types.Ndx)
427 7bc82927 Iustin Pop
428 7bc82927 Iustin Pop
prop_Instance_setBoth inst pdx sdx =
429 2060348b Iustin Pop
    Instance.pNode si == pdx && Instance.sNode si == sdx
430 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, pdx::Types.Ndx, sdx::Types.Ndx)
431 7bc82927 Iustin Pop
          si = Instance.setBoth inst pdx sdx
432 7bc82927 Iustin Pop
433 a1cd7c1e Iustin Pop
prop_Instance_runStatus_True =
434 a1cd7c1e Iustin Pop
    forAll (arbitrary `suchThat`
435 a1cd7c1e Iustin Pop
            ((`elem` Instance.runningStates) . Instance.runSt))
436 a1cd7c1e Iustin Pop
    Instance.running
437 1ae7a904 Iustin Pop
438 1ae7a904 Iustin Pop
prop_Instance_runStatus_False inst =
439 1ae7a904 Iustin Pop
    let run_st = Instance.running inst
440 2060348b Iustin Pop
        run_tx = Instance.runSt inst
441 1ae7a904 Iustin Pop
    in
442 a46f34d7 Iustin Pop
      run_tx `notElem` Instance.runningStates ==> not run_st
443 1ae7a904 Iustin Pop
444 8fcf251f Iustin Pop
prop_Instance_shrinkMG inst =
445 8fcf251f Iustin Pop
    Instance.mem inst >= 2 * Types.unitMem ==>
446 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailMem of
447 8fcf251f Iustin Pop
          Types.Ok inst' ->
448 8fcf251f Iustin Pop
              Instance.mem inst' == Instance.mem inst - Types.unitMem
449 8fcf251f Iustin Pop
          _ -> False
450 8fcf251f Iustin Pop
451 8fcf251f Iustin Pop
prop_Instance_shrinkMF inst =
452 41085bd3 Iustin Pop
    forAll (choose (0, 2 * Types.unitMem - 1)) $ \mem ->
453 41085bd3 Iustin Pop
    let inst' = inst { Instance.mem = mem}
454 41085bd3 Iustin Pop
    in Types.isBad $ Instance.shrinkByType inst' Types.FailMem
455 8fcf251f Iustin Pop
456 8fcf251f Iustin Pop
prop_Instance_shrinkCG inst =
457 8fcf251f Iustin Pop
    Instance.vcpus inst >= 2 * Types.unitCpu ==>
458 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailCPU of
459 8fcf251f Iustin Pop
          Types.Ok inst' ->
460 8fcf251f Iustin Pop
              Instance.vcpus inst' == Instance.vcpus inst - Types.unitCpu
461 8fcf251f Iustin Pop
          _ -> False
462 8fcf251f Iustin Pop
463 8fcf251f Iustin Pop
prop_Instance_shrinkCF inst =
464 41085bd3 Iustin Pop
    forAll (choose (0, 2 * Types.unitCpu - 1)) $ \vcpus ->
465 41085bd3 Iustin Pop
    let inst' = inst { Instance.vcpus = vcpus }
466 41085bd3 Iustin Pop
    in Types.isBad $ Instance.shrinkByType inst' Types.FailCPU
467 8fcf251f Iustin Pop
468 8fcf251f Iustin Pop
prop_Instance_shrinkDG inst =
469 8fcf251f Iustin Pop
    Instance.dsk inst >= 2 * Types.unitDsk ==>
470 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailDisk of
471 8fcf251f Iustin Pop
          Types.Ok inst' ->
472 8fcf251f Iustin Pop
              Instance.dsk inst' == Instance.dsk inst - Types.unitDsk
473 8fcf251f Iustin Pop
          _ -> False
474 8fcf251f Iustin Pop
475 8fcf251f Iustin Pop
prop_Instance_shrinkDF inst =
476 41085bd3 Iustin Pop
    forAll (choose (0, 2 * Types.unitDsk - 1)) $ \dsk ->
477 41085bd3 Iustin Pop
    let inst' = inst { Instance.dsk = dsk }
478 41085bd3 Iustin Pop
    in Types.isBad $ Instance.shrinkByType inst' Types.FailDisk
479 8fcf251f Iustin Pop
480 8fcf251f Iustin Pop
prop_Instance_setMovable inst m =
481 8fcf251f Iustin Pop
    Instance.movable inst' == m
482 4a007641 Iustin Pop
    where inst' = Instance.setMovable inst m
483 8fcf251f Iustin Pop
484 c15f7183 Iustin Pop
testInstance =
485 39d11971 Iustin Pop
    [ run prop_Instance_creat
486 39d11971 Iustin Pop
    , run prop_Instance_setIdx
487 7dd5ee6c Iustin Pop
    , run prop_Instance_setName
488 39d11971 Iustin Pop
    , run prop_Instance_setAlias
489 7dd5ee6c Iustin Pop
    , run prop_Instance_setPri
490 7dd5ee6c Iustin Pop
    , run prop_Instance_setSec
491 7dd5ee6c Iustin Pop
    , run prop_Instance_setBoth
492 1ae7a904 Iustin Pop
    , run prop_Instance_runStatus_True
493 1ae7a904 Iustin Pop
    , run prop_Instance_runStatus_False
494 8fcf251f Iustin Pop
    , run prop_Instance_shrinkMG
495 8fcf251f Iustin Pop
    , run prop_Instance_shrinkMF
496 8fcf251f Iustin Pop
    , run prop_Instance_shrinkCG
497 8fcf251f Iustin Pop
    , run prop_Instance_shrinkCF
498 8fcf251f Iustin Pop
    , run prop_Instance_shrinkDG
499 8fcf251f Iustin Pop
    , run prop_Instance_shrinkDF
500 8fcf251f Iustin Pop
    , run prop_Instance_setMovable
501 1ae7a904 Iustin Pop
    ]
502 1ae7a904 Iustin Pop
503 525bfb36 Iustin Pop
-- ** Text backend tests
504 525bfb36 Iustin Pop
505 1ae7a904 Iustin Pop
-- Instance text loader tests
506 1ae7a904 Iustin Pop
507 a1cd7c1e Iustin Pop
prop_Text_Load_Instance name mem dsk vcpus status
508 a1cd7c1e Iustin Pop
                        (NonEmpty pnode) snode
509 6429e8d8 Iustin Pop
                        (NonNegative pdx) (NonNegative sdx) autobal dt =
510 309e7c9a Iustin Pop
    pnode /= snode && pdx /= sdx ==>
511 1ae7a904 Iustin Pop
    let vcpus_s = show vcpus
512 1ae7a904 Iustin Pop
        dsk_s = show dsk
513 1ae7a904 Iustin Pop
        mem_s = show mem
514 39d11971 Iustin Pop
        ndx = if null snode
515 39d11971 Iustin Pop
              then [(pnode, pdx)]
516 309e7c9a Iustin Pop
              else [(pnode, pdx), (snode, sdx)]
517 99b63608 Iustin Pop
        nl = Data.Map.fromList ndx
518 434c15d5 Iustin Pop
        tags = ""
519 bc782180 Iustin Pop
        sbal = if autobal then "Y" else "N"
520 6429e8d8 Iustin Pop
        sdt = Types.dtToString dt
521 99b63608 Iustin Pop
        inst = Text.loadInst nl
522 bc782180 Iustin Pop
               [name, mem_s, dsk_s, vcpus_s, status,
523 6429e8d8 Iustin Pop
                sbal, pnode, snode, sdt, tags]
524 99b63608 Iustin Pop
        fail1 = Text.loadInst nl
525 bc782180 Iustin Pop
               [name, mem_s, dsk_s, vcpus_s, status,
526 6429e8d8 Iustin Pop
                sbal, pnode, pnode, tags]
527 1ae7a904 Iustin Pop
        _types = ( name::String, mem::Int, dsk::Int
528 1ae7a904 Iustin Pop
                 , vcpus::Int, status::String
529 a1cd7c1e Iustin Pop
                 , snode::String
530 bc782180 Iustin Pop
                 , autobal::Bool)
531 1ae7a904 Iustin Pop
    in
532 1ae7a904 Iustin Pop
      case inst of
533 6429e8d8 Iustin Pop
        Types.Bad msg -> printTestCase ("Failed to load instance: " ++ msg)
534 6429e8d8 Iustin Pop
                         False
535 6429e8d8 Iustin Pop
        Types.Ok (_, i) -> printTestCase ("Mismatch in some field while\
536 6429e8d8 Iustin Pop
                                          \ loading the instance") $
537 cc532bdd Iustin Pop
            Instance.name i == name &&
538 cc532bdd Iustin Pop
            Instance.vcpus i == vcpus &&
539 cc532bdd Iustin Pop
            Instance.mem i == mem &&
540 cc532bdd Iustin Pop
            Instance.pNode i == pdx &&
541 cc532bdd Iustin Pop
            Instance.sNode i == (if null snode
542 cc532bdd Iustin Pop
                                 then Node.noSecondary
543 309e7c9a Iustin Pop
                                 else sdx) &&
544 0e09422b Iustin Pop
            Instance.autoBalance i == autobal &&
545 6429e8d8 Iustin Pop
            Types.isBad fail1
546 39d11971 Iustin Pop
547 39d11971 Iustin Pop
prop_Text_Load_InstanceFail ktn fields =
548 6429e8d8 Iustin Pop
    length fields /= 10 ==>
549 bc782180 Iustin Pop
    case Text.loadInst nl fields of
550 6429e8d8 Iustin Pop
      Types.Ok _ -> printTestCase "Managed to load instance from invalid\
551 6429e8d8 Iustin Pop
                                  \ data" False
552 6429e8d8 Iustin Pop
      Types.Bad msg -> printTestCase ("Unrecognised error message: " ++ msg) $
553 6429e8d8 Iustin Pop
                       "Invalid/incomplete instance data: '" `isPrefixOf` msg
554 99b63608 Iustin Pop
    where nl = Data.Map.fromList ktn
555 39d11971 Iustin Pop
556 39d11971 Iustin Pop
prop_Text_Load_Node name tm nm fm td fd tc fo =
557 39d11971 Iustin Pop
    let conv v = if v < 0
558 39d11971 Iustin Pop
                    then "?"
559 39d11971 Iustin Pop
                    else show v
560 39d11971 Iustin Pop
        tm_s = conv tm
561 39d11971 Iustin Pop
        nm_s = conv nm
562 39d11971 Iustin Pop
        fm_s = conv fm
563 39d11971 Iustin Pop
        td_s = conv td
564 39d11971 Iustin Pop
        fd_s = conv fd
565 39d11971 Iustin Pop
        tc_s = conv tc
566 39d11971 Iustin Pop
        fo_s = if fo
567 39d11971 Iustin Pop
               then "Y"
568 39d11971 Iustin Pop
               else "N"
569 39d11971 Iustin Pop
        any_broken = any (< 0) [tm, nm, fm, td, fd, tc]
570 10ef6b4e Iustin Pop
        gid = Group.uuid defGroup
571 10ef6b4e Iustin Pop
    in case Text.loadNode defGroupAssoc
572 10ef6b4e Iustin Pop
           [name, tm_s, nm_s, fm_s, td_s, fd_s, tc_s, fo_s, gid] of
573 39d11971 Iustin Pop
         Nothing -> False
574 39d11971 Iustin Pop
         Just (name', node) ->
575 39d11971 Iustin Pop
             if fo || any_broken
576 39d11971 Iustin Pop
             then Node.offline node
577 4a007641 Iustin Pop
             else Node.name node == name' && name' == name &&
578 4a007641 Iustin Pop
                  Node.alias node == name &&
579 4a007641 Iustin Pop
                  Node.tMem node == fromIntegral tm &&
580 4a007641 Iustin Pop
                  Node.nMem node == nm &&
581 4a007641 Iustin Pop
                  Node.fMem node == fm &&
582 4a007641 Iustin Pop
                  Node.tDsk node == fromIntegral td &&
583 4a007641 Iustin Pop
                  Node.fDsk node == fd &&
584 4a007641 Iustin Pop
                  Node.tCpu node == fromIntegral tc
585 39d11971 Iustin Pop
586 39d11971 Iustin Pop
prop_Text_Load_NodeFail fields =
587 10ef6b4e Iustin Pop
    length fields /= 8 ==> isNothing $ Text.loadNode Data.Map.empty fields
588 1ae7a904 Iustin Pop
589 50811e2c Iustin Pop
prop_Text_NodeLSIdempotent node =
590 10ef6b4e Iustin Pop
    (Text.loadNode defGroupAssoc.
591 10ef6b4e Iustin Pop
         Utils.sepSplit '|' . Text.serializeNode defGroupList) n ==
592 50811e2c Iustin Pop
    Just (Node.name n, n)
593 50811e2c Iustin Pop
    -- override failN1 to what loadNode returns by default
594 50811e2c Iustin Pop
    where n = node { Node.failN1 = True, Node.offline = False }
595 50811e2c Iustin Pop
596 c15f7183 Iustin Pop
testText =
597 1ae7a904 Iustin Pop
    [ run prop_Text_Load_Instance
598 39d11971 Iustin Pop
    , run prop_Text_Load_InstanceFail
599 39d11971 Iustin Pop
    , run prop_Text_Load_Node
600 39d11971 Iustin Pop
    , run prop_Text_Load_NodeFail
601 50811e2c Iustin Pop
    , run prop_Text_NodeLSIdempotent
602 7dd5ee6c Iustin Pop
    ]
603 7dd5ee6c Iustin Pop
604 525bfb36 Iustin Pop
-- ** Node tests
605 7dd5ee6c Iustin Pop
606 82ea2874 Iustin Pop
prop_Node_setAlias node name =
607 82ea2874 Iustin Pop
    Node.name newnode == Node.name node &&
608 82ea2874 Iustin Pop
    Node.alias newnode == name
609 82ea2874 Iustin Pop
    where _types = (node::Node.Node, name::String)
610 82ea2874 Iustin Pop
          newnode = Node.setAlias node name
611 82ea2874 Iustin Pop
612 82ea2874 Iustin Pop
prop_Node_setOffline node status =
613 82ea2874 Iustin Pop
    Node.offline newnode == status
614 82ea2874 Iustin Pop
    where newnode = Node.setOffline node status
615 82ea2874 Iustin Pop
616 82ea2874 Iustin Pop
prop_Node_setXmem node xm =
617 82ea2874 Iustin Pop
    Node.xMem newnode == xm
618 82ea2874 Iustin Pop
    where newnode = Node.setXmem node xm
619 82ea2874 Iustin Pop
620 82ea2874 Iustin Pop
prop_Node_setMcpu node mc =
621 82ea2874 Iustin Pop
    Node.mCpu newnode == mc
622 82ea2874 Iustin Pop
    where newnode = Node.setMcpu node mc
623 82ea2874 Iustin Pop
624 525bfb36 Iustin Pop
-- | Check that an instance add with too high memory or disk will be
625 525bfb36 Iustin Pop
-- rejected.
626 8fcf251f Iustin Pop
prop_Node_addPriFM node inst = Instance.mem inst >= Node.fMem node &&
627 8fcf251f Iustin Pop
                               not (Node.failN1 node)
628 8fcf251f Iustin Pop
                               ==>
629 8fcf251f Iustin Pop
                               case Node.addPri node inst'' of
630 8fcf251f Iustin Pop
                                 Types.OpFail Types.FailMem -> True
631 8fcf251f Iustin Pop
                                 _ -> False
632 15f4c8ca Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
633 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
634 8fcf251f Iustin Pop
          inst'' = inst' { Instance.mem = Instance.mem inst }
635 8fcf251f Iustin Pop
636 8fcf251f Iustin Pop
prop_Node_addPriFD node inst = Instance.dsk inst >= Node.fDsk node &&
637 8fcf251f Iustin Pop
                               not (Node.failN1 node)
638 8fcf251f Iustin Pop
                               ==>
639 8fcf251f Iustin Pop
                               case Node.addPri node inst'' of
640 8fcf251f Iustin Pop
                                 Types.OpFail Types.FailDisk -> True
641 8fcf251f Iustin Pop
                                 _ -> False
642 8fcf251f Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
643 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
644 8fcf251f Iustin Pop
          inst'' = inst' { Instance.dsk = Instance.dsk inst }
645 8fcf251f Iustin Pop
646 41085bd3 Iustin Pop
prop_Node_addPriFC node inst (Positive extra) =
647 41085bd3 Iustin Pop
    not (Node.failN1 node) ==>
648 41085bd3 Iustin Pop
        case Node.addPri node inst'' of
649 41085bd3 Iustin Pop
          Types.OpFail Types.FailCPU -> True
650 41085bd3 Iustin Pop
          _ -> False
651 8fcf251f Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
652 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
653 41085bd3 Iustin Pop
          inst'' = inst' { Instance.vcpus = Node.availCpu node + extra }
654 7bc82927 Iustin Pop
655 525bfb36 Iustin Pop
-- | Check that an instance add with too high memory or disk will be
656 525bfb36 Iustin Pop
-- rejected.
657 15f4c8ca Iustin Pop
prop_Node_addSec node inst pdx =
658 2060348b Iustin Pop
    (Instance.mem inst >= (Node.fMem node - Node.rMem node) ||
659 2060348b Iustin Pop
     Instance.dsk inst >= Node.fDsk node) &&
660 9f6dcdea Iustin Pop
    not (Node.failN1 node)
661 79a72ce7 Iustin Pop
    ==> isFailure (Node.addSec node inst pdx)
662 15f4c8ca Iustin Pop
        where _types = (node::Node.Node, inst::Instance.Instance, pdx::Int)
663 7dd5ee6c Iustin Pop
664 525bfb36 Iustin Pop
-- | Checks for memory reservation changes.
665 752635d3 Iustin Pop
prop_Node_rMem inst =
666 752635d3 Iustin Pop
    forAll (arbitrary `suchThat` ((> 0) . Node.fMem)) $ \node ->
667 9cbc1edb Iustin Pop
    -- ab = auto_balance, nb = non-auto_balance
668 9cbc1edb Iustin Pop
    -- we use -1 as the primary node of the instance
669 0e09422b Iustin Pop
    let inst' = inst { Instance.pNode = -1, Instance.autoBalance = True }
670 9cbc1edb Iustin Pop
        inst_ab = setInstanceSmallerThanNode node inst'
671 0e09422b Iustin Pop
        inst_nb = inst_ab { Instance.autoBalance = False }
672 9cbc1edb Iustin Pop
        -- now we have the two instances, identical except the
673 0e09422b Iustin Pop
        -- autoBalance attribute
674 9cbc1edb Iustin Pop
        orig_rmem = Node.rMem node
675 9cbc1edb Iustin Pop
        inst_idx = Instance.idx inst_ab
676 9cbc1edb Iustin Pop
        node_add_ab = Node.addSec node inst_ab (-1)
677 9cbc1edb Iustin Pop
        node_add_nb = Node.addSec node inst_nb (-1)
678 9cbc1edb Iustin Pop
        node_del_ab = liftM (flip Node.removeSec inst_ab) node_add_ab
679 9cbc1edb Iustin Pop
        node_del_nb = liftM (flip Node.removeSec inst_nb) node_add_nb
680 9cbc1edb Iustin Pop
    in case (node_add_ab, node_add_nb, node_del_ab, node_del_nb) of
681 9cbc1edb Iustin Pop
         (Types.OpGood a_ab, Types.OpGood a_nb,
682 9cbc1edb Iustin Pop
          Types.OpGood d_ab, Types.OpGood d_nb) ->
683 752635d3 Iustin Pop
             printTestCase "Consistency checks failed" $
684 9cbc1edb Iustin Pop
             Node.rMem a_ab >  orig_rmem &&
685 9cbc1edb Iustin Pop
             Node.rMem a_ab - orig_rmem == Instance.mem inst_ab &&
686 9cbc1edb Iustin Pop
             Node.rMem a_nb == orig_rmem &&
687 9cbc1edb Iustin Pop
             Node.rMem d_ab == orig_rmem &&
688 9cbc1edb Iustin Pop
             Node.rMem d_nb == orig_rmem &&
689 9cbc1edb Iustin Pop
             -- this is not related to rMem, but as good a place to
690 9cbc1edb Iustin Pop
             -- test as any
691 9cbc1edb Iustin Pop
             inst_idx `elem` Node.sList a_ab &&
692 9cbc1edb Iustin Pop
             not (inst_idx `elem` Node.sList d_ab)
693 752635d3 Iustin Pop
         x -> printTestCase ("Failed to add/remove instances: " ++ show x)
694 752635d3 Iustin Pop
              False
695 9cbc1edb Iustin Pop
696 525bfb36 Iustin Pop
-- | Check mdsk setting.
697 8fcf251f Iustin Pop
prop_Node_setMdsk node mx =
698 8fcf251f Iustin Pop
    Node.loDsk node' >= 0 &&
699 8fcf251f Iustin Pop
    fromIntegral (Node.loDsk node') <= Node.tDsk node &&
700 8fcf251f Iustin Pop
    Node.availDisk node' >= 0 &&
701 8fcf251f Iustin Pop
    Node.availDisk node' <= Node.fDsk node' &&
702 82ea2874 Iustin Pop
    fromIntegral (Node.availDisk node') <= Node.tDsk node' &&
703 82ea2874 Iustin Pop
    Node.mDsk node' == mx'
704 8fcf251f Iustin Pop
    where _types = (node::Node.Node, mx::SmallRatio)
705 8fcf251f Iustin Pop
          node' = Node.setMdsk node mx'
706 8fcf251f Iustin Pop
          SmallRatio mx' = mx
707 8fcf251f Iustin Pop
708 8fcf251f Iustin Pop
-- Check tag maps
709 8fcf251f Iustin Pop
prop_Node_tagMaps_idempotent tags =
710 8fcf251f Iustin Pop
    Node.delTags (Node.addTags m tags) tags == m
711 4a007641 Iustin Pop
    where m = Data.Map.empty
712 8fcf251f Iustin Pop
713 8fcf251f Iustin Pop
prop_Node_tagMaps_reject tags =
714 8fcf251f Iustin Pop
    not (null tags) ==>
715 8fcf251f Iustin Pop
    any (\t -> Node.rejectAddTags m [t]) tags
716 4a007641 Iustin Pop
    where m = Node.addTags Data.Map.empty tags
717 8fcf251f Iustin Pop
718 82ea2874 Iustin Pop
prop_Node_showField node =
719 82ea2874 Iustin Pop
  forAll (elements Node.defaultFields) $ \ field ->
720 82ea2874 Iustin Pop
  fst (Node.showHeader field) /= Types.unknownField &&
721 82ea2874 Iustin Pop
  Node.showField node field /= Types.unknownField
722 82ea2874 Iustin Pop
723 d8bcd0a8 Iustin Pop
724 d8bcd0a8 Iustin Pop
prop_Node_computeGroups nodes =
725 d8bcd0a8 Iustin Pop
  let ng = Node.computeGroups nodes
726 d8bcd0a8 Iustin Pop
      onlyuuid = map fst ng
727 d8bcd0a8 Iustin Pop
  in length nodes == sum (map (length . snd) ng) &&
728 d8bcd0a8 Iustin Pop
     all (\(guuid, ns) -> all ((== guuid) . Node.group) ns) ng &&
729 d8bcd0a8 Iustin Pop
     length (nub onlyuuid) == length onlyuuid &&
730 cc532bdd Iustin Pop
     (null nodes || not (null ng))
731 d8bcd0a8 Iustin Pop
732 c15f7183 Iustin Pop
testNode =
733 82ea2874 Iustin Pop
    [ run prop_Node_setAlias
734 82ea2874 Iustin Pop
    , run prop_Node_setOffline
735 82ea2874 Iustin Pop
    , run prop_Node_setMcpu
736 82ea2874 Iustin Pop
    , run prop_Node_setXmem
737 82ea2874 Iustin Pop
    , run prop_Node_addPriFM
738 8fcf251f Iustin Pop
    , run prop_Node_addPriFD
739 8fcf251f Iustin Pop
    , run prop_Node_addPriFC
740 7dd5ee6c Iustin Pop
    , run prop_Node_addSec
741 9cbc1edb Iustin Pop
    , run prop_Node_rMem
742 8fcf251f Iustin Pop
    , run prop_Node_setMdsk
743 8fcf251f Iustin Pop
    , run prop_Node_tagMaps_idempotent
744 8fcf251f Iustin Pop
    , run prop_Node_tagMaps_reject
745 82ea2874 Iustin Pop
    , run prop_Node_showField
746 d8bcd0a8 Iustin Pop
    , run prop_Node_computeGroups
747 7dd5ee6c Iustin Pop
    ]
748 cf35a869 Iustin Pop
749 cf35a869 Iustin Pop
750 525bfb36 Iustin Pop
-- ** Cluster tests
751 cf35a869 Iustin Pop
752 525bfb36 Iustin Pop
-- | Check that the cluster score is close to zero for a homogeneous
753 525bfb36 Iustin Pop
-- cluster.
754 8e4f6d56 Iustin Pop
prop_Score_Zero node =
755 8e4f6d56 Iustin Pop
    forAll (choose (1, 1024)) $ \count ->
756 3a3c1eb4 Iustin Pop
    (not (Node.offline node) && not (Node.failN1 node) && (count > 0) &&
757 2060348b Iustin Pop
     (Node.tDsk node > 0) && (Node.tMem node > 0)) ==>
758 cf35a869 Iustin Pop
    let fn = Node.buildPeers node Container.empty
759 3a3c1eb4 Iustin Pop
        nlst = zip [1..] $ replicate count fn::[(Types.Ndx, Node.Node)]
760 cb0c77ff Iustin Pop
        nl = Container.fromList nlst
761 cf35a869 Iustin Pop
        score = Cluster.compCV nl
762 cf35a869 Iustin Pop
    -- we can't say == 0 here as the floating point errors accumulate;
763 cf35a869 Iustin Pop
    -- this should be much lower than the default score in CLI.hs
764 8e4f6d56 Iustin Pop
    in score <= 1e-12
765 cf35a869 Iustin Pop
766 525bfb36 Iustin Pop
-- | Check that cluster stats are sane.
767 8e4f6d56 Iustin Pop
prop_CStats_sane node =
768 8e4f6d56 Iustin Pop
    forAll (choose (1, 1024)) $ \count ->
769 8e4f6d56 Iustin Pop
    (not (Node.offline node) && not (Node.failN1 node) &&
770 3fea6959 Iustin Pop
     (Node.availDisk node > 0) && (Node.availMem node > 0)) ==>
771 8fcf251f Iustin Pop
    let fn = Node.buildPeers node Container.empty
772 8fcf251f Iustin Pop
        nlst = zip [1..] $ replicate count fn::[(Types.Ndx, Node.Node)]
773 cb0c77ff Iustin Pop
        nl = Container.fromList nlst
774 8fcf251f Iustin Pop
        cstats = Cluster.totalResources nl
775 8fcf251f Iustin Pop
    in Cluster.csAdsk cstats >= 0 &&
776 8fcf251f Iustin Pop
       Cluster.csAdsk cstats <= Cluster.csFdsk cstats
777 8fcf251f Iustin Pop
778 3fea6959 Iustin Pop
-- | Check that one instance is allocated correctly, without
779 525bfb36 Iustin Pop
-- rebalances needed.
780 3fea6959 Iustin Pop
prop_ClusterAlloc_sane node inst =
781 3fea6959 Iustin Pop
    forAll (choose (5, 20)) $ \count ->
782 3fea6959 Iustin Pop
    not (Node.offline node)
783 3fea6959 Iustin Pop
            && not (Node.failN1 node)
784 3fea6959 Iustin Pop
            && Node.availDisk node > 0
785 3fea6959 Iustin Pop
            && Node.availMem node > 0
786 3fea6959 Iustin Pop
            ==>
787 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
788 3fea6959 Iustin Pop
        il = Container.empty
789 3fea6959 Iustin Pop
        inst' = setInstanceSmallerThanNode node inst
790 6d0bc5ca Iustin Pop
    in case Cluster.genAllocNodes defGroupList nl 2 True >>=
791 6d0bc5ca Iustin Pop
       Cluster.tryAlloc nl il inst' of
792 3fea6959 Iustin Pop
         Types.Bad _ -> False
793 85d0ddc3 Iustin Pop
         Types.Ok as ->
794 85d0ddc3 Iustin Pop
             case Cluster.asSolutions as of
795 3fea6959 Iustin Pop
               [] -> False
796 a334d536 Iustin Pop
               (xnl, xi, _, cv):[] ->
797 7d3f4253 Iustin Pop
                   let il' = Container.add (Instance.idx xi) xi il
798 3fea6959 Iustin Pop
                       tbl = Cluster.Table xnl il' cv []
799 e08424a8 Guido Trotter
                   in not (canBalance tbl True True False)
800 3fea6959 Iustin Pop
               _ -> False
801 3fea6959 Iustin Pop
802 3fea6959 Iustin Pop
-- | Checks that on a 2-5 node cluster, we can allocate a random
803 3fea6959 Iustin Pop
-- instance spec via tiered allocation (whatever the original instance
804 525bfb36 Iustin Pop
-- spec), on either one or two nodes.
805 3fea6959 Iustin Pop
prop_ClusterCanTieredAlloc node inst =
806 3fea6959 Iustin Pop
    forAll (choose (2, 5)) $ \count ->
807 3fea6959 Iustin Pop
    forAll (choose (1, 2)) $ \rqnodes ->
808 3fea6959 Iustin Pop
    not (Node.offline node)
809 3fea6959 Iustin Pop
            && not (Node.failN1 node)
810 3fea6959 Iustin Pop
            && isNodeBig node 4
811 3fea6959 Iustin Pop
            ==>
812 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
813 3fea6959 Iustin Pop
        il = Container.empty
814 6d0bc5ca Iustin Pop
        allocnodes = Cluster.genAllocNodes defGroupList nl rqnodes True
815 41b5c85a Iustin Pop
    in case allocnodes >>= \allocnodes' ->
816 41b5c85a Iustin Pop
        Cluster.tieredAlloc nl il inst allocnodes' [] [] of
817 3fea6959 Iustin Pop
         Types.Bad _ -> False
818 d5ccec02 Iustin Pop
         Types.Ok (_, _, il', ixes, cstats) -> not (null ixes) &&
819 d5ccec02 Iustin Pop
                                      IntMap.size il' == length ixes &&
820 d5ccec02 Iustin Pop
                                      length ixes == length cstats
821 3fea6959 Iustin Pop
822 3fea6959 Iustin Pop
-- | Checks that on a 4-8 node cluster, once we allocate an instance,
823 525bfb36 Iustin Pop
-- we can also evacuate it.
824 3fea6959 Iustin Pop
prop_ClusterAllocEvac node inst =
825 3fea6959 Iustin Pop
    forAll (choose (4, 8)) $ \count ->
826 3fea6959 Iustin Pop
    not (Node.offline node)
827 3fea6959 Iustin Pop
            && not (Node.failN1 node)
828 3fea6959 Iustin Pop
            && isNodeBig node 4
829 3fea6959 Iustin Pop
            ==>
830 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
831 3fea6959 Iustin Pop
        il = Container.empty
832 3fea6959 Iustin Pop
        inst' = setInstanceSmallerThanNode node inst
833 6d0bc5ca Iustin Pop
    in case Cluster.genAllocNodes defGroupList nl 2 True >>=
834 6d0bc5ca Iustin Pop
       Cluster.tryAlloc nl il inst' of
835 3fea6959 Iustin Pop
         Types.Bad _ -> False
836 85d0ddc3 Iustin Pop
         Types.Ok as ->
837 85d0ddc3 Iustin Pop
             case Cluster.asSolutions as of
838 3fea6959 Iustin Pop
               [] -> False
839 a334d536 Iustin Pop
               (xnl, xi, _, _):[] ->
840 3fea6959 Iustin Pop
                   let sdx = Instance.sNode xi
841 3fea6959 Iustin Pop
                       il' = Container.add (Instance.idx xi) xi il
842 1bc47d38 Iustin Pop
                   in case Cluster.tryEvac xnl il' [Instance.idx xi] [sdx] of
843 3fea6959 Iustin Pop
                        Just _ -> True
844 3fea6959 Iustin Pop
                        _ -> False
845 3fea6959 Iustin Pop
               _ -> False
846 3fea6959 Iustin Pop
847 3fea6959 Iustin Pop
-- | Check that allocating multiple instances on a cluster, then
848 525bfb36 Iustin Pop
-- adding an empty node, results in a valid rebalance.
849 00c75986 Iustin Pop
prop_ClusterAllocBalance =
850 00c75986 Iustin Pop
    forAll (genNode (Just 5) (Just 128)) $ \node ->
851 3fea6959 Iustin Pop
    forAll (choose (3, 5)) $ \count ->
852 00c75986 Iustin Pop
    not (Node.offline node) && not (Node.failN1 node) ==>
853 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
854 3fea6959 Iustin Pop
        (hnode, nl') = IntMap.deleteFindMax nl
855 3fea6959 Iustin Pop
        il = Container.empty
856 6d0bc5ca Iustin Pop
        allocnodes = Cluster.genAllocNodes defGroupList nl' 2 True
857 3fea6959 Iustin Pop
        i_templ = createInstance Types.unitMem Types.unitDsk Types.unitCpu
858 41b5c85a Iustin Pop
    in case allocnodes >>= \allocnodes' ->
859 41b5c85a Iustin Pop
        Cluster.iterateAlloc nl' il i_templ allocnodes' [] [] of
860 3fea6959 Iustin Pop
         Types.Bad _ -> False
861 d5ccec02 Iustin Pop
         Types.Ok (_, xnl, il', _, _) ->
862 3fea6959 Iustin Pop
                   let ynl = Container.add (Node.idx hnode) hnode xnl
863 3fea6959 Iustin Pop
                       cv = Cluster.compCV ynl
864 3fea6959 Iustin Pop
                       tbl = Cluster.Table ynl il' cv []
865 e08424a8 Guido Trotter
                   in canBalance tbl True True False
866 3fea6959 Iustin Pop
867 525bfb36 Iustin Pop
-- | Checks consistency.
868 32b8d9c0 Iustin Pop
prop_ClusterCheckConsistency node inst =
869 32b8d9c0 Iustin Pop
  let nl = makeSmallCluster node 3
870 32b8d9c0 Iustin Pop
      [node1, node2, node3] = Container.elems nl
871 10ef6b4e Iustin Pop
      node3' = node3 { Node.group = 1 }
872 32b8d9c0 Iustin Pop
      nl' = Container.add (Node.idx node3') node3' nl
873 32b8d9c0 Iustin Pop
      inst1 = Instance.setBoth inst (Node.idx node1) (Node.idx node2)
874 32b8d9c0 Iustin Pop
      inst2 = Instance.setBoth inst (Node.idx node1) Node.noSecondary
875 32b8d9c0 Iustin Pop
      inst3 = Instance.setBoth inst (Node.idx node1) (Node.idx node3)
876 cb0c77ff Iustin Pop
      ccheck = Cluster.findSplitInstances nl' . Container.fromList
877 32b8d9c0 Iustin Pop
  in null (ccheck [(0, inst1)]) &&
878 32b8d9c0 Iustin Pop
     null (ccheck [(0, inst2)]) &&
879 32b8d9c0 Iustin Pop
     (not . null $ ccheck [(0, inst3)])
880 32b8d9c0 Iustin Pop
881 525bfb36 Iustin Pop
-- | For now, we only test that we don't lose instances during the split.
882 f4161783 Iustin Pop
prop_ClusterSplitCluster node inst =
883 f4161783 Iustin Pop
  forAll (choose (0, 100)) $ \icnt ->
884 f4161783 Iustin Pop
  let nl = makeSmallCluster node 2
885 f4161783 Iustin Pop
      (nl', il') = foldl (\(ns, is) _ -> assignInstance ns is inst 0 1)
886 f4161783 Iustin Pop
                   (nl, Container.empty) [1..icnt]
887 f4161783 Iustin Pop
      gni = Cluster.splitCluster nl' il'
888 f4161783 Iustin Pop
  in sum (map (Container.size . snd . snd) gni) == icnt &&
889 f4161783 Iustin Pop
     all (\(guuid, (nl'', _)) -> all ((== guuid) . Node.group)
890 f4161783 Iustin Pop
                                 (Container.elems nl'')) gni
891 32b8d9c0 Iustin Pop
892 c15f7183 Iustin Pop
testCluster =
893 cf35a869 Iustin Pop
    [ run prop_Score_Zero
894 8fcf251f Iustin Pop
    , run prop_CStats_sane
895 3fea6959 Iustin Pop
    , run prop_ClusterAlloc_sane
896 3fea6959 Iustin Pop
    , run prop_ClusterCanTieredAlloc
897 3fea6959 Iustin Pop
    , run prop_ClusterAllocEvac
898 3fea6959 Iustin Pop
    , run prop_ClusterAllocBalance
899 32b8d9c0 Iustin Pop
    , run prop_ClusterCheckConsistency
900 f4161783 Iustin Pop
    , run prop_ClusterSplitCluster
901 cf35a869 Iustin Pop
    ]
902 88f25dd0 Iustin Pop
903 525bfb36 Iustin Pop
-- ** OpCodes tests
904 88f25dd0 Iustin Pop
905 525bfb36 Iustin Pop
-- | Check that opcode serialization is idempotent.
906 88f25dd0 Iustin Pop
prop_OpCodes_serialization op =
907 88f25dd0 Iustin Pop
  case J.readJSON (J.showJSON op) of
908 88f25dd0 Iustin Pop
    J.Error _ -> False
909 88f25dd0 Iustin Pop
    J.Ok op' -> op == op'
910 4a007641 Iustin Pop
  where _types = op::OpCodes.OpCode
911 88f25dd0 Iustin Pop
912 88f25dd0 Iustin Pop
testOpCodes =
913 88f25dd0 Iustin Pop
  [ run prop_OpCodes_serialization
914 88f25dd0 Iustin Pop
  ]
915 c088674b Iustin Pop
916 525bfb36 Iustin Pop
-- ** Jobs tests
917 525bfb36 Iustin Pop
918 525bfb36 Iustin Pop
-- | Check that (queued) job\/opcode status serialization is idempotent.
919 db079755 Iustin Pop
prop_OpStatus_serialization os =
920 db079755 Iustin Pop
  case J.readJSON (J.showJSON os) of
921 db079755 Iustin Pop
    J.Error _ -> False
922 db079755 Iustin Pop
    J.Ok os' -> os == os'
923 db079755 Iustin Pop
  where _types = os::Jobs.OpStatus
924 db079755 Iustin Pop
925 db079755 Iustin Pop
prop_JobStatus_serialization js =
926 db079755 Iustin Pop
  case J.readJSON (J.showJSON js) of
927 db079755 Iustin Pop
    J.Error _ -> False
928 db079755 Iustin Pop
    J.Ok js' -> js == js'
929 db079755 Iustin Pop
  where _types = js::Jobs.JobStatus
930 db079755 Iustin Pop
931 db079755 Iustin Pop
testJobs =
932 db079755 Iustin Pop
  [ run prop_OpStatus_serialization
933 db079755 Iustin Pop
  , run prop_JobStatus_serialization
934 db079755 Iustin Pop
  ]
935 db079755 Iustin Pop
936 525bfb36 Iustin Pop
-- ** Loader tests
937 c088674b Iustin Pop
938 c088674b Iustin Pop
prop_Loader_lookupNode ktn inst node =
939 99b63608 Iustin Pop
  Loader.lookupNode nl inst node == Data.Map.lookup node nl
940 99b63608 Iustin Pop
  where nl = Data.Map.fromList ktn
941 c088674b Iustin Pop
942 c088674b Iustin Pop
prop_Loader_lookupInstance kti inst =
943 99b63608 Iustin Pop
  Loader.lookupInstance il inst == Data.Map.lookup inst il
944 99b63608 Iustin Pop
  where il = Data.Map.fromList kti
945 99b63608 Iustin Pop
946 99b63608 Iustin Pop
prop_Loader_assignIndices nodes =
947 99b63608 Iustin Pop
  Data.Map.size nassoc == length nodes &&
948 99b63608 Iustin Pop
  Container.size kt == length nodes &&
949 99b63608 Iustin Pop
  (if not (null nodes)
950 99b63608 Iustin Pop
   then maximum (IntMap.keys kt) == length nodes - 1
951 c088674b Iustin Pop
   else True)
952 99b63608 Iustin Pop
  where (nassoc, kt) = Loader.assignIndices (map (\n -> (Node.name n, n)) nodes)
953 c088674b Iustin Pop
954 c088674b Iustin Pop
-- | Checks that the number of primary instances recorded on the nodes
955 525bfb36 Iustin Pop
-- is zero.
956 c088674b Iustin Pop
prop_Loader_mergeData ns =
957 cb0c77ff Iustin Pop
  let na = Container.fromList $ map (\n -> (Node.idx n, n)) ns
958 2d1708e0 Guido Trotter
  in case Loader.mergeData [] [] [] []
959 f4f6eb0b Iustin Pop
         (Loader.emptyCluster {Loader.cdNodes = na}) of
960 c088674b Iustin Pop
    Types.Bad _ -> False
961 017a0c3d Iustin Pop
    Types.Ok (Loader.ClusterData _ nl il _) ->
962 c088674b Iustin Pop
      let nodes = Container.elems nl
963 c088674b Iustin Pop
          instances = Container.elems il
964 c088674b Iustin Pop
      in (sum . map (length . Node.pList)) nodes == 0 &&
965 4a007641 Iustin Pop
         null instances
966 c088674b Iustin Pop
967 c088674b Iustin Pop
testLoader =
968 c088674b Iustin Pop
  [ run prop_Loader_lookupNode
969 c088674b Iustin Pop
  , run prop_Loader_lookupInstance
970 c088674b Iustin Pop
  , run prop_Loader_assignIndices
971 c088674b Iustin Pop
  , run prop_Loader_mergeData
972 c088674b Iustin Pop
  ]
973 3c002a13 Iustin Pop
974 3c002a13 Iustin Pop
-- ** Types tests
975 3c002a13 Iustin Pop
976 3c002a13 Iustin Pop
prop_AllocPolicy_serialisation apol =
977 3c002a13 Iustin Pop
    case Types.apolFromString (Types.apolToString apol) of
978 3c002a13 Iustin Pop
      Types.Ok p -> printTestCase ("invalid deserialisation " ++ show p) $
979 3c002a13 Iustin Pop
                    p == apol
980 3c002a13 Iustin Pop
      Types.Bad s -> printTestCase ("failed to deserialise: " ++ s) False
981 3c002a13 Iustin Pop
982 3c002a13 Iustin Pop
prop_DiskTemplate_serialisation dt =
983 3c002a13 Iustin Pop
    case Types.dtFromString (Types.dtToString dt) of
984 3c002a13 Iustin Pop
      Types.Ok p -> printTestCase ("invalid deserialisation " ++ show p) $
985 3c002a13 Iustin Pop
                    p == dt
986 3c002a13 Iustin Pop
      Types.Bad s -> printTestCase ("failed to deserialise: " ++ s) False
987 3c002a13 Iustin Pop
988 3c002a13 Iustin Pop
testTypes =
989 3c002a13 Iustin Pop
    [ run prop_AllocPolicy_serialisation
990 3c002a13 Iustin Pop
    , run prop_DiskTemplate_serialisation
991 3c002a13 Iustin Pop
    ]