Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Rpc.hs @ c1c5aab1

History | View | Annotate | Download (7.8 kB)

1 eaed5f19 Agata Murawska
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, CPP,
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 d4709cce Agata Murawska
Copyright (C) 2012 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
  , RpcResult
32 d4709cce Agata Murawska
  , Rpc
33 d4709cce Agata Murawska
  , RpcError(..)
34 eaed5f19 Agata Murawska
  , executeRpcCall
35 d4709cce Agata Murawska
36 d4709cce Agata Murawska
  , rpcCallName
37 d4709cce Agata Murawska
  , rpcCallTimeout
38 d4709cce Agata Murawska
  , rpcCallData
39 d4709cce Agata Murawska
  , rpcCallAcceptOffline
40 d4709cce Agata Murawska
41 d4709cce Agata Murawska
  , rpcResultFill
42 96dad12d Agata Murawska
43 96dad12d Agata Murawska
  , InstanceInfo(..)
44 96dad12d Agata Murawska
  , RpcCallAllInstancesInfo(..)
45 96dad12d Agata Murawska
  , RpcResultAllInstancesInfo(..)
46 96dad12d Agata Murawska
47 c1c5aab1 Agata Murawska
  , RpcCallInstanceList(..)
48 c1c5aab1 Agata Murawska
  , RpcResultInstanceList(..)
49 c1c5aab1 Agata Murawska
50 96dad12d Agata Murawska
  , rpcTimeoutFromRaw -- FIXME: Not used anywhere
51 d4709cce Agata Murawska
  ) where
52 d4709cce Agata Murawska
53 d4709cce Agata Murawska
import qualified Text.JSON as J
54 96dad12d Agata Murawska
import Text.JSON (makeObj)
55 d4709cce Agata Murawska
56 eaed5f19 Agata Murawska
#ifndef NO_CURL
57 eaed5f19 Agata Murawska
import Network.Curl
58 eaed5f19 Agata Murawska
#endif
59 eaed5f19 Agata Murawska
60 eaed5f19 Agata Murawska
import qualified Ganeti.Constants as C
61 d4709cce Agata Murawska
import Ganeti.Objects
62 96dad12d Agata Murawska
import Ganeti.THH
63 eaed5f19 Agata Murawska
import Ganeti.HTools.Compat
64 96dad12d Agata Murawska
import Ganeti.HTools.JSON
65 eaed5f19 Agata Murawska
66 eaed5f19 Agata Murawska
#ifndef NO_CURL
67 eaed5f19 Agata Murawska
-- | The curl options used for RPC.
68 eaed5f19 Agata Murawska
curlOpts :: [CurlOption]
69 eaed5f19 Agata Murawska
curlOpts = [ CurlFollowLocation False
70 eaed5f19 Agata Murawska
           , CurlCAInfo C.nodedCertFile
71 eaed5f19 Agata Murawska
           , CurlSSLVerifyHost 0
72 eaed5f19 Agata Murawska
           , CurlSSLVerifyPeer True
73 eaed5f19 Agata Murawska
           , CurlSSLCertType "PEM"
74 eaed5f19 Agata Murawska
           , CurlSSLCert C.nodedCertFile
75 eaed5f19 Agata Murawska
           , CurlSSLKeyType "PEM"
76 eaed5f19 Agata Murawska
           , CurlSSLKey C.nodedCertFile
77 eaed5f19 Agata Murawska
           , CurlConnectTimeout (fromIntegral C.rpcConnectTimeout)
78 eaed5f19 Agata Murawska
           ]
79 eaed5f19 Agata Murawska
#endif
80 d4709cce Agata Murawska
81 d4709cce Agata Murawska
-- | Data type for RPC error reporting.
82 d4709cce Agata Murawska
data RpcError
83 d4709cce Agata Murawska
  = CurlDisabledError
84 d4709cce Agata Murawska
  | CurlLayerError Node String
85 d4709cce Agata Murawska
  | JsonDecodeError String
86 d4709cce Agata Murawska
  | OfflineNodeError Node
87 d4709cce Agata Murawska
  deriving Eq
88 d4709cce Agata Murawska
89 d4709cce Agata Murawska
instance Show RpcError where
90 d4709cce Agata Murawska
  show CurlDisabledError =
91 d4709cce Agata Murawska
    "RPC/curl backend disabled at compile time"
92 d4709cce Agata Murawska
  show (CurlLayerError node code) =
93 d4709cce Agata Murawska
    "Curl error for " ++ nodeName node ++ ", error " ++ code
94 d4709cce Agata Murawska
  show (JsonDecodeError msg) =
95 d4709cce Agata Murawska
    "Error while decoding JSON from HTTP response " ++ msg
96 d4709cce Agata Murawska
  show (OfflineNodeError node) =
97 d4709cce Agata Murawska
    "Node " ++ nodeName node ++ " is marked as offline"
98 d4709cce Agata Murawska
99 d4709cce Agata Murawska
rpcErrorJsonReport :: (Monad m) => J.Result a -> m (Either RpcError a)
100 d4709cce Agata Murawska
rpcErrorJsonReport (J.Error x) = return $ Left $ JsonDecodeError x
101 d4709cce Agata Murawska
rpcErrorJsonReport (J.Ok x) = return $ Right x
102 d4709cce Agata Murawska
103 96dad12d Agata Murawska
-- | Basic timeouts for RPC calls.
104 96dad12d Agata Murawska
$(declareIADT "RpcTimeout"
105 96dad12d Agata Murawska
  [ ( "Urgent",    'C.rpcTmoUrgent )
106 96dad12d Agata Murawska
  , ( "Fast",      'C.rpcTmoFast )
107 96dad12d Agata Murawska
  , ( "Normal",    'C.rpcTmoNormal )
108 96dad12d Agata Murawska
  , ( "Slow",      'C.rpcTmoSlow )
109 96dad12d Agata Murawska
  , ( "FourHours", 'C.rpcTmo4hrs )
110 96dad12d Agata Murawska
  , ( "OneDay",    'C.rpcTmo1day )
111 96dad12d Agata Murawska
  ])
112 96dad12d Agata Murawska
113 d4709cce Agata Murawska
-- | A generic class for RPC calls.
114 d4709cce Agata Murawska
class (J.JSON a) => RpcCall a where
115 d4709cce Agata Murawska
  -- | Give the (Python) name of the procedure.
116 d4709cce Agata Murawska
  rpcCallName :: a -> String
117 d4709cce Agata Murawska
  -- | Calculate the timeout value for the call execution.
118 d4709cce Agata Murawska
  rpcCallTimeout :: a -> Int
119 d4709cce Agata Murawska
  -- | Prepare arguments of the call to be send as POST.
120 d4709cce Agata Murawska
  rpcCallData :: Node -> a -> String
121 d4709cce Agata Murawska
  -- | Whether we accept offline nodes when making a call.
122 d4709cce Agata Murawska
  rpcCallAcceptOffline :: a -> Bool
123 d4709cce Agata Murawska
124 d4709cce Agata Murawska
  rpcCallData _ = J.encode
125 d4709cce Agata Murawska
126 d4709cce Agata Murawska
-- | A generic class for RPC results with default implementation.
127 d4709cce Agata Murawska
class (J.JSON a) => RpcResult a where
128 d4709cce Agata Murawska
  -- | Create a result based on the received HTTP response.
129 d4709cce Agata Murawska
  rpcResultFill :: (Monad m) => String -> m (Either RpcError a)
130 d4709cce Agata Murawska
131 d4709cce Agata Murawska
  rpcResultFill res = rpcErrorJsonReport $  J.decode res
132 d4709cce Agata Murawska
133 d4709cce Agata Murawska
-- | Generic class that ensures matching RPC call with its respective
134 d4709cce Agata Murawska
-- result.
135 d4709cce Agata Murawska
class (RpcCall a, RpcResult b) => Rpc a b | a -> b
136 eaed5f19 Agata Murawska
137 eaed5f19 Agata Murawska
-- | Http Request definition.
138 eaed5f19 Agata Murawska
data HttpClientRequest = HttpClientRequest
139 eaed5f19 Agata Murawska
  { requestTimeout :: Int
140 eaed5f19 Agata Murawska
  , requestUrl :: String
141 eaed5f19 Agata Murawska
  , requestPostData :: String
142 eaed5f19 Agata Murawska
  }
143 eaed5f19 Agata Murawska
144 eaed5f19 Agata Murawska
-- | Execute the request and return the result as a plain String. When
145 eaed5f19 Agata Murawska
-- curl reports an error, we propagate it.
146 eaed5f19 Agata Murawska
executeHttpRequest :: Node -> Either RpcError HttpClientRequest
147 eaed5f19 Agata Murawska
                   -> IO (Either RpcError String)
148 eaed5f19 Agata Murawska
149 eaed5f19 Agata Murawska
executeHttpRequest _ (Left rpc_err) = return $ Left rpc_err
150 eaed5f19 Agata Murawska
#ifdef NO_CURL
151 eaed5f19 Agata Murawska
executeHttpRequest _ _ = return $ Left CurlDisabledError
152 eaed5f19 Agata Murawska
#else
153 eaed5f19 Agata Murawska
executeHttpRequest node (Right request) = do
154 eaed5f19 Agata Murawska
  let reqOpts = [ CurlTimeout (fromIntegral $ requestTimeout request)
155 eaed5f19 Agata Murawska
                , CurlPostFields [requestPostData request]
156 eaed5f19 Agata Murawska
                ]
157 eaed5f19 Agata Murawska
      url = requestUrl request
158 eaed5f19 Agata Murawska
  -- FIXME: This is very similar to getUrl in Htools/Rapi.hs
159 eaed5f19 Agata Murawska
  (code, !body) <- curlGetString url $ curlOpts ++ reqOpts
160 eaed5f19 Agata Murawska
  case code of
161 eaed5f19 Agata Murawska
    CurlOK -> return $ Right body
162 eaed5f19 Agata Murawska
    _ -> return $ Left $ CurlLayerError node (show code)
163 eaed5f19 Agata Murawska
#endif
164 eaed5f19 Agata Murawska
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 eaed5f19 Agata Murawska
      port = snd C.daemonsPortsGanetiNoded
170 eaed5f19 Agata Murawska
      path_prefix = "https://" ++ (node_ip) ++ ":" ++ (show port) in
171 eaed5f19 Agata Murawska
  path_prefix ++ "/" ++ rpcCallName call
172 eaed5f19 Agata Murawska
173 eaed5f19 Agata Murawska
-- | Create HTTP request for a given node provided it is online,
174 eaed5f19 Agata Murawska
-- otherwise create empty response.
175 eaed5f19 Agata Murawska
prepareHttpRequest ::  (RpcCall a) => Node -> a
176 eaed5f19 Agata Murawska
                   -> Either RpcError HttpClientRequest
177 eaed5f19 Agata Murawska
prepareHttpRequest node call
178 eaed5f19 Agata Murawska
  | rpcCallAcceptOffline call ||
179 eaed5f19 Agata Murawska
    (not $ nodeOffline node) =
180 eaed5f19 Agata Murawska
      Right $ HttpClientRequest { requestTimeout = rpcCallTimeout call
181 eaed5f19 Agata Murawska
                                , requestUrl = prepareUrl node call
182 eaed5f19 Agata Murawska
                                , requestPostData = rpcCallData node call
183 eaed5f19 Agata Murawska
                                }
184 eaed5f19 Agata Murawska
  | otherwise = Left $ OfflineNodeError node
185 eaed5f19 Agata Murawska
186 eaed5f19 Agata Murawska
-- | Parse the response or propagate the error.
187 eaed5f19 Agata Murawska
parseHttpResponse :: (Monad m, RpcResult a) => Either RpcError String
188 eaed5f19 Agata Murawska
                  -> m (Either RpcError a)
189 eaed5f19 Agata Murawska
parseHttpResponse (Left err) = return $ Left err
190 eaed5f19 Agata Murawska
parseHttpResponse (Right response) = rpcResultFill response
191 eaed5f19 Agata Murawska
192 eaed5f19 Agata Murawska
-- | Execute RPC call for a sigle node.
193 eaed5f19 Agata Murawska
executeSingleRpcCall :: (Rpc a b) => Node -> a -> IO (Node, Either RpcError b)
194 eaed5f19 Agata Murawska
executeSingleRpcCall node call = do
195 eaed5f19 Agata Murawska
  let request = prepareHttpRequest node call
196 eaed5f19 Agata Murawska
  response <- executeHttpRequest node request
197 eaed5f19 Agata Murawska
  result <- parseHttpResponse response
198 eaed5f19 Agata Murawska
  return (node, result)
199 eaed5f19 Agata Murawska
200 eaed5f19 Agata Murawska
-- | Execute RPC call for many nodes in parallel.
201 eaed5f19 Agata Murawska
executeRpcCall :: (Rpc a b) => [Node] -> a -> IO [(Node, Either RpcError b)]
202 eaed5f19 Agata Murawska
executeRpcCall nodes call =
203 eaed5f19 Agata Murawska
  sequence $ parMap rwhnf (uncurry executeSingleRpcCall)
204 eaed5f19 Agata Murawska
               (zip nodes $ repeat call)
205 96dad12d Agata Murawska
206 96dad12d Agata Murawska
-- * RPC calls and results
207 96dad12d Agata Murawska
208 96dad12d Agata Murawska
-- | AllInstancesInfo
209 96dad12d Agata Murawska
--   Returns information about all instances on the given nodes
210 96dad12d Agata Murawska
$(buildObject "RpcCallAllInstancesInfo" "rpcCallAllInstInfo" $
211 96dad12d Agata Murawska
  [ simpleField "hypervisors" [t| [Hypervisor] |] ])
212 96dad12d Agata Murawska
213 96dad12d Agata Murawska
$(buildObject "InstanceInfo" "instInfo" $
214 96dad12d Agata Murawska
  [ simpleField "name"   [t| String |]
215 96dad12d Agata Murawska
  , simpleField "memory" [t| Int|]
216 96dad12d Agata Murawska
  , simpleField "state"  [t| AdminState |]
217 96dad12d Agata Murawska
  , simpleField "vcpus"  [t| Int |]
218 96dad12d Agata Murawska
  , simpleField "time"   [t| Int |]
219 96dad12d Agata Murawska
  ])
220 96dad12d Agata Murawska
221 96dad12d Agata Murawska
$(buildObject "RpcResultAllInstancesInfo" "rpcResAllInstInfo" $
222 96dad12d Agata Murawska
  [ simpleField "instances" [t| [InstanceInfo] |] ])
223 96dad12d Agata Murawska
224 96dad12d Agata Murawska
instance RpcCall RpcCallAllInstancesInfo where
225 96dad12d Agata Murawska
  rpcCallName _ = "all_instances_info"
226 96dad12d Agata Murawska
  rpcCallTimeout _ = rpcTimeoutToRaw Urgent
227 96dad12d Agata Murawska
  rpcCallAcceptOffline _ = False
228 96dad12d Agata Murawska
229 96dad12d Agata Murawska
instance RpcResult RpcResultAllInstancesInfo
230 96dad12d Agata Murawska
231 96dad12d Agata Murawska
instance Rpc RpcCallAllInstancesInfo RpcResultAllInstancesInfo
232 c1c5aab1 Agata Murawska
233 c1c5aab1 Agata Murawska
-- | InstanceList
234 c1c5aab1 Agata Murawska
-- Returns the list of running instances on the given nodes.
235 c1c5aab1 Agata Murawska
$(buildObject "RpcCallInstanceList" "rpcCallInstList" $
236 c1c5aab1 Agata Murawska
  [ simpleField "hypervisors" [t| [Hypervisor] |] ])
237 c1c5aab1 Agata Murawska
238 c1c5aab1 Agata Murawska
$(buildObject "RpcResultInstanceList" "rpcResInstList" $
239 c1c5aab1 Agata Murawska
  [ simpleField "node"      [t| Node |]
240 c1c5aab1 Agata Murawska
  , simpleField "instances" [t| [String] |]
241 c1c5aab1 Agata Murawska
  ])
242 c1c5aab1 Agata Murawska
243 c1c5aab1 Agata Murawska
instance RpcCall RpcCallInstanceList where
244 c1c5aab1 Agata Murawska
  rpcCallName _ = "instance_list"
245 c1c5aab1 Agata Murawska
  rpcCallTimeout _ = rpcTimeoutToRaw Urgent
246 c1c5aab1 Agata Murawska
  rpcCallAcceptOffline _ = False
247 c1c5aab1 Agata Murawska
248 c1c5aab1 Agata Murawska
instance RpcResult RpcResultInstanceList
249 c1c5aab1 Agata Murawska
250 c1c5aab1 Agata Murawska
instance Rpc RpcCallInstanceList RpcResultInstanceList