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