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