Revision 47163f0f htools/Ganeti/Rpc.hs

b/htools/Ganeti/Rpc.hs
28 28

  
29 29
module Ganeti.Rpc
30 30
  ( RpcCall
31
  , RpcResult
32 31
  , Rpc
33 32
  , RpcError(..)
34 33
  , ERpcError
......
56 55
  , RpcCallVersion(..)
57 56
  , RpcResultVersion(..)
58 57

  
58
  , StorageType(..)
59
  , StorageField(..)
60
  , RpcCallStorageList(..)
61
  , RpcResultStorageList(..)
62

  
59 63
  , rpcTimeoutFromRaw -- FIXME: Not used anywhere
60 64
  ) where
61 65

  
......
134 138
  -- | Whether we accept offline nodes when making a call.
135 139
  rpcCallAcceptOffline :: a -> Bool
136 140

  
137
-- | A generic class for RPC results with default implementation.
138
class (J.JSON a) => RpcResult a where
139
  -- | Create a result based on the received HTTP response.
140
  rpcResultFill :: (Monad m) => J.JSValue -> m (ERpcError a)
141

  
142 141
-- | Generic class that ensures matching RPC call with its respective
143 142
-- result.
144
class (RpcCall a, RpcResult b) => Rpc a b | a -> b
143
class (RpcCall a, J.JSON b) => Rpc a b  | a -> b, b -> a where
144
  -- | Create a result based on the received HTTP response.
145
  rpcResultFill :: (Monad m) => a -> J.JSValue -> m (ERpcError b)
145 146

  
146 147
-- | Http Request definition.
147 148
data HttpClientRequest = HttpClientRequest
......
192 193
  | otherwise = Left $ OfflineNodeError node
193 194

  
194 195
-- | Parse a result based on the received HTTP response.
195
rpcResultParse :: (Monad m, RpcResult a) => String -> m (ERpcError a)
196
rpcResultParse res = do
196
rpcResultParse :: (Monad m, Rpc a b) => a -> String -> m (ERpcError b)
197
rpcResultParse call res = do
197 198
  res' <- fromJResult "Reading JSON response" $ J.decode res
198 199
  case res' of
199 200
    (True, res'') ->
200
       rpcResultFill res''
201
       rpcResultFill call res''
201 202
    (False, jerr) -> case jerr of
202 203
       J.JSString msg -> return . Left $ RpcResultError (J.fromJSString msg)
203 204
       _ -> (return . Left) . JsonDecodeError $ show (pp_value jerr)
204 205

  
205 206
-- | Parse the response or propagate the error.
206
parseHttpResponse :: (Monad m, RpcResult a) => ERpcError String
207
                  -> m (ERpcError a)
208
parseHttpResponse (Left err) = return $ Left err
209
parseHttpResponse (Right response) = rpcResultParse response
207
parseHttpResponse :: (Rpc a b) => a -> ERpcError String -> IO (ERpcError b)
208
parseHttpResponse _ (Left err) = return $ Left err
209
parseHttpResponse call (Right response) = rpcResultParse call response
210 210

  
211 211
-- | Execute RPC call for a sigle node.
212 212
executeSingleRpcCall :: (Rpc a b) => Node -> a -> IO (Node, ERpcError b)
213 213
executeSingleRpcCall node call = do
214 214
  let request = prepareHttpRequest node call
215 215
  response <- executeHttpRequest node request
216
  result <- parseHttpResponse response
216
  result <- parseHttpResponse call response
217 217
  return (node, result)
218 218

  
219 219
-- | Execute RPC call for many nodes in parallel.
......
254 254
  rpcCallAcceptOffline _ = False
255 255
  rpcCallData _ call = J.encode [rpcCallAllInstInfoHypervisors call]
256 256

  
257
instance RpcResult RpcResultAllInstancesInfo where
257
instance Rpc RpcCallAllInstancesInfo RpcResultAllInstancesInfo where
258 258
  -- FIXME: Is there a simpler way to do it?
259
  rpcResultFill res =
259
  rpcResultFill _ res =
260 260
    return $ case res of
261 261
      J.JSObject res' -> do
262 262
        let res'' = map (second J.readJSON) (J.fromJSObject res')
......
267 267
      _ -> Left $ JsonDecodeError
268 268
           ("Expected JSObject, got " ++ show res)
269 269

  
270
instance Rpc RpcCallAllInstancesInfo RpcResultAllInstancesInfo
271

  
272 270
-- | InstanceList
273 271
-- Returns the list of running instances on the given nodes.
274 272
$(buildObject "RpcCallInstanceList" "rpcCallInstList"
......
283 281
  rpcCallAcceptOffline _ = False
284 282
  rpcCallData _ call = J.encode [rpcCallInstListHypervisors call]
285 283

  
286
instance RpcResult RpcResultInstanceList where
287
  rpcResultFill res =
284

  
285
instance Rpc RpcCallInstanceList RpcResultInstanceList where
286
  rpcResultFill _ res =
288 287
    return $ case J.readJSON res of
289 288
      J.Error err -> Left $ JsonDecodeError err
290 289
      J.Ok insts -> Right $ RpcResultInstanceList insts
291 290

  
292
instance Rpc RpcCallInstanceList RpcResultInstanceList
293

  
294 291
-- | NodeInfo
295 292
-- Return node information.
296 293
$(buildObject "RpcCallNodeInfo" "rpcCallNodeInfo"
......
328 325
                                , rpcCallNodeInfoHypervisors call
329 326
                                )
330 327

  
331
instance RpcResult RpcResultNodeInfo where
332
  rpcResultFill res =
328
instance Rpc RpcCallNodeInfo RpcResultNodeInfo where
329
  rpcResultFill _ res =
333 330
    return $ case J.readJSON res of
334 331
      J.Error err -> Left $ JsonDecodeError err
335 332
      J.Ok (boot_id, vg_info, hv_info) ->
336 333
          Right $ RpcResultNodeInfo boot_id vg_info hv_info
337 334

  
338
instance Rpc RpcCallNodeInfo RpcResultNodeInfo
339

  
340 335
-- | Version
341 336
-- Query node version.
342 337
-- Note: We can't use THH as it does not know what to do with empty dict
......
358 353
  rpcCallAcceptOffline _ = True
359 354
  rpcCallData call _ = J.encode [call]
360 355

  
361
instance RpcResult RpcResultVersion where
362
  rpcResultFill res =
356
instance Rpc RpcCallVersion RpcResultVersion where
357
  rpcResultFill _ res =
363 358
    return $ case J.readJSON res of
364 359
      J.Error err -> Left $ JsonDecodeError err
365 360
      J.Ok ver -> Right $ RpcResultVersion ver
366 361

  
367
instance Rpc RpcCallVersion RpcResultVersion
362
-- | StorageList
363
-- Get list of storage units.
364
-- FIXME: This may be moved to Objects
365
$(declareSADT "StorageType"
366
  [ ( "STLvmPv", 'C.stLvmPv )
367
  , ( "STFile",  'C.stFile )
368
  , ( "STLvmVg", 'C.stLvmVg )
369
  ])
370
$(makeJSONInstance ''StorageType)
371

  
372
-- FIXME: This may be moved to Objects
373
$(declareSADT "StorageField"
374
  [ ( "SFUsed",        'C.sfUsed)
375
  , ( "SFName",        'C.sfName)
376
  , ( "SFAllocatable", 'C.sfAllocatable)
377
  , ( "SFFree",        'C.sfFree)
378
  , ( "SFSize",        'C.sfSize)
379
  ])
380
$(makeJSONInstance ''StorageField)
381

  
382
$(buildObject "RpcCallStorageList" "rpcCallStorageList"
383
  [ simpleField "su_name" [t| StorageType |]
384
  , simpleField "su_args" [t| [String] |]
385
  , simpleField "name"    [t| String |]
386
  , simpleField "fields"  [t| [StorageField] |]
387
  ])
388

  
389
-- FIXME: The resulting JSValues should have types appropriate for their
390
-- StorageField value: Used -> Bool, Name -> String etc
391
$(buildObject "RpcResultStorageList" "rpcResStorageList"
392
  [ simpleField "storage" [t| [[(StorageField, J.JSValue)]] |] ])
393

  
394
instance RpcCall RpcCallStorageList where
395
  rpcCallName _ = "storage_list"
396
  rpcCallTimeout _ = rpcTimeoutToRaw Normal
397
  rpcCallAcceptOffline _ = False
398
  rpcCallData _ call = J.encode
399
    ( rpcCallStorageListSuName call
400
    , rpcCallStorageListSuArgs call
401
    , rpcCallStorageListName call
402
    , rpcCallStorageListFields call
403
    )
404

  
405
instance Rpc RpcCallStorageList RpcResultStorageList where
406
  rpcResultFill call res =
407
    let sfields = rpcCallStorageListFields call in
408
    return $ case J.readJSON res of
409
      J.Error err -> Left $ JsonDecodeError err
410
      J.Ok res_lst -> Right $ RpcResultStorageList (map (zip sfields) res_lst)
411

  

Also available in: Unified diff