Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / Objects.hs @ 0359e5d0

History | View | Annotate | Download (21.6 kB)

1
{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances,
2
  OverloadedStrings #-}
3
{-# OPTIONS_GHC -fno-warn-orphans #-}
4

    
5
{-| Unittests for ganeti-htools.
6

    
7
-}
8

    
9
{-
10

    
11
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
12

    
13
This program is free software; you can redistribute it and/or modify
14
it under the terms of the GNU General Public License as published by
15
the Free Software Foundation; either version 2 of the License, or
16
(at your option) any later version.
17

    
18
This program is distributed in the hope that it will be useful, but
19
WITHOUT ANY WARRANTY; without even the implied warranty of
20
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21
General Public License for more details.
22

    
23
You should have received a copy of the GNU General Public License
24
along with this program; if not, write to the Free Software
25
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26
02110-1301, USA.
27

    
28
-}
29

    
30
module Test.Ganeti.Objects
31
  ( testObjects
32
  , Node(..)
33
  , genConfigDataWithNetworks
34
  , genDisk
35
  , genDiskWithChildren
36
  , genEmptyCluster
37
  , genInst
38
  , genInstWithNets
39
  , genValidNetwork
40
  , genBitStringMaxLen
41
  ) where
42

    
43
import Test.QuickCheck
44
import qualified Test.HUnit as HUnit
45

    
46
import Control.Applicative
47
import Control.Monad
48
import Data.Char
49
import qualified Data.List as List
50
import qualified Data.Map as Map
51
import Data.Maybe (fromMaybe)
52
import qualified Data.Set as Set
53
import GHC.Exts (IsString(..))
54
import qualified Text.JSON as J
55

    
56
import Test.Ganeti.TestHelper
57
import Test.Ganeti.TestCommon
58
import Test.Ganeti.Types ()
59

    
60
import qualified Ganeti.Constants as C
61
import Ganeti.Network
62
import Ganeti.Objects as Objects
63
import Ganeti.JSON
64
import Ganeti.Types
65

    
66
-- * Arbitrary instances
67

    
68
$(genArbitrary ''PartialNDParams)
69

    
70
instance Arbitrary Node where
71
  arbitrary = Node <$> genFQDN <*> genFQDN <*> genFQDN
72
              <*> arbitrary <*> arbitrary <*> arbitrary <*> genFQDN
73
              <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
74
              <*> arbitrary <*> arbitrary <*> genFQDN <*> arbitrary
75
              <*> (Set.fromList <$> genTags)
76

    
77
$(genArbitrary ''BlockDriver)
78

    
79
$(genArbitrary ''DiskMode)
80

    
81
instance Arbitrary DiskLogicalId where
82
  arbitrary = oneof [ LIDPlain <$> arbitrary <*> arbitrary
83
                    , LIDDrbd8 <$> genFQDN <*> genFQDN <*> arbitrary
84
                               <*> arbitrary <*> arbitrary <*> arbitrary
85
                    , LIDFile  <$> arbitrary <*> arbitrary
86
                    , LIDBlockDev <$> arbitrary <*> arbitrary
87
                    , LIDRados <$> arbitrary <*> arbitrary
88
                    ]
89

    
90
-- | 'Disk' 'arbitrary' instance. Since we don't test disk hierarchy
91
-- properties, we only generate disks with no children (FIXME), as
92
-- generating recursive datastructures is a bit more work.
93
instance Arbitrary Disk where
94
  arbitrary = Disk <$> arbitrary <*> pure [] <*> arbitrary
95
                   <*> arbitrary <*> arbitrary <*> arbitrary
96
                   <*> arbitrary <*> arbitrary
97

    
98
-- FIXME: we should generate proper values, >=0, etc., but this is
99
-- hard for partial ones, where all must be wrapped in a 'Maybe'
100
$(genArbitrary ''PartialBeParams)
101

    
102
$(genArbitrary ''AdminState)
103

    
104
$(genArbitrary ''PartialNicParams)
105

    
106
$(genArbitrary ''PartialNic)
107

    
108
instance Arbitrary Instance where
109
  arbitrary =
110
    Instance
111
      -- name
112
      <$> genFQDN
113
      -- primary node
114
      <*> genFQDN
115
      -- OS
116
      <*> genFQDN
117
      -- hypervisor
118
      <*> arbitrary
119
      -- hvparams
120
      -- FIXME: add non-empty hvparams when they're a proper type
121
      <*> pure (GenericContainer Map.empty)
122
      -- beparams
123
      <*> arbitrary
124
      -- osparams
125
      <*> pure (GenericContainer Map.empty)
126
      -- admin_state
127
      <*> arbitrary
128
      -- nics
129
      <*> arbitrary
130
      -- disks
131
      <*> vectorOf 5 genDisk
132
      -- disk template
133
      <*> arbitrary
134
      -- disks active
135
      <*> arbitrary
136
      -- network port
137
      <*> arbitrary
138
      -- ts
139
      <*> arbitrary <*> arbitrary
140
      -- uuid
141
      <*> arbitrary
142
      -- serial
143
      <*> arbitrary
144
      -- tags
145
      <*> (Set.fromList <$> genTags)
146

    
147
-- | Generates an instance that is connected to the given networks
148
-- and possibly some other networks
149
genInstWithNets :: [String] -> Gen Instance
150
genInstWithNets nets = do
151
  plain_inst <- arbitrary
152
  enhanceInstWithNets plain_inst nets
153

    
154
-- | Generates an instance that is connected to some networks
155
genInst :: Gen Instance
156
genInst = genInstWithNets []
157

    
158
-- | Enhances a given instance with network information, by connecting it to the
159
-- given networks and possibly some other networks
160
enhanceInstWithNets :: Instance -> [String] -> Gen Instance
161
enhanceInstWithNets inst nets = do
162
  mac <- arbitrary
163
  ip <- arbitrary
164
  nicparams <- arbitrary
165
  name <- arbitrary
166
  uuid <- arbitrary
167
  -- generate some more networks than the given ones
168
  num_more_nets <- choose (0,3)
169
  more_nets <- vectorOf num_more_nets genName
170
  let genNic net = PartialNic mac ip nicparams net name uuid
171
      partial_nics = map (genNic . Just)
172
                         (List.nub (nets ++ more_nets))
173
      new_inst = inst { instNics = partial_nics }
174
  return new_inst
175

    
176
genDiskWithChildren :: Int -> Gen Disk
177
genDiskWithChildren num_children = do
178
  logicalid <- arbitrary
179
  children <- vectorOf num_children (genDiskWithChildren 0)
180
  ivname <- genName
181
  size <- arbitrary
182
  mode <- arbitrary
183
  name <- genMaybe genName
184
  spindles <- arbitrary
185
  uuid <- genName
186
  let disk = Disk logicalid children ivname size mode name spindles uuid
187
  return disk
188

    
189
genDisk :: Gen Disk
190
genDisk = genDiskWithChildren 3
191

    
192
-- | FIXME: This generates completely random data, without normal
193
-- validation rules.
194
$(genArbitrary ''PartialISpecParams)
195

    
196
-- | FIXME: This generates completely random data, without normal
197
-- validation rules.
198
$(genArbitrary ''PartialIPolicy)
199

    
200
$(genArbitrary ''FilledISpecParams)
201
$(genArbitrary ''MinMaxISpecs)
202
$(genArbitrary ''FilledIPolicy)
203
$(genArbitrary ''IpFamily)
204
$(genArbitrary ''FilledNDParams)
205
$(genArbitrary ''FilledNicParams)
206
$(genArbitrary ''FilledBeParams)
207

    
208
-- | No real arbitrary instance for 'ClusterHvParams' yet.
209
instance Arbitrary ClusterHvParams where
210
  arbitrary = return $ GenericContainer Map.empty
211

    
212
-- | No real arbitrary instance for 'OsHvParams' yet.
213
instance Arbitrary OsHvParams where
214
  arbitrary = return $ GenericContainer Map.empty
215

    
216
instance Arbitrary ClusterNicParams where
217
  arbitrary = (GenericContainer . Map.singleton C.ppDefault) <$> arbitrary
218

    
219
instance Arbitrary OsParams where
220
  arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
221

    
222
instance Arbitrary ClusterOsParams where
223
  arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
224

    
225
instance Arbitrary ClusterBeParams where
226
  arbitrary = (GenericContainer . Map.fromList) <$> arbitrary
227

    
228
instance Arbitrary TagSet where
229
  arbitrary = Set.fromList <$> genTags
230

    
231
instance Arbitrary IAllocatorParams where
232
  arbitrary = return $ GenericContainer Map.empty
233

    
234
$(genArbitrary ''Cluster)
235

    
236
instance Arbitrary Network where
237
  arbitrary = genValidNetwork
238

    
239
-- | Generates a network instance with minimum netmasks of /24. Generating
240
-- bigger networks slows down the tests, because long bit strings are generated
241
-- for the reservations.
242
genValidNetwork :: Gen Objects.Network
243
genValidNetwork = do
244
  -- generate netmask for the IPv4 network
245
  netmask <- fromIntegral <$> choose (24::Int, 30)
246
  name <- genName >>= mkNonEmpty
247
  mac_prefix <- genMaybe genName
248
  net <- arbitrary
249
  net6 <- genMaybe genIp6Net
250
  gateway <- genMaybe arbitrary
251
  gateway6 <- genMaybe genIp6Addr
252
  res <- liftM Just (genBitString $ netmask2NumHosts netmask)
253
  ext_res <- liftM Just (genBitString $ netmask2NumHosts netmask)
254
  uuid <- arbitrary
255
  ctime <- arbitrary
256
  mtime <- arbitrary
257
  let n = Network name mac_prefix (Ip4Network net netmask) net6 gateway
258
          gateway6 res ext_res uuid ctime mtime 0 Set.empty
259
  return n
260

    
261
-- | Generate an arbitrary string consisting of '0' and '1' of the given length.
262
genBitString :: Int -> Gen String
263
genBitString len = vectorOf len (elements "01")
264

    
265
-- | Generate an arbitrary string consisting of '0' and '1' of the maximum given
266
-- length.
267
genBitStringMaxLen :: Int -> Gen String
268
genBitStringMaxLen maxLen = choose (0, maxLen) >>= genBitString
269

    
270
-- | Generator for config data with an empty cluster (no instances),
271
-- with N defined nodes.
272
genEmptyCluster :: Int -> Gen ConfigData
273
genEmptyCluster ncount = do
274
  nodes <- vector ncount
275
  version <- arbitrary
276
  grp <- arbitrary
277
  let guuid = groupUuid grp
278
      nodes' = zipWith (\n idx ->
279
                          let newname = nodeName n ++ "-" ++ show idx
280
                          in (newname, n { nodeGroup = guuid,
281
                                           nodeName = newname}))
282
               nodes [(1::Int)..]
283
      nodemap = Map.fromList nodes'
284
      contnodes = if Map.size nodemap /= ncount
285
                    then error ("Inconsistent node map, duplicates in" ++
286
                                " node name list? Names: " ++
287
                                show (map fst nodes'))
288
                    else GenericContainer nodemap
289
      continsts = GenericContainer Map.empty
290
      networks = GenericContainer Map.empty
291
  let contgroups = GenericContainer $ Map.singleton guuid grp
292
  serial <- arbitrary
293
  cluster <- resize 8 arbitrary
294
  let c = ConfigData version cluster contnodes contgroups continsts networks
295
            serial
296
  return c
297

    
298
-- | FIXME: make an even simpler base version of creating a cluster.
299

    
300
-- | Generates config data with a couple of networks.
301
genConfigDataWithNetworks :: ConfigData -> Gen ConfigData
302
genConfigDataWithNetworks old_cfg = do
303
  num_nets <- choose (0, 3)
304
  -- generate a list of network names (no duplicates)
305
  net_names <- genUniquesList num_nets genName >>= mapM mkNonEmpty
306
  -- generate a random list of networks (possibly with duplicate names)
307
  nets <- vectorOf num_nets genValidNetwork
308
  -- use unique names for the networks
309
  let nets_unique = map ( \(name, net) -> net { networkName = name } )
310
        (zip net_names nets)
311
      net_map = GenericContainer $ Map.fromList
312
        (map (\n -> (networkUuid n, n)) nets_unique)
313
      new_cfg = old_cfg { configNetworks = net_map }
314
  return new_cfg
315

    
316
-- * Test properties
317

    
318
-- | Tests that fillDict behaves correctly
319
prop_fillDict :: [(Int, Int)] -> [(Int, Int)] -> Property
320
prop_fillDict defaults custom =
321
  let d_map = Map.fromList defaults
322
      d_keys = map fst defaults
323
      c_map = Map.fromList custom
324
      c_keys = map fst custom
325
  in conjoin [ printTestCase "Empty custom filling"
326
               (fillDict d_map Map.empty [] == d_map)
327
             , printTestCase "Empty defaults filling"
328
               (fillDict Map.empty c_map [] == c_map)
329
             , printTestCase "Delete all keys"
330
               (fillDict d_map c_map (d_keys++c_keys) == Map.empty)
331
             ]
332

    
333
-- | Test that the serialisation of 'DiskLogicalId', which is
334
-- implemented manually, is idempotent. Since we don't have a
335
-- standalone JSON instance for DiskLogicalId (it's a data type that
336
-- expands over two fields in a JSObject), we test this by actially
337
-- testing entire Disk serialisations. So this tests two things at
338
-- once, basically.
339
prop_Disk_serialisation :: Disk -> Property
340
prop_Disk_serialisation = testSerialisation
341

    
342
-- | Check that node serialisation is idempotent.
343
prop_Node_serialisation :: Node -> Property
344
prop_Node_serialisation = testSerialisation
345

    
346
-- | Check that instance serialisation is idempotent.
347
prop_Inst_serialisation :: Instance -> Property
348
prop_Inst_serialisation = testSerialisation
349

    
350
-- | Check that network serialisation is idempotent.
351
prop_Network_serialisation :: Network -> Property
352
prop_Network_serialisation = testSerialisation
353

    
354
-- | Check config serialisation.
355
prop_Config_serialisation :: Property
356
prop_Config_serialisation =
357
  forAll (choose (0, maxNodes `div` 4) >>= genEmptyCluster) testSerialisation
358

    
359
-- | Custom HUnit test to check the correspondence between Haskell-generated
360
-- networks and their Python decoded, validated and re-encoded version.
361
-- For the technical background of this unit test, check the documentation
362
-- of "case_py_compat_types" of test/hs/Test/Ganeti/Opcodes.hs
363
casePyCompatNetworks :: HUnit.Assertion
364
casePyCompatNetworks = do
365
  let num_networks = 500::Int
366
  networks <- genSample (vectorOf num_networks genValidNetwork)
367
  let networks_with_properties = map getNetworkProperties networks
368
      serialized = J.encode networks
369
  -- check for non-ASCII fields, usually due to 'arbitrary :: String'
370
  mapM_ (\net -> when (any (not . isAscii) (J.encode net)) .
371
                 HUnit.assertFailure $
372
                 "Network has non-ASCII fields: " ++ show net
373
        ) networks
374
  py_stdout <-
375
    runPython "from ganeti import network\n\
376
              \from ganeti import objects\n\
377
              \from ganeti import serializer\n\
378
              \import sys\n\
379
              \net_data = serializer.Load(sys.stdin.read())\n\
380
              \decoded = [objects.Network.FromDict(n) for n in net_data]\n\
381
              \encoded = []\n\
382
              \for net in decoded:\n\
383
              \  a = network.AddressPool(net)\n\
384
              \  encoded.append((a.GetFreeCount(), a.GetReservedCount(), \\\n\
385
              \    net.ToDict()))\n\
386
              \print serializer.Dump(encoded)" serialized
387
    >>= checkPythonResult
388
  let deserialised = J.decode py_stdout::J.Result [(Int, Int, Network)]
389
  decoded <- case deserialised of
390
               J.Ok ops -> return ops
391
               J.Error msg ->
392
                 HUnit.assertFailure ("Unable to decode networks: " ++ msg)
393
                 -- this already raised an expection, but we need it
394
                 -- for proper types
395
                 >> fail "Unable to decode networks"
396
  HUnit.assertEqual "Mismatch in number of returned networks"
397
    (length decoded) (length networks_with_properties)
398
  mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
399
        ) $ zip decoded networks_with_properties
400

    
401
-- | Creates a tuple of the given network combined with some of its properties
402
-- to be compared against the same properties generated by the python code.
403
getNetworkProperties :: Network -> (Int, Int, Network)
404
getNetworkProperties net =
405
  let maybePool = createAddressPool net
406
  in  case maybePool of
407
           (Just pool) -> (getFreeCount pool, getReservedCount pool, net)
408
           Nothing -> (-1, -1, net)
409

    
410
-- | Tests the compatibility between Haskell-serialized node groups and their
411
-- python-decoded and encoded version.
412
casePyCompatNodegroups :: HUnit.Assertion
413
casePyCompatNodegroups = do
414
  let num_groups = 500::Int
415
  groups <- genSample (vectorOf num_groups genNodeGroup)
416
  let serialized = J.encode groups
417
  -- check for non-ASCII fields, usually due to 'arbitrary :: String'
418
  mapM_ (\group -> when (any (not . isAscii) (J.encode group)) .
419
                 HUnit.assertFailure $
420
                 "Node group has non-ASCII fields: " ++ show group
421
        ) groups
422
  py_stdout <-
423
    runPython "from ganeti import objects\n\
424
              \from ganeti import serializer\n\
425
              \import sys\n\
426
              \group_data = serializer.Load(sys.stdin.read())\n\
427
              \decoded = [objects.NodeGroup.FromDict(g) for g in group_data]\n\
428
              \encoded = [g.ToDict() for g in decoded]\n\
429
              \print serializer.Dump(encoded)" serialized
430
    >>= checkPythonResult
431
  let deserialised = J.decode py_stdout::J.Result [NodeGroup]
432
  decoded <- case deserialised of
433
               J.Ok ops -> return ops
434
               J.Error msg ->
435
                 HUnit.assertFailure ("Unable to decode node groups: " ++ msg)
436
                 -- this already raised an expection, but we need it
437
                 -- for proper types
438
                 >> fail "Unable to decode node groups"
439
  HUnit.assertEqual "Mismatch in number of returned node groups"
440
    (length decoded) (length groups)
441
  mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
442
        ) $ zip decoded groups
443

    
444
-- | Generates a node group with up to 3 networks.
445
-- | FIXME: This generates still somewhat completely random data, without normal
446
-- validation rules.
447
genNodeGroup :: Gen NodeGroup
448
genNodeGroup = do
449
  name <- genFQDN
450
  members <- pure []
451
  ndparams <- arbitrary
452
  alloc_policy <- arbitrary
453
  ipolicy <- arbitrary
454
  diskparams <- pure (GenericContainer Map.empty)
455
  num_networks <- choose (0, 3)
456
  net_uuid_list <- vectorOf num_networks (arbitrary::Gen String)
457
  nic_param_list <- vectorOf num_networks (arbitrary::Gen PartialNicParams)
458
  net_map <- pure (GenericContainer . Map.fromList $
459
    zip net_uuid_list nic_param_list)
460
  -- timestamp fields
461
  ctime <- arbitrary
462
  mtime <- arbitrary
463
  uuid <- genFQDN `suchThat` (/= name)
464
  serial <- arbitrary
465
  tags <- Set.fromList <$> genTags
466
  let group = NodeGroup name members ndparams alloc_policy ipolicy diskparams
467
              net_map ctime mtime uuid serial tags
468
  return group
469

    
470
instance Arbitrary NodeGroup where
471
  arbitrary = genNodeGroup
472

    
473
$(genArbitrary ''Ip4Address)
474

    
475
$(genArbitrary ''Ip4Network)
476

    
477
-- | Helper to compute absolute value of an IPv4 address.
478
ip4AddrValue :: Ip4Address -> Integer
479
ip4AddrValue (Ip4Address a b c d) =
480
  fromIntegral a * (2^(24::Integer)) +
481
  fromIntegral b * (2^(16::Integer)) +
482
  fromIntegral c * (2^(8::Integer)) + fromIntegral d
483

    
484
-- | Tests that any difference between IPv4 consecutive addresses is 1.
485
prop_nextIp4Address :: Ip4Address -> Property
486
prop_nextIp4Address ip4 =
487
  ip4AddrValue (nextIp4Address ip4) ==? ip4AddrValue ip4 + 1
488

    
489
-- | IsString instance for 'Ip4Address', to help write the tests.
490
instance IsString Ip4Address where
491
  fromString s =
492
    fromMaybe (error $ "Failed to parse address from " ++ s) (readIp4Address s)
493

    
494
-- | Tests a few simple cases of IPv4 next address.
495
caseNextIp4Address :: HUnit.Assertion
496
caseNextIp4Address = do
497
  HUnit.assertEqual "" "0.0.0.1" $ nextIp4Address "0.0.0.0"
498
  HUnit.assertEqual "" "0.0.0.0" $ nextIp4Address "255.255.255.255"
499
  HUnit.assertEqual "" "1.2.3.5" $ nextIp4Address "1.2.3.4"
500
  HUnit.assertEqual "" "1.3.0.0" $ nextIp4Address "1.2.255.255"
501
  HUnit.assertEqual "" "1.2.255.63" $ nextIp4Address "1.2.255.62"
502

    
503
-- | Tests the compatibility between Haskell-serialized instances and their
504
-- python-decoded and encoded version.
505
-- Note: this can be enhanced with logical validations on the decoded objects
506
casePyCompatInstances :: HUnit.Assertion
507
casePyCompatInstances = do
508
  let num_inst = 500::Int
509
  instances <- genSample (vectorOf num_inst genInst)
510
  let serialized = J.encode instances
511
  -- check for non-ASCII fields, usually due to 'arbitrary :: String'
512
  mapM_ (\inst -> when (any (not . isAscii) (J.encode inst)) .
513
                 HUnit.assertFailure $
514
                 "Instance has non-ASCII fields: " ++ show inst
515
        ) instances
516
  py_stdout <-
517
    runPython "from ganeti import objects\n\
518
              \from ganeti import serializer\n\
519
              \import sys\n\
520
              \inst_data = serializer.Load(sys.stdin.read())\n\
521
              \decoded = [objects.Instance.FromDict(i) for i in inst_data]\n\
522
              \encoded = [i.ToDict() for i in decoded]\n\
523
              \print serializer.Dump(encoded)" serialized
524
    >>= checkPythonResult
525
  let deserialised = J.decode py_stdout::J.Result [Instance]
526
  decoded <- case deserialised of
527
               J.Ok ops -> return ops
528
               J.Error msg ->
529
                 HUnit.assertFailure ("Unable to decode instance: " ++ msg)
530
                 -- this already raised an expection, but we need it
531
                 -- for proper types
532
                 >> fail "Unable to decode instances"
533
  HUnit.assertEqual "Mismatch in number of returned instances"
534
    (length decoded) (length instances)
535
  mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
536
        ) $ zip decoded instances
537

    
538
-- | Tests that the logical ID is correctly found in a plain disk
539
caseIncludeLogicalIdPlain :: HUnit.Assertion
540
caseIncludeLogicalIdPlain =
541
  let vg_name = "xenvg" :: String
542
      lv_name = "1234sdf-qwef-2134-asff-asd2-23145d.data" :: String
543
      d =
544
        Disk (LIDPlain vg_name lv_name) [] "diskname" 1000 DiskRdWr
545
          Nothing Nothing "asdfgr-1234-5123-daf3-sdfw-134f43"
546
  in
547
    HUnit.assertBool "Unable to detect that plain Disk includes logical ID" $
548
      includesLogicalId vg_name lv_name d
549

    
550
-- | Tests that the logical ID is correctly found in a DRBD disk
551
caseIncludeLogicalIdDrbd :: HUnit.Assertion
552
caseIncludeLogicalIdDrbd =
553
  let vg_name = "xenvg" :: String
554
      lv_name = "1234sdf-qwef-2134-asff-asd2-23145d.data" :: String
555
      d = 
556
        Disk
557
          (LIDDrbd8 "node1.example.com" "node2.example.com" 2000 1 5 "secret")
558
          [ Disk (LIDPlain "onevg" "onelv") [] "disk1" 1000 DiskRdWr Nothing
559
              Nothing "145145-asdf-sdf2-2134-asfd-534g2x"
560
          , Disk (LIDPlain vg_name lv_name) [] "disk2" 1000 DiskRdWr Nothing
561
              Nothing "6gd3sd-423f-ag2j-563b-dg34-gj3fse"
562
          ] "diskname" 1000 DiskRdWr Nothing Nothing
563
          "asdfgr-1234-5123-daf3-sdfw-134f43"
564
  in
565
    HUnit.assertBool "Unable to detect that plain Disk includes logical ID" $
566
      includesLogicalId vg_name lv_name d
567

    
568
-- | Tests that the logical ID is correctly NOT found in a plain disk
569
caseNotIncludeLogicalIdPlain :: HUnit.Assertion
570
caseNotIncludeLogicalIdPlain =
571
  let vg_name = "xenvg" :: String
572
      lv_name = "1234sdf-qwef-2134-asff-asd2-23145d.data" :: String
573
      d =
574
        Disk (LIDPlain "othervg" "otherlv") [] "diskname" 1000 DiskRdWr
575
          Nothing Nothing "asdfgr-1234-5123-daf3-sdfw-134f43"
576
  in
577
    HUnit.assertBool "Unable to detect that plain Disk includes logical ID" $
578
      not (includesLogicalId vg_name lv_name d)
579

    
580
testSuite "Objects"
581
  [ 'prop_fillDict
582
  , 'prop_Disk_serialisation
583
  , 'prop_Inst_serialisation
584
  , 'prop_Network_serialisation
585
  , 'prop_Node_serialisation
586
  , 'prop_Config_serialisation
587
  , 'casePyCompatNetworks
588
  , 'casePyCompatNodegroups
589
  , 'casePyCompatInstances
590
  , 'prop_nextIp4Address
591
  , 'caseNextIp4Address
592
  , 'caseIncludeLogicalIdPlain
593
  , 'caseIncludeLogicalIdDrbd
594
  , 'caseNotIncludeLogicalIdPlain
595
  ]