Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / QC.hs @ 7c14b50a

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