Statistics
| Branch: | Tag: | Revision:

root / test / hs / Test / Ganeti / OpCodes.hs @ 22096c14

History | View | Annotate | Download (30.3 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 <*> 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
          <*> genMaybe genMacPrefix        -- mac_prefix
200
          <*> arbitrary                    -- master_netdev
201
          <*> arbitrary                    -- master_netmask
202
          <*> arbitrary                    -- reserved_lvs
203
          <*> arbitrary                    -- hidden_os
204
          <*> arbitrary                    -- blacklisted_os
205
          <*> arbitrary                    -- use_external_mip_script
206
          <*> arbitrary                    -- enabled_disk_templates
207
          <*> arbitrary                    -- modify_etc_hosts
208
          <*> genMaybe genName             -- file_storage_dir
209
          <*> genMaybe genName             -- shared_file_storage_dir
210
          <*> genMaybe genName             -- gluster_file_storage_dir
211
          <*> arbitrary                    -- instance_communication_network
212
          <*> arbitrary                    -- zeroing_image
213
      "OP_CLUSTER_REDIST_CONF" -> pure OpCodes.OpClusterRedistConf
214
      "OP_CLUSTER_ACTIVATE_MASTER_IP" ->
215
        pure OpCodes.OpClusterActivateMasterIp
216
      "OP_CLUSTER_DEACTIVATE_MASTER_IP" ->
217
        pure OpCodes.OpClusterDeactivateMasterIp
218
      "OP_QUERY" ->
219
        OpCodes.OpQuery <$> arbitrary <*> arbitrary <*> arbitrary <*>
220
        pure Nothing
221
      "OP_QUERY_FIELDS" ->
222
        OpCodes.OpQueryFields <$> arbitrary <*> arbitrary
223
      "OP_OOB_COMMAND" ->
224
        OpCodes.OpOobCommand <$> genNodeNamesNE <*> return Nothing <*>
225
          arbitrary <*> arbitrary <*> arbitrary <*>
226
          (arbitrary `suchThat` (>0))
227
      "OP_NODE_REMOVE" ->
228
        OpCodes.OpNodeRemove <$> genNodeNameNE <*> return Nothing
229
      "OP_NODE_ADD" ->
230
        OpCodes.OpNodeAdd <$> genNodeNameNE <*> emptyMUD <*> emptyMUD <*>
231
          genMaybe genNameNE <*> genMaybe genNameNE <*> arbitrary <*>
232
          genMaybe genNameNE <*> arbitrary <*> arbitrary <*> emptyMUD
233
      "OP_NODE_QUERYVOLS" ->
234
        OpCodes.OpNodeQueryvols <$> arbitrary <*> genNodeNamesNE
235
      "OP_NODE_QUERY_STORAGE" ->
236
        OpCodes.OpNodeQueryStorage <$> arbitrary <*> arbitrary <*>
237
          genNodeNamesNE <*> genMaybe genNameNE
238
      "OP_NODE_MODIFY_STORAGE" ->
239
        OpCodes.OpNodeModifyStorage <$> genNodeNameNE <*> return Nothing <*>
240
          arbitrary <*> genMaybe genNameNE <*> pure emptyJSObject
241
      "OP_REPAIR_NODE_STORAGE" ->
242
        OpCodes.OpRepairNodeStorage <$> genNodeNameNE <*> return Nothing <*>
243
          arbitrary <*> genMaybe genNameNE <*> arbitrary
244
      "OP_NODE_SET_PARAMS" ->
245
        OpCodes.OpNodeSetParams <$> genNodeNameNE <*> return Nothing <*>
246
          arbitrary <*> emptyMUD <*> emptyMUD <*> arbitrary <*> arbitrary <*>
247
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
248
          genMaybe genNameNE <*> emptyMUD <*> arbitrary
249
      "OP_NODE_POWERCYCLE" ->
250
        OpCodes.OpNodePowercycle <$> genNodeNameNE <*> return Nothing <*>
251
          arbitrary
252
      "OP_NODE_MIGRATE" ->
253
        OpCodes.OpNodeMigrate <$> genNodeNameNE <*> return Nothing <*>
254
          arbitrary <*> arbitrary <*> genMaybe genNodeNameNE <*>
255
          return Nothing <*> arbitrary <*> arbitrary <*> genMaybe genNameNE
256
      "OP_NODE_EVACUATE" ->
257
        OpCodes.OpNodeEvacuate <$> arbitrary <*> genNodeNameNE <*>
258
          return Nothing <*> genMaybe genNodeNameNE <*> return Nothing <*>
259
          genMaybe genNameNE <*> arbitrary
260
      "OP_INSTANCE_CREATE" ->
261
        OpCodes.OpInstanceCreate
262
          <$> genFQDN                         -- instance_name
263
          <*> arbitrary                       -- force_variant
264
          <*> arbitrary                       -- wait_for_sync
265
          <*> arbitrary                       -- name_check
266
          <*> arbitrary                       -- ignore_ipolicy
267
          <*> arbitrary                       -- opportunistic_locking
268
          <*> pure emptyJSObject              -- beparams
269
          <*> arbitrary                       -- disks
270
          <*> arbitrary                       -- disk_template
271
          <*> arbitrary                       -- file_driver
272
          <*> genMaybe genNameNE              -- file_storage_dir
273
          <*> pure emptyJSObject              -- hvparams
274
          <*> arbitrary                       -- hypervisor
275
          <*> genMaybe genNameNE              -- iallocator
276
          <*> arbitrary                       -- identify_defaults
277
          <*> arbitrary                       -- ip_check
278
          <*> arbitrary                       -- conflicts_check
279
          <*> arbitrary                       -- mode
280
          <*> arbitrary                       -- nics
281
          <*> arbitrary                       -- no_install
282
          <*> pure emptyJSObject              -- osparams
283
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_private
284
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_secret
285
          <*> genMaybe genNameNE              -- os_type
286
          <*> genMaybe genNodeNameNE          -- pnode
287
          <*> return Nothing                  -- pnode_uuid
288
          <*> genMaybe genNodeNameNE          -- snode
289
          <*> return Nothing                  -- snode_uuid
290
          <*> genMaybe (pure [])              -- source_handshake
291
          <*> genMaybe genNodeNameNE          -- source_instance_name
292
          <*> arbitrary                       -- source_shutdown_timeout
293
          <*> genMaybe genNodeNameNE          -- source_x509_ca
294
          <*> return Nothing                  -- src_node
295
          <*> genMaybe genNodeNameNE          -- src_node_uuid
296
          <*> genMaybe genNameNE              -- src_path
297
          <*> arbitrary                       -- compress
298
          <*> arbitrary                       -- start
299
          <*> (genTags >>= mapM mkNonEmpty)   -- tags
300
          <*> arbitrary                       -- instance_communication
301
      "OP_INSTANCE_MULTI_ALLOC" ->
302
        OpCodes.OpInstanceMultiAlloc <$> arbitrary <*> genMaybe genNameNE <*>
303
        pure []
304
      "OP_INSTANCE_REINSTALL" ->
305
        OpCodes.OpInstanceReinstall <$> genFQDN <*> return Nothing <*>
306
          arbitrary <*> genMaybe genNameNE <*> genMaybe (pure emptyJSObject)
307
          <*> genMaybe arbitraryPrivateJSObj <*> genMaybe arbitraryPrivateJSObj
308
      "OP_INSTANCE_REMOVE" ->
309
        OpCodes.OpInstanceRemove <$> genFQDN <*> return Nothing <*>
310
          arbitrary <*> arbitrary
311
      "OP_INSTANCE_RENAME" ->
312
        OpCodes.OpInstanceRename <$> genFQDN <*> return Nothing <*>
313
          genNodeNameNE <*> arbitrary <*> arbitrary
314
      "OP_INSTANCE_STARTUP" ->
315
        OpCodes.OpInstanceStartup <$> genFQDN <*> return Nothing <*>
316
          arbitrary <*> arbitrary <*> pure emptyJSObject <*>
317
          pure emptyJSObject <*> arbitrary <*> arbitrary
318
      "OP_INSTANCE_SHUTDOWN" ->
319
        OpCodes.OpInstanceShutdown <$> genFQDN <*> return Nothing <*>
320
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
321
      "OP_INSTANCE_REBOOT" ->
322
        OpCodes.OpInstanceReboot <$> genFQDN <*> return Nothing <*>
323
          arbitrary <*> arbitrary <*> arbitrary
324
      "OP_INSTANCE_MOVE" ->
325
        OpCodes.OpInstanceMove <$> genFQDN <*> return Nothing <*>
326
          arbitrary <*> arbitrary <*> genNodeNameNE <*> return Nothing <*>
327
          arbitrary <*> arbitrary
328
      "OP_INSTANCE_CONSOLE" -> OpCodes.OpInstanceConsole <$> genFQDN <*>
329
          return Nothing
330
      "OP_INSTANCE_ACTIVATE_DISKS" ->
331
        OpCodes.OpInstanceActivateDisks <$> genFQDN <*> return Nothing <*>
332
          arbitrary <*> arbitrary
333
      "OP_INSTANCE_DEACTIVATE_DISKS" ->
334
        OpCodes.OpInstanceDeactivateDisks <$> genFQDN <*> return Nothing <*>
335
          arbitrary
336
      "OP_INSTANCE_RECREATE_DISKS" ->
337
        OpCodes.OpInstanceRecreateDisks <$> genFQDN <*> return Nothing <*>
338
          arbitrary <*> genNodeNamesNE <*> return Nothing <*>
339
          genMaybe genNameNE
340
      "OP_INSTANCE_QUERY_DATA" ->
341
        OpCodes.OpInstanceQueryData <$> arbitrary <*>
342
          genNodeNamesNE <*> arbitrary
343
      "OP_INSTANCE_SET_PARAMS" ->
344
        OpCodes.OpInstanceSetParams
345
          <$> genFQDN                         -- instance_name
346
          <*> return Nothing                  -- instance_uuid
347
          <*> arbitrary                       -- force
348
          <*> arbitrary                       -- force_variant
349
          <*> arbitrary                       -- ignore_ipolicy
350
          <*> arbitrary                       -- nics
351
          <*> arbitrary                       -- disks
352
          <*> pure emptyJSObject              -- beparams
353
          <*> arbitrary                       -- runtime_mem
354
          <*> pure emptyJSObject              -- hvparams
355
          <*> arbitrary                       -- disk_template
356
          <*> genMaybe genNodeNameNE          -- pnode
357
          <*> return Nothing                  -- pnode_uuid
358
          <*> genMaybe genNodeNameNE          -- remote_node
359
          <*> return Nothing                  -- remote_node_uuid
360
          <*> genMaybe genNameNE              -- os_name
361
          <*> pure emptyJSObject              -- osparams
362
          <*> genMaybe arbitraryPrivateJSObj  -- osparams_private
363
          <*> arbitrary                       -- wait_for_sync
364
          <*> arbitrary                       -- offline
365
          <*> arbitrary                       -- conflicts_check
366
          <*> arbitrary                       -- hotplug
367
          <*> arbitrary                       -- hotplug_if_possible
368
          <*> arbitrary                       -- instance_communication
369
      "OP_INSTANCE_GROW_DISK" ->
370
        OpCodes.OpInstanceGrowDisk <$> genFQDN <*> return Nothing <*>
371
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
372
      "OP_INSTANCE_CHANGE_GROUP" ->
373
        OpCodes.OpInstanceChangeGroup <$> genFQDN <*> return Nothing <*>
374
          arbitrary <*> genMaybe genNameNE <*>
375
          genMaybe (resize maxNodes (listOf genNameNE))
376
      "OP_GROUP_ADD" ->
377
        OpCodes.OpGroupAdd <$> genNameNE <*> arbitrary <*>
378
          emptyMUD <*> genMaybe genEmptyContainer <*>
379
          emptyMUD <*> emptyMUD <*> emptyMUD
380
      "OP_GROUP_ASSIGN_NODES" ->
381
        OpCodes.OpGroupAssignNodes <$> genNameNE <*> arbitrary <*>
382
          genNodeNamesNE <*> return Nothing
383
      "OP_GROUP_SET_PARAMS" ->
384
        OpCodes.OpGroupSetParams <$> genNameNE <*> arbitrary <*>
385
          emptyMUD <*> genMaybe genEmptyContainer <*>
386
          emptyMUD <*> emptyMUD <*> emptyMUD
387
      "OP_GROUP_REMOVE" ->
388
        OpCodes.OpGroupRemove <$> genNameNE
389
      "OP_GROUP_RENAME" ->
390
        OpCodes.OpGroupRename <$> genNameNE <*> genNameNE
391
      "OP_GROUP_EVACUATE" ->
392
        OpCodes.OpGroupEvacuate <$> genNameNE <*> arbitrary <*>
393
          genMaybe genNameNE <*> genMaybe genNamesNE
394
      "OP_OS_DIAGNOSE" ->
395
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
396
      "OP_EXT_STORAGE_DIAGNOSE" ->
397
        OpCodes.OpOsDiagnose <$> genFieldsNE <*> genNamesNE
398
      "OP_BACKUP_PREPARE" ->
399
        OpCodes.OpBackupPrepare <$> genFQDN <*> return Nothing <*> arbitrary
400
      "OP_BACKUP_EXPORT" ->
401
        OpCodes.OpBackupExport <$> genFQDN <*> return Nothing <*>
402
          arbitrary <*> arbitrary <*> arbitrary <*> return Nothing <*>
403
          arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
404
          genMaybe (pure []) <*> genMaybe genNameNE <*> arbitrary <*>
405
          arbitrary <*> arbitrary
406
      "OP_BACKUP_REMOVE" ->
407
        OpCodes.OpBackupRemove <$> genFQDN <*> return Nothing
408
      "OP_TEST_ALLOCATOR" ->
409
        OpCodes.OpTestAllocator <$> arbitrary <*> arbitrary <*>
410
          genNameNE <*> genMaybe (pure []) <*> genMaybe (pure []) <*>
411
          arbitrary <*> genMaybe genNameNE <*>
412
          (genTags >>= mapM mkNonEmpty) <*>
413
          arbitrary <*> arbitrary <*> genMaybe genNameNE <*>
414
          arbitrary <*> genMaybe genNodeNamesNE <*> arbitrary <*>
415
          genMaybe genNamesNE <*> arbitrary <*> arbitrary
416
      "OP_TEST_JQUEUE" ->
417
        OpCodes.OpTestJqueue <$> arbitrary <*> arbitrary <*>
418
          resize 20 (listOf genFQDN) <*> arbitrary
419
      "OP_TEST_DUMMY" ->
420
        OpCodes.OpTestDummy <$> pure J.JSNull <*> pure J.JSNull <*>
421
          pure J.JSNull <*> pure J.JSNull
422
      "OP_NETWORK_ADD" ->
423
        OpCodes.OpNetworkAdd <$> genNameNE <*> genIPv4Network <*>
424
          genMaybe genIPv4Address <*> pure Nothing <*> pure Nothing <*>
425
          genMaybe genMacPrefix <*> genMaybe (listOf genIPv4Address) <*>
426
          arbitrary <*> (genTags >>= mapM mkNonEmpty)
427
      "OP_NETWORK_REMOVE" ->
428
        OpCodes.OpNetworkRemove <$> genNameNE <*> arbitrary
429
      "OP_NETWORK_SET_PARAMS" ->
430
        OpCodes.OpNetworkSetParams <$> genNameNE <*>
431
          genMaybe genIPv4Address <*> pure Nothing <*> pure Nothing <*>
432
          genMaybe genMacPrefix <*> genMaybe (listOf genIPv4Address) <*>
433
          genMaybe (listOf genIPv4Address)
434
      "OP_NETWORK_CONNECT" ->
435
        OpCodes.OpNetworkConnect <$> genNameNE <*> genNameNE <*>
436
          arbitrary <*> genNameNE <*> arbitrary
437
      "OP_NETWORK_DISCONNECT" ->
438
        OpCodes.OpNetworkDisconnect <$> genNameNE <*> genNameNE
439
      "OP_RESTRICTED_COMMAND" ->
440
        OpCodes.OpRestrictedCommand <$> arbitrary <*> genNodeNamesNE <*>
441
          return Nothing <*> genNameNE
442
      _ -> fail $ "Undefined arbitrary for opcode " ++ op_id
443

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

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

    
454
instance Arbitrary OpCodes.CommonOpParams where
455
  arbitrary = OpCodes.CommonOpParams <$> arbitrary <*> arbitrary <*>
456
                arbitrary <*> resize 5 arbitrary <*> genMaybe genName <*>
457
                genReasonTrail
458

    
459
-- * Helper functions
460

    
461
-- | Empty JSObject.
462
emptyJSObject :: J.JSObject J.JSValue
463
emptyJSObject = J.toJSObject []
464

    
465
-- | Empty maybe unchecked dictionary.
466
emptyMUD :: Gen (Maybe (J.JSObject J.JSValue))
467
emptyMUD = genMaybe $ pure emptyJSObject
468

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

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

    
479
-- | Generates a list of node names.
480
genNodeNames :: Gen [String]
481
genNodeNames = resize maxNodes (listOf genFQDN)
482

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

    
487
-- | Gets a node name in non-empty type.
488
genNodeNameNE :: Gen NonEmptyString
489
genNodeNameNE = genFQDN >>= mkNonEmpty
490

    
491
-- | Gets a name (non-fqdn) in non-empty type.
492
genNameNE :: Gen NonEmptyString
493
genNameNE = genName >>= mkNonEmpty
494

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

    
499
-- | Returns a list of non-empty fields.
500
genFieldsNE :: Gen [NonEmptyString]
501
genFieldsNE = genFields >>= mapM mkNonEmpty
502

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

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

    
519
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering.
520
$(genArbitrary ''OpCodes.MetaOpCode)
521

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

    
527
-- * Test cases
528

    
529
-- | Check that opcode serialization is idempotent.
530
prop_serialization :: OpCodes.OpCode -> Property
531
prop_serialization = testSerialisation
532

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

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

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

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

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

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

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

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

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