Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Rpc.hs @ 289e7fcc

History | View | Annotate | Download (16.4 kB)

1 1ca709c1 Iustin Pop
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
2 96dad12d Agata Murawska
  BangPatterns, TemplateHaskell #-}
3 d4709cce Agata Murawska
4 d4709cce Agata Murawska
{-| Implementation of the RPC client.
5 d4709cce Agata Murawska
6 d4709cce Agata Murawska
-}
7 d4709cce Agata Murawska
8 d4709cce Agata Murawska
{-
9 d4709cce Agata Murawska
10 1ca709c1 Iustin Pop
Copyright (C) 2012, 2013 Google Inc.
11 d4709cce Agata Murawska
12 d4709cce Agata Murawska
This program is free software; you can redistribute it and/or modify
13 d4709cce Agata Murawska
it under the terms of the GNU General Public License as published by
14 d4709cce Agata Murawska
the Free Software Foundation; either version 2 of the License, or
15 d4709cce Agata Murawska
(at your option) any later version.
16 d4709cce Agata Murawska
17 d4709cce Agata Murawska
This program is distributed in the hope that it will be useful, but
18 d4709cce Agata Murawska
WITHOUT ANY WARRANTY; without even the implied warranty of
19 d4709cce Agata Murawska
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 d4709cce Agata Murawska
General Public License for more details.
21 d4709cce Agata Murawska
22 d4709cce Agata Murawska
You should have received a copy of the GNU General Public License
23 d4709cce Agata Murawska
along with this program; if not, write to the Free Software
24 d4709cce Agata Murawska
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 d4709cce Agata Murawska
02110-1301, USA.
26 d4709cce Agata Murawska
27 d4709cce Agata Murawska
-}
28 d4709cce Agata Murawska
29 d4709cce Agata Murawska
module Ganeti.Rpc
30 d4709cce Agata Murawska
  ( RpcCall
31 d4709cce Agata Murawska
  , Rpc
32 d4709cce Agata Murawska
  , RpcError(..)
33 599239ad Agata Murawska
  , ERpcError
34 60443f61 Agata Murawska
  , explainRpcError
35 eaed5f19 Agata Murawska
  , executeRpcCall
36 a246ce76 Michele Tartara
  , logRpcErrors
37 d4709cce Agata Murawska
38 d4709cce Agata Murawska
  , rpcCallName
39 d4709cce Agata Murawska
  , rpcCallTimeout
40 d4709cce Agata Murawska
  , rpcCallData
41 d4709cce Agata Murawska
  , rpcCallAcceptOffline
42 d4709cce Agata Murawska
43 d4709cce Agata Murawska
  , rpcResultFill
44 96dad12d Agata Murawska
45 96dad12d Agata Murawska
  , InstanceInfo(..)
46 5188fdb7 Agata Murawska
  , RpcCallInstanceInfo(..)
47 5188fdb7 Agata Murawska
  , RpcResultInstanceInfo(..)
48 5188fdb7 Agata Murawska
49 96dad12d Agata Murawska
  , RpcCallAllInstancesInfo(..)
50 96dad12d Agata Murawska
  , RpcResultAllInstancesInfo(..)
51 96dad12d Agata Murawska
52 c1c5aab1 Agata Murawska
  , RpcCallInstanceList(..)
53 c1c5aab1 Agata Murawska
  , RpcResultInstanceList(..)
54 c1c5aab1 Agata Murawska
55 dc623a95 Agata Murawska
  , HvInfo(..)
56 dc623a95 Agata Murawska
  , VgInfo(..)
57 dc623a95 Agata Murawska
  , RpcCallNodeInfo(..)
58 dc623a95 Agata Murawska
  , RpcResultNodeInfo(..)
59 dc623a95 Agata Murawska
60 8779d21a Agata Murawska
  , RpcCallVersion(..)
61 8779d21a Agata Murawska
  , RpcResultVersion(..)
62 8779d21a Agata Murawska
63 47163f0f Agata Murawska
  , StorageField(..)
64 47163f0f Agata Murawska
  , RpcCallStorageList(..)
65 47163f0f Agata Murawska
  , RpcResultStorageList(..)
66 47163f0f Agata Murawska
67 de2a5704 Iustin Pop
  , RpcCallTestDelay(..)
68 de2a5704 Iustin Pop
  , RpcResultTestDelay(..)
69 de2a5704 Iustin Pop
70 842515dd Iustin Pop
  , RpcCallExportList(..)
71 842515dd Iustin Pop
  , RpcResultExportList(..)
72 842515dd Iustin Pop
73 96dad12d Agata Murawska
  , rpcTimeoutFromRaw -- FIXME: Not used anywhere
74 d4709cce Agata Murawska
  ) where
75 d4709cce Agata Murawska
76 9b09c0be Agata Murawska
import Control.Arrow (second)
77 319322a7 Bernardo Dal Seno
import qualified Data.Map as Map
78 319322a7 Bernardo Dal Seno
import Data.Maybe (fromMaybe)
79 d4709cce Agata Murawska
import qualified Text.JSON as J
80 9b09c0be Agata Murawska
import Text.JSON.Pretty (pp_value)
81 d4709cce Agata Murawska
82 eaed5f19 Agata Murawska
import Network.Curl
83 7766de33 Agata Murawska
import qualified Ganeti.Path as P
84 eaed5f19 Agata Murawska
85 8920fa09 Iustin Pop
import Ganeti.BasicTypes
86 eaed5f19 Agata Murawska
import qualified Ganeti.Constants as C
87 a246ce76 Michele Tartara
import Ganeti.Logging
88 d4709cce Agata Murawska
import Ganeti.Objects
89 96dad12d Agata Murawska
import Ganeti.THH
90 22381768 Iustin Pop
import Ganeti.Types
91 8920fa09 Iustin Pop
import Ganeti.Curl.Multi
92 8920fa09 Iustin Pop
import Ganeti.Utils
93 eaed5f19 Agata Murawska
94 de2a5704 Iustin Pop
-- * Base RPC functionality and types
95 de2a5704 Iustin Pop
96 eaed5f19 Agata Murawska
-- | The curl options used for RPC.
97 eaed5f19 Agata Murawska
curlOpts :: [CurlOption]
98 eaed5f19 Agata Murawska
curlOpts = [ CurlFollowLocation False
99 eaed5f19 Agata Murawska
           , CurlSSLVerifyHost 0
100 eaed5f19 Agata Murawska
           , CurlSSLVerifyPeer True
101 eaed5f19 Agata Murawska
           , CurlSSLCertType "PEM"
102 eaed5f19 Agata Murawska
           , CurlSSLKeyType "PEM"
103 eaed5f19 Agata Murawska
           , CurlConnectTimeout (fromIntegral C.rpcConnectTimeout)
104 eaed5f19 Agata Murawska
           ]
105 d4709cce Agata Murawska
106 d4709cce Agata Murawska
-- | Data type for RPC error reporting.
107 d4709cce Agata Murawska
data RpcError
108 9c0a27d0 Iustin Pop
  = CurlLayerError String
109 d4709cce Agata Murawska
  | JsonDecodeError String
110 6fddde87 Agata Murawska
  | RpcResultError String
111 9c0a27d0 Iustin Pop
  | OfflineNodeError
112 60443f61 Agata Murawska
  deriving (Show, Eq)
113 d4709cce Agata Murawska
114 60443f61 Agata Murawska
-- | Provide explanation to RPC errors.
115 60443f61 Agata Murawska
explainRpcError :: RpcError -> String
116 9c0a27d0 Iustin Pop
explainRpcError (CurlLayerError code) =
117 9c0a27d0 Iustin Pop
    "Curl error:" ++ code
118 60443f61 Agata Murawska
explainRpcError (JsonDecodeError msg) =
119 6fddde87 Agata Murawska
    "Error while decoding JSON from HTTP response: " ++ msg
120 60443f61 Agata Murawska
explainRpcError (RpcResultError msg) =
121 6fddde87 Agata Murawska
    "Error reponse received from RPC server: " ++ msg
122 9c0a27d0 Iustin Pop
explainRpcError OfflineNodeError =
123 9c0a27d0 Iustin Pop
    "Node is marked offline"
124 d4709cce Agata Murawska
125 599239ad Agata Murawska
type ERpcError = Either RpcError
126 599239ad Agata Murawska
127 96dad12d Agata Murawska
-- | Basic timeouts for RPC calls.
128 96dad12d Agata Murawska
$(declareIADT "RpcTimeout"
129 96dad12d Agata Murawska
  [ ( "Urgent",    'C.rpcTmoUrgent )
130 96dad12d Agata Murawska
  , ( "Fast",      'C.rpcTmoFast )
131 96dad12d Agata Murawska
  , ( "Normal",    'C.rpcTmoNormal )
132 96dad12d Agata Murawska
  , ( "Slow",      'C.rpcTmoSlow )
133 96dad12d Agata Murawska
  , ( "FourHours", 'C.rpcTmo4hrs )
134 96dad12d Agata Murawska
  , ( "OneDay",    'C.rpcTmo1day )
135 96dad12d Agata Murawska
  ])
136 96dad12d Agata Murawska
137 d4709cce Agata Murawska
-- | A generic class for RPC calls.
138 d4709cce Agata Murawska
class (J.JSON a) => RpcCall a where
139 d4709cce Agata Murawska
  -- | Give the (Python) name of the procedure.
140 d4709cce Agata Murawska
  rpcCallName :: a -> String
141 d4709cce Agata Murawska
  -- | Calculate the timeout value for the call execution.
142 d4709cce Agata Murawska
  rpcCallTimeout :: a -> Int
143 d4709cce Agata Murawska
  -- | Prepare arguments of the call to be send as POST.
144 d4709cce Agata Murawska
  rpcCallData :: Node -> a -> String
145 d4709cce Agata Murawska
  -- | Whether we accept offline nodes when making a call.
146 d4709cce Agata Murawska
  rpcCallAcceptOffline :: a -> Bool
147 d4709cce Agata Murawska
148 d4709cce Agata Murawska
-- | Generic class that ensures matching RPC call with its respective
149 d4709cce Agata Murawska
-- result.
150 47163f0f Agata Murawska
class (RpcCall a, J.JSON b) => Rpc a b  | a -> b, b -> a where
151 47163f0f Agata Murawska
  -- | Create a result based on the received HTTP response.
152 a93b711b Agata Murawska
  rpcResultFill :: a -> J.JSValue -> ERpcError b
153 eaed5f19 Agata Murawska
154 eaed5f19 Agata Murawska
-- | Http Request definition.
155 eaed5f19 Agata Murawska
data HttpClientRequest = HttpClientRequest
156 85f6a869 Iustin Pop
  { requestUrl  :: String       -- ^ The actual URL for the node endpoint
157 85f6a869 Iustin Pop
  , requestData :: String       -- ^ The arguments for the call
158 85f6a869 Iustin Pop
  , requestOpts :: [CurlOption] -- ^ The various curl options
159 eaed5f19 Agata Murawska
  }
160 eaed5f19 Agata Murawska
161 b5c3a4f2 Guido Trotter
-- | Check if a string represented address is IPv6
162 b5c3a4f2 Guido Trotter
isIpV6 :: String -> Bool
163 289e7fcc Klaus Aehlig
isIpV6 = (':' `elem`)
164 b5c3a4f2 Guido Trotter
165 eaed5f19 Agata Murawska
-- | Prepare url for the HTTP request.
166 eaed5f19 Agata Murawska
prepareUrl :: (RpcCall a) => Node -> a -> String
167 eaed5f19 Agata Murawska
prepareUrl node call =
168 eaed5f19 Agata Murawska
  let node_ip = nodePrimaryIp node
169 b5c3a4f2 Guido Trotter
      node_address = if isIpV6 node_ip
170 b5c3a4f2 Guido Trotter
                     then "[" ++ node_ip ++ "]"
171 b5c3a4f2 Guido Trotter
                     else node_ip
172 eaed5f19 Agata Murawska
      port = snd C.daemonsPortsGanetiNoded
173 b5c3a4f2 Guido Trotter
      path_prefix = "https://" ++ node_address ++ ":" ++ show port
174 5b11f8db Iustin Pop
  in path_prefix ++ "/" ++ rpcCallName call
175 eaed5f19 Agata Murawska
176 eaed5f19 Agata Murawska
-- | Create HTTP request for a given node provided it is online,
177 eaed5f19 Agata Murawska
-- otherwise create empty response.
178 85f6a869 Iustin Pop
prepareHttpRequest :: (RpcCall a) => [CurlOption] -> Node -> a
179 599239ad Agata Murawska
                   -> ERpcError HttpClientRequest
180 85f6a869 Iustin Pop
prepareHttpRequest opts node call
181 5b11f8db Iustin Pop
  | rpcCallAcceptOffline call || not (nodeOffline node) =
182 85f6a869 Iustin Pop
      Right HttpClientRequest { requestUrl  = prepareUrl node call
183 85f6a869 Iustin Pop
                              , requestData = rpcCallData node call
184 85f6a869 Iustin Pop
                              , requestOpts = opts ++ curlOpts
185 5b11f8db Iustin Pop
                              }
186 9c0a27d0 Iustin Pop
  | otherwise = Left OfflineNodeError
187 eaed5f19 Agata Murawska
188 8920fa09 Iustin Pop
-- | Parse an HTTP reply.
189 8920fa09 Iustin Pop
parseHttpReply :: (Rpc a b) =>
190 8920fa09 Iustin Pop
                  a -> ERpcError (CurlCode, String) -> ERpcError b
191 8920fa09 Iustin Pop
parseHttpReply _ (Left e) = Left e
192 8920fa09 Iustin Pop
parseHttpReply call (Right (CurlOK, body)) = parseHttpResponse call body
193 8920fa09 Iustin Pop
parseHttpReply _ (Right (code, err)) =
194 8920fa09 Iustin Pop
  Left . CurlLayerError $ "code: " ++ show code ++ ", explanation: " ++ err
195 8920fa09 Iustin Pop
196 9b09c0be Agata Murawska
-- | Parse a result based on the received HTTP response.
197 8920fa09 Iustin Pop
parseHttpResponse :: (Rpc a b) => a -> String -> ERpcError b
198 8920fa09 Iustin Pop
parseHttpResponse call res =
199 a93b711b Agata Murawska
  case J.decode res of
200 a93b711b Agata Murawska
    J.Error val -> Left $ JsonDecodeError val
201 a93b711b Agata Murawska
    J.Ok (True, res'') -> rpcResultFill call res''
202 a93b711b Agata Murawska
    J.Ok (False, jerr) -> case jerr of
203 a93b711b Agata Murawska
       J.JSString msg -> Left $ RpcResultError (J.fromJSString msg)
204 a93b711b Agata Murawska
       _ -> Left . JsonDecodeError $ show (pp_value jerr)
205 9b09c0be Agata Murawska
206 a246ce76 Michele Tartara
-- | Scan the list of results produced by executeRpcCall and log all the RPC
207 a246ce76 Michele Tartara
-- errors.
208 a246ce76 Michele Tartara
logRpcErrors :: [(a, ERpcError b)] -> IO ()
209 a246ce76 Michele Tartara
logRpcErrors allElems =
210 a246ce76 Michele Tartara
  let logOneRpcErr (_, Right _) = return ()
211 a246ce76 Michele Tartara
      logOneRpcErr (_, Left err) =
212 a246ce76 Michele Tartara
        logError $ "Error in the RPC HTTP reply: " ++ show err
213 a246ce76 Michele Tartara
  in mapM_ logOneRpcErr allElems
214 a246ce76 Michele Tartara
215 eaed5f19 Agata Murawska
-- | Execute RPC call for many nodes in parallel.
216 599239ad Agata Murawska
executeRpcCall :: (Rpc a b) => [Node] -> a -> IO [(Node, ERpcError b)]
217 85f6a869 Iustin Pop
executeRpcCall nodes call = do
218 85f6a869 Iustin Pop
  cert_file <- P.nodedCertFile
219 85f6a869 Iustin Pop
  let opts = [ CurlTimeout (fromIntegral $ rpcCallTimeout call)
220 85f6a869 Iustin Pop
             , CurlSSLCert cert_file
221 85f6a869 Iustin Pop
             , CurlSSLKey cert_file
222 85f6a869 Iustin Pop
             , CurlCAInfo cert_file
223 85f6a869 Iustin Pop
             ]
224 8920fa09 Iustin Pop
      opts_urls = map (\n ->
225 8920fa09 Iustin Pop
                         case prepareHttpRequest opts n call of
226 8920fa09 Iustin Pop
                           Left v -> Left v
227 8920fa09 Iustin Pop
                           Right request ->
228 8920fa09 Iustin Pop
                             Right (CurlPostFields [requestData request]:
229 8920fa09 Iustin Pop
                                    requestOpts request,
230 8920fa09 Iustin Pop
                                    requestUrl request)
231 8920fa09 Iustin Pop
                      ) nodes
232 8920fa09 Iustin Pop
  -- split the opts_urls list; we don't want to pass the
233 8920fa09 Iustin Pop
  -- failed-already nodes to Curl
234 8920fa09 Iustin Pop
  let (lefts, rights, trail) = splitEithers opts_urls
235 8920fa09 Iustin Pop
  results <- execMultiCall rights
236 8920fa09 Iustin Pop
  results' <- case recombineEithers lefts results trail of
237 8920fa09 Iustin Pop
                Bad msg -> error msg
238 8920fa09 Iustin Pop
                Ok r -> return r
239 8920fa09 Iustin Pop
  -- now parse the replies
240 8920fa09 Iustin Pop
  let results'' = map (parseHttpReply call) results'
241 c393abbf Michele Tartara
      pairedList = zip nodes results''
242 c393abbf Michele Tartara
  logRpcErrors pairedList
243 c393abbf Michele Tartara
  return pairedList
244 96dad12d Agata Murawska
245 9b09c0be Agata Murawska
-- | Helper function that is used to read dictionaries of values.
246 9b09c0be Agata Murawska
sanitizeDictResults :: [(String, J.Result a)] -> ERpcError [(String, a)]
247 3d3f562b Agata Murawska
sanitizeDictResults =
248 3d3f562b Agata Murawska
  foldr sanitize1 (Right [])
249 3d3f562b Agata Murawska
  where
250 3d3f562b Agata Murawska
    sanitize1 _ (Left e) = Left e
251 3d3f562b Agata Murawska
    sanitize1 (_, J.Error e) _ = Left $ JsonDecodeError e
252 3d3f562b Agata Murawska
    sanitize1 (name, J.Ok v) (Right res) = Right $ (name, v) : res
253 9b09c0be Agata Murawska
254 7328a28c Agata Murawska
-- | Helper function to tranform JSON Result to Either RpcError b.
255 7328a28c Agata Murawska
-- Note: For now we really only use it for b s.t. Rpc c b for some c
256 7328a28c Agata Murawska
fromJResultToRes :: J.Result a -> (a -> b) -> ERpcError b
257 7328a28c Agata Murawska
fromJResultToRes (J.Error v) _ = Left $ JsonDecodeError v
258 7328a28c Agata Murawska
fromJResultToRes (J.Ok v) f = Right $ f v
259 7328a28c Agata Murawska
260 7328a28c Agata Murawska
-- | Helper function transforming JSValue to Rpc result type.
261 7328a28c Agata Murawska
fromJSValueToRes :: (J.JSON a) => J.JSValue -> (a -> b) -> ERpcError b
262 7328a28c Agata Murawska
fromJSValueToRes val = fromJResultToRes (J.readJSON val)
263 7328a28c Agata Murawska
264 96dad12d Agata Murawska
-- * RPC calls and results
265 96dad12d Agata Murawska
266 de2a5704 Iustin Pop
-- ** Instance info
267 de2a5704 Iustin Pop
268 5188fdb7 Agata Murawska
-- | InstanceInfo
269 5188fdb7 Agata Murawska
--   Returns information about a single instance.
270 5188fdb7 Agata Murawska
271 5188fdb7 Agata Murawska
$(buildObject "RpcCallInstanceInfo" "rpcCallInstInfo"
272 5188fdb7 Agata Murawska
  [ simpleField "instance" [t| String |]
273 5188fdb7 Agata Murawska
  , simpleField "hname" [t| Hypervisor |]
274 5188fdb7 Agata Murawska
  ])
275 96dad12d Agata Murawska
276 5b11f8db Iustin Pop
$(buildObject "InstanceInfo" "instInfo"
277 9b09c0be Agata Murawska
  [ simpleField "memory" [t| Int|]
278 9b09c0be Agata Murawska
  , simpleField "state"  [t| String |] -- It depends on hypervisor :(
279 96dad12d Agata Murawska
  , simpleField "vcpus"  [t| Int |]
280 96dad12d Agata Murawska
  , simpleField "time"   [t| Int |]
281 96dad12d Agata Murawska
  ])
282 96dad12d Agata Murawska
283 5188fdb7 Agata Murawska
-- This is optional here because the result may be empty if instance is
284 5188fdb7 Agata Murawska
-- not on a node - and this is not considered an error.
285 5188fdb7 Agata Murawska
$(buildObject "RpcResultInstanceInfo" "rpcResInstInfo"
286 5188fdb7 Agata Murawska
  [ optionalField $ simpleField "inst_info" [t| InstanceInfo |]])
287 5188fdb7 Agata Murawska
288 5188fdb7 Agata Murawska
instance RpcCall RpcCallInstanceInfo where
289 274366e5 Agata Murawska
  rpcCallName _          = "instance_info"
290 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Urgent
291 5188fdb7 Agata Murawska
  rpcCallAcceptOffline _ = False
292 274366e5 Agata Murawska
  rpcCallData _ call     = J.encode
293 5188fdb7 Agata Murawska
    ( rpcCallInstInfoInstance call
294 5188fdb7 Agata Murawska
    , rpcCallInstInfoHname call
295 5188fdb7 Agata Murawska
    )
296 5188fdb7 Agata Murawska
297 5188fdb7 Agata Murawska
instance Rpc RpcCallInstanceInfo RpcResultInstanceInfo where
298 5188fdb7 Agata Murawska
  rpcResultFill _ res =
299 a93b711b Agata Murawska
    case res of
300 5188fdb7 Agata Murawska
      J.JSObject res' ->
301 5188fdb7 Agata Murawska
        case J.fromJSObject res' of
302 5188fdb7 Agata Murawska
          [] -> Right $ RpcResultInstanceInfo Nothing
303 7328a28c Agata Murawska
          _ -> fromJSValueToRes res (RpcResultInstanceInfo . Just)
304 5188fdb7 Agata Murawska
      _ -> Left $ JsonDecodeError
305 70c2362e Agata Murawska
           ("Expected JSObject, got " ++ show (pp_value res))
306 5188fdb7 Agata Murawska
307 de2a5704 Iustin Pop
-- ** AllInstancesInfo
308 de2a5704 Iustin Pop
309 5188fdb7 Agata Murawska
-- | AllInstancesInfo
310 5188fdb7 Agata Murawska
--   Returns information about all running instances on the given nodes
311 5188fdb7 Agata Murawska
$(buildObject "RpcCallAllInstancesInfo" "rpcCallAllInstInfo"
312 5188fdb7 Agata Murawska
  [ simpleField "hypervisors" [t| [Hypervisor] |] ])
313 5188fdb7 Agata Murawska
314 5b11f8db Iustin Pop
$(buildObject "RpcResultAllInstancesInfo" "rpcResAllInstInfo"
315 9b09c0be Agata Murawska
  [ simpleField "instances" [t| [(String, InstanceInfo)] |] ])
316 96dad12d Agata Murawska
317 96dad12d Agata Murawska
instance RpcCall RpcCallAllInstancesInfo where
318 274366e5 Agata Murawska
  rpcCallName _          = "all_instances_info"
319 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Urgent
320 96dad12d Agata Murawska
  rpcCallAcceptOffline _ = False
321 274366e5 Agata Murawska
  rpcCallData _ call     = J.encode [rpcCallAllInstInfoHypervisors call]
322 9b09c0be Agata Murawska
323 47163f0f Agata Murawska
instance Rpc RpcCallAllInstancesInfo RpcResultAllInstancesInfo where
324 9b09c0be Agata Murawska
  -- FIXME: Is there a simpler way to do it?
325 47163f0f Agata Murawska
  rpcResultFill _ res =
326 a93b711b Agata Murawska
    case res of
327 a93b711b Agata Murawska
      J.JSObject res' ->
328 9b09c0be Agata Murawska
        let res'' = map (second J.readJSON) (J.fromJSObject res')
329 a93b711b Agata Murawska
                        :: [(String, J.Result InstanceInfo)] in
330 9b09c0be Agata Murawska
        case sanitizeDictResults res'' of
331 9b09c0be Agata Murawska
          Left err -> Left err
332 9b09c0be Agata Murawska
          Right insts -> Right $ RpcResultAllInstancesInfo insts
333 9b09c0be Agata Murawska
      _ -> Left $ JsonDecodeError
334 70c2362e Agata Murawska
           ("Expected JSObject, got " ++ show (pp_value res))
335 96dad12d Agata Murawska
336 de2a5704 Iustin Pop
-- ** InstanceList
337 de2a5704 Iustin Pop
338 c1c5aab1 Agata Murawska
-- | InstanceList
339 c1c5aab1 Agata Murawska
-- Returns the list of running instances on the given nodes.
340 5b11f8db Iustin Pop
$(buildObject "RpcCallInstanceList" "rpcCallInstList"
341 c1c5aab1 Agata Murawska
  [ simpleField "hypervisors" [t| [Hypervisor] |] ])
342 c1c5aab1 Agata Murawska
343 5b11f8db Iustin Pop
$(buildObject "RpcResultInstanceList" "rpcResInstList"
344 9b09c0be Agata Murawska
  [ simpleField "instances" [t| [String] |] ])
345 c1c5aab1 Agata Murawska
346 c1c5aab1 Agata Murawska
instance RpcCall RpcCallInstanceList where
347 274366e5 Agata Murawska
  rpcCallName _          = "instance_list"
348 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Urgent
349 c1c5aab1 Agata Murawska
  rpcCallAcceptOffline _ = False
350 274366e5 Agata Murawska
  rpcCallData _ call     = J.encode [rpcCallInstListHypervisors call]
351 c1c5aab1 Agata Murawska
352 47163f0f Agata Murawska
instance Rpc RpcCallInstanceList RpcResultInstanceList where
353 7328a28c Agata Murawska
  rpcResultFill _ res = fromJSValueToRes res RpcResultInstanceList
354 c1c5aab1 Agata Murawska
355 de2a5704 Iustin Pop
-- ** NodeInfo
356 de2a5704 Iustin Pop
357 dc623a95 Agata Murawska
-- | NodeInfo
358 dc623a95 Agata Murawska
-- Return node information.
359 5b11f8db Iustin Pop
$(buildObject "RpcCallNodeInfo" "rpcCallNodeInfo"
360 9b09c0be Agata Murawska
  [ simpleField "volume_groups" [t| [String] |]
361 9b09c0be Agata Murawska
  , simpleField "hypervisors" [t| [Hypervisor] |]
362 319322a7 Bernardo Dal Seno
  , simpleField "exclusive_storage" [t| Map.Map String Bool |]
363 dc623a95 Agata Murawska
  ])
364 dc623a95 Agata Murawska
365 5b11f8db Iustin Pop
$(buildObject "VgInfo" "vgInfo"
366 dc623a95 Agata Murawska
  [ simpleField "name" [t| String |]
367 9b09c0be Agata Murawska
  , optionalField $ simpleField "vg_free" [t| Int |]
368 9b09c0be Agata Murawska
  , optionalField $ simpleField "vg_size" [t| Int |]
369 dc623a95 Agata Murawska
  ])
370 dc623a95 Agata Murawska
371 dc623a95 Agata Murawska
-- | We only provide common fields as described in hv_base.py.
372 5b11f8db Iustin Pop
$(buildObject "HvInfo" "hvInfo"
373 dc623a95 Agata Murawska
  [ simpleField "memory_total" [t| Int |]
374 dc623a95 Agata Murawska
  , simpleField "memory_free" [t| Int |]
375 dc623a95 Agata Murawska
  , simpleField "memory_dom0" [t| Int |]
376 dc623a95 Agata Murawska
  , simpleField "cpu_total" [t| Int |]
377 dc623a95 Agata Murawska
  , simpleField "cpu_nodes" [t| Int |]
378 dc623a95 Agata Murawska
  , simpleField "cpu_sockets" [t| Int |]
379 dc623a95 Agata Murawska
  ])
380 dc623a95 Agata Murawska
381 5b11f8db Iustin Pop
$(buildObject "RpcResultNodeInfo" "rpcResNodeInfo"
382 dc623a95 Agata Murawska
  [ simpleField "boot_id" [t| String |]
383 dc623a95 Agata Murawska
  , simpleField "vg_info" [t| [VgInfo] |]
384 dc623a95 Agata Murawska
  , simpleField "hv_info" [t| [HvInfo] |]
385 dc623a95 Agata Murawska
  ])
386 dc623a95 Agata Murawska
387 dc623a95 Agata Murawska
instance RpcCall RpcCallNodeInfo where
388 274366e5 Agata Murawska
  rpcCallName _          = "node_info"
389 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Urgent
390 dc623a95 Agata Murawska
  rpcCallAcceptOffline _ = False
391 319322a7 Bernardo Dal Seno
  rpcCallData n call     = J.encode
392 274366e5 Agata Murawska
    ( rpcCallNodeInfoVolumeGroups call
393 274366e5 Agata Murawska
    , rpcCallNodeInfoHypervisors call
394 319322a7 Bernardo Dal Seno
    , fromMaybe (error $ "Programmer error: missing parameter for node named "
395 319322a7 Bernardo Dal Seno
                         ++ nodeName n)
396 319322a7 Bernardo Dal Seno
                $ Map.lookup (nodeName n) (rpcCallNodeInfoExclusiveStorage call)
397 274366e5 Agata Murawska
    )
398 9b09c0be Agata Murawska
399 47163f0f Agata Murawska
instance Rpc RpcCallNodeInfo RpcResultNodeInfo where
400 47163f0f Agata Murawska
  rpcResultFill _ res =
401 7328a28c Agata Murawska
    fromJSValueToRes res (\(b, vg, hv) -> RpcResultNodeInfo b vg hv)
402 dc623a95 Agata Murawska
403 de2a5704 Iustin Pop
-- ** Version
404 de2a5704 Iustin Pop
405 08f7d24d Iustin Pop
-- | Query node version.
406 08f7d24d Iustin Pop
$(buildObject "RpcCallVersion" "rpcCallVersion" [])
407 8779d21a Agata Murawska
408 08f7d24d Iustin Pop
-- | Query node reply.
409 8779d21a Agata Murawska
$(buildObject "RpcResultVersion" "rpcResultVersion"
410 8779d21a Agata Murawska
  [ simpleField "version" [t| Int |]
411 8779d21a Agata Murawska
  ])
412 8779d21a Agata Murawska
413 8779d21a Agata Murawska
instance RpcCall RpcCallVersion where
414 274366e5 Agata Murawska
  rpcCallName _          = "version"
415 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Urgent
416 8779d21a Agata Murawska
  rpcCallAcceptOffline _ = True
417 ff8f0049 Iustin Pop
  rpcCallData _          = J.encode
418 8779d21a Agata Murawska
419 47163f0f Agata Murawska
instance Rpc RpcCallVersion RpcResultVersion where
420 7328a28c Agata Murawska
  rpcResultFill _ res = fromJSValueToRes res RpcResultVersion
421 8779d21a Agata Murawska
422 de2a5704 Iustin Pop
-- ** StorageList
423 de2a5704 Iustin Pop
424 47163f0f Agata Murawska
-- | StorageList
425 47163f0f Agata Murawska
426 47163f0f Agata Murawska
-- FIXME: This may be moved to Objects
427 47163f0f Agata Murawska
$(declareSADT "StorageField"
428 47163f0f Agata Murawska
  [ ( "SFUsed",        'C.sfUsed)
429 47163f0f Agata Murawska
  , ( "SFName",        'C.sfName)
430 47163f0f Agata Murawska
  , ( "SFAllocatable", 'C.sfAllocatable)
431 47163f0f Agata Murawska
  , ( "SFFree",        'C.sfFree)
432 47163f0f Agata Murawska
  , ( "SFSize",        'C.sfSize)
433 47163f0f Agata Murawska
  ])
434 47163f0f Agata Murawska
$(makeJSONInstance ''StorageField)
435 47163f0f Agata Murawska
436 47163f0f Agata Murawska
$(buildObject "RpcCallStorageList" "rpcCallStorageList"
437 47163f0f Agata Murawska
  [ simpleField "su_name" [t| StorageType |]
438 47163f0f Agata Murawska
  , simpleField "su_args" [t| [String] |]
439 47163f0f Agata Murawska
  , simpleField "name"    [t| String |]
440 47163f0f Agata Murawska
  , simpleField "fields"  [t| [StorageField] |]
441 47163f0f Agata Murawska
  ])
442 47163f0f Agata Murawska
443 47163f0f Agata Murawska
-- FIXME: The resulting JSValues should have types appropriate for their
444 47163f0f Agata Murawska
-- StorageField value: Used -> Bool, Name -> String etc
445 47163f0f Agata Murawska
$(buildObject "RpcResultStorageList" "rpcResStorageList"
446 47163f0f Agata Murawska
  [ simpleField "storage" [t| [[(StorageField, J.JSValue)]] |] ])
447 47163f0f Agata Murawska
448 47163f0f Agata Murawska
instance RpcCall RpcCallStorageList where
449 274366e5 Agata Murawska
  rpcCallName _          = "storage_list"
450 274366e5 Agata Murawska
  rpcCallTimeout _       = rpcTimeoutToRaw Normal
451 47163f0f Agata Murawska
  rpcCallAcceptOffline _ = False
452 274366e5 Agata Murawska
  rpcCallData _ call     = J.encode
453 47163f0f Agata Murawska
    ( rpcCallStorageListSuName call
454 47163f0f Agata Murawska
    , rpcCallStorageListSuArgs call
455 47163f0f Agata Murawska
    , rpcCallStorageListName call
456 47163f0f Agata Murawska
    , rpcCallStorageListFields call
457 47163f0f Agata Murawska
    )
458 47163f0f Agata Murawska
459 47163f0f Agata Murawska
instance Rpc RpcCallStorageList RpcResultStorageList where
460 47163f0f Agata Murawska
  rpcResultFill call res =
461 47163f0f Agata Murawska
    let sfields = rpcCallStorageListFields call in
462 7328a28c Agata Murawska
    fromJSValueToRes res (RpcResultStorageList . map (zip sfields))
463 de2a5704 Iustin Pop
464 de2a5704 Iustin Pop
-- ** TestDelay
465 de2a5704 Iustin Pop
466 de2a5704 Iustin Pop
-- | Call definition for test delay.
467 de2a5704 Iustin Pop
$(buildObject "RpcCallTestDelay" "rpcCallTestDelay"
468 de2a5704 Iustin Pop
  [ simpleField "duration" [t| Double |]
469 de2a5704 Iustin Pop
  ])
470 de2a5704 Iustin Pop
471 de2a5704 Iustin Pop
-- | Result definition for test delay.
472 de2a5704 Iustin Pop
data RpcResultTestDelay = RpcResultTestDelay
473 de2a5704 Iustin Pop
                          deriving Show
474 de2a5704 Iustin Pop
475 de2a5704 Iustin Pop
-- | Custom JSON instance for null result.
476 de2a5704 Iustin Pop
instance J.JSON RpcResultTestDelay where
477 de2a5704 Iustin Pop
  showJSON _        = J.JSNull
478 de2a5704 Iustin Pop
  readJSON J.JSNull = return RpcResultTestDelay
479 de2a5704 Iustin Pop
  readJSON _        = fail "Unable to read RpcResultTestDelay"
480 de2a5704 Iustin Pop
481 de2a5704 Iustin Pop
instance RpcCall RpcCallTestDelay where
482 de2a5704 Iustin Pop
  rpcCallName _          = "test_delay"
483 de2a5704 Iustin Pop
  rpcCallTimeout         = ceiling . (+ 5) . rpcCallTestDelayDuration
484 de2a5704 Iustin Pop
  rpcCallAcceptOffline _ = False
485 de2a5704 Iustin Pop
  rpcCallData _ call     = J.encode [rpcCallTestDelayDuration call]
486 de2a5704 Iustin Pop
487 de2a5704 Iustin Pop
instance Rpc RpcCallTestDelay RpcResultTestDelay where
488 de2a5704 Iustin Pop
  rpcResultFill _ res = fromJSValueToRes res id
489 842515dd Iustin Pop
490 842515dd Iustin Pop
-- ** ExportList
491 842515dd Iustin Pop
492 842515dd Iustin Pop
-- | Call definition for export list.
493 842515dd Iustin Pop
494 842515dd Iustin Pop
$(buildObject "RpcCallExportList" "rpcCallExportList" [])
495 842515dd Iustin Pop
496 842515dd Iustin Pop
-- | Result definition for export list.
497 842515dd Iustin Pop
$(buildObject "RpcResultExportList" "rpcResExportList"
498 842515dd Iustin Pop
  [ simpleField "exports" [t| [String] |]
499 842515dd Iustin Pop
  ])
500 842515dd Iustin Pop
501 842515dd Iustin Pop
instance RpcCall RpcCallExportList where
502 842515dd Iustin Pop
  rpcCallName _          = "export_list"
503 842515dd Iustin Pop
  rpcCallTimeout _       = rpcTimeoutToRaw Fast
504 842515dd Iustin Pop
  rpcCallAcceptOffline _ = False
505 842515dd Iustin Pop
  rpcCallData _          = J.encode
506 842515dd Iustin Pop
507 842515dd Iustin Pop
instance Rpc RpcCallExportList RpcResultExportList where
508 842515dd Iustin Pop
  rpcResultFill _ res = fromJSValueToRes res RpcResultExportList