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 |