Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / QC.hs @ c478f837

History | View | Annotate | Download (30 kB)

1 9b1e1cc9 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 7dd5ee6c Iustin Pop
    ) where
38 15f4c8ca Iustin Pop
39 15f4c8ca Iustin Pop
import Test.QuickCheck
40 7dd5ee6c Iustin Pop
import Test.QuickCheck.Batch
41 d8bcd0a8 Iustin Pop
import Data.List (findIndex, intercalate, nub)
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 15f4c8ca Iustin Pop
68 3fea6959 Iustin Pop
-- * Constants
69 3fea6959 Iustin Pop
70 8fcf251f Iustin Pop
-- | Maximum memory (1TiB, somewhat random value)
71 8fcf251f Iustin Pop
maxMem :: Int
72 8fcf251f Iustin Pop
maxMem = 1024 * 1024
73 8fcf251f Iustin Pop
74 49f9627a Iustin Pop
-- | Maximum disk (8TiB, somewhat random value)
75 8fcf251f Iustin Pop
maxDsk :: Int
76 49f9627a Iustin Pop
maxDsk = 1024 * 1024 * 8
77 8fcf251f Iustin Pop
78 8fcf251f Iustin Pop
-- | Max CPUs (1024, somewhat random value)
79 8fcf251f Iustin Pop
maxCpu :: Int
80 8fcf251f Iustin Pop
maxCpu = 1024
81 8fcf251f Iustin Pop
82 10ef6b4e Iustin Pop
defGroup :: Group.Group
83 10ef6b4e Iustin Pop
defGroup = flip Group.setIdx 0 $
84 10ef6b4e Iustin Pop
               Group.create "default" Utils.defaultGroupID
85 10ef6b4e Iustin Pop
                    Types.AllocPreferred
86 10ef6b4e Iustin Pop
87 10ef6b4e Iustin Pop
defGroupList :: Group.List
88 cb0c77ff Iustin Pop
defGroupList = Container.fromList [(Group.idx defGroup, defGroup)]
89 10ef6b4e Iustin Pop
90 10ef6b4e Iustin Pop
defGroupAssoc :: Data.Map.Map String Types.Gdx
91 10ef6b4e Iustin Pop
defGroupAssoc = Data.Map.singleton (Group.uuid defGroup) (Group.idx defGroup)
92 10ef6b4e Iustin Pop
93 3fea6959 Iustin Pop
-- * Helper functions
94 3fea6959 Iustin Pop
95 79a72ce7 Iustin Pop
-- | Simple checker for whether OpResult is fail or pass
96 79a72ce7 Iustin Pop
isFailure :: Types.OpResult a -> Bool
97 79a72ce7 Iustin Pop
isFailure (Types.OpFail _) = True
98 79a72ce7 Iustin Pop
isFailure _ = False
99 79a72ce7 Iustin Pop
100 3fea6959 Iustin Pop
-- | Update an instance to be smaller than a node
101 3fea6959 Iustin Pop
setInstanceSmallerThanNode node inst =
102 4a007641 Iustin Pop
    inst { Instance.mem = Node.availMem node `div` 2
103 4a007641 Iustin Pop
         , Instance.dsk = Node.availDisk node `div` 2
104 4a007641 Iustin Pop
         , Instance.vcpus = Node.availCpu node `div` 2
105 3fea6959 Iustin Pop
         }
106 3fea6959 Iustin Pop
107 3fea6959 Iustin Pop
-- | Create an instance given its spec
108 3fea6959 Iustin Pop
createInstance mem dsk vcpus =
109 3fea6959 Iustin Pop
    Instance.create "inst-unnamed" mem dsk vcpus "running" [] (-1) (-1)
110 3fea6959 Iustin Pop
111 3fea6959 Iustin Pop
-- | Create a small cluster by repeating a node spec
112 3fea6959 Iustin Pop
makeSmallCluster :: Node.Node -> Int -> Node.List
113 3fea6959 Iustin Pop
makeSmallCluster node count =
114 3fea6959 Iustin Pop
    let fn = Node.buildPeers node Container.empty
115 3fea6959 Iustin Pop
        namelst = map (\n -> (Node.name n, n)) (replicate count fn)
116 3fea6959 Iustin Pop
        (_, nlst) = Loader.assignIndices namelst
117 99b63608 Iustin Pop
    in nlst
118 3fea6959 Iustin Pop
119 3fea6959 Iustin Pop
-- | Checks if a node is "big" enough
120 3fea6959 Iustin Pop
isNodeBig :: Node.Node -> Int -> Bool
121 3fea6959 Iustin Pop
isNodeBig node size = Node.availDisk node > size * Types.unitDsk
122 3fea6959 Iustin Pop
                      && Node.availMem node > size * Types.unitMem
123 3fea6959 Iustin Pop
                      && Node.availCpu node > size * Types.unitCpu
124 3fea6959 Iustin Pop
125 3fea6959 Iustin Pop
canBalance :: Cluster.Table -> Bool -> Bool -> Bool
126 848b65c9 Iustin Pop
canBalance tbl dm evac = isJust $ Cluster.tryBalance tbl dm evac 0 0
127 3fea6959 Iustin Pop
128 f4161783 Iustin Pop
-- | Assigns a new fresh instance to a cluster; this is not
129 f4161783 Iustin Pop
-- allocation, so no resource checks are done
130 f4161783 Iustin Pop
assignInstance :: Node.List -> Instance.List -> Instance.Instance ->
131 f4161783 Iustin Pop
                  Types.Idx -> Types.Idx ->
132 f4161783 Iustin Pop
                  (Node.List, Instance.List)
133 f4161783 Iustin Pop
assignInstance nl il inst pdx sdx =
134 f4161783 Iustin Pop
  let pnode = Container.find pdx nl
135 f4161783 Iustin Pop
      snode = Container.find sdx nl
136 f4161783 Iustin Pop
      maxiidx = if Container.null il
137 f4161783 Iustin Pop
                then 0
138 f4161783 Iustin Pop
                else fst (Container.findMax il) + 1
139 f4161783 Iustin Pop
      inst' = inst { Instance.idx = maxiidx,
140 f4161783 Iustin Pop
                     Instance.pNode = pdx, Instance.sNode = sdx }
141 f4161783 Iustin Pop
      pnode' = Node.setPri pnode inst'
142 f4161783 Iustin Pop
      snode' = Node.setSec snode inst'
143 f4161783 Iustin Pop
      nl' = Container.addTwo pdx pnode' sdx snode' nl
144 f4161783 Iustin Pop
      il' = Container.add maxiidx inst' il
145 f4161783 Iustin Pop
  in (nl', il')
146 f4161783 Iustin Pop
147 3fea6959 Iustin Pop
-- * Arbitrary instances
148 3fea6959 Iustin Pop
149 15f4c8ca Iustin Pop
-- copied from the introduction to quickcheck
150 15f4c8ca Iustin Pop
instance Arbitrary Char where
151 095d7ac0 Iustin Pop
    arbitrary = choose ('\32', '\128')
152 15f4c8ca Iustin Pop
153 a070c426 Iustin Pop
newtype DNSChar = DNSChar { dnsGetChar::Char }
154 a070c426 Iustin Pop
instance Arbitrary DNSChar where
155 a070c426 Iustin Pop
    arbitrary = do
156 a070c426 Iustin Pop
      x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
157 a070c426 Iustin Pop
      return (DNSChar x)
158 a070c426 Iustin Pop
159 a070c426 Iustin Pop
getName :: Gen String
160 a070c426 Iustin Pop
getName = do
161 a070c426 Iustin Pop
  n <- choose (1, 64)
162 a070c426 Iustin Pop
  dn <- vector n::Gen [DNSChar]
163 a070c426 Iustin Pop
  return (map dnsGetChar dn)
164 a070c426 Iustin Pop
165 a070c426 Iustin Pop
166 a070c426 Iustin Pop
getFQDN :: Gen String
167 a070c426 Iustin Pop
getFQDN = do
168 a070c426 Iustin Pop
  felem <- getName
169 a070c426 Iustin Pop
  ncomps <- choose (1, 4)
170 a070c426 Iustin Pop
  frest <- vector ncomps::Gen [[DNSChar]]
171 a070c426 Iustin Pop
  let frest' = map (map dnsGetChar) frest
172 a070c426 Iustin Pop
  return (felem ++ "." ++ intercalate "." frest')
173 a070c426 Iustin Pop
174 15f4c8ca Iustin Pop
-- let's generate a random instance
175 15f4c8ca Iustin Pop
instance Arbitrary Instance.Instance where
176 15f4c8ca Iustin Pop
    arbitrary = do
177 a070c426 Iustin Pop
      name <- getFQDN
178 8fcf251f Iustin Pop
      mem <- choose (0, maxMem)
179 8fcf251f Iustin Pop
      dsk <- choose (0, maxDsk)
180 1ae7a904 Iustin Pop
      run_st <- elements ["ERROR_up", "ERROR_down", "ADMIN_down"
181 1ae7a904 Iustin Pop
                         , "ERROR_nodedown", "ERROR_nodeoffline"
182 1ae7a904 Iustin Pop
                         , "running"
183 1ae7a904 Iustin Pop
                         , "no_such_status1", "no_such_status2"]
184 15f4c8ca Iustin Pop
      pn <- arbitrary
185 15f4c8ca Iustin Pop
      sn <- arbitrary
186 8fcf251f Iustin Pop
      vcpus <- choose (0, maxCpu)
187 434c15d5 Iustin Pop
      return $ Instance.create name mem dsk vcpus run_st [] pn sn
188 15f4c8ca Iustin Pop
189 15f4c8ca Iustin Pop
-- and a random node
190 15f4c8ca Iustin Pop
instance Arbitrary Node.Node where
191 15f4c8ca Iustin Pop
    arbitrary = do
192 a070c426 Iustin Pop
      name <- getFQDN
193 8fcf251f Iustin Pop
      mem_t <- choose (0, maxMem)
194 15f4c8ca Iustin Pop
      mem_f <- choose (0, mem_t)
195 15f4c8ca Iustin Pop
      mem_n <- choose (0, mem_t - mem_f)
196 8fcf251f Iustin Pop
      dsk_t <- choose (0, maxDsk)
197 15f4c8ca Iustin Pop
      dsk_f <- choose (0, dsk_t)
198 8fcf251f Iustin Pop
      cpu_t <- choose (0, maxCpu)
199 15f4c8ca Iustin Pop
      offl <- arbitrary
200 15f4c8ca Iustin Pop
      let n = Node.create name (fromIntegral mem_t) mem_n mem_f
201 8fcf251f Iustin Pop
              (fromIntegral dsk_t) dsk_f (fromIntegral cpu_t) offl
202 10ef6b4e Iustin Pop
              0
203 9cf4267a Iustin Pop
          n' = Node.buildPeers n Container.empty
204 15f4c8ca Iustin Pop
      return n'
205 15f4c8ca Iustin Pop
206 88f25dd0 Iustin Pop
-- replace disks
207 88f25dd0 Iustin Pop
instance Arbitrary OpCodes.ReplaceDisksMode where
208 88f25dd0 Iustin Pop
  arbitrary = elements [ OpCodes.ReplaceOnPrimary
209 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceOnSecondary
210 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceNewSecondary
211 88f25dd0 Iustin Pop
                       , OpCodes.ReplaceAuto
212 88f25dd0 Iustin Pop
                       ]
213 88f25dd0 Iustin Pop
214 88f25dd0 Iustin Pop
instance Arbitrary OpCodes.OpCode where
215 88f25dd0 Iustin Pop
  arbitrary = do
216 88f25dd0 Iustin Pop
    op_id <- elements [ "OP_TEST_DELAY"
217 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_REPLACE_DISKS"
218 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_FAILOVER"
219 88f25dd0 Iustin Pop
                      , "OP_INSTANCE_MIGRATE"
220 88f25dd0 Iustin Pop
                      ]
221 88f25dd0 Iustin Pop
    (case op_id of
222 88f25dd0 Iustin Pop
        "OP_TEST_DELAY" ->
223 88f25dd0 Iustin Pop
          liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary
224 88f25dd0 Iustin Pop
        "OP_INSTANCE_REPLACE_DISKS" ->
225 88f25dd0 Iustin Pop
          liftM5 OpCodes.OpReplaceDisks arbitrary arbitrary
226 88f25dd0 Iustin Pop
          arbitrary arbitrary arbitrary
227 88f25dd0 Iustin Pop
        "OP_INSTANCE_FAILOVER" ->
228 88f25dd0 Iustin Pop
          liftM2 OpCodes.OpFailoverInstance arbitrary arbitrary
229 88f25dd0 Iustin Pop
        "OP_INSTANCE_MIGRATE" ->
230 88f25dd0 Iustin Pop
          liftM3 OpCodes.OpMigrateInstance arbitrary arbitrary arbitrary
231 88f25dd0 Iustin Pop
        _ -> fail "Wrong opcode")
232 88f25dd0 Iustin Pop
233 db079755 Iustin Pop
instance Arbitrary Jobs.OpStatus where
234 db079755 Iustin Pop
  arbitrary = elements [minBound..maxBound]
235 db079755 Iustin Pop
236 db079755 Iustin Pop
instance Arbitrary Jobs.JobStatus where
237 db079755 Iustin Pop
  arbitrary = elements [minBound..maxBound]
238 db079755 Iustin Pop
239 3fea6959 Iustin Pop
-- * Actual tests
240 8fcf251f Iustin Pop
241 691dcd2a Iustin Pop
-- If the list is not just an empty element, and if the elements do
242 691dcd2a Iustin Pop
-- not contain commas, then join+split should be idepotent
243 691dcd2a Iustin Pop
prop_Utils_commaJoinSplit lst = lst /= [""] &&
244 691dcd2a Iustin Pop
                                all (not . elem ',') lst ==>
245 691dcd2a Iustin Pop
                                Utils.sepSplit ',' (Utils.commaJoin lst) == lst
246 691dcd2a Iustin Pop
-- Split and join should always be idempotent
247 691dcd2a Iustin Pop
prop_Utils_commaSplitJoin s = Utils.commaJoin (Utils.sepSplit ',' s) == s
248 691dcd2a Iustin Pop
249 691dcd2a Iustin Pop
testUtils =
250 691dcd2a Iustin Pop
  [ run prop_Utils_commaJoinSplit
251 691dcd2a Iustin Pop
  , run prop_Utils_commaSplitJoin
252 691dcd2a Iustin Pop
  ]
253 691dcd2a Iustin Pop
254 15f4c8ca Iustin Pop
-- | Make sure add is idempotent
255 fbb95f28 Iustin Pop
prop_PeerMap_addIdempotent pmap key em =
256 15f4c8ca Iustin Pop
    fn puniq == fn (fn puniq)
257 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap,
258 fbb95f28 Iustin Pop
                    key::PeerMap.Key, em::PeerMap.Elem)
259 fbb95f28 Iustin Pop
          fn = PeerMap.add key em
260 7bc82927 Iustin Pop
          puniq = PeerMap.accumArray const pmap
261 15f4c8ca Iustin Pop
262 15f4c8ca Iustin Pop
-- | Make sure remove is idempotent
263 15f4c8ca Iustin Pop
prop_PeerMap_removeIdempotent pmap key =
264 15f4c8ca Iustin Pop
    fn puniq == fn (fn puniq)
265 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
266 7bc82927 Iustin Pop
          fn = PeerMap.remove key
267 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
268 15f4c8ca Iustin Pop
269 15f4c8ca Iustin Pop
-- | Make sure a missing item returns 0
270 15f4c8ca Iustin Pop
prop_PeerMap_findMissing pmap key =
271 15f4c8ca Iustin Pop
    PeerMap.find key (PeerMap.remove key puniq) == 0
272 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
273 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
274 15f4c8ca Iustin Pop
275 15f4c8ca Iustin Pop
-- | Make sure an added item is found
276 fbb95f28 Iustin Pop
prop_PeerMap_addFind pmap key em =
277 fbb95f28 Iustin Pop
    PeerMap.find key (PeerMap.add key em puniq) == em
278 7bc82927 Iustin Pop
    where _types = (pmap::PeerMap.PeerMap,
279 fbb95f28 Iustin Pop
                    key::PeerMap.Key, em::PeerMap.Elem)
280 7bc82927 Iustin Pop
          puniq = PeerMap.accumArray const pmap
281 15f4c8ca Iustin Pop
282 15f4c8ca Iustin Pop
-- | Manual check that maxElem returns the maximum indeed, or 0 for null
283 15f4c8ca Iustin Pop
prop_PeerMap_maxElem pmap =
284 15f4c8ca Iustin Pop
    PeerMap.maxElem puniq == if null puniq then 0
285 15f4c8ca Iustin Pop
                             else (maximum . snd . unzip) puniq
286 7bc82927 Iustin Pop
    where _types = pmap::PeerMap.PeerMap
287 15f4c8ca Iustin Pop
          puniq = PeerMap.accumArray const pmap
288 15f4c8ca Iustin Pop
289 c15f7183 Iustin Pop
testPeerMap =
290 7dd5ee6c Iustin Pop
    [ run prop_PeerMap_addIdempotent
291 7dd5ee6c Iustin Pop
    , run prop_PeerMap_removeIdempotent
292 7dd5ee6c Iustin Pop
    , run prop_PeerMap_maxElem
293 7dd5ee6c Iustin Pop
    , run prop_PeerMap_addFind
294 7dd5ee6c Iustin Pop
    , run prop_PeerMap_findMissing
295 7dd5ee6c Iustin Pop
    ]
296 7dd5ee6c Iustin Pop
297 095d7ac0 Iustin Pop
-- Container tests
298 095d7ac0 Iustin Pop
299 095d7ac0 Iustin Pop
prop_Container_addTwo cdata i1 i2 =
300 095d7ac0 Iustin Pop
    fn i1 i2 cont == fn i2 i1 cont &&
301 095d7ac0 Iustin Pop
       fn i1 i2 cont == fn i1 i2 (fn i1 i2 cont)
302 095d7ac0 Iustin Pop
    where _types = (cdata::[Int],
303 095d7ac0 Iustin Pop
                    i1::Int, i2::Int)
304 095d7ac0 Iustin Pop
          cont = foldl (\c x -> Container.add x x c) Container.empty cdata
305 095d7ac0 Iustin Pop
          fn x1 x2 = Container.addTwo x1 x1 x2 x2
306 095d7ac0 Iustin Pop
307 5ef78537 Iustin Pop
prop_Container_nameOf node =
308 5ef78537 Iustin Pop
  let nl = makeSmallCluster node 1
309 5ef78537 Iustin Pop
      fnode = head (Container.elems nl)
310 5ef78537 Iustin Pop
  in Container.nameOf nl (Node.idx fnode) == Node.name fnode
311 5ef78537 Iustin Pop
312 5ef78537 Iustin Pop
-- We test that in a cluster, given a random node, we can find it by
313 5ef78537 Iustin Pop
-- its name and alias, as long as all names and aliases are unique,
314 5ef78537 Iustin Pop
-- and that we fail to find a non-existing name
315 5ef78537 Iustin Pop
prop_Container_findByName node othername =
316 5ef78537 Iustin Pop
  forAll (choose (1, 20)) $ \ cnt ->
317 5ef78537 Iustin Pop
  forAll (choose (0, cnt - 1)) $ \ fidx ->
318 5ef78537 Iustin Pop
  forAll (vector cnt) $ \ names ->
319 5ef78537 Iustin Pop
  (length . nub) (map fst names ++ map snd names) ==
320 5ef78537 Iustin Pop
  length names * 2 &&
321 5ef78537 Iustin Pop
  not (othername `elem` (map fst names ++ map snd names)) ==>
322 5ef78537 Iustin Pop
  let nl = makeSmallCluster node cnt
323 5ef78537 Iustin Pop
      nodes = Container.elems nl
324 5ef78537 Iustin Pop
      nodes' = map (\((name, alias), nn) -> (Node.idx nn,
325 5ef78537 Iustin Pop
                                             nn { Node.name = name,
326 5ef78537 Iustin Pop
                                                  Node.alias = alias }))
327 5ef78537 Iustin Pop
               $ zip names nodes
328 cb0c77ff Iustin Pop
      nl' = Container.fromList nodes'
329 5ef78537 Iustin Pop
      target = snd (nodes' !! fidx)
330 5ef78537 Iustin Pop
  in Container.findByName nl' (Node.name target) == Just target &&
331 5ef78537 Iustin Pop
     Container.findByName nl' (Node.alias target) == Just target &&
332 5ef78537 Iustin Pop
     Container.findByName nl' othername == Nothing
333 5ef78537 Iustin Pop
334 c15f7183 Iustin Pop
testContainer =
335 5ef78537 Iustin Pop
    [ run prop_Container_addTwo
336 5ef78537 Iustin Pop
    , run prop_Container_nameOf
337 5ef78537 Iustin Pop
    , run prop_Container_findByName
338 5ef78537 Iustin Pop
    ]
339 095d7ac0 Iustin Pop
340 7bc82927 Iustin Pop
-- Simple instance tests, we only have setter/getters
341 7bc82927 Iustin Pop
342 39d11971 Iustin Pop
prop_Instance_creat inst =
343 39d11971 Iustin Pop
    Instance.name inst == Instance.alias inst
344 39d11971 Iustin Pop
345 7bc82927 Iustin Pop
prop_Instance_setIdx inst idx =
346 7bc82927 Iustin Pop
    Instance.idx (Instance.setIdx inst idx) == idx
347 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, idx::Types.Idx)
348 7bc82927 Iustin Pop
349 7bc82927 Iustin Pop
prop_Instance_setName inst name =
350 39d11971 Iustin Pop
    Instance.name newinst == name &&
351 39d11971 Iustin Pop
    Instance.alias newinst == name
352 39d11971 Iustin Pop
    where _types = (inst::Instance.Instance, name::String)
353 39d11971 Iustin Pop
          newinst = Instance.setName inst name
354 39d11971 Iustin Pop
355 39d11971 Iustin Pop
prop_Instance_setAlias inst name =
356 39d11971 Iustin Pop
    Instance.name newinst == Instance.name inst &&
357 39d11971 Iustin Pop
    Instance.alias newinst == name
358 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, name::String)
359 39d11971 Iustin Pop
          newinst = Instance.setAlias inst name
360 7bc82927 Iustin Pop
361 7bc82927 Iustin Pop
prop_Instance_setPri inst pdx =
362 2060348b Iustin Pop
    Instance.pNode (Instance.setPri inst pdx) == pdx
363 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, pdx::Types.Ndx)
364 7bc82927 Iustin Pop
365 7bc82927 Iustin Pop
prop_Instance_setSec inst sdx =
366 2060348b Iustin Pop
    Instance.sNode (Instance.setSec inst sdx) == sdx
367 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, sdx::Types.Ndx)
368 7bc82927 Iustin Pop
369 7bc82927 Iustin Pop
prop_Instance_setBoth inst pdx sdx =
370 2060348b Iustin Pop
    Instance.pNode si == pdx && Instance.sNode si == sdx
371 7bc82927 Iustin Pop
    where _types = (inst::Instance.Instance, pdx::Types.Ndx, sdx::Types.Ndx)
372 7bc82927 Iustin Pop
          si = Instance.setBoth inst pdx sdx
373 7bc82927 Iustin Pop
374 1ae7a904 Iustin Pop
prop_Instance_runStatus_True inst =
375 1ae7a904 Iustin Pop
    let run_st = Instance.running inst
376 2060348b Iustin Pop
        run_tx = Instance.runSt inst
377 1ae7a904 Iustin Pop
    in
378 a46f34d7 Iustin Pop
      run_tx `elem` Instance.runningStates ==> run_st
379 1ae7a904 Iustin Pop
380 1ae7a904 Iustin Pop
prop_Instance_runStatus_False inst =
381 1ae7a904 Iustin Pop
    let run_st = Instance.running inst
382 2060348b Iustin Pop
        run_tx = Instance.runSt inst
383 1ae7a904 Iustin Pop
    in
384 a46f34d7 Iustin Pop
      run_tx `notElem` Instance.runningStates ==> not run_st
385 1ae7a904 Iustin Pop
386 8fcf251f Iustin Pop
prop_Instance_shrinkMG inst =
387 8fcf251f Iustin Pop
    Instance.mem inst >= 2 * Types.unitMem ==>
388 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailMem of
389 8fcf251f Iustin Pop
          Types.Ok inst' ->
390 8fcf251f Iustin Pop
              Instance.mem inst' == Instance.mem inst - Types.unitMem
391 8fcf251f Iustin Pop
          _ -> False
392 8fcf251f Iustin Pop
393 8fcf251f Iustin Pop
prop_Instance_shrinkMF inst =
394 8fcf251f Iustin Pop
    Instance.mem inst < 2 * Types.unitMem ==>
395 06fb841e Iustin Pop
        Types.isBad $ Instance.shrinkByType inst Types.FailMem
396 8fcf251f Iustin Pop
397 8fcf251f Iustin Pop
prop_Instance_shrinkCG inst =
398 8fcf251f Iustin Pop
    Instance.vcpus inst >= 2 * Types.unitCpu ==>
399 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailCPU of
400 8fcf251f Iustin Pop
          Types.Ok inst' ->
401 8fcf251f Iustin Pop
              Instance.vcpus inst' == Instance.vcpus inst - Types.unitCpu
402 8fcf251f Iustin Pop
          _ -> False
403 8fcf251f Iustin Pop
404 8fcf251f Iustin Pop
prop_Instance_shrinkCF inst =
405 8fcf251f Iustin Pop
    Instance.vcpus inst < 2 * Types.unitCpu ==>
406 06fb841e Iustin Pop
        Types.isBad $ Instance.shrinkByType inst Types.FailCPU
407 8fcf251f Iustin Pop
408 8fcf251f Iustin Pop
prop_Instance_shrinkDG inst =
409 8fcf251f Iustin Pop
    Instance.dsk inst >= 2 * Types.unitDsk ==>
410 8fcf251f Iustin Pop
        case Instance.shrinkByType inst Types.FailDisk of
411 8fcf251f Iustin Pop
          Types.Ok inst' ->
412 8fcf251f Iustin Pop
              Instance.dsk inst' == Instance.dsk inst - Types.unitDsk
413 8fcf251f Iustin Pop
          _ -> False
414 8fcf251f Iustin Pop
415 8fcf251f Iustin Pop
prop_Instance_shrinkDF inst =
416 8fcf251f Iustin Pop
    Instance.dsk inst < 2 * Types.unitDsk ==>
417 06fb841e Iustin Pop
        Types.isBad $ Instance.shrinkByType inst Types.FailDisk
418 8fcf251f Iustin Pop
419 8fcf251f Iustin Pop
prop_Instance_setMovable inst m =
420 8fcf251f Iustin Pop
    Instance.movable inst' == m
421 4a007641 Iustin Pop
    where inst' = Instance.setMovable inst m
422 8fcf251f Iustin Pop
423 c15f7183 Iustin Pop
testInstance =
424 39d11971 Iustin Pop
    [ run prop_Instance_creat
425 39d11971 Iustin Pop
    , run prop_Instance_setIdx
426 7dd5ee6c Iustin Pop
    , run prop_Instance_setName
427 39d11971 Iustin Pop
    , run prop_Instance_setAlias
428 7dd5ee6c Iustin Pop
    , run prop_Instance_setPri
429 7dd5ee6c Iustin Pop
    , run prop_Instance_setSec
430 7dd5ee6c Iustin Pop
    , run prop_Instance_setBoth
431 1ae7a904 Iustin Pop
    , run prop_Instance_runStatus_True
432 1ae7a904 Iustin Pop
    , run prop_Instance_runStatus_False
433 8fcf251f Iustin Pop
    , run prop_Instance_shrinkMG
434 8fcf251f Iustin Pop
    , run prop_Instance_shrinkMF
435 8fcf251f Iustin Pop
    , run prop_Instance_shrinkCG
436 8fcf251f Iustin Pop
    , run prop_Instance_shrinkCF
437 8fcf251f Iustin Pop
    , run prop_Instance_shrinkDG
438 8fcf251f Iustin Pop
    , run prop_Instance_shrinkDF
439 8fcf251f Iustin Pop
    , run prop_Instance_setMovable
440 1ae7a904 Iustin Pop
    ]
441 1ae7a904 Iustin Pop
442 1ae7a904 Iustin Pop
-- Instance text loader tests
443 1ae7a904 Iustin Pop
444 1ae7a904 Iustin Pop
prop_Text_Load_Instance name mem dsk vcpus status pnode snode pdx sdx =
445 39d11971 Iustin Pop
    not (null pnode) && pdx >= 0 && sdx >= 0 ==>
446 1ae7a904 Iustin Pop
    let vcpus_s = show vcpus
447 1ae7a904 Iustin Pop
        dsk_s = show dsk
448 1ae7a904 Iustin Pop
        mem_s = show mem
449 1ae7a904 Iustin Pop
        rsdx = if pdx == sdx
450 1ae7a904 Iustin Pop
               then sdx + 1
451 1ae7a904 Iustin Pop
               else sdx
452 39d11971 Iustin Pop
        ndx = if null snode
453 39d11971 Iustin Pop
              then [(pnode, pdx)]
454 39d11971 Iustin Pop
              else [(pnode, pdx), (snode, rsdx)]
455 99b63608 Iustin Pop
        nl = Data.Map.fromList ndx
456 434c15d5 Iustin Pop
        tags = ""
457 99b63608 Iustin Pop
        inst = Text.loadInst nl
458 39d11971 Iustin Pop
               [name, mem_s, dsk_s, vcpus_s, status, pnode, snode, tags]::
459 39d11971 Iustin Pop
               Maybe (String, Instance.Instance)
460 99b63608 Iustin Pop
        fail1 = Text.loadInst nl
461 39d11971 Iustin Pop
               [name, mem_s, dsk_s, vcpus_s, status, pnode, pnode, tags]::
462 1ae7a904 Iustin Pop
               Maybe (String, Instance.Instance)
463 1ae7a904 Iustin Pop
        _types = ( name::String, mem::Int, dsk::Int
464 1ae7a904 Iustin Pop
                 , vcpus::Int, status::String
465 1ae7a904 Iustin Pop
                 , pnode::String, snode::String
466 1ae7a904 Iustin Pop
                 , pdx::Types.Ndx, sdx::Types.Ndx)
467 1ae7a904 Iustin Pop
    in
468 1ae7a904 Iustin Pop
      case inst of
469 1ae7a904 Iustin Pop
        Nothing -> False
470 1ae7a904 Iustin Pop
        Just (_, i) ->
471 1ae7a904 Iustin Pop
            (Instance.name i == name &&
472 1ae7a904 Iustin Pop
             Instance.vcpus i == vcpus &&
473 1ae7a904 Iustin Pop
             Instance.mem i == mem &&
474 2060348b Iustin Pop
             Instance.pNode i == pdx &&
475 39d11971 Iustin Pop
             Instance.sNode i == (if null snode
476 39d11971 Iustin Pop
                                  then Node.noSecondary
477 39d11971 Iustin Pop
                                  else rsdx) &&
478 39d11971 Iustin Pop
             isNothing fail1)
479 39d11971 Iustin Pop
480 39d11971 Iustin Pop
prop_Text_Load_InstanceFail ktn fields =
481 99b63608 Iustin Pop
    length fields /= 8 ==> isNothing $ Text.loadInst nl fields
482 99b63608 Iustin Pop
    where nl = Data.Map.fromList ktn
483 39d11971 Iustin Pop
484 39d11971 Iustin Pop
prop_Text_Load_Node name tm nm fm td fd tc fo =
485 39d11971 Iustin Pop
    let conv v = if v < 0
486 39d11971 Iustin Pop
                    then "?"
487 39d11971 Iustin Pop
                    else show v
488 39d11971 Iustin Pop
        tm_s = conv tm
489 39d11971 Iustin Pop
        nm_s = conv nm
490 39d11971 Iustin Pop
        fm_s = conv fm
491 39d11971 Iustin Pop
        td_s = conv td
492 39d11971 Iustin Pop
        fd_s = conv fd
493 39d11971 Iustin Pop
        tc_s = conv tc
494 39d11971 Iustin Pop
        fo_s = if fo
495 39d11971 Iustin Pop
               then "Y"
496 39d11971 Iustin Pop
               else "N"
497 39d11971 Iustin Pop
        any_broken = any (< 0) [tm, nm, fm, td, fd, tc]
498 10ef6b4e Iustin Pop
        gid = Group.uuid defGroup
499 10ef6b4e Iustin Pop
    in case Text.loadNode defGroupAssoc
500 10ef6b4e Iustin Pop
           [name, tm_s, nm_s, fm_s, td_s, fd_s, tc_s, fo_s, gid] of
501 39d11971 Iustin Pop
         Nothing -> False
502 39d11971 Iustin Pop
         Just (name', node) ->
503 39d11971 Iustin Pop
             if fo || any_broken
504 39d11971 Iustin Pop
             then Node.offline node
505 4a007641 Iustin Pop
             else Node.name node == name' && name' == name &&
506 4a007641 Iustin Pop
                  Node.alias node == name &&
507 4a007641 Iustin Pop
                  Node.tMem node == fromIntegral tm &&
508 4a007641 Iustin Pop
                  Node.nMem node == nm &&
509 4a007641 Iustin Pop
                  Node.fMem node == fm &&
510 4a007641 Iustin Pop
                  Node.tDsk node == fromIntegral td &&
511 4a007641 Iustin Pop
                  Node.fDsk node == fd &&
512 4a007641 Iustin Pop
                  Node.tCpu node == fromIntegral tc
513 39d11971 Iustin Pop
514 39d11971 Iustin Pop
prop_Text_Load_NodeFail fields =
515 10ef6b4e Iustin Pop
    length fields /= 8 ==> isNothing $ Text.loadNode Data.Map.empty fields
516 1ae7a904 Iustin Pop
517 50811e2c Iustin Pop
prop_Text_NodeLSIdempotent node =
518 10ef6b4e Iustin Pop
    (Text.loadNode defGroupAssoc.
519 10ef6b4e Iustin Pop
         Utils.sepSplit '|' . Text.serializeNode defGroupList) n ==
520 50811e2c Iustin Pop
    Just (Node.name n, n)
521 50811e2c Iustin Pop
    -- override failN1 to what loadNode returns by default
522 50811e2c Iustin Pop
    where n = node { Node.failN1 = True, Node.offline = False }
523 50811e2c Iustin Pop
524 c15f7183 Iustin Pop
testText =
525 1ae7a904 Iustin Pop
    [ run prop_Text_Load_Instance
526 39d11971 Iustin Pop
    , run prop_Text_Load_InstanceFail
527 39d11971 Iustin Pop
    , run prop_Text_Load_Node
528 39d11971 Iustin Pop
    , run prop_Text_Load_NodeFail
529 50811e2c Iustin Pop
    , run prop_Text_NodeLSIdempotent
530 7dd5ee6c Iustin Pop
    ]
531 7dd5ee6c Iustin Pop
532 7dd5ee6c Iustin Pop
-- Node tests
533 7dd5ee6c Iustin Pop
534 82ea2874 Iustin Pop
prop_Node_setAlias node name =
535 82ea2874 Iustin Pop
    Node.name newnode == Node.name node &&
536 82ea2874 Iustin Pop
    Node.alias newnode == name
537 82ea2874 Iustin Pop
    where _types = (node::Node.Node, name::String)
538 82ea2874 Iustin Pop
          newnode = Node.setAlias node name
539 82ea2874 Iustin Pop
540 82ea2874 Iustin Pop
prop_Node_setOffline node status =
541 82ea2874 Iustin Pop
    Node.offline newnode == status
542 82ea2874 Iustin Pop
    where newnode = Node.setOffline node status
543 82ea2874 Iustin Pop
544 82ea2874 Iustin Pop
prop_Node_setXmem node xm =
545 82ea2874 Iustin Pop
    Node.xMem newnode == xm
546 82ea2874 Iustin Pop
    where newnode = Node.setXmem node xm
547 82ea2874 Iustin Pop
548 82ea2874 Iustin Pop
prop_Node_setMcpu node mc =
549 82ea2874 Iustin Pop
    Node.mCpu newnode == mc
550 82ea2874 Iustin Pop
    where newnode = Node.setMcpu node mc
551 82ea2874 Iustin Pop
552 7bc82927 Iustin Pop
-- | Check that an instance add with too high memory or disk will be rejected
553 8fcf251f Iustin Pop
prop_Node_addPriFM node inst = Instance.mem inst >= Node.fMem node &&
554 8fcf251f Iustin Pop
                               not (Node.failN1 node)
555 8fcf251f Iustin Pop
                               ==>
556 8fcf251f Iustin Pop
                               case Node.addPri node inst'' of
557 8fcf251f Iustin Pop
                                 Types.OpFail Types.FailMem -> True
558 8fcf251f Iustin Pop
                                 _ -> False
559 15f4c8ca Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
560 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
561 8fcf251f Iustin Pop
          inst'' = inst' { Instance.mem = Instance.mem inst }
562 8fcf251f Iustin Pop
563 8fcf251f Iustin Pop
prop_Node_addPriFD node inst = Instance.dsk inst >= Node.fDsk node &&
564 8fcf251f Iustin Pop
                               not (Node.failN1 node)
565 8fcf251f Iustin Pop
                               ==>
566 8fcf251f Iustin Pop
                               case Node.addPri node inst'' of
567 8fcf251f Iustin Pop
                                 Types.OpFail Types.FailDisk -> True
568 8fcf251f Iustin Pop
                                 _ -> False
569 8fcf251f Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
570 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
571 8fcf251f Iustin Pop
          inst'' = inst' { Instance.dsk = Instance.dsk inst }
572 8fcf251f Iustin Pop
573 8fcf251f Iustin Pop
prop_Node_addPriFC node inst = Instance.vcpus inst > Node.availCpu node &&
574 8fcf251f Iustin Pop
                               not (Node.failN1 node)
575 8fcf251f Iustin Pop
                               ==>
576 8fcf251f Iustin Pop
                               case Node.addPri node inst'' of
577 8fcf251f Iustin Pop
                                 Types.OpFail Types.FailCPU -> True
578 8fcf251f Iustin Pop
                                 _ -> False
579 8fcf251f Iustin Pop
    where _types = (node::Node.Node, inst::Instance.Instance)
580 8fcf251f Iustin Pop
          inst' = setInstanceSmallerThanNode node inst
581 8fcf251f Iustin Pop
          inst'' = inst' { Instance.vcpus = Instance.vcpus inst }
582 7bc82927 Iustin Pop
583 7bc82927 Iustin Pop
-- | Check that an instance add with too high memory or disk will be rejected
584 15f4c8ca Iustin Pop
prop_Node_addSec node inst pdx =
585 2060348b Iustin Pop
    (Instance.mem inst >= (Node.fMem node - Node.rMem node) ||
586 2060348b Iustin Pop
     Instance.dsk inst >= Node.fDsk node) &&
587 9f6dcdea Iustin Pop
    not (Node.failN1 node)
588 79a72ce7 Iustin Pop
    ==> isFailure (Node.addSec node inst pdx)
589 15f4c8ca Iustin Pop
        where _types = (node::Node.Node, inst::Instance.Instance, pdx::Int)
590 7dd5ee6c Iustin Pop
591 8fcf251f Iustin Pop
newtype SmallRatio = SmallRatio Double deriving Show
592 8fcf251f Iustin Pop
instance Arbitrary SmallRatio where
593 8fcf251f Iustin Pop
    arbitrary = do
594 8fcf251f Iustin Pop
      v <- choose (0, 1)
595 8fcf251f Iustin Pop
      return $ SmallRatio v
596 8fcf251f Iustin Pop
597 8fcf251f Iustin Pop
-- | Check mdsk setting
598 8fcf251f Iustin Pop
prop_Node_setMdsk node mx =
599 8fcf251f Iustin Pop
    Node.loDsk node' >= 0 &&
600 8fcf251f Iustin Pop
    fromIntegral (Node.loDsk node') <= Node.tDsk node &&
601 8fcf251f Iustin Pop
    Node.availDisk node' >= 0 &&
602 8fcf251f Iustin Pop
    Node.availDisk node' <= Node.fDsk node' &&
603 82ea2874 Iustin Pop
    fromIntegral (Node.availDisk node') <= Node.tDsk node' &&
604 82ea2874 Iustin Pop
    Node.mDsk node' == mx'
605 8fcf251f Iustin Pop
    where _types = (node::Node.Node, mx::SmallRatio)
606 8fcf251f Iustin Pop
          node' = Node.setMdsk node mx'
607 8fcf251f Iustin Pop
          SmallRatio mx' = mx
608 8fcf251f Iustin Pop
609 8fcf251f Iustin Pop
-- Check tag maps
610 8fcf251f Iustin Pop
prop_Node_tagMaps_idempotent tags =
611 8fcf251f Iustin Pop
    Node.delTags (Node.addTags m tags) tags == m
612 4a007641 Iustin Pop
    where m = Data.Map.empty
613 8fcf251f Iustin Pop
614 8fcf251f Iustin Pop
prop_Node_tagMaps_reject tags =
615 8fcf251f Iustin Pop
    not (null tags) ==>
616 8fcf251f Iustin Pop
    any (\t -> Node.rejectAddTags m [t]) tags
617 4a007641 Iustin Pop
    where m = Node.addTags Data.Map.empty tags
618 8fcf251f Iustin Pop
619 82ea2874 Iustin Pop
prop_Node_showField node =
620 82ea2874 Iustin Pop
  forAll (elements Node.defaultFields) $ \ field ->
621 82ea2874 Iustin Pop
  fst (Node.showHeader field) /= Types.unknownField &&
622 82ea2874 Iustin Pop
  Node.showField node field /= Types.unknownField
623 82ea2874 Iustin Pop
624 d8bcd0a8 Iustin Pop
625 d8bcd0a8 Iustin Pop
prop_Node_computeGroups nodes =
626 d8bcd0a8 Iustin Pop
  let ng = Node.computeGroups nodes
627 d8bcd0a8 Iustin Pop
      onlyuuid = map fst ng
628 d8bcd0a8 Iustin Pop
  in length nodes == sum (map (length . snd) ng) &&
629 d8bcd0a8 Iustin Pop
     all (\(guuid, ns) -> all ((== guuid) . Node.group) ns) ng &&
630 d8bcd0a8 Iustin Pop
     length (nub onlyuuid) == length onlyuuid &&
631 d8bcd0a8 Iustin Pop
     if null nodes then True else not (null ng)
632 d8bcd0a8 Iustin Pop
633 c15f7183 Iustin Pop
testNode =
634 82ea2874 Iustin Pop
    [ run prop_Node_setAlias
635 82ea2874 Iustin Pop
    , run prop_Node_setOffline
636 82ea2874 Iustin Pop
    , run prop_Node_setMcpu
637 82ea2874 Iustin Pop
    , run prop_Node_setXmem
638 82ea2874 Iustin Pop
    , run prop_Node_addPriFM
639 8fcf251f Iustin Pop
    , run prop_Node_addPriFD
640 8fcf251f Iustin Pop
    , run prop_Node_addPriFC
641 7dd5ee6c Iustin Pop
    , run prop_Node_addSec
642 8fcf251f Iustin Pop
    , run prop_Node_setMdsk
643 8fcf251f Iustin Pop
    , run prop_Node_tagMaps_idempotent
644 8fcf251f Iustin Pop
    , run prop_Node_tagMaps_reject
645 82ea2874 Iustin Pop
    , run prop_Node_showField
646 d8bcd0a8 Iustin Pop
    , run prop_Node_computeGroups
647 7dd5ee6c Iustin Pop
    ]
648 cf35a869 Iustin Pop
649 cf35a869 Iustin Pop
650 cf35a869 Iustin Pop
-- Cluster tests
651 cf35a869 Iustin Pop
652 cf35a869 Iustin Pop
-- | Check that the cluster score is close to zero for a homogeneous cluster
653 cf35a869 Iustin Pop
prop_Score_Zero node count =
654 3a3c1eb4 Iustin Pop
    (not (Node.offline node) && not (Node.failN1 node) && (count > 0) &&
655 2060348b Iustin Pop
     (Node.tDsk node > 0) && (Node.tMem node > 0)) ==>
656 cf35a869 Iustin Pop
    let fn = Node.buildPeers node Container.empty
657 3a3c1eb4 Iustin Pop
        nlst = zip [1..] $ replicate count fn::[(Types.Ndx, Node.Node)]
658 cb0c77ff Iustin Pop
        nl = Container.fromList nlst
659 cf35a869 Iustin Pop
        score = Cluster.compCV nl
660 cf35a869 Iustin Pop
    -- we can't say == 0 here as the floating point errors accumulate;
661 cf35a869 Iustin Pop
    -- this should be much lower than the default score in CLI.hs
662 685f5bc6 Iustin Pop
    in score <= 1e-15
663 cf35a869 Iustin Pop
664 8fcf251f Iustin Pop
-- | Check that cluster stats are sane
665 8fcf251f Iustin Pop
prop_CStats_sane node count =
666 8fcf251f Iustin Pop
    (not (Node.offline node) && not (Node.failN1 node) && (count > 0) &&
667 3fea6959 Iustin Pop
     (Node.availDisk node > 0) && (Node.availMem node > 0)) ==>
668 8fcf251f Iustin Pop
    let fn = Node.buildPeers node Container.empty
669 8fcf251f Iustin Pop
        nlst = zip [1..] $ replicate count fn::[(Types.Ndx, Node.Node)]
670 cb0c77ff Iustin Pop
        nl = Container.fromList nlst
671 8fcf251f Iustin Pop
        cstats = Cluster.totalResources nl
672 8fcf251f Iustin Pop
    in Cluster.csAdsk cstats >= 0 &&
673 8fcf251f Iustin Pop
       Cluster.csAdsk cstats <= Cluster.csFdsk cstats
674 8fcf251f Iustin Pop
675 3fea6959 Iustin Pop
-- | Check that one instance is allocated correctly, without
676 3fea6959 Iustin Pop
-- rebalances needed
677 3fea6959 Iustin Pop
prop_ClusterAlloc_sane node inst =
678 3fea6959 Iustin Pop
    forAll (choose (5, 20)) $ \count ->
679 3fea6959 Iustin Pop
    not (Node.offline node)
680 3fea6959 Iustin Pop
            && not (Node.failN1 node)
681 3fea6959 Iustin Pop
            && Node.availDisk node > 0
682 3fea6959 Iustin Pop
            && Node.availMem node > 0
683 3fea6959 Iustin Pop
            ==>
684 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
685 3fea6959 Iustin Pop
        il = Container.empty
686 3fea6959 Iustin Pop
        inst' = setInstanceSmallerThanNode node inst
687 6d0bc5ca Iustin Pop
    in case Cluster.genAllocNodes defGroupList nl 2 True >>=
688 6d0bc5ca Iustin Pop
       Cluster.tryAlloc nl il inst' of
689 3fea6959 Iustin Pop
         Types.Bad _ -> False
690 85d0ddc3 Iustin Pop
         Types.Ok as ->
691 85d0ddc3 Iustin Pop
             case Cluster.asSolutions as of
692 3fea6959 Iustin Pop
               [] -> False
693 a334d536 Iustin Pop
               (xnl, xi, _, cv):[] ->
694 7d3f4253 Iustin Pop
                   let il' = Container.add (Instance.idx xi) xi il
695 3fea6959 Iustin Pop
                       tbl = Cluster.Table xnl il' cv []
696 3fea6959 Iustin Pop
                   in not (canBalance tbl True False)
697 3fea6959 Iustin Pop
               _ -> False
698 3fea6959 Iustin Pop
699 3fea6959 Iustin Pop
-- | Checks that on a 2-5 node cluster, we can allocate a random
700 3fea6959 Iustin Pop
-- instance spec via tiered allocation (whatever the original instance
701 3fea6959 Iustin Pop
-- spec), on either one or two nodes
702 3fea6959 Iustin Pop
prop_ClusterCanTieredAlloc node inst =
703 3fea6959 Iustin Pop
    forAll (choose (2, 5)) $ \count ->
704 3fea6959 Iustin Pop
    forAll (choose (1, 2)) $ \rqnodes ->
705 3fea6959 Iustin Pop
    not (Node.offline node)
706 3fea6959 Iustin Pop
            && not (Node.failN1 node)
707 3fea6959 Iustin Pop
            && isNodeBig node 4
708 3fea6959 Iustin Pop
            ==>
709 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
710 3fea6959 Iustin Pop
        il = Container.empty
711 6d0bc5ca Iustin Pop
        allocnodes = Cluster.genAllocNodes defGroupList nl rqnodes True
712 41b5c85a Iustin Pop
    in case allocnodes >>= \allocnodes' ->
713 41b5c85a Iustin Pop
        Cluster.tieredAlloc nl il inst allocnodes' [] [] of
714 3fea6959 Iustin Pop
         Types.Bad _ -> False
715 d5ccec02 Iustin Pop
         Types.Ok (_, _, il', ixes, cstats) -> not (null ixes) &&
716 d5ccec02 Iustin Pop
                                      IntMap.size il' == length ixes &&
717 d5ccec02 Iustin Pop
                                      length ixes == length cstats
718 3fea6959 Iustin Pop
719 3fea6959 Iustin Pop
-- | Checks that on a 4-8 node cluster, once we allocate an instance,
720 3fea6959 Iustin Pop
-- we can also evacuate it
721 3fea6959 Iustin Pop
prop_ClusterAllocEvac node inst =
722 3fea6959 Iustin Pop
    forAll (choose (4, 8)) $ \count ->
723 3fea6959 Iustin Pop
    not (Node.offline node)
724 3fea6959 Iustin Pop
            && not (Node.failN1 node)
725 3fea6959 Iustin Pop
            && isNodeBig node 4
726 3fea6959 Iustin Pop
            ==>
727 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
728 3fea6959 Iustin Pop
        il = Container.empty
729 3fea6959 Iustin Pop
        inst' = setInstanceSmallerThanNode node inst
730 6d0bc5ca Iustin Pop
    in case Cluster.genAllocNodes defGroupList nl 2 True >>=
731 6d0bc5ca Iustin Pop
       Cluster.tryAlloc nl il inst' of
732 3fea6959 Iustin Pop
         Types.Bad _ -> False
733 85d0ddc3 Iustin Pop
         Types.Ok as ->
734 85d0ddc3 Iustin Pop
             case Cluster.asSolutions as of
735 3fea6959 Iustin Pop
               [] -> False
736 a334d536 Iustin Pop
               (xnl, xi, _, _):[] ->
737 3fea6959 Iustin Pop
                   let sdx = Instance.sNode xi
738 3fea6959 Iustin Pop
                       il' = Container.add (Instance.idx xi) xi il
739 1bc47d38 Iustin Pop
                   in case Cluster.tryEvac xnl il' [Instance.idx xi] [sdx] of
740 3fea6959 Iustin Pop
                        Just _ -> True
741 3fea6959 Iustin Pop
                        _ -> False
742 3fea6959 Iustin Pop
               _ -> False
743 3fea6959 Iustin Pop
744 3fea6959 Iustin Pop
-- | Check that allocating multiple instances on a cluster, then
745 3fea6959 Iustin Pop
-- adding an empty node, results in a valid rebalance
746 3fea6959 Iustin Pop
prop_ClusterAllocBalance node =
747 3fea6959 Iustin Pop
    forAll (choose (3, 5)) $ \count ->
748 3fea6959 Iustin Pop
    not (Node.offline node)
749 3fea6959 Iustin Pop
            && not (Node.failN1 node)
750 3fea6959 Iustin Pop
            && isNodeBig node 4
751 3fea6959 Iustin Pop
            && not (isNodeBig node 8)
752 3fea6959 Iustin Pop
            ==>
753 3fea6959 Iustin Pop
    let nl = makeSmallCluster node count
754 3fea6959 Iustin Pop
        (hnode, nl') = IntMap.deleteFindMax nl
755 3fea6959 Iustin Pop
        il = Container.empty
756 6d0bc5ca Iustin Pop
        allocnodes = Cluster.genAllocNodes defGroupList nl' 2 True
757 3fea6959 Iustin Pop
        i_templ = createInstance Types.unitMem Types.unitDsk Types.unitCpu
758 41b5c85a Iustin Pop
    in case allocnodes >>= \allocnodes' ->
759 41b5c85a Iustin Pop
        Cluster.iterateAlloc nl' il i_templ allocnodes' [] [] of
760 3fea6959 Iustin Pop
         Types.Bad _ -> False
761 d5ccec02 Iustin Pop
         Types.Ok (_, xnl, il', _, _) ->
762 3fea6959 Iustin Pop
                   let ynl = Container.add (Node.idx hnode) hnode xnl
763 3fea6959 Iustin Pop
                       cv = Cluster.compCV ynl
764 3fea6959 Iustin Pop
                       tbl = Cluster.Table ynl il' cv []
765 3fea6959 Iustin Pop
                   in canBalance tbl True False
766 3fea6959 Iustin Pop
767 32b8d9c0 Iustin Pop
-- | Checks consistency
768 32b8d9c0 Iustin Pop
prop_ClusterCheckConsistency node inst =
769 32b8d9c0 Iustin Pop
  let nl = makeSmallCluster node 3
770 32b8d9c0 Iustin Pop
      [node1, node2, node3] = Container.elems nl
771 10ef6b4e Iustin Pop
      node3' = node3 { Node.group = 1 }
772 32b8d9c0 Iustin Pop
      nl' = Container.add (Node.idx node3') node3' nl
773 32b8d9c0 Iustin Pop
      inst1 = Instance.setBoth inst (Node.idx node1) (Node.idx node2)
774 32b8d9c0 Iustin Pop
      inst2 = Instance.setBoth inst (Node.idx node1) Node.noSecondary
775 32b8d9c0 Iustin Pop
      inst3 = Instance.setBoth inst (Node.idx node1) (Node.idx node3)
776 cb0c77ff Iustin Pop
      ccheck = Cluster.findSplitInstances nl' . Container.fromList
777 32b8d9c0 Iustin Pop
  in null (ccheck [(0, inst1)]) &&
778 32b8d9c0 Iustin Pop
     null (ccheck [(0, inst2)]) &&
779 32b8d9c0 Iustin Pop
     (not . null $ ccheck [(0, inst3)])
780 32b8d9c0 Iustin Pop
781 f4161783 Iustin Pop
-- For now, we only test that we don't lose instances during the split
782 f4161783 Iustin Pop
prop_ClusterSplitCluster node inst =
783 f4161783 Iustin Pop
  forAll (choose (0, 100)) $ \icnt ->
784 f4161783 Iustin Pop
  let nl = makeSmallCluster node 2
785 f4161783 Iustin Pop
      (nl', il') = foldl (\(ns, is) _ -> assignInstance ns is inst 0 1)
786 f4161783 Iustin Pop
                   (nl, Container.empty) [1..icnt]
787 f4161783 Iustin Pop
      gni = Cluster.splitCluster nl' il'
788 f4161783 Iustin Pop
  in sum (map (Container.size . snd . snd) gni) == icnt &&
789 f4161783 Iustin Pop
     all (\(guuid, (nl'', _)) -> all ((== guuid) . Node.group)
790 f4161783 Iustin Pop
                                 (Container.elems nl'')) gni
791 32b8d9c0 Iustin Pop
792 c15f7183 Iustin Pop
testCluster =
793 cf35a869 Iustin Pop
    [ run prop_Score_Zero
794 8fcf251f Iustin Pop
    , run prop_CStats_sane
795 3fea6959 Iustin Pop
    , run prop_ClusterAlloc_sane
796 3fea6959 Iustin Pop
    , run prop_ClusterCanTieredAlloc
797 3fea6959 Iustin Pop
    , run prop_ClusterAllocEvac
798 3fea6959 Iustin Pop
    , run prop_ClusterAllocBalance
799 32b8d9c0 Iustin Pop
    , run prop_ClusterCheckConsistency
800 f4161783 Iustin Pop
    , run prop_ClusterSplitCluster
801 cf35a869 Iustin Pop
    ]
802 88f25dd0 Iustin Pop
803 88f25dd0 Iustin Pop
-- | Check that opcode serialization is idempotent
804 88f25dd0 Iustin Pop
805 88f25dd0 Iustin Pop
prop_OpCodes_serialization op =
806 88f25dd0 Iustin Pop
  case J.readJSON (J.showJSON op) of
807 88f25dd0 Iustin Pop
    J.Error _ -> False
808 88f25dd0 Iustin Pop
    J.Ok op' -> op == op'
809 4a007641 Iustin Pop
  where _types = op::OpCodes.OpCode
810 88f25dd0 Iustin Pop
811 88f25dd0 Iustin Pop
testOpCodes =
812 88f25dd0 Iustin Pop
  [ run prop_OpCodes_serialization
813 88f25dd0 Iustin Pop
  ]
814 c088674b Iustin Pop
815 95446d7a Iustin Pop
-- | Check that (queued) job\/opcode status serialization is idempotent
816 db079755 Iustin Pop
prop_OpStatus_serialization os =
817 db079755 Iustin Pop
  case J.readJSON (J.showJSON os) of
818 db079755 Iustin Pop
    J.Error _ -> False
819 db079755 Iustin Pop
    J.Ok os' -> os == os'
820 db079755 Iustin Pop
  where _types = os::Jobs.OpStatus
821 db079755 Iustin Pop
822 db079755 Iustin Pop
prop_JobStatus_serialization js =
823 db079755 Iustin Pop
  case J.readJSON (J.showJSON js) of
824 db079755 Iustin Pop
    J.Error _ -> False
825 db079755 Iustin Pop
    J.Ok js' -> js == js'
826 db079755 Iustin Pop
  where _types = js::Jobs.JobStatus
827 db079755 Iustin Pop
828 db079755 Iustin Pop
testJobs =
829 db079755 Iustin Pop
  [ run prop_OpStatus_serialization
830 db079755 Iustin Pop
  , run prop_JobStatus_serialization
831 db079755 Iustin Pop
  ]
832 db079755 Iustin Pop
833 c088674b Iustin Pop
-- | Loader tests
834 c088674b Iustin Pop
835 c088674b Iustin Pop
prop_Loader_lookupNode ktn inst node =
836 99b63608 Iustin Pop
  Loader.lookupNode nl inst node == Data.Map.lookup node nl
837 99b63608 Iustin Pop
  where nl = Data.Map.fromList ktn
838 c088674b Iustin Pop
839 c088674b Iustin Pop
prop_Loader_lookupInstance kti inst =
840 99b63608 Iustin Pop
  Loader.lookupInstance il inst == Data.Map.lookup inst il
841 99b63608 Iustin Pop
  where il = Data.Map.fromList kti
842 99b63608 Iustin Pop
843 99b63608 Iustin Pop
prop_Loader_assignIndices nodes =
844 99b63608 Iustin Pop
  Data.Map.size nassoc == length nodes &&
845 99b63608 Iustin Pop
  Container.size kt == length nodes &&
846 99b63608 Iustin Pop
  (if not (null nodes)
847 99b63608 Iustin Pop
   then maximum (IntMap.keys kt) == length nodes - 1
848 c088674b Iustin Pop
   else True)
849 99b63608 Iustin Pop
  where (nassoc, kt) = Loader.assignIndices (map (\n -> (Node.name n, n)) nodes)
850 c088674b Iustin Pop
851 c088674b Iustin Pop
852 c088674b Iustin Pop
-- | Checks that the number of primary instances recorded on the nodes
853 c088674b Iustin Pop
-- is zero
854 c088674b Iustin Pop
prop_Loader_mergeData ns =
855 cb0c77ff Iustin Pop
  let na = Container.fromList $ map (\n -> (Node.idx n, n)) ns
856 a679e9dc Iustin Pop
  in case Loader.mergeData [] [] []
857 f4f6eb0b Iustin Pop
         (Loader.emptyCluster {Loader.cdNodes = na}) of
858 c088674b Iustin Pop
    Types.Bad _ -> False
859 017a0c3d Iustin Pop
    Types.Ok (Loader.ClusterData _ nl il _) ->
860 c088674b Iustin Pop
      let nodes = Container.elems nl
861 c088674b Iustin Pop
          instances = Container.elems il
862 c088674b Iustin Pop
      in (sum . map (length . Node.pList)) nodes == 0 &&
863 4a007641 Iustin Pop
         null instances
864 c088674b Iustin Pop
865 c088674b Iustin Pop
testLoader =
866 c088674b Iustin Pop
  [ run prop_Loader_lookupNode
867 c088674b Iustin Pop
  , run prop_Loader_lookupInstance
868 c088674b Iustin Pop
  , run prop_Loader_assignIndices
869 c088674b Iustin Pop
  , run prop_Loader_mergeData
870 c088674b Iustin Pop
  ]