root / src / Ganeti / Luxi.hs @ 952cf6e2
History | View | Annotate | Download (11.8 kB)
1 | a0090487 | Agata Murawska | {-# LANGUAGE TemplateHaskell #-} |
---|---|---|---|
2 | a0090487 | Agata Murawska | |
3 | 6583e677 | Iustin Pop | {-| Implementation of the Ganeti LUXI interface. |
4 | 6583e677 | Iustin Pop | |
5 | 6583e677 | Iustin Pop | -} |
6 | 6583e677 | Iustin Pop | |
7 | 6583e677 | Iustin Pop | {- |
8 | 6583e677 | Iustin Pop | |
9 | 71a4c605 | Petr Pudlak | Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc. |
10 | 6583e677 | Iustin Pop | |
11 | 6583e677 | Iustin Pop | This program is free software; you can redistribute it and/or modify |
12 | 6583e677 | Iustin Pop | it under the terms of the GNU General Public License as published by |
13 | 6583e677 | Iustin Pop | the Free Software Foundation; either version 2 of the License, or |
14 | 6583e677 | Iustin Pop | (at your option) any later version. |
15 | 6583e677 | Iustin Pop | |
16 | 6583e677 | Iustin Pop | This program is distributed in the hope that it will be useful, but |
17 | 6583e677 | Iustin Pop | WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | 6583e677 | Iustin Pop | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | 6583e677 | Iustin Pop | General Public License for more details. |
20 | 6583e677 | Iustin Pop | |
21 | 6583e677 | Iustin Pop | You should have received a copy of the GNU General Public License |
22 | 6583e677 | Iustin Pop | along with this program; if not, write to the Free Software |
23 | 6583e677 | Iustin Pop | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
24 | 6583e677 | Iustin Pop | 02110-1301, USA. |
25 | 6583e677 | Iustin Pop | |
26 | 6583e677 | Iustin Pop | -} |
27 | 6583e677 | Iustin Pop | |
28 | 6583e677 | Iustin Pop | module Ganeti.Luxi |
29 | ebf38064 | Iustin Pop | ( LuxiOp(..) |
30 | 95d0d502 | Iustin Pop | , LuxiReq(..) |
31 | ebf38064 | Iustin Pop | , Client |
32 | 0fbc8447 | Petr Pudlak | , Server |
33 | ccc817a2 | Iustin Pop | , JobId |
34 | c48711d5 | Iustin Pop | , fromJobId |
35 | c48711d5 | Iustin Pop | , makeJobId |
36 | 0aff2293 | Iustin Pop | , RecvResult(..) |
37 | 0aff2293 | Iustin Pop | , strOfOp |
38 | d605e261 | Petr Pudlak | , getLuxiClient |
39 | d605e261 | Petr Pudlak | , getLuxiServer |
40 | 13f2321c | Iustin Pop | , acceptClient |
41 | ebf38064 | Iustin Pop | , closeClient |
42 | 0aff2293 | Iustin Pop | , closeServer |
43 | ebf38064 | Iustin Pop | , callMethod |
44 | ebf38064 | Iustin Pop | , submitManyJobs |
45 | ebf38064 | Iustin Pop | , queryJobsStatus |
46 | cdd495ae | Iustin Pop | , buildCall |
47 | 0aff2293 | Iustin Pop | , buildResponse |
48 | d79a6502 | Petr Pudlak | , decodeLuxiCall |
49 | 13f2321c | Iustin Pop | , recvMsg |
50 | 0aff2293 | Iustin Pop | , recvMsgExt |
51 | 13f2321c | Iustin Pop | , sendMsg |
52 | 471b6c46 | Iustin Pop | , allLuxiCalls |
53 | ebf38064 | Iustin Pop | ) where |
54 | 6583e677 | Iustin Pop | |
55 | ed7f7fd9 | Petr Pudlak | import Control.Applicative (optional) |
56 | 6583e677 | Iustin Pop | import Control.Monad |
57 | 71a4c605 | Petr Pudlak | import qualified Data.ByteString.UTF8 as UTF8 |
58 | 0903280b | Iustin Pop | import Text.JSON (encodeStrict, decodeStrict) |
59 | 6583e677 | Iustin Pop | import qualified Text.JSON as J |
60 | 7adb7dff | Iustin Pop | import Text.JSON.Pretty (pp_value) |
61 | 6583e677 | Iustin Pop | import Text.JSON.Types |
62 | 6583e677 | Iustin Pop | |
63 | 4cd79ca8 | Iustin Pop | import Ganeti.BasicTypes |
64 | 92678b3c | Iustin Pop | import Ganeti.Constants |
65 | 7adb7dff | Iustin Pop | import Ganeti.Errors |
66 | 7adb7dff | Iustin Pop | import Ganeti.JSON |
67 | 71a4c605 | Petr Pudlak | import Ganeti.UDSServer |
68 | fa10983e | Iustin Pop | import Ganeti.OpParams (pTagsObject) |
69 | 367c4241 | Dato Simó | import Ganeti.OpCodes |
70 | 4cab6703 | Iustin Pop | import qualified Ganeti.Query.Language as Qlang |
71 | 0fbc8447 | Petr Pudlak | import Ganeti.Runtime (GanetiDaemon(..)) |
72 | a0090487 | Agata Murawska | import Ganeti.THH |
73 | 72375ff8 | Petr Pudlak | import Ganeti.THH.Field |
74 | c48711d5 | Iustin Pop | import Ganeti.Types |
75 | 6583e677 | Iustin Pop | |
76 | 0aff2293 | Iustin Pop | |
77 | a0090487 | Agata Murawska | -- | Currently supported Luxi operations and JSON serialization. |
78 | a0090487 | Agata Murawska | $(genLuxiOp "LuxiOp" |
79 | 72295708 | Iustin Pop | [ (luxiReqQuery, |
80 | 88609f00 | Iustin Pop | [ simpleField "what" [t| Qlang.ItemType |] |
81 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
82 | 88609f00 | Iustin Pop | , simpleField "qfilter" [t| Qlang.Filter Qlang.FilterField |] |
83 | ebf38064 | Iustin Pop | ]) |
84 | 72295708 | Iustin Pop | , (luxiReqQueryFields, |
85 | 88609f00 | Iustin Pop | [ simpleField "what" [t| Qlang.ItemType |] |
86 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
87 | 72295708 | Iustin Pop | ]) |
88 | fae980e5 | Iustin Pop | , (luxiReqQueryNodes, |
89 | 88609f00 | Iustin Pop | [ simpleField "names" [t| [String] |] |
90 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
91 | 88609f00 | Iustin Pop | , simpleField "lock" [t| Bool |] |
92 | ebf38064 | Iustin Pop | ]) |
93 | fae980e5 | Iustin Pop | , (luxiReqQueryGroups, |
94 | 88609f00 | Iustin Pop | [ simpleField "names" [t| [String] |] |
95 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
96 | 88609f00 | Iustin Pop | , simpleField "lock" [t| Bool |] |
97 | ebf38064 | Iustin Pop | ]) |
98 | 795d035d | Klaus Aehlig | , (luxiReqQueryNetworks, |
99 | 795d035d | Klaus Aehlig | [ simpleField "names" [t| [String] |] |
100 | 795d035d | Klaus Aehlig | , simpleField "fields" [t| [String] |] |
101 | 795d035d | Klaus Aehlig | , simpleField "lock" [t| Bool |] |
102 | 795d035d | Klaus Aehlig | ]) |
103 | fae980e5 | Iustin Pop | , (luxiReqQueryInstances, |
104 | 88609f00 | Iustin Pop | [ simpleField "names" [t| [String] |] |
105 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
106 | 88609f00 | Iustin Pop | , simpleField "lock" [t| Bool |] |
107 | ebf38064 | Iustin Pop | ]) |
108 | fae980e5 | Iustin Pop | , (luxiReqQueryJobs, |
109 | c48711d5 | Iustin Pop | [ simpleField "ids" [t| [JobId] |] |
110 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String] |] |
111 | ebf38064 | Iustin Pop | ]) |
112 | fae980e5 | Iustin Pop | , (luxiReqQueryExports, |
113 | 88609f00 | Iustin Pop | [ simpleField "nodes" [t| [String] |] |
114 | 88609f00 | Iustin Pop | , simpleField "lock" [t| Bool |] |
115 | ebf38064 | Iustin Pop | ]) |
116 | fae980e5 | Iustin Pop | , (luxiReqQueryConfigValues, |
117 | 88609f00 | Iustin Pop | [ simpleField "fields" [t| [String] |] ] |
118 | ebf38064 | Iustin Pop | ) |
119 | fae980e5 | Iustin Pop | , (luxiReqQueryClusterInfo, []) |
120 | fae980e5 | Iustin Pop | , (luxiReqQueryTags, |
121 | 9131274c | Jose A. Lopes | [ pTagsObject |
122 | 34af39e8 | Jose A. Lopes | , simpleField "name" [t| String |] |
123 | 34af39e8 | Jose A. Lopes | ]) |
124 | fae980e5 | Iustin Pop | , (luxiReqSubmitJob, |
125 | 7e723913 | Iustin Pop | [ simpleField "job" [t| [MetaOpCode] |] ] |
126 | ebf38064 | Iustin Pop | ) |
127 | 346c3037 | Klaus Aehlig | , (luxiReqSubmitJobToDrainedQueue, |
128 | 346c3037 | Klaus Aehlig | [ simpleField "job" [t| [MetaOpCode] |] ] |
129 | 346c3037 | Klaus Aehlig | ) |
130 | fae980e5 | Iustin Pop | , (luxiReqSubmitManyJobs, |
131 | 7e723913 | Iustin Pop | [ simpleField "ops" [t| [[MetaOpCode]] |] ] |
132 | ebf38064 | Iustin Pop | ) |
133 | fae980e5 | Iustin Pop | , (luxiReqWaitForJobChange, |
134 | c48711d5 | Iustin Pop | [ simpleField "job" [t| JobId |] |
135 | 88609f00 | Iustin Pop | , simpleField "fields" [t| [String]|] |
136 | 88609f00 | Iustin Pop | , simpleField "prev_job" [t| JSValue |] |
137 | 88609f00 | Iustin Pop | , simpleField "prev_log" [t| JSValue |] |
138 | 88609f00 | Iustin Pop | , simpleField "tmout" [t| Int |] |
139 | ebf38064 | Iustin Pop | ]) |
140 | d9d1e541 | Klaus Aehlig | , (luxiReqPickupJob, |
141 | d9d1e541 | Klaus Aehlig | [ simpleField "job" [t| JobId |] ] |
142 | d9d1e541 | Klaus Aehlig | ) |
143 | fae980e5 | Iustin Pop | , (luxiReqArchiveJob, |
144 | c48711d5 | Iustin Pop | [ simpleField "job" [t| JobId |] ] |
145 | ebf38064 | Iustin Pop | ) |
146 | fae980e5 | Iustin Pop | , (luxiReqAutoArchiveJobs, |
147 | 88609f00 | Iustin Pop | [ simpleField "age" [t| Int |] |
148 | 88609f00 | Iustin Pop | , simpleField "tmout" [t| Int |] |
149 | ebf38064 | Iustin Pop | ]) |
150 | fae980e5 | Iustin Pop | , (luxiReqCancelJob, |
151 | c48711d5 | Iustin Pop | [ simpleField "job" [t| JobId |] ] |
152 | ebf38064 | Iustin Pop | ) |
153 | f63ffb37 | Michael Hanselmann | , (luxiReqChangeJobPriority, |
154 | c48711d5 | Iustin Pop | [ simpleField "job" [t| JobId |] |
155 | f63ffb37 | Michael Hanselmann | , simpleField "priority" [t| Int |] ] |
156 | f63ffb37 | Michael Hanselmann | ) |
157 | fae980e5 | Iustin Pop | , (luxiReqSetDrainFlag, |
158 | 88609f00 | Iustin Pop | [ simpleField "flag" [t| Bool |] ] |
159 | ebf38064 | Iustin Pop | ) |
160 | fae980e5 | Iustin Pop | , (luxiReqSetWatcherPause, |
161 | d819aba6 | Klaus Aehlig | [ optionalNullSerField |
162 | ed7f7fd9 | Petr Pudlak | $ timeAsDoubleField "duration" ] |
163 | ebf38064 | Iustin Pop | ) |
164 | a0090487 | Agata Murawska | ]) |
165 | 6583e677 | Iustin Pop | |
166 | 95d0d502 | Iustin Pop | $(makeJSONInstance ''LuxiReq) |
167 | 95d0d502 | Iustin Pop | |
168 | 471b6c46 | Iustin Pop | -- | List of all defined Luxi calls. |
169 | 471b6c46 | Iustin Pop | $(genAllConstr (drop 3) ''LuxiReq "allLuxiCalls") |
170 | 471b6c46 | Iustin Pop | |
171 | 6583e677 | Iustin Pop | -- | The serialisation of LuxiOps into strings in messages. |
172 | a0090487 | Agata Murawska | $(genStrOfOp ''LuxiOp "strOfOp") |
173 | 6583e677 | Iustin Pop | |
174 | cdd495ae | Iustin Pop | |
175 | 0fbc8447 | Petr Pudlak | luxiConnectConfig :: ConnectConfig |
176 | 0fbc8447 | Petr Pudlak | luxiConnectConfig = ConnectConfig { connDaemon = GanetiLuxid |
177 | 0fbc8447 | Petr Pudlak | , recvTmo = luxiDefRwto |
178 | 0fbc8447 | Petr Pudlak | , sendTmo = luxiDefRwto |
179 | 0fbc8447 | Petr Pudlak | } |
180 | 0fbc8447 | Petr Pudlak | |
181 | 0fbc8447 | Petr Pudlak | -- | Connects to the master daemon and returns a luxi Client. |
182 | 0fbc8447 | Petr Pudlak | getLuxiClient :: String -> IO Client |
183 | 0fbc8447 | Petr Pudlak | getLuxiClient = connectClient luxiConnectConfig luxiDefCtmo |
184 | 0fbc8447 | Petr Pudlak | |
185 | 0fbc8447 | Petr Pudlak | -- | Creates and returns a server endpoint. |
186 | 0fbc8447 | Petr Pudlak | getLuxiServer :: Bool -> FilePath -> IO Server |
187 | 0fbc8447 | Petr Pudlak | getLuxiServer = connectServer luxiConnectConfig |
188 | 0fbc8447 | Petr Pudlak | |
189 | 6583e677 | Iustin Pop | -- | Serialize a request to String. |
190 | 6583e677 | Iustin Pop | buildCall :: LuxiOp -- ^ The method |
191 | 6583e677 | Iustin Pop | -> String -- ^ The serialized form |
192 | 683b1ca7 | Iustin Pop | buildCall lo = |
193 | 2cdaf225 | Iustin Pop | let ja = [ (strOfKey Method, J.showJSON $ strOfOp lo) |
194 | 2cdaf225 | Iustin Pop | , (strOfKey Args, opToArgs lo) |
195 | ebf38064 | Iustin Pop | ] |
196 | ebf38064 | Iustin Pop | jo = toJSObject ja |
197 | ebf38064 | Iustin Pop | in encodeStrict jo |
198 | 6583e677 | Iustin Pop | |
199 | cdd495ae | Iustin Pop | |
200 | cdd495ae | Iustin Pop | -- | Converts Luxi call arguments into a 'LuxiOp' data structure. |
201 | d79a6502 | Petr Pudlak | -- This is used for building a Luxi 'Handler'. |
202 | cdd495ae | Iustin Pop | -- |
203 | cdd495ae | Iustin Pop | -- This is currently hand-coded until we make it more uniform so that |
204 | cdd495ae | Iustin Pop | -- it can be generated using TH. |
205 | d79a6502 | Petr Pudlak | decodeLuxiCall :: JSValue -> JSValue -> Result LuxiOp |
206 | d79a6502 | Petr Pudlak | decodeLuxiCall method args = do |
207 | d79a6502 | Petr Pudlak | call <- fromJResult "Unable to parse LUXI request method" $ J.readJSON method |
208 | cdd495ae | Iustin Pop | case call of |
209 | cdd495ae | Iustin Pop | ReqQueryJobs -> do |
210 | c48711d5 | Iustin Pop | (jids, jargs) <- fromJVal args |
211 | d2970809 | Iustin Pop | jids' <- case jids of |
212 | d2970809 | Iustin Pop | JSNull -> return [] |
213 | d2970809 | Iustin Pop | _ -> fromJVal jids |
214 | d2970809 | Iustin Pop | return $ QueryJobs jids' jargs |
215 | cdd495ae | Iustin Pop | ReqQueryInstances -> do |
216 | cdd495ae | Iustin Pop | (names, fields, locking) <- fromJVal args |
217 | cdd495ae | Iustin Pop | return $ QueryInstances names fields locking |
218 | cdd495ae | Iustin Pop | ReqQueryNodes -> do |
219 | cdd495ae | Iustin Pop | (names, fields, locking) <- fromJVal args |
220 | cdd495ae | Iustin Pop | return $ QueryNodes names fields locking |
221 | cdd495ae | Iustin Pop | ReqQueryGroups -> do |
222 | cdd495ae | Iustin Pop | (names, fields, locking) <- fromJVal args |
223 | cdd495ae | Iustin Pop | return $ QueryGroups names fields locking |
224 | 5b11f8db | Iustin Pop | ReqQueryClusterInfo -> |
225 | cdd495ae | Iustin Pop | return QueryClusterInfo |
226 | 795d035d | Klaus Aehlig | ReqQueryNetworks -> do |
227 | 795d035d | Klaus Aehlig | (names, fields, locking) <- fromJVal args |
228 | 795d035d | Klaus Aehlig | return $ QueryNetworks names fields locking |
229 | cdd495ae | Iustin Pop | ReqQuery -> do |
230 | 9a94c848 | Iustin Pop | (what, fields, qfilter) <- fromJVal args |
231 | 9a94c848 | Iustin Pop | return $ Query what fields qfilter |
232 | 72295708 | Iustin Pop | ReqQueryFields -> do |
233 | 72295708 | Iustin Pop | (what, fields) <- fromJVal args |
234 | 72295708 | Iustin Pop | fields' <- case fields of |
235 | 72295708 | Iustin Pop | JSNull -> return [] |
236 | 72295708 | Iustin Pop | _ -> fromJVal fields |
237 | 72295708 | Iustin Pop | return $ QueryFields what fields' |
238 | cdd495ae | Iustin Pop | ReqSubmitJob -> do |
239 | cdd495ae | Iustin Pop | [ops1] <- fromJVal args |
240 | cdd495ae | Iustin Pop | ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1 |
241 | cdd495ae | Iustin Pop | return $ SubmitJob ops2 |
242 | 346c3037 | Klaus Aehlig | ReqSubmitJobToDrainedQueue -> do |
243 | 346c3037 | Klaus Aehlig | [ops1] <- fromJVal args |
244 | 346c3037 | Klaus Aehlig | ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1 |
245 | 346c3037 | Klaus Aehlig | return $ SubmitJobToDrainedQueue ops2 |
246 | cdd495ae | Iustin Pop | ReqSubmitManyJobs -> do |
247 | cdd495ae | Iustin Pop | [ops1] <- fromJVal args |
248 | cdd495ae | Iustin Pop | ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1 |
249 | cdd495ae | Iustin Pop | return $ SubmitManyJobs ops2 |
250 | cdd495ae | Iustin Pop | ReqWaitForJobChange -> do |
251 | cdd495ae | Iustin Pop | (jid, fields, pinfo, pidx, wtmout) <- |
252 | cdd495ae | Iustin Pop | -- No instance for 5-tuple, code copied from the |
253 | cdd495ae | Iustin Pop | -- json sources and adapted |
254 | cdd495ae | Iustin Pop | fromJResult "Parsing WaitForJobChange message" $ |
255 | cdd495ae | Iustin Pop | case args of |
256 | cdd495ae | Iustin Pop | JSArray [a, b, c, d, e] -> |
257 | cdd495ae | Iustin Pop | (,,,,) `fmap` |
258 | cdd495ae | Iustin Pop | J.readJSON a `ap` |
259 | cdd495ae | Iustin Pop | J.readJSON b `ap` |
260 | cdd495ae | Iustin Pop | J.readJSON c `ap` |
261 | cdd495ae | Iustin Pop | J.readJSON d `ap` |
262 | cdd495ae | Iustin Pop | J.readJSON e |
263 | cdd495ae | Iustin Pop | _ -> J.Error "Not enough values" |
264 | c48711d5 | Iustin Pop | return $ WaitForJobChange jid fields pinfo pidx wtmout |
265 | d9d1e541 | Klaus Aehlig | ReqPickupJob -> do |
266 | d9d1e541 | Klaus Aehlig | [jid] <- fromJVal args |
267 | d9d1e541 | Klaus Aehlig | return $ PickupJob jid |
268 | cdd495ae | Iustin Pop | ReqArchiveJob -> do |
269 | cdd495ae | Iustin Pop | [jid] <- fromJVal args |
270 | c48711d5 | Iustin Pop | return $ ArchiveJob jid |
271 | cdd495ae | Iustin Pop | ReqAutoArchiveJobs -> do |
272 | cdd495ae | Iustin Pop | (age, tmout) <- fromJVal args |
273 | cdd495ae | Iustin Pop | return $ AutoArchiveJobs age tmout |
274 | cdd495ae | Iustin Pop | ReqQueryExports -> do |
275 | cdd495ae | Iustin Pop | (nodes, lock) <- fromJVal args |
276 | cdd495ae | Iustin Pop | return $ QueryExports nodes lock |
277 | cdd495ae | Iustin Pop | ReqQueryConfigValues -> do |
278 | cdd495ae | Iustin Pop | [fields] <- fromJVal args |
279 | cdd495ae | Iustin Pop | return $ QueryConfigValues fields |
280 | cdd495ae | Iustin Pop | ReqQueryTags -> do |
281 | cdd495ae | Iustin Pop | (kind, name) <- fromJVal args |
282 | 34af39e8 | Jose A. Lopes | return $ QueryTags kind name |
283 | cdd495ae | Iustin Pop | ReqCancelJob -> do |
284 | c48711d5 | Iustin Pop | [jid] <- fromJVal args |
285 | c48711d5 | Iustin Pop | return $ CancelJob jid |
286 | f63ffb37 | Michael Hanselmann | ReqChangeJobPriority -> do |
287 | c48711d5 | Iustin Pop | (jid, priority) <- fromJVal args |
288 | c48711d5 | Iustin Pop | return $ ChangeJobPriority jid priority |
289 | cdd495ae | Iustin Pop | ReqSetDrainFlag -> do |
290 | cdd495ae | Iustin Pop | [flag] <- fromJVal args |
291 | cdd495ae | Iustin Pop | return $ SetDrainFlag flag |
292 | cdd495ae | Iustin Pop | ReqSetWatcherPause -> do |
293 | ed7f7fd9 | Petr Pudlak | duration <- optional $ do |
294 | ed7f7fd9 | Petr Pudlak | [x] <- fromJVal args |
295 | ed7f7fd9 | Petr Pudlak | liftM unTimeAsDoubleJSON $ fromJVal x |
296 | cdd495ae | Iustin Pop | return $ SetWatcherPause duration |
297 | cdd495ae | Iustin Pop | |
298 | 6583e677 | Iustin Pop | -- | Check that luxi responses contain the required keys and that the |
299 | 6583e677 | Iustin Pop | -- call was successful. |
300 | 7adb7dff | Iustin Pop | validateResult :: String -> ErrorResult JSValue |
301 | 6583e677 | Iustin Pop | validateResult s = do |
302 | e821050d | Iustin Pop | when (UTF8.replacement_char `elem` s) $ |
303 | e821050d | Iustin Pop | fail "Failed to decode UTF-8, detected replacement char after decoding" |
304 | 7adb7dff | Iustin Pop | oarr <- fromJResult "Parsing LUXI response" (decodeStrict s) |
305 | 262f3e6c | Iustin Pop | let arr = J.fromJSObject oarr |
306 | 7adb7dff | Iustin Pop | status <- fromObj arr (strOfKey Success) |
307 | 7adb7dff | Iustin Pop | result <- fromObj arr (strOfKey Result) |
308 | 3603605a | Iustin Pop | if status |
309 | 7adb7dff | Iustin Pop | then return result |
310 | 7adb7dff | Iustin Pop | else decodeError result |
311 | 7adb7dff | Iustin Pop | |
312 | 7adb7dff | Iustin Pop | -- | Try to decode an error from the server response. This function |
313 | 7adb7dff | Iustin Pop | -- will always fail, since it's called only on the error path (when |
314 | 7adb7dff | Iustin Pop | -- status is False). |
315 | 7adb7dff | Iustin Pop | decodeError :: JSValue -> ErrorResult JSValue |
316 | 7adb7dff | Iustin Pop | decodeError val = |
317 | 7adb7dff | Iustin Pop | case fromJVal val of |
318 | 7adb7dff | Iustin Pop | Ok e -> Bad e |
319 | 7adb7dff | Iustin Pop | Bad msg -> Bad $ GenericError msg |
320 | 6583e677 | Iustin Pop | |
321 | 6583e677 | Iustin Pop | -- | Generic luxi method call. |
322 | 7adb7dff | Iustin Pop | callMethod :: LuxiOp -> Client -> IO (ErrorResult JSValue) |
323 | 683b1ca7 | Iustin Pop | callMethod method s = do |
324 | 683b1ca7 | Iustin Pop | sendMsg s $ buildCall method |
325 | 6583e677 | Iustin Pop | result <- recvMsg s |
326 | 6583e677 | Iustin Pop | let rval = validateResult result |
327 | 6583e677 | Iustin Pop | return rval |
328 | 9a2ff880 | Iustin Pop | |
329 | 619e89c8 | Iustin Pop | -- | Parse job submission result. |
330 | 7adb7dff | Iustin Pop | parseSubmitJobResult :: JSValue -> ErrorResult JobId |
331 | 7adb7dff | Iustin Pop | parseSubmitJobResult (JSArray [JSBool True, v]) = |
332 | c48711d5 | Iustin Pop | case J.readJSON v of |
333 | c48711d5 | Iustin Pop | J.Error msg -> Bad $ LuxiError msg |
334 | c48711d5 | Iustin Pop | J.Ok v' -> Ok v' |
335 | 619e89c8 | Iustin Pop | parseSubmitJobResult (JSArray [JSBool False, JSString x]) = |
336 | 7adb7dff | Iustin Pop | Bad . LuxiError $ fromJSString x |
337 | 7adb7dff | Iustin Pop | parseSubmitJobResult v = |
338 | 7adb7dff | Iustin Pop | Bad . LuxiError $ "Unknown result from the master daemon: " ++ |
339 | 7adb7dff | Iustin Pop | show (pp_value v) |
340 | 619e89c8 | Iustin Pop | |
341 | 9a2ff880 | Iustin Pop | -- | Specialized submitManyJobs call. |
342 | 7e723913 | Iustin Pop | submitManyJobs :: Client -> [[MetaOpCode]] -> IO (ErrorResult [JobId]) |
343 | 9a2ff880 | Iustin Pop | submitManyJobs s jobs = do |
344 | 683b1ca7 | Iustin Pop | rval <- callMethod (SubmitManyJobs jobs) s |
345 | 9a2ff880 | Iustin Pop | -- map each result (status, payload) pair into a nice Result ADT |
346 | 9a2ff880 | Iustin Pop | return $ case rval of |
347 | 9a2ff880 | Iustin Pop | Bad x -> Bad x |
348 | 619e89c8 | Iustin Pop | Ok (JSArray r) -> mapM parseSubmitJobResult r |
349 | 7adb7dff | Iustin Pop | x -> Bad . LuxiError $ |
350 | 7adb7dff | Iustin Pop | "Cannot parse response from Ganeti: " ++ show x |
351 | 9a2ff880 | Iustin Pop | |
352 | 9a2ff880 | Iustin Pop | -- | Custom queryJobs call. |
353 | 7adb7dff | Iustin Pop | queryJobsStatus :: Client -> [JobId] -> IO (ErrorResult [JobStatus]) |
354 | 9a2ff880 | Iustin Pop | queryJobsStatus s jids = do |
355 | 76b62028 | Iustin Pop | rval <- callMethod (QueryJobs jids ["status"]) s |
356 | 9a2ff880 | Iustin Pop | return $ case rval of |
357 | 9a2ff880 | Iustin Pop | Bad x -> Bad x |
358 | 9a2ff880 | Iustin Pop | Ok y -> case J.readJSON y::(J.Result [[JobStatus]]) of |
359 | 9a2ff880 | Iustin Pop | J.Ok vals -> if any null vals |
360 | 7adb7dff | Iustin Pop | then Bad $ |
361 | 7adb7dff | Iustin Pop | LuxiError "Missing job status field" |
362 | 9a2ff880 | Iustin Pop | else Ok (map head vals) |
363 | 7adb7dff | Iustin Pop | J.Error x -> Bad $ LuxiError x |