Revision cdd495ae

b/htools/Ganeti/HTools/QC.hs
40 40
  , testTypes
41 41
  , testCLI
42 42
  , testJSON
43
  , testLUXI
43 44
  ) where
44 45

  
45 46
import Test.QuickCheck
......
56 57

  
57 58
import qualified Ganeti.OpCodes as OpCodes
58 59
import qualified Ganeti.Jobs as Jobs
59
import qualified Ganeti.Luxi
60
import qualified Ganeti.Luxi as Luxi
60 61
import qualified Ganeti.HTools.CLI as CLI
61 62
import qualified Ganeti.HTools.Cluster as Cluster
62 63
import qualified Ganeti.HTools.Container as Container
......
65 66
import qualified Ganeti.HTools.Instance as Instance
66 67
import qualified Ganeti.HTools.JSON as JSON
67 68
import qualified Ganeti.HTools.Loader as Loader
68
import qualified Ganeti.HTools.Luxi
69
import qualified Ganeti.HTools.Luxi as HTools.Luxi
69 70
import qualified Ganeti.HTools.Node as Node
70 71
import qualified Ganeti.HTools.Group as Group
71 72
import qualified Ganeti.HTools.PeerMap as PeerMap
......
1683 1684
          [ 'prop_JSON_toArray
1684 1685
          , 'prop_JSON_toArrayFail
1685 1686
          ]
1687

  
1688
-- * Luxi tests
1689

  
1690
instance Arbitrary Luxi.LuxiReq where
1691
  arbitrary = elements [minBound..maxBound]
1692

  
1693
instance Arbitrary Luxi.QrViaLuxi where
1694
  arbitrary = elements [minBound..maxBound]
1695

  
1696
instance Arbitrary Luxi.LuxiOp where
1697
  arbitrary = do
1698
    lreq <- arbitrary
1699
    case lreq of
1700
      Luxi.ReqQuery -> Luxi.Query <$> arbitrary <*> getFields <*> arbitrary
1701
      Luxi.ReqQueryNodes -> Luxi.QueryNodes <$> (listOf getFQDN) <*>
1702
                            getFields <*> arbitrary
1703
      Luxi.ReqQueryGroups -> Luxi.QueryGroups <$> arbitrary <*>
1704
                             arbitrary <*> arbitrary
1705
      Luxi.ReqQueryInstances -> Luxi.QueryInstances <$> (listOf getFQDN) <*>
1706
                                getFields <*> arbitrary
1707
      Luxi.ReqQueryJobs -> Luxi.QueryJobs <$> arbitrary <*> getFields
1708
      Luxi.ReqQueryExports -> Luxi.QueryExports <$>
1709
                              (listOf getFQDN) <*> arbitrary
1710
      Luxi.ReqQueryConfigValues -> Luxi.QueryConfigValues <$> getFields
1711
      Luxi.ReqQueryClusterInfo -> pure Luxi.QueryClusterInfo
1712
      Luxi.ReqQueryTags -> Luxi.QueryTags <$> getName <*> getFQDN
1713
      Luxi.ReqSubmitJob -> Luxi.SubmitJob <$> (resize maxOpCodes arbitrary)
1714
      Luxi.ReqSubmitManyJobs -> Luxi.SubmitManyJobs <$>
1715
                                (resize maxOpCodes arbitrary)
1716
      Luxi.ReqWaitForJobChange -> Luxi.WaitForJobChange <$> arbitrary <*>
1717
                                  getFields <*> pure J.JSNull <*>
1718
                                  pure J.JSNull <*> arbitrary
1719
      Luxi.ReqArchiveJob -> Luxi.ArchiveJob <$> arbitrary
1720
      Luxi.ReqAutoArchiveJobs -> Luxi.AutoArchiveJobs <$> arbitrary <*>
1721
                                 arbitrary
1722
      Luxi.ReqCancelJob -> Luxi.CancelJob <$> arbitrary
1723
      Luxi.ReqSetDrainFlag -> Luxi.SetDrainFlag <$> arbitrary
1724
      Luxi.ReqSetWatcherPause -> Luxi.SetWatcherPause <$> arbitrary
1725

  
1726
-- | Simple check that encoding/decoding of LuxiOp works.
1727
prop_Luxi_CallEncoding :: Luxi.LuxiOp -> Property
1728
prop_Luxi_CallEncoding op =
1729
  (Luxi.validateCall (Luxi.buildCall op) >>= Luxi.decodeCall) ==? Types.Ok op
1730

  
1731
testSuite "LUXI"
1732
          [ 'prop_Luxi_CallEncoding
1733
          ]
b/htools/Ganeti/Luxi.hs
37 37
  , callMethod
38 38
  , submitManyJobs
39 39
  , queryJobsStatus
40
  , buildCall
41
  , validateCall
42
  , decodeCall
40 43
  ) where
41 44

  
42 45
import Data.IORef
......
49 52

  
50 53
import Ganeti.HTools.JSON
51 54
import Ganeti.HTools.Types
55
import Ganeti.HTools.Utils
52 56

  
53 57
import Ganeti.Constants
54 58
import Ganeti.Jobs (JobStatus)
......
121 125
     [ ("ops", [t| [[OpCode]] |], [| id |]) ]
122 126
    )
123 127
  , (luxiReqWaitForJobChange,
124
     [ ("job",      [t| Int     |], [| id |])
128
     [ ("job",      [t| Int     |], [| show |])
125 129
     , ("fields",   [t| [String]|], [| id |])
126 130
     , ("prev_job", [t| JSValue |], [| id |])
127 131
     , ("prev_log", [t| JSValue |], [| id |])
......
160 164

  
161 165
$(makeJSONInstance ''ResultStatus)
162 166

  
167
-- | Type holding the initial (unparsed) Luxi call.
168
data LuxiCall = LuxiCall LuxiReq JSValue
169

  
163 170
-- | Check that ResultStatus is success or fail with descriptive message.
164 171
checkRS :: (Monad m) => ResultStatus -> a -> m a
165 172
checkRS RSNormal val    = return val
......
238 245
      jo = toJSObject ja
239 246
  in encodeStrict jo
240 247

  
248
-- | Check that luxi request contains the required keys and parse it.
249
validateCall :: String -> Result LuxiCall
250
validateCall s = do
251
  arr <- fromJResult "luxi call" $ decodeStrict s::Result (JSObject JSValue)
252
  let aobj = fromJSObject arr
253
  call <- fromObj aobj (strOfKey Method)::Result LuxiReq
254
  args <- fromObj aobj (strOfKey Args)
255
  return (LuxiCall call args)
256

  
257
-- | Converts Luxi call arguments into a 'LuxiOp' data structure.
258
--
259
-- This is currently hand-coded until we make it more uniform so that
260
-- it can be generated using TH.
261
decodeCall :: LuxiCall -> Result LuxiOp
262
decodeCall (LuxiCall call args) =
263
  case call of
264
    ReqQueryJobs -> do
265
              (jid, jargs) <- fromJVal args
266
              rid <- mapM (tryRead "parsing job ID" . fromJSString) jid
267
              let rargs = map fromJSString jargs
268
              return $ QueryJobs rid rargs
269
    ReqQueryInstances -> do
270
              (names, fields, locking) <- fromJVal args
271
              return $ QueryInstances names fields locking
272
    ReqQueryNodes -> do
273
              (names, fields, locking) <- fromJVal args
274
              return $ QueryNodes names fields locking
275
    ReqQueryGroups -> do
276
              (names, fields, locking) <- fromJVal args
277
              return $ QueryGroups names fields locking
278
    ReqQueryClusterInfo -> do
279
              return QueryClusterInfo
280
    ReqQuery -> do
281
              (what, fields, _) <-
282
                fromJVal args::Result (QrViaLuxi, [String], JSValue)
283
              return $ Query what fields ()
284
    ReqSubmitJob -> do
285
              [ops1] <- fromJVal args
286
              ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1
287
              return $ SubmitJob ops2
288
    ReqSubmitManyJobs -> do
289
              [ops1] <- fromJVal args
290
              ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1
291
              return $ SubmitManyJobs ops2
292
    ReqWaitForJobChange -> do
293
              (jid, fields, pinfo, pidx, wtmout) <-
294
                -- No instance for 5-tuple, code copied from the
295
                -- json sources and adapted
296
                fromJResult "Parsing WaitForJobChange message" $
297
                case args of
298
                  JSArray [a, b, c, d, e] ->
299
                    (,,,,) `fmap`
300
                    J.readJSON a `ap`
301
                    J.readJSON b `ap`
302
                    J.readJSON c `ap`
303
                    J.readJSON d `ap`
304
                    J.readJSON e
305
                  _ -> J.Error "Not enough values"
306
              rid <- tryRead "parsing job ID" jid
307
              return $ WaitForJobChange rid fields pinfo pidx wtmout
308
    ReqArchiveJob -> do
309
              [jid] <- fromJVal args
310
              rid <- tryRead "parsing job ID" jid
311
              return $ ArchiveJob rid
312
    ReqAutoArchiveJobs -> do
313
              (age, tmout) <- fromJVal args
314
              return $ AutoArchiveJobs age tmout
315
    ReqQueryExports -> do
316
              (nodes, lock) <- fromJVal args
317
              return $ QueryExports nodes lock
318
    ReqQueryConfigValues -> do
319
              [fields] <- fromJVal args
320
              return $ QueryConfigValues fields
321
    ReqQueryTags -> do
322
              (kind, name) <- fromJVal args
323
              return $ QueryTags kind name
324
    ReqCancelJob -> do
325
              [job] <- fromJVal args
326
              rid <- tryRead "parsing job ID" job
327
              return $ CancelJob rid
328
    ReqSetDrainFlag -> do
329
              [flag] <- fromJVal args
330
              return $ SetDrainFlag flag
331
    ReqSetWatcherPause -> do
332
              [duration] <- fromJVal args
333
              return $ SetWatcherPause duration
334

  
241 335
-- | Check that luxi responses contain the required keys and that the
242 336
-- call was successful.
243 337
validateResult :: String -> Result JSValue
b/htools/Ganeti/THH.hs
541 541
                               fields
542 542
                    return $ NormalC (mkName cname) fields')
543 543
            cons
544
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read]
544
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read, ''Eq]
545 545
  (savesig, savefn) <- genSaveLuxiOp cons
546 546
  req_defs <- declareSADT "LuxiReq" .
547 547
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
b/htools/test.hs
124 124
  , (fast, testTypes)
125 125
  , (fast, testCLI)
126 126
  , (fast, testJSON)
127
  , (fast, testLUXI)
127 128
  , (slow, testCluster)
128 129
  ]
129 130

  

Also available in: Unified diff