Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / OpCodes.hs @ 93f1e606

History | View | Annotate | Download (30.2 kB)

1
{-# LANGUAGE TemplateHaskell #-}
2
{-# OPTIONS_GHC -fno-warn-orphans #-}
3

    
4
{-| Unittests for ganeti-htools.
5

    
6
-}
7

    
8
{-
9

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

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

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

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

    
27
-}
28

    
29
module Test.Ganeti.OpCodes
30
  ( testOpCodes
31
  , OpCodes.OpCode(..)
32
  ) where
33

    
34
import Test.HUnit as HUnit
35
import Test.QuickCheck as QuickCheck
36

    
37
import Control.Applicative
38
import Control.Monad
39
import Data.Char
40
import Data.List
41
import qualified Data.Map as Map
42
import qualified Text.JSON as J
43
import Text.Printf (printf)
44

    
45
import Test.Ganeti.TestHelper
46
import Test.Ganeti.TestCommon
47
import Test.Ganeti.Types ()
48
import Test.Ganeti.Query.Language ()
49

    
50
import Ganeti.BasicTypes
51
import qualified Ganeti.Constants as C
52
import qualified Ganeti.OpCodes as OpCodes
53
import Ganeti.Types
54
import Ganeti.OpParams
55
import Ganeti.JSON
56

    
57
{-# ANN module "HLint: ignore Use camelCase" #-}
58

    
59
-- * Arbitrary instances
60

    
61
instance (Ord k, Arbitrary k, Arbitrary a) => Arbitrary (Map.Map k a) where
62
  arbitrary = Map.fromList <$> arbitrary
63

    
64
arbitraryOpTagsGet :: Gen OpCodes.OpCode
65
arbitraryOpTagsGet = do
66
  kind <- arbitrary
67
  OpCodes.OpTagsSet kind <$> arbitrary <*> genOpCodesTagName kind
68

    
69
arbitraryOpTagsSet :: Gen OpCodes.OpCode
70
arbitraryOpTagsSet = do
71
  kind <- arbitrary
72
  OpCodes.OpTagsSet kind <$> genTags <*> genOpCodesTagName kind
73

    
74
arbitraryOpTagsDel :: Gen OpCodes.OpCode
75
arbitraryOpTagsDel = do
76
  kind <- arbitrary
77
  OpCodes.OpTagsDel kind <$> genTags <*> genOpCodesTagName kind
78

    
79
$(genArbitrary ''OpCodes.ReplaceDisksMode)
80

    
81
$(genArbitrary ''DiskAccess)
82

    
83
$(genArbitrary ''ImportExportCompression)
84

    
85
instance Arbitrary OpCodes.DiskIndex where
86
  arbitrary = choose (0, C.maxDisks - 1) >>= OpCodes.mkDiskIndex
87

    
88
instance Arbitrary INicParams where
89
  arbitrary = INicParams <$> genMaybe genNameNE <*> genMaybe genName <*>
90
              genMaybe genNameNE <*> genMaybe genNameNE <*>
91
              genMaybe genNameNE <*> genMaybe genName <*>
92
              genMaybe genNameNE
93

    
94
instance Arbitrary IDiskParams where
95
  arbitrary = IDiskParams <$> arbitrary <*> arbitrary <*>
96
              genMaybe genNameNE <*> genMaybe genNameNE <*>
97
              genMaybe genNameNE <*> genMaybe genNameNE <*>
98
              genMaybe genNameNE <*> arbitrary <*> genAndRestArguments
99

    
100
instance Arbitrary RecreateDisksInfo where
101
  arbitrary = oneof [ pure RecreateDisksAll
102
                    , RecreateDisksIndices <$> arbitrary
103
                    , RecreateDisksParams <$> arbitrary
104
                    ]
105

    
106
instance Arbitrary DdmOldChanges where
107
  arbitrary = oneof [ DdmOldIndex <$> arbitrary
108
                    , DdmOldMod   <$> arbitrary
109
                    ]
110

    
111
instance (Arbitrary a) => Arbitrary (SetParamsMods a) where
112
  arbitrary = oneof [ pure SetParamsEmpty
113
                    , SetParamsDeprecated <$> arbitrary
114
                    , SetParamsNew        <$> arbitrary
115
                    ]
116

    
117
instance Arbitrary ExportTarget where
118
  arbitrary = oneof [ ExportTargetLocal <$> genNodeNameNE
119
                    , ExportTargetRemote <$> pure []
120
                    ]
121

    
122
instance Arbitrary OpCodes.OpCode where
123
  arbitrary = do
124
    op_id <- elements OpCodes.allOpIDs
125
    case op_id of
126
      "OP_TEST_DELAY" ->
127
        OpCodes.OpTestDelay <$> arbitrary <*> arbitrary <*>
128
          genNodeNamesNE <*> return Nothing <*> arbitrary
129
      "OP_INSTANCE_REPLACE_DISKS" ->
130
        OpCodes.OpInstanceReplaceDisks <$> genFQDN <*> return Nothing <*>
131
          arbitrary <*> arbitrary <*> arbitrary <*> genDiskIndices <*>
132
          genMaybe genNodeNameNE <*> return Nothing <*> genMaybe genNameNE
133
      "OP_INSTANCE_FAILOVER" ->
134
        OpCodes.OpInstanceFailover <$> genFQDN <*> return Nothing <*>
135
        arbitrary <*> arbitrary <*> genMaybe genNodeNameNE <*>
136
        return Nothing <*> arbitrary <*> arbitrary <*> genMaybe genNameNE
137
      "OP_INSTANCE_MIGRATE" ->
138
        OpCodes.OpInstanceMigrate <$> genFQDN <*> return Nothing <*>
139
          arbitrary <*> arbitrary <*> genMaybe genNodeNameNE <*>
140
          return Nothing <*> arbitrary <*> arbitrary <*> arbitrary <*>
141
          genMaybe genNameNE <*> arbitrary
142
      "OP_TAGS_GET" ->
143
        arbitraryOpTagsGet
144
      "OP_TAGS_SEARCH" ->
145
        OpCodes.OpTagsSearch <$> genNameNE
146
      "OP_TAGS_SET" ->
147
        arbitraryOpTagsSet
148
      "OP_TAGS_DEL" ->
149
        arbitraryOpTagsDel
150
      "OP_CLUSTER_POST_INIT" -> pure OpCodes.OpClusterPostInit
151
      "OP_CLUSTER_RENEW_CRYPTO" -> pure OpCodes.OpClusterRenewCrypto
152
      "OP_CLUSTER_DESTROY" -> pure OpCodes.OpClusterDestroy
153
      "OP_CLUSTER_QUERY" -> pure OpCodes.OpClusterQuery
154
      "OP_CLUSTER_VERIFY" ->
155
        OpCodes.OpClusterVerify <$> arbitrary <*> arbitrary <*>
156
          genListSet Nothing <*> genListSet Nothing <*> arbitrary <*>
157
          genMaybe genNameNE
158
      "OP_CLUSTER_VERIFY_CONFIG" ->
159
        OpCodes.OpClusterVerifyConfig <$> arbitrary <*> arbitrary <*>
160
          genListSet Nothing <*> arbitrary
161
      "OP_CLUSTER_VERIFY_GROUP" ->
162
        OpCodes.OpClusterVerifyGroup <$> genNameNE <*> arbitrary <*>
163
          arbitrary <*> genListSet Nothing <*> genListSet Nothing <*> arbitrary
164
      "OP_CLUSTER_VERIFY_DISKS" -> pure OpCodes.OpClusterVerifyDisks
165
      "OP_GROUP_VERIFY_DISKS" ->
166
        OpCodes.OpGroupVerifyDisks <$> genNameNE
167
      "OP_CLUSTER_REPAIR_DISK_SIZES" ->
168
        OpCodes.OpClusterRepairDiskSizes <$> genNodeNamesNE
169
      "OP_CLUSTER_CONFIG_QUERY" ->
170
        OpCodes.OpClusterConfigQuery <$> genFieldsNE
171
      "OP_CLUSTER_RENAME" ->
172
        OpCodes.OpClusterRename <$> genNameNE
173
      "OP_CLUSTER_SET_PARAMS" ->
174
        OpCodes.OpClusterSetParams
175
          <$> arbitrary                    -- force
176
          <*> emptyMUD                     -- hv_state
177
          <*> emptyMUD                     -- disk_state
178
          <*> arbitrary                    -- vg_name
179
          <*> genMaybe arbitrary           -- enabled_hypervisors
180
          <*> genMaybe genEmptyContainer   -- hvparams
181
          <*> emptyMUD                     -- beparams
182
          <*> genMaybe genEmptyContainer   -- os_hvp
183
          <*> genMaybe genEmptyContainer   -- osparams
184
          <*> genMaybe genEmptyContainer   -- osparams_private_cluster
185
          <*> genMaybe genEmptyContainer   -- diskparams
186
          <*> genMaybe arbitrary           -- candidate_pool_size
187
          <*> genMaybe arbitrary           -- max_running_jobs
188
          <*> arbitrary                    -- uid_pool
189
          <*> arbitrary                    -- add_uids
190
          <*> arbitrary                    -- remove_uids
191
          <*> arbitrary                    -- maintain_node_health
192
          <*> arbitrary                    -- prealloc_wipe_disks
193
          <*> arbitrary                    -- nicparams
194
          <*> emptyMUD                     -- ndparams
195
          <*> emptyMUD                     -- ipolicy
196
          <*> arbitrary                    -- drbd_helper
197
          <*> arbitrary                    -- default_iallocator
198
          <*> emptyMUD                     -- default_iallocator_params
199
          <*> arbitrary                    -- master_netdev
200
          <*> arbitrary                    -- master_netmask
201
          <*> arbitrary                    -- reserved_lvs
202
          <*> arbitrary                    -- hidden_os
203
          <*> arbitrary                    -- blacklisted_os
204
          <*> arbitrary                    -- use_external_mip_script
205
          <*> arbitrary                    -- enabled_disk_templates
206
          <*> arbitrary                    -- modify_etc_hosts
207
          <*> genMaybe genName             -- file_storage_dir
208
          <*> genMaybe genName             -- shared_file_storage_dir
209
          <*> genMaybe genName             -- gluster_file_storage_dir
210
          <*> arbitrary                    -- instance_communication_network
211
      "OP_CLUSTER_REDIST_CONF" -> pure OpCodes.OpClusterRedistConf
212
      "OP_CLUSTER_ACTIVATE_MASTER_IP" ->
213
        pure OpCodes.OpClusterActivateMasterIp
214
      "OP_CLUSTER_DEACTIVATE_MASTER_IP" ->
215
        pure OpCodes.OpClusterDeactivateMasterIp
216
      "OP_QUERY" ->
217
        OpCodes.OpQuery <$> arbitrary <*> arbitrary <*> arbitrary <*>
218
        pure Nothing
219
      "OP_QUERY_FIELDS" ->
220
        OpCodes.OpQueryFields <$> arbitrary <*> arbitrary
221
      "OP_OOB_COMMAND" ->
222
        OpCodes.OpOobCommand <$> genNodeNamesNE <*> return Nothing <*>
223
          arbitrary <*> arbitrary <*> arbitrary <*>
224
          (arbitrary `suchThat` (>0))
225
      "OP_NODE_REMOVE" ->
226
        OpCodes.OpNodeRemove <$> genNodeNameNE <*> return Nothing
227
      "OP_NODE_ADD" ->
228
        OpCodes.OpNodeAdd <$> genNodeNameNE <*> emptyMUD <*> emptyMUD <*>
229
          genMaybe genNameNE <*> genMaybe genNameNE <*> arbitrary <*>
230
          genMaybe genNameNE <*> arbitrary <*> arbitrary <*> emptyMUD
231
      "OP_NODE_QUERYVOLS" ->
232
        OpCodes.OpNodeQueryvols <$> arbitrary <*> genNodeNamesNE
233
      "OP_NODE_QUERY_STORAGE" ->
234
        OpCodes.OpNodeQueryStorage <$> arbitrary <*> arbitrary <*>
235
          genNodeNamesNE <*> genMaybe genNameNE
236
      "OP_NODE_MODIFY_STORAGE" ->
237
        OpCodes.OpNodeModifyStorage <$> genNodeNameNE <*> return Nothing <*>
238
          arbitrary <*> genMaybe genNameNE <*> pure emptyJSObject
239
      "OP_REPAIR_NODE_STORAGE" ->
240
        OpCodes.OpRepairNodeStorage <$> genNodeNameNE <*> return Nothing <*>
241
          arbitrary <*> genMaybe genNameNE <*> arbitrary
242
      "OP_NODE_SET_PARAMS" ->
243
        OpCodes.OpNodeSetParams <$> genNodeNameNE <*> return Nothing <*>
244
          arbitrary <*> emptyMUD <*> emptyMUD <*> arbitrary <*> arbitrary <*>
245
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
246
          genMaybe genNameNE <*> emptyMUD <*> arbitrary
247
      "OP_NODE_POWERCYCLE" ->
248
        OpCodes.OpNodePowercycle <$> genNodeNameNE <*> return Nothing <*>
249
          arbitrary
250
      "OP_NODE_MIGRATE" ->
251
        OpCodes.OpNodeMigrate <$> genNodeNameNE <*> return Nothing <*>
252
          arbitrary <*> arbitrary <*> genMaybe genNodeNameNE <*>
253
          return Nothing <*> arbitrary <*> arbitrary <*> genMaybe genNameNE
254
      "OP_NODE_EVACUATE" ->
255
        OpCodes.OpNodeEvacuate <$> arbitrary <*> genNodeNameNE <*>
256
          return Nothing <*> genMaybe genNodeNameNE <*> return Nothing <*>
257
          genMaybe genNameNE <*> arbitrary
258
      "OP_INSTANCE_CREATE" ->
259
        OpCodes.OpInstanceCreate
260
          <$> genFQDN                         -- instance_name
261
          <*> arbitrary                       -- force_variant
262
          <*> arbitrary                       -- wait_for_sync
263
          <*> arbitrary                       -- name_check
264
          <*> arbitrary                       -- ignore_ipolicy
265
          <*> arbitrary                       -- opportunistic_locking
266
          <*> pure emptyJSObject              -- beparams
267
          <*> arbitrary                       -- disks
268
          <*> arbitrary                       -- disk_template
269
          <*> arbitrary                       -- file_driver
270
          <*> genMaybe genNameNE              -- file_storage_dir
271
          <*> pure emptyJSObject              -- hvparams
272
          <*> arbitrary                       -- hypervisor
273
          <*> genMaybe genNameNE              -- iallocator
274
          <*> arbitrary                       -- identify_defaults
275
          <*> arbitrary                       -- ip_check
276
          <*> arbitrary                       -- conflicts_check
277
          <*> arbitrary                       -- mode
278
          <*> arbitrary                       -- nics
279
          <*> arbitrary                       -- no_install
280
          <*> pure emptyJSObject              -- osparams
281
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_private
282
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_secret
283
          <*> genMaybe genNameNE              -- os_type
284
          <*> genMaybe genNodeNameNE          -- pnode
285
          <*> return Nothing                  -- pnode_uuid
286
          <*> genMaybe genNodeNameNE          -- snode
287
          <*> return Nothing                  -- snode_uuid
288
          <*> genMaybe (pure [])              -- source_handshake
289
          <*> genMaybe genNodeNameNE          -- source_instance_name
290
          <*> arbitrary                       -- source_shutdown_timeout
291
          <*> genMaybe genNodeNameNE          -- source_x509_ca
292
          <*> return Nothing                  -- src_node
293
          <*> genMaybe genNodeNameNE          -- src_node_uuid
294
          <*> genMaybe genNameNE              -- src_path
295
          <*> arbitrary                       -- compress
296
          <*> arbitrary                       -- start
297
          <*> (genTags >>= mapM mkNonEmpty)   -- tags
298
          <*> arbitrary                       -- instance_communication
299
      "OP_INSTANCE_MULTI_ALLOC" ->
300
        OpCodes.OpInstanceMultiAlloc <$> arbitrary <*> genMaybe genNameNE <*>
301
        pure []
302
      "OP_INSTANCE_REINSTALL" ->
303
        OpCodes.OpInstanceReinstall <$> genFQDN <*> return Nothing <*>
304
          arbitrary <*> genMaybe genNameNE <*> genMaybe (pure emptyJSObject)
305
          <*> genMaybe arbitraryPrivateJSObj <*> genMaybe arbitraryPrivateJSObj
306
      "OP_INSTANCE_REMOVE" ->
307
        OpCodes.OpInstanceRemove <$> genFQDN <*> return Nothing <*>
308
          arbitrary <*> arbitrary
309
      "OP_INSTANCE_RENAME" ->
310
        OpCodes.OpInstanceRename <$> genFQDN <*> return Nothing <*>
311
          genNodeNameNE <*> arbitrary <*> arbitrary
312
      "OP_INSTANCE_STARTUP" ->
313
        OpCodes.OpInstanceStartup <$> genFQDN <*> return Nothing <*>
314
          arbitrary <*> arbitrary <*> pure emptyJSObject <*>
315
          pure emptyJSObject <*> arbitrary <*> arbitrary
316
      "OP_INSTANCE_SHUTDOWN" ->
317
        OpCodes.OpInstanceShutdown <$> genFQDN <*> return Nothing <*>
318
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
319
      "OP_INSTANCE_REBOOT" ->
320
        OpCodes.OpInstanceReboot <$> genFQDN <*> return Nothing <*>
321
          arbitrary <*> arbitrary <*> arbitrary
322
      "OP_INSTANCE_MOVE" ->
323
        OpCodes.OpInstanceMove <$> genFQDN <*> return Nothing <*>
324
          arbitrary <*> arbitrary <*> genNodeNameNE <*> return Nothing <*>
325
          arbitrary <*> arbitrary
326
      "OP_INSTANCE_CONSOLE" -> OpCodes.OpInstanceConsole <$> genFQDN <*>
327
          return Nothing
328
      "OP_INSTANCE_ACTIVATE_DISKS" ->
329
        OpCodes.OpInstanceActivateDisks <$> genFQDN <*> return Nothing <*>
330
          arbitrary <*> arbitrary
331
      "OP_INSTANCE_DEACTIVATE_DISKS" ->
332
        OpCodes.OpInstanceDeactivateDisks <$> genFQDN <*> return Nothing <*>
333
          arbitrary
334
      "OP_INSTANCE_RECREATE_DISKS" ->
335
        OpCodes.OpInstanceRecreateDisks <$> genFQDN <*> return Nothing <*>
336
          arbitrary <*> genNodeNamesNE <*> return Nothing <*>
337
          genMaybe genNameNE
338
      "OP_INSTANCE_QUERY_DATA" ->
339
        OpCodes.OpInstanceQueryData <$> arbitrary <*>
340
          genNodeNamesNE <*> arbitrary
341
      "OP_INSTANCE_SET_PARAMS" ->
342
        OpCodes.OpInstanceSetParams
343
          <$> genFQDN                         -- instance_name
344
          <*> return Nothing                  -- instance_uuid
345
          <*> arbitrary                       -- force
346
          <*> arbitrary                       -- force_variant
347
          <*> arbitrary                       -- ignore_ipolicy
348
          <*> arbitrary                       -- nics
349
          <*> arbitrary                       -- disks
350
          <*> pure emptyJSObject              -- beparams
351
          <*> arbitrary                       -- runtime_mem
352
          <*> pure emptyJSObject              -- hvparams
353
          <*> arbitrary                       -- disk_template
354
          <*> genMaybe genNodeNameNE          -- pnode
355
          <*> return Nothing                  -- pnode_uuid
356
          <*> genMaybe genNodeNameNE          -- remote_node
357
          <*> return Nothing                  -- remote_node_uuid
358
          <*> genMaybe genNameNE              -- os_name
359
          <*> pure emptyJSObject              -- osparams
360
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_private
361
          <*> arbitrary                       -- wait_for_sync
362
          <*> arbitrary                       -- offline
363
          <*> arbitrary                       -- conflicts_check
364
          <*> arbitrary                       -- hotplug
365
          <*> arbitrary                       -- hotplug_if_possible
366
          <*> arbitrary                       -- instance_communication
367
      "OP_INSTANCE_GROW_DISK" ->
368
        OpCodes.OpInstanceGrowDisk <$> genFQDN <*> return Nothing <*>
369
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
370
      "OP_INSTANCE_CHANGE_GROUP" ->
371
        OpCodes.OpInstanceChangeGroup <$> genFQDN <*> return Nothing <*>
372
          arbitrary <*> genMaybe genNameNE <*>
373
          genMaybe (resize maxNodes (listOf genNameNE))
374
      "OP_GROUP_ADD" ->
375
        OpCodes.OpGroupAdd <$> genNameNE <*> arbitrary <*>
376
          emptyMUD <*> genMaybe genEmptyContainer <*>
377
          emptyMUD <*> emptyMUD <*> emptyMUD
378
      "OP_GROUP_ASSIGN_NODES" ->
379
        OpCodes.OpGroupAssignNodes <$> genNameNE <*> arbitrary <*>
380
          genNodeNamesNE <*> return Nothing
381
      "OP_GROUP_SET_PARAMS" ->
382
        OpCodes.OpGroupSetParams <$> genNameNE <*> arbitrary <*>
383
          emptyMUD <*> genMaybe genEmptyContainer <*>
384
          emptyMUD <*> emptyMUD <*> emptyMUD
385
      "OP_GROUP_REMOVE" ->
386
        OpCodes.OpGroupRemove <$> genNameNE
387
      "OP_GROUP_RENAME" ->
388
        OpCodes.OpGroupRename <$> genNameNE <*> genNameNE
389
      "OP_GROUP_EVACUATE" ->
390
        OpCodes.OpGroupEvacuate <$> genNameNE <*> arbitrary <*>
391
          genMaybe genNameNE <*> genMaybe genNamesNE
392
      "OP_OS_DIAGNOSE" ->
393
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
394
      "OP_EXT_STORAGE_DIAGNOSE" ->
395
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
396
      "OP_BACKUP_PREPARE" ->
397
        OpCodes.OpBackupPrepare <$> genFQDN <*> return Nothing <*> arbitrary
398
      "OP_BACKUP_EXPORT" ->
399
        OpCodes.OpBackupExport <$> genFQDN <*> return Nothing <*>
400
          arbitrary <*> arbitrary <*> arbitrary <*> return Nothing <*>
401
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
402
          genMaybe (pure []) <*> genMaybe genNameNE
403
      "OP_BACKUP_REMOVE" ->
404
        OpCodes.OpBackupRemove <$> genFQDN <*> return Nothing
405
      "OP_TEST_ALLOCATOR" ->
406
        OpCodes.OpTestAllocator <$> arbitrary <*> arbitrary <*>
407
          genNameNE <*> genMaybe (pure []) <*> genMaybe (pure []) <*>
408
          arbitrary <*> genMaybe genNameNE <*>
409
          (genTags >>= mapM mkNonEmpty) <*>
410
          arbitrary <*> arbitrary <*> genMaybe genNameNE <*>
411
          arbitrary <*> genMaybe genNodeNamesNE <*> arbitrary <*>
412
          genMaybe genNamesNE <*> arbitrary <*> arbitrary
413
      "OP_TEST_JQUEUE" ->
414
        OpCodes.OpTestJqueue <$> arbitrary <*> arbitrary <*>
415
          resize 20 (listOf genFQDN) <*> arbitrary
416
      "OP_TEST_DUMMY" ->
417
        OpCodes.OpTestDummy <$> pure J.JSNull <*> pure J.JSNull <*>
418
          pure J.JSNull <*> pure J.JSNull
419
      "OP_NETWORK_ADD" ->
420
        OpCodes.OpNetworkAdd <$> genNameNE <*> genIPv4Network <*>
421
          genMaybe genIPv4Address <*> pure Nothing <*> pure Nothing <*>
422
          genMaybe genMacPrefix <*> genMaybe (listOf genIPv4Address) <*>
423
          arbitrary <*> (genTags >>= mapM mkNonEmpty)
424
      "OP_NETWORK_REMOVE" ->
425
        OpCodes.OpNetworkRemove <$> genNameNE <*> arbitrary
426
      "OP_NETWORK_SET_PARAMS" ->
427
        OpCodes.OpNetworkSetParams <$> genNameNE <*>
428
          genMaybe genIPv4Address <*> pure Nothing <*> pure Nothing <*>
429
          genMaybe genMacPrefix <*> genMaybe (listOf genIPv4Address) <*>
430
          genMaybe (listOf genIPv4Address)
431
      "OP_NETWORK_CONNECT" ->
432
        OpCodes.OpNetworkConnect <$> genNameNE <*> genNameNE <*>
433
          arbitrary <*> genNameNE <*> arbitrary
434
      "OP_NETWORK_DISCONNECT" ->
435
        OpCodes.OpNetworkDisconnect <$> genNameNE <*> genNameNE
436
      "OP_RESTRICTED_COMMAND" ->
437
        OpCodes.OpRestrictedCommand <$> arbitrary <*> genNodeNamesNE <*>
438
          return Nothing <*> genNameNE
439
      _ -> fail $ "Undefined arbitrary for opcode " ++ op_id
440

    
441
-- | Generates one element of a reason trail
442
genReasonElem :: Gen ReasonElem
443
genReasonElem = (,,) <$> genFQDN <*> genFQDN <*> arbitrary
444

    
445
-- | Generates a reason trail
446
genReasonTrail :: Gen ReasonTrail
447
genReasonTrail = do
448
  size <- choose (0, 10)
449
  vectorOf size genReasonElem
450

    
451
instance Arbitrary OpCodes.CommonOpParams where
452
  arbitrary = OpCodes.CommonOpParams <$> arbitrary <*> arbitrary <*>
453
                arbitrary <*> resize 5 arbitrary <*> genMaybe genName <*>
454
                genReasonTrail
455

    
456
-- * Helper functions
457

    
458
-- | Empty JSObject.
459
emptyJSObject :: J.JSObject J.JSValue
460
emptyJSObject = J.toJSObject []
461

    
462
-- | Empty maybe unchecked dictionary.
463
emptyMUD :: Gen (Maybe (J.JSObject J.JSValue))
464
emptyMUD = genMaybe $ pure emptyJSObject
465

    
466
-- | Generates an empty container.
467
genEmptyContainer :: (Ord a) => Gen (GenericContainer a b)
468
genEmptyContainer = pure . GenericContainer $ Map.fromList []
469

    
470
-- | Generates list of disk indices.
471
genDiskIndices :: Gen [DiskIndex]
472
genDiskIndices = do
473
  cnt <- choose (0, C.maxDisks)
474
  genUniquesList cnt arbitrary
475

    
476
-- | Generates a list of node names.
477
genNodeNames :: Gen [String]
478
genNodeNames = resize maxNodes (listOf genFQDN)
479

    
480
-- | Generates a list of node names in non-empty string type.
481
genNodeNamesNE :: Gen [NonEmptyString]
482
genNodeNamesNE = genNodeNames >>= mapM mkNonEmpty
483

    
484
-- | Gets a node name in non-empty type.
485
genNodeNameNE :: Gen NonEmptyString
486
genNodeNameNE = genFQDN >>= mkNonEmpty
487

    
488
-- | Gets a name (non-fqdn) in non-empty type.
489
genNameNE :: Gen NonEmptyString
490
genNameNE = genName >>= mkNonEmpty
491

    
492
-- | Gets a list of names (non-fqdn) in non-empty type.
493
genNamesNE :: Gen [NonEmptyString]
494
genNamesNE = resize maxNodes (listOf genNameNE)
495

    
496
-- | Returns a list of non-empty fields.
497
genFieldsNE :: Gen [NonEmptyString]
498
genFieldsNE = genFields >>= mapM mkNonEmpty
499

    
500
-- | Generate a 3-byte MAC prefix.
501
genMacPrefix :: Gen NonEmptyString
502
genMacPrefix = do
503
  octets <- vectorOf 3 $ choose (0::Int, 255)
504
  mkNonEmpty . intercalate ":" $ map (printf "%02x") octets
505

    
506
-- | JSObject of arbitrary data.
507
--
508
-- Since JSValue does not implement Arbitrary, I'll simply generate
509
-- (String, String) objects.
510
arbitraryPrivateJSObj :: Gen (J.JSObject (Private J.JSValue))
511
arbitraryPrivateJSObj =
512
  constructor <$> (fromNonEmpty <$> genNameNE)
513
              <*> (fromNonEmpty <$> genNameNE)
514
    where constructor k v = showPrivateJSObject [(k, v)]
515

    
516
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering.
517
$(genArbitrary ''OpCodes.MetaOpCode)
518

    
519
-- | Small helper to check for a failed JSON deserialisation
520
isJsonError :: J.Result a -> Bool
521
isJsonError (J.Error _) = True
522
isJsonError _           = False
523

    
524
-- * Test cases
525

    
526
-- | Check that opcode serialization is idempotent.
527
prop_serialization :: OpCodes.OpCode -> Property
528
prop_serialization = testSerialisation
529

    
530
-- | Check that Python and Haskell defined the same opcode list.
531
case_AllDefined :: HUnit.Assertion
532
case_AllDefined = do
533
  py_stdout <-
534
     runPython "from ganeti import opcodes\n\
535
               \from ganeti import serializer\n\
536
               \import sys\n\
537
               \print serializer.Dump([opid for opid in opcodes.OP_MAPPING])\n"
538
               ""
539
     >>= checkPythonResult
540
  py_ops <- case J.decode py_stdout::J.Result [String] of
541
               J.Ok ops -> return ops
542
               J.Error msg ->
543
                 HUnit.assertFailure ("Unable to decode opcode names: " ++ msg)
544
                 -- this already raised an expection, but we need it
545
                 -- for proper types
546
                 >> fail "Unable to decode opcode names"
547
  let hs_ops = sort OpCodes.allOpIDs
548
      extra_py = py_ops \\ hs_ops
549
      extra_hs = hs_ops \\ py_ops
550
  HUnit.assertBool ("Missing OpCodes from the Haskell code:\n" ++
551
                    unlines extra_py) (null extra_py)
552
  HUnit.assertBool ("Extra OpCodes in the Haskell code code:\n" ++
553
                    unlines extra_hs) (null extra_hs)
554

    
555
-- | Custom HUnit test case that forks a Python process and checks
556
-- correspondence between Haskell-generated OpCodes and their Python
557
-- decoded, validated and re-encoded version.
558
--
559
-- Note that we have a strange beast here: since launching Python is
560
-- expensive, we don't do this via a usual QuickProperty, since that's
561
-- slow (I've tested it, and it's indeed quite slow). Rather, we use a
562
-- single HUnit assertion, and in it we manually use QuickCheck to
563
-- generate 500 opcodes times the number of defined opcodes, which
564
-- then we pass in bulk to Python. The drawbacks to this method are
565
-- two fold: we cannot control the number of generated opcodes, since
566
-- HUnit assertions don't get access to the test options, and for the
567
-- same reason we can't run a repeatable seed. We should probably find
568
-- a better way to do this, for example by having a
569
-- separately-launched Python process (if not running the tests would
570
-- be skipped).
571
case_py_compat_types :: HUnit.Assertion
572
case_py_compat_types = do
573
  let num_opcodes = length OpCodes.allOpIDs * 100
574
  opcodes <- genSample (vectorOf num_opcodes
575
                                   (arbitrary::Gen OpCodes.MetaOpCode))
576
  let with_sum = map (\o -> (OpCodes.opSummary $
577
                             OpCodes.metaOpCode o, o)) opcodes
578
      serialized = J.encode opcodes
579
  -- check for non-ASCII fields, usually due to 'arbitrary :: String'
580
  mapM_ (\op -> when (any (not . isAscii) (J.encode op)) .
581
                HUnit.assertFailure $
582
                  "OpCode has non-ASCII fields: " ++ show op
583
        ) opcodes
584
  py_stdout <-
585
     runPython "from ganeti import opcodes\n\
586
               \from ganeti import serializer\n\
587
               \import sys\n\
588
               \op_data = serializer.Load(sys.stdin.read())\n\
589
               \decoded = [opcodes.OpCode.LoadOpCode(o) for o in op_data]\n\
590
               \for op in decoded:\n\
591
               \  op.Validate(True)\n\
592
               \encoded = [(op.Summary(), op.__getstate__())\n\
593
               \           for op in decoded]\n\
594
               \print serializer.Dump(\
595
               \  encoded,\
596
               \  private_encoder=serializer.EncodeWithPrivateFields)"
597
               serialized
598
     >>= checkPythonResult
599
  let deserialised =
600
        J.decode py_stdout::J.Result [(String, OpCodes.MetaOpCode)]
601
  decoded <- case deserialised of
602
               J.Ok ops -> return ops
603
               J.Error msg ->
604
                 HUnit.assertFailure ("Unable to decode opcodes: " ++ msg)
605
                 -- this already raised an expection, but we need it
606
                 -- for proper types
607
                 >> fail "Unable to decode opcodes"
608
  HUnit.assertEqual "Mismatch in number of returned opcodes"
609
    (length decoded) (length with_sum)
610
  mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding")
611
        ) $ zip with_sum decoded
612

    
613
-- | Custom HUnit test case that forks a Python process and checks
614
-- correspondence between Haskell OpCodes fields and their Python
615
-- equivalent.
616
case_py_compat_fields :: HUnit.Assertion
617
case_py_compat_fields = do
618
  let hs_fields = sort $ map (\op_id -> (op_id, OpCodes.allOpFields op_id))
619
                         OpCodes.allOpIDs
620
  py_stdout <-
621
     runPython "from ganeti import opcodes\n\
622
               \import sys\n\
623
               \from ganeti import serializer\n\
624
               \fields = [(k, sorted([p[0] for p in v.OP_PARAMS]))\n\
625
               \           for k, v in opcodes.OP_MAPPING.items()]\n\
626
               \print serializer.Dump(fields)" ""
627
     >>= checkPythonResult
628
  let deserialised = J.decode py_stdout::J.Result [(String, [String])]
629
  py_fields <- case deserialised of
630
                 J.Ok v -> return $ sort v
631
                 J.Error msg ->
632
                   HUnit.assertFailure ("Unable to decode op fields: " ++ msg)
633
                   -- this already raised an expection, but we need it
634
                   -- for proper types
635
                   >> fail "Unable to decode op fields"
636
  HUnit.assertEqual "Mismatch in number of returned opcodes"
637
    (length hs_fields) (length py_fields)
638
  HUnit.assertEqual "Mismatch in defined OP_IDs"
639
    (map fst hs_fields) (map fst py_fields)
640
  mapM_ (\((py_id, py_flds), (hs_id, hs_flds)) -> do
641
           HUnit.assertEqual "Mismatch in OP_ID" py_id hs_id
642
           HUnit.assertEqual ("Mismatch in fields for " ++ hs_id)
643
             py_flds hs_flds
644
        ) $ zip hs_fields py_fields
645

    
646
-- | Checks that setOpComment works correctly.
647
prop_setOpComment :: OpCodes.MetaOpCode -> String -> Property
648
prop_setOpComment op comment =
649
  let (OpCodes.MetaOpCode common _) = OpCodes.setOpComment comment op
650
  in OpCodes.opComment common ==? Just comment
651

    
652
-- | Tests wrong (negative) disk index.
653
prop_mkDiskIndex_fail :: QuickCheck.Positive Int -> Property
654
prop_mkDiskIndex_fail (Positive i) =
655
  case mkDiskIndex (negate i) of
656
    Bad msg -> printTestCase "error message " $
657
               "Invalid value" `isPrefixOf` msg
658
    Ok v -> failTest $ "Succeeded to build disk index '" ++ show v ++
659
                       "' from negative value " ++ show (negate i)
660

    
661
-- | Tests a few invalid 'readRecreateDisks' cases.
662
case_readRecreateDisks_fail :: Assertion
663
case_readRecreateDisks_fail = do
664
  assertBool "null" $
665
    isJsonError (J.readJSON J.JSNull::J.Result RecreateDisksInfo)
666
  assertBool "string" $
667
    isJsonError (J.readJSON (J.showJSON "abc")::J.Result RecreateDisksInfo)
668

    
669
-- | Tests a few invalid 'readDdmOldChanges' cases.
670
case_readDdmOldChanges_fail :: Assertion
671
case_readDdmOldChanges_fail = do
672
  assertBool "null" $
673
    isJsonError (J.readJSON J.JSNull::J.Result DdmOldChanges)
674
  assertBool "string" $
675
    isJsonError (J.readJSON (J.showJSON "abc")::J.Result DdmOldChanges)
676

    
677
-- | Tests a few invalid 'readExportTarget' cases.
678
case_readExportTarget_fail :: Assertion
679
case_readExportTarget_fail = do
680
  assertBool "null" $
681
    isJsonError (J.readJSON J.JSNull::J.Result ExportTarget)
682
  assertBool "int" $
683
    isJsonError (J.readJSON (J.showJSON (5::Int))::J.Result ExportTarget)
684

    
685
testSuite "OpCodes"
686
            [ 'prop_serialization
687
            , 'case_AllDefined
688
            , 'case_py_compat_types
689
            , 'case_py_compat_fields
690
            , 'prop_setOpComment
691
            , 'prop_mkDiskIndex_fail
692
            , 'case_readRecreateDisks_fail
693
            , 'case_readDdmOldChanges_fail
694
            , 'case_readExportTarget_fail
695
            ]