Revision c48711d5
b/htest/Test/Ganeti/Types.hs | ||
---|---|---|
33 | 33 |
, InstanceStatus(..) |
34 | 34 |
, NonEmpty(..) |
35 | 35 |
, Hypervisor(..) |
36 |
, JobId(..) |
|
36 | 37 |
) where |
37 | 38 |
|
38 | 39 |
import Data.List (sort) |
39 | 40 |
import Test.QuickCheck as QuickCheck hiding (Result) |
40 | 41 |
import Test.HUnit |
42 |
import qualified Text.JSON as J |
|
41 | 43 |
|
42 | 44 |
import Test.Ganeti.TestHelper |
43 | 45 |
import Test.Ganeti.TestCommon |
... | ... | |
45 | 47 |
import Ganeti.BasicTypes |
46 | 48 |
import qualified Ganeti.Constants as C |
47 | 49 |
import Ganeti.Types as Types |
50 |
import Ganeti.Luxi as Luxi |
|
48 | 51 |
|
49 | 52 |
{-# ANN module "HLint: ignore Use camelCase" #-} |
50 | 53 |
|
... | ... | |
109 | 112 |
|
110 | 113 |
$(genArbitrary ''FinalizedJobStatus) |
111 | 114 |
|
115 |
instance Arbitrary Luxi.JobId where |
|
116 |
arbitrary = do |
|
117 |
(Positive i) <- arbitrary |
|
118 |
Luxi.makeJobId i |
|
119 |
|
|
112 | 120 |
-- * Properties |
113 | 121 |
|
114 | 122 |
prop_AllocPolicy_serialisation :: AllocPolicy -> Property |
... | ... | |
271 | 279 |
[minBound..maxBound] |
272 | 280 |
assertEqual "for FinalizedJobStatus equivalence" all_py_codes all_hs_codes |
273 | 281 |
|
282 |
-- | Tests JobId serialisation (both from string and ints). |
|
283 |
prop_JobId_serialisation :: JobId -> Property |
|
284 |
prop_JobId_serialisation jid = |
|
285 |
testSerialisation jid .&&. |
|
286 |
(J.readJSON . J.showJSON . show $ fromJobId jid) ==? J.Ok jid |
|
287 |
|
|
274 | 288 |
testSuite "Types" |
275 | 289 |
[ 'prop_AllocPolicy_serialisation |
276 | 290 |
, 'prop_DiskTemplate_serialisation |
... | ... | |
304 | 318 |
, 'case_NICMode_pyequiv |
305 | 319 |
, 'prop_FinalizedJobStatus_serialisation |
306 | 320 |
, 'case_FinalizedJobStatus_pyequiv |
321 |
, 'prop_JobId_serialisation |
|
307 | 322 |
] |
b/htools/Ganeti/Luxi.hs | ||
---|---|---|
30 | 30 |
, LuxiReq(..) |
31 | 31 |
, Client |
32 | 32 |
, JobId |
33 |
, fromJobId |
|
34 |
, makeJobId |
|
33 | 35 |
, RecvResult(..) |
34 | 36 |
, strOfOp |
35 | 37 |
, getClient |
... | ... | |
52 | 54 |
|
53 | 55 |
import Control.Exception (catch) |
54 | 56 |
import Data.IORef |
55 |
import Data.Ratio (numerator, denominator) |
|
56 | 57 |
import qualified Data.ByteString as B |
57 | 58 |
import qualified Data.ByteString.UTF8 as UTF8 |
58 | 59 |
import Data.Word (Word8) |
... | ... | |
74 | 75 |
import Ganeti.Jobs (JobStatus) |
75 | 76 |
import Ganeti.OpParams (pTagsObject) |
76 | 77 |
import Ganeti.OpCodes |
77 |
import Ganeti.Utils |
|
78 | 78 |
import qualified Ganeti.Query.Language as Qlang |
79 | 79 |
import Ganeti.THH |
80 |
import Ganeti.Types |
|
80 | 81 |
|
81 | 82 |
-- * Utility functions |
82 | 83 |
|
... | ... | |
96 | 97 |
| RecvOk String -- ^ Successfull receive |
97 | 98 |
deriving (Show, Eq) |
98 | 99 |
|
99 |
-- | The Ganeti job type. |
|
100 |
type JobId = Int |
|
101 |
|
|
102 | 100 |
-- | Currently supported Luxi operations and JSON serialization. |
103 | 101 |
$(genLuxiOp "LuxiOp" |
104 | 102 |
[ (luxiReqQuery, |
... | ... | |
126 | 124 |
, simpleField "lock" [t| Bool |] |
127 | 125 |
]) |
128 | 126 |
, (luxiReqQueryJobs, |
129 |
[ simpleField "ids" [t| [Int] |]
|
|
127 |
[ simpleField "ids" [t| [JobId] |]
|
|
130 | 128 |
, simpleField "fields" [t| [String] |] |
131 | 129 |
]) |
132 | 130 |
, (luxiReqQueryExports, |
... | ... | |
146 | 144 |
[ simpleField "ops" [t| [[OpCode]] |] ] |
147 | 145 |
) |
148 | 146 |
, (luxiReqWaitForJobChange, |
149 |
[ simpleField "job" [t| Int |]
|
|
147 |
[ simpleField "job" [t| JobId |]
|
|
150 | 148 |
, simpleField "fields" [t| [String]|] |
151 | 149 |
, simpleField "prev_job" [t| JSValue |] |
152 | 150 |
, simpleField "prev_log" [t| JSValue |] |
153 | 151 |
, simpleField "tmout" [t| Int |] |
154 | 152 |
]) |
155 | 153 |
, (luxiReqArchiveJob, |
156 |
[ simpleField "job" [t| Int |] ]
|
|
154 |
[ simpleField "job" [t| JobId |] ]
|
|
157 | 155 |
) |
158 | 156 |
, (luxiReqAutoArchiveJobs, |
159 | 157 |
[ simpleField "age" [t| Int |] |
160 | 158 |
, simpleField "tmout" [t| Int |] |
161 | 159 |
]) |
162 | 160 |
, (luxiReqCancelJob, |
163 |
[ simpleField "job" [t| Int |] ]
|
|
161 |
[ simpleField "job" [t| JobId |] ]
|
|
164 | 162 |
) |
165 | 163 |
, (luxiReqChangeJobPriority, |
166 |
[ simpleField "job" [t| Int |]
|
|
164 |
[ simpleField "job" [t| JobId |]
|
|
167 | 165 |
, simpleField "priority" [t| Int |] ] |
168 | 166 |
) |
169 | 167 |
, (luxiReqSetDrainFlag, |
... | ... | |
326 | 324 |
decodeCall (LuxiCall call args) = |
327 | 325 |
case call of |
328 | 326 |
ReqQueryJobs -> do |
329 |
(jid, jargs) <- fromJVal args |
|
330 |
rid <- mapM parseJobId jid |
|
327 |
(jids, jargs) <- fromJVal args |
|
331 | 328 |
let rargs = map fromJSString jargs |
332 |
return $ QueryJobs rid rargs
|
|
329 |
return $ QueryJobs jids rargs
|
|
333 | 330 |
ReqQueryInstances -> do |
334 | 331 |
(names, fields, locking) <- fromJVal args |
335 | 332 |
return $ QueryInstances names fields locking |
... | ... | |
372 | 369 |
J.readJSON d `ap` |
373 | 370 |
J.readJSON e |
374 | 371 |
_ -> J.Error "Not enough values" |
375 |
rid <- parseJobId jid |
|
376 |
return $ WaitForJobChange rid fields pinfo pidx wtmout |
|
372 |
return $ WaitForJobChange jid fields pinfo pidx wtmout |
|
377 | 373 |
ReqArchiveJob -> do |
378 | 374 |
[jid] <- fromJVal args |
379 |
rid <- parseJobId jid |
|
380 |
return $ ArchiveJob rid |
|
375 |
return $ ArchiveJob jid |
|
381 | 376 |
ReqAutoArchiveJobs -> do |
382 | 377 |
(age, tmout) <- fromJVal args |
383 | 378 |
return $ AutoArchiveJobs age tmout |
... | ... | |
392 | 387 |
item <- tagObjectFrom kind name |
393 | 388 |
return $ QueryTags item |
394 | 389 |
ReqCancelJob -> do |
395 |
[job] <- fromJVal args |
|
396 |
rid <- parseJobId job |
|
397 |
return $ CancelJob rid |
|
390 |
[jid] <- fromJVal args |
|
391 |
return $ CancelJob jid |
|
398 | 392 |
ReqChangeJobPriority -> do |
399 |
(job, priority) <- fromJVal args |
|
400 |
rid <- parseJobId job |
|
401 |
return $ ChangeJobPriority rid priority |
|
393 |
(jid, priority) <- fromJVal args |
|
394 |
return $ ChangeJobPriority jid priority |
|
402 | 395 |
ReqSetDrainFlag -> do |
403 | 396 |
[flag] <- fromJVal args |
404 | 397 |
return $ SetDrainFlag flag |
... | ... | |
437 | 430 |
let rval = validateResult result |
438 | 431 |
return rval |
439 | 432 |
|
440 |
-- | Parses a job ID. |
|
441 |
parseJobId :: JSValue -> Result JobId |
|
442 |
parseJobId (JSString x) = tryRead "parsing job id" . fromJSString $ x |
|
443 |
parseJobId (JSRational _ x) = |
|
444 |
if denominator x /= 1 |
|
445 |
then Bad $ "Got fractional job ID from master daemon?! Value:" ++ show x |
|
446 |
-- FIXME: potential integer overflow here on 32-bit platforms |
|
447 |
else Ok . fromIntegral . numerator $ x |
|
448 |
parseJobId x = Bad $ "Wrong type/value for job id: " ++ show x |
|
449 |
|
|
450 | 433 |
-- | Parse job submission result. |
451 | 434 |
parseSubmitJobResult :: JSValue -> ErrorResult JobId |
452 | 435 |
parseSubmitJobResult (JSArray [JSBool True, v]) = |
453 |
case parseJobId v of
|
|
454 |
Bad msg -> Bad $ LuxiError msg
|
|
455 |
Ok v' -> Ok v' |
|
436 |
case J.readJSON v of
|
|
437 |
J.Error msg -> Bad $ LuxiError msg
|
|
438 |
J.Ok v' -> Ok v'
|
|
456 | 439 |
parseSubmitJobResult (JSArray [JSBool False, JSString x]) = |
457 | 440 |
Bad . LuxiError $ fromJSString x |
458 | 441 |
parseSubmitJobResult v = |
b/htools/Ganeti/Types.hs | ||
---|---|---|
73 | 73 |
, nICModeToRaw |
74 | 74 |
, FinalizedJobStatus(..) |
75 | 75 |
, finalizedJobStatusToRaw |
76 |
, JobId |
|
77 |
, fromJobId |
|
78 |
, makeJobId |
|
76 | 79 |
) where |
77 | 80 |
|
78 | 81 |
import qualified Text.JSON as JSON |
82 |
import Data.Ratio (numerator, denominator) |
|
79 | 83 |
|
80 | 84 |
import qualified Ganeti.Constants as C |
81 | 85 |
import qualified Ganeti.THH as THH |
82 | 86 |
import Ganeti.JSON |
87 |
import Ganeti.Utils |
|
83 | 88 |
|
84 | 89 |
-- * Generic types |
85 | 90 |
|
... | ... | |
342 | 347 |
, ("JobStatusFailed", 'C.jobStatusError) |
343 | 348 |
]) |
344 | 349 |
$(THH.makeJSONInstance ''FinalizedJobStatus) |
350 |
|
|
351 |
-- | The Ganeti job type. |
|
352 |
newtype JobId = JobId { fromJobId :: Int } |
|
353 |
deriving (Show, Eq) |
|
354 |
|
|
355 |
-- | Builds a job ID. |
|
356 |
makeJobId :: (Monad m) => Int -> m JobId |
|
357 |
makeJobId i | i >= 0 = return $ JobId i |
|
358 |
| otherwise = fail $ "Invalid value for job ID ' " ++ show i ++ "'" |
|
359 |
|
|
360 |
-- | Parses a job ID. |
|
361 |
parseJobId :: (Monad m) => JSON.JSValue -> m JobId |
|
362 |
parseJobId (JSON.JSString x) = |
|
363 |
tryRead "parsing job id" (JSON.fromJSString x) >>= makeJobId |
|
364 |
parseJobId (JSON.JSRational _ x) = |
|
365 |
if denominator x /= 1 |
|
366 |
then fail $ "Got fractional job ID from master daemon?! Value:" ++ show x |
|
367 |
-- FIXME: potential integer overflow here on 32-bit platforms |
|
368 |
else makeJobId . fromIntegral . numerator $ x |
|
369 |
parseJobId x = fail $ "Wrong type/value for job id: " ++ show x |
|
370 |
|
|
371 |
instance JSON.JSON JobId where |
|
372 |
showJSON = JSON.showJSON . fromJobId |
|
373 |
readJSON = parseJobId |
Also available in: Unified diff