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