Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Luxi.hs @ d81ec8b7

History | View | Annotate | Download (12.5 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 fae980e5 Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012 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 ebf38064 Iustin Pop
  , QrViaLuxi(..)
31 ebf38064 Iustin Pop
  , ResultStatus(..)
32 95d0d502 Iustin Pop
  , LuxiReq(..)
33 ebf38064 Iustin Pop
  , Client
34 ebf38064 Iustin Pop
  , checkRS
35 ebf38064 Iustin Pop
  , getClient
36 ebf38064 Iustin Pop
  , closeClient
37 ebf38064 Iustin Pop
  , callMethod
38 ebf38064 Iustin Pop
  , submitManyJobs
39 ebf38064 Iustin Pop
  , queryJobsStatus
40 cdd495ae Iustin Pop
  , buildCall
41 cdd495ae Iustin Pop
  , validateCall
42 cdd495ae Iustin Pop
  , decodeCall
43 ebf38064 Iustin Pop
  ) where
44 6583e677 Iustin Pop
45 6583e677 Iustin Pop
import Data.IORef
46 6583e677 Iustin Pop
import Control.Monad
47 0903280b Iustin Pop
import Text.JSON (encodeStrict, decodeStrict)
48 6583e677 Iustin Pop
import qualified Text.JSON as J
49 6583e677 Iustin Pop
import Text.JSON.Types
50 6583e677 Iustin Pop
import System.Timeout
51 6583e677 Iustin Pop
import qualified Network.Socket as S
52 6583e677 Iustin Pop
53 b69be409 Iustin Pop
import Ganeti.HTools.JSON
54 6583e677 Iustin Pop
import Ganeti.HTools.Types
55 cdd495ae Iustin Pop
import Ganeti.HTools.Utils
56 6583e677 Iustin Pop
57 92678b3c Iustin Pop
import Ganeti.Constants
58 9a2ff880 Iustin Pop
import Ganeti.Jobs (JobStatus)
59 683b1ca7 Iustin Pop
import Ganeti.OpCodes (OpCode)
60 a0090487 Agata Murawska
import Ganeti.THH
61 9a2ff880 Iustin Pop
62 6583e677 Iustin Pop
-- * Utility functions
63 6583e677 Iustin Pop
64 6583e677 Iustin Pop
-- | Wrapper over System.Timeout.timeout that fails in the IO monad.
65 6583e677 Iustin Pop
withTimeout :: Int -> String -> IO a -> IO a
66 6583e677 Iustin Pop
withTimeout secs descr action = do
67 ebf38064 Iustin Pop
  result <- timeout (secs * 1000000) action
68 3603605a Iustin Pop
  case result of
69 3603605a Iustin Pop
    Nothing -> fail $ "Timeout in " ++ descr
70 3603605a Iustin Pop
    Just v -> return v
71 6583e677 Iustin Pop
72 6583e677 Iustin Pop
-- * Generic protocol functionality
73 6583e677 Iustin Pop
74 92678b3c Iustin Pop
$(declareSADT "QrViaLuxi"
75 ebf38064 Iustin Pop
  [ ("QRLock", 'qrLock)
76 ebf38064 Iustin Pop
  , ("QRInstance", 'qrInstance)
77 ebf38064 Iustin Pop
  , ("QRNode", 'qrNode)
78 ebf38064 Iustin Pop
  , ("QRGroup", 'qrGroup)
79 ebf38064 Iustin Pop
  , ("QROs", 'qrOs)
80 ebf38064 Iustin Pop
  ])
81 92678b3c Iustin Pop
$(makeJSONInstance ''QrViaLuxi)
82 92678b3c Iustin Pop
83 a0090487 Agata Murawska
-- | Currently supported Luxi operations and JSON serialization.
84 a0090487 Agata Murawska
$(genLuxiOp "LuxiOp"
85 fae980e5 Iustin Pop
  [(luxiReqQuery,
86 ebf38064 Iustin Pop
    [ ("what",    [t| QrViaLuxi |], [| id |])
87 ebf38064 Iustin Pop
    , ("fields",  [t| [String]  |], [| id |])
88 ebf38064 Iustin Pop
    , ("qfilter", [t| ()        |], [| const JSNull |])
89 ebf38064 Iustin Pop
    ])
90 fae980e5 Iustin Pop
  , (luxiReqQueryNodes,
91 ebf38064 Iustin Pop
     [ ("names",  [t| [String] |], [| id |])
92 ebf38064 Iustin Pop
     , ("fields", [t| [String] |], [| id |])
93 ebf38064 Iustin Pop
     , ("lock",   [t| Bool     |], [| id |])
94 ebf38064 Iustin Pop
     ])
95 fae980e5 Iustin Pop
  , (luxiReqQueryGroups,
96 ebf38064 Iustin Pop
     [ ("names",  [t| [String] |], [| id |])
97 ebf38064 Iustin Pop
     , ("fields", [t| [String] |], [| id |])
98 ebf38064 Iustin Pop
     , ("lock",   [t| Bool     |], [| id |])
99 ebf38064 Iustin Pop
     ])
100 fae980e5 Iustin Pop
  , (luxiReqQueryInstances,
101 ebf38064 Iustin Pop
     [ ("names",  [t| [String] |], [| id |])
102 ebf38064 Iustin Pop
     , ("fields", [t| [String] |], [| id |])
103 ebf38064 Iustin Pop
     , ("lock",   [t| Bool     |], [| id |])
104 ebf38064 Iustin Pop
     ])
105 fae980e5 Iustin Pop
  , (luxiReqQueryJobs,
106 ebf38064 Iustin Pop
     [ ("ids",    [t| [Int]    |], [| map show |])
107 ebf38064 Iustin Pop
     , ("fields", [t| [String] |], [| id |])
108 ebf38064 Iustin Pop
     ])
109 fae980e5 Iustin Pop
  , (luxiReqQueryExports,
110 ebf38064 Iustin Pop
     [ ("nodes", [t| [String] |], [| id |])
111 ebf38064 Iustin Pop
     , ("lock",  [t| Bool     |], [| id |])
112 ebf38064 Iustin Pop
     ])
113 fae980e5 Iustin Pop
  , (luxiReqQueryConfigValues,
114 ebf38064 Iustin Pop
     [ ("fields", [t| [String] |], [| id |]) ]
115 ebf38064 Iustin Pop
    )
116 fae980e5 Iustin Pop
  , (luxiReqQueryClusterInfo, [])
117 fae980e5 Iustin Pop
  , (luxiReqQueryTags,
118 ebf38064 Iustin Pop
     [ ("kind", [t| String |], [| id |])
119 ebf38064 Iustin Pop
     , ("name", [t| String |], [| id |])
120 ebf38064 Iustin Pop
     ])
121 fae980e5 Iustin Pop
  , (luxiReqSubmitJob,
122 ebf38064 Iustin Pop
     [ ("job", [t| [OpCode] |], [| id |]) ]
123 ebf38064 Iustin Pop
    )
124 fae980e5 Iustin Pop
  , (luxiReqSubmitManyJobs,
125 ebf38064 Iustin Pop
     [ ("ops", [t| [[OpCode]] |], [| id |]) ]
126 ebf38064 Iustin Pop
    )
127 fae980e5 Iustin Pop
  , (luxiReqWaitForJobChange,
128 cdd495ae Iustin Pop
     [ ("job",      [t| Int     |], [| show |])
129 ebf38064 Iustin Pop
     , ("fields",   [t| [String]|], [| id |])
130 ebf38064 Iustin Pop
     , ("prev_job", [t| JSValue |], [| id |])
131 ebf38064 Iustin Pop
     , ("prev_log", [t| JSValue |], [| id |])
132 ebf38064 Iustin Pop
     , ("tmout",    [t| Int     |], [| id |])
133 ebf38064 Iustin Pop
     ])
134 fae980e5 Iustin Pop
  , (luxiReqArchiveJob,
135 ebf38064 Iustin Pop
     [ ("job", [t| Int |], [| show |]) ]
136 ebf38064 Iustin Pop
    )
137 fae980e5 Iustin Pop
  , (luxiReqAutoArchiveJobs,
138 ebf38064 Iustin Pop
     [ ("age",   [t| Int |], [| id |])
139 ebf38064 Iustin Pop
     , ("tmout", [t| Int |], [| id |])
140 ebf38064 Iustin Pop
     ])
141 fae980e5 Iustin Pop
  , (luxiReqCancelJob,
142 ebf38064 Iustin Pop
     [ ("job", [t| Int |], [| show |]) ]
143 ebf38064 Iustin Pop
    )
144 fae980e5 Iustin Pop
  , (luxiReqSetDrainFlag,
145 ebf38064 Iustin Pop
     [ ("flag", [t| Bool |], [| id |]) ]
146 ebf38064 Iustin Pop
    )
147 fae980e5 Iustin Pop
  , (luxiReqSetWatcherPause,
148 ebf38064 Iustin Pop
     [ ("duration", [t| Double |], [| id |]) ]
149 ebf38064 Iustin Pop
    )
150 a0090487 Agata Murawska
  ])
151 6583e677 Iustin Pop
152 95d0d502 Iustin Pop
$(makeJSONInstance ''LuxiReq)
153 95d0d502 Iustin Pop
154 6583e677 Iustin Pop
-- | The serialisation of LuxiOps into strings in messages.
155 a0090487 Agata Murawska
$(genStrOfOp ''LuxiOp "strOfOp")
156 6583e677 Iustin Pop
157 260d0bda Agata Murawska
$(declareIADT "ResultStatus"
158 ebf38064 Iustin Pop
  [ ("RSNormal", 'rsNormal)
159 ebf38064 Iustin Pop
  , ("RSUnknown", 'rsUnknown)
160 ebf38064 Iustin Pop
  , ("RSNoData", 'rsNodata)
161 ebf38064 Iustin Pop
  , ("RSUnavailable", 'rsUnavail)
162 ebf38064 Iustin Pop
  , ("RSOffline", 'rsOffline)
163 ebf38064 Iustin Pop
  ])
164 5f828ce4 Agata Murawska
165 5f828ce4 Agata Murawska
$(makeJSONInstance ''ResultStatus)
166 260d0bda Agata Murawska
167 cdd495ae Iustin Pop
-- | Type holding the initial (unparsed) Luxi call.
168 cdd495ae Iustin Pop
data LuxiCall = LuxiCall LuxiReq JSValue
169 cdd495ae Iustin Pop
170 260d0bda Agata Murawska
-- | Check that ResultStatus is success or fail with descriptive message.
171 260d0bda Agata Murawska
checkRS :: (Monad m) => ResultStatus -> a -> m a
172 260d0bda Agata Murawska
checkRS RSNormal val    = return val
173 260d0bda Agata Murawska
checkRS RSUnknown _     = fail "Unknown field"
174 260d0bda Agata Murawska
checkRS RSNoData _      = fail "No data for a field"
175 260d0bda Agata Murawska
checkRS RSUnavailable _ = fail "Ganeti reports unavailable data"
176 260d0bda Agata Murawska
checkRS RSOffline _     = fail "Ganeti reports resource as offline"
177 260d0bda Agata Murawska
178 6583e677 Iustin Pop
-- | The end-of-message separator.
179 6583e677 Iustin Pop
eOM :: Char
180 6583e677 Iustin Pop
eOM = '\3'
181 6583e677 Iustin Pop
182 6583e677 Iustin Pop
-- | Valid keys in the requests and responses.
183 6583e677 Iustin Pop
data MsgKeys = Method
184 6583e677 Iustin Pop
             | Args
185 6583e677 Iustin Pop
             | Success
186 6583e677 Iustin Pop
             | Result
187 6583e677 Iustin Pop
188 6583e677 Iustin Pop
-- | The serialisation of MsgKeys into strings in messages.
189 a0090487 Agata Murawska
$(genStrOfKey ''MsgKeys "strOfKey")
190 6583e677 Iustin Pop
191 6583e677 Iustin Pop
-- | Luxi client encapsulation.
192 6583e677 Iustin Pop
data Client = Client { socket :: S.Socket   -- ^ The socket of the client
193 6583e677 Iustin Pop
                     , rbuf :: IORef String -- ^ Already received buffer
194 6583e677 Iustin Pop
                     }
195 6583e677 Iustin Pop
196 6583e677 Iustin Pop
-- | Connects to the master daemon and returns a luxi Client.
197 6583e677 Iustin Pop
getClient :: String -> IO Client
198 6583e677 Iustin Pop
getClient path = do
199 ebf38064 Iustin Pop
  s <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
200 ebf38064 Iustin Pop
  withTimeout connTimeout "creating luxi connection" $
201 ebf38064 Iustin Pop
              S.connect s (S.SockAddrUnix path)
202 ebf38064 Iustin Pop
  rf <- newIORef ""
203 ebf38064 Iustin Pop
  return Client { socket=s, rbuf=rf}
204 6583e677 Iustin Pop
205 6583e677 Iustin Pop
-- | Closes the client socket.
206 6583e677 Iustin Pop
closeClient :: Client -> IO ()
207 6583e677 Iustin Pop
closeClient = S.sClose . socket
208 6583e677 Iustin Pop
209 6583e677 Iustin Pop
-- | Sends a message over a luxi transport.
210 6583e677 Iustin Pop
sendMsg :: Client -> String -> IO ()
211 6583e677 Iustin Pop
sendMsg s buf =
212 ebf38064 Iustin Pop
  let _send obuf = do
213 ebf38064 Iustin Pop
        sbytes <- withTimeout queryTimeout
214 ebf38064 Iustin Pop
                  "sending luxi message" $
215 ebf38064 Iustin Pop
                  S.send (socket s) obuf
216 ebf38064 Iustin Pop
        unless (sbytes == length obuf) $ _send (drop sbytes obuf)
217 ebf38064 Iustin Pop
  in _send (buf ++ [eOM])
218 6583e677 Iustin Pop
219 6583e677 Iustin Pop
-- | Waits for a message over a luxi transport.
220 6583e677 Iustin Pop
recvMsg :: Client -> IO String
221 6583e677 Iustin Pop
recvMsg s = do
222 6583e677 Iustin Pop
  let _recv obuf = do
223 6583e677 Iustin Pop
              nbuf <- withTimeout queryTimeout "reading luxi response" $
224 6583e677 Iustin Pop
                      S.recv (socket s) 4096
225 95f490de Iustin Pop
              let (msg, remaining) = break (eOM ==) nbuf
226 3603605a Iustin Pop
              if null remaining
227 3603605a Iustin Pop
                then _recv (obuf ++ msg)
228 3603605a Iustin Pop
                else return (obuf ++ msg, tail remaining)
229 6583e677 Iustin Pop
  cbuf <- readIORef $ rbuf s
230 95f490de Iustin Pop
  let (imsg, ibuf) = break (eOM ==) cbuf
231 95f490de Iustin Pop
  (msg, nbuf) <-
232 3603605a Iustin Pop
    if null ibuf      -- if old buffer didn't contain a full message
233 3603605a Iustin Pop
      then _recv cbuf   -- then we read from network
234 3603605a Iustin Pop
      else return (imsg, tail ibuf) -- else we return data from our buffer
235 6583e677 Iustin Pop
  writeIORef (rbuf s) nbuf
236 6583e677 Iustin Pop
  return msg
237 6583e677 Iustin Pop
238 6583e677 Iustin Pop
-- | Serialize a request to String.
239 6583e677 Iustin Pop
buildCall :: LuxiOp  -- ^ The method
240 6583e677 Iustin Pop
          -> String  -- ^ The serialized form
241 683b1ca7 Iustin Pop
buildCall lo =
242 ebf38064 Iustin Pop
  let ja = [ (strOfKey Method, JSString $ toJSString $ strOfOp lo::JSValue)
243 ebf38064 Iustin Pop
           , (strOfKey Args, opToArgs lo::JSValue)
244 ebf38064 Iustin Pop
           ]
245 ebf38064 Iustin Pop
      jo = toJSObject ja
246 ebf38064 Iustin Pop
  in encodeStrict jo
247 6583e677 Iustin Pop
248 cdd495ae Iustin Pop
-- | Check that luxi request contains the required keys and parse it.
249 cdd495ae Iustin Pop
validateCall :: String -> Result LuxiCall
250 cdd495ae Iustin Pop
validateCall s = do
251 cdd495ae Iustin Pop
  arr <- fromJResult "luxi call" $ decodeStrict s::Result (JSObject JSValue)
252 cdd495ae Iustin Pop
  let aobj = fromJSObject arr
253 cdd495ae Iustin Pop
  call <- fromObj aobj (strOfKey Method)::Result LuxiReq
254 cdd495ae Iustin Pop
  args <- fromObj aobj (strOfKey Args)
255 cdd495ae Iustin Pop
  return (LuxiCall call args)
256 cdd495ae Iustin Pop
257 cdd495ae Iustin Pop
-- | Converts Luxi call arguments into a 'LuxiOp' data structure.
258 cdd495ae Iustin Pop
--
259 cdd495ae Iustin Pop
-- This is currently hand-coded until we make it more uniform so that
260 cdd495ae Iustin Pop
-- it can be generated using TH.
261 cdd495ae Iustin Pop
decodeCall :: LuxiCall -> Result LuxiOp
262 cdd495ae Iustin Pop
decodeCall (LuxiCall call args) =
263 cdd495ae Iustin Pop
  case call of
264 cdd495ae Iustin Pop
    ReqQueryJobs -> do
265 cdd495ae Iustin Pop
              (jid, jargs) <- fromJVal args
266 cdd495ae Iustin Pop
              rid <- mapM (tryRead "parsing job ID" . fromJSString) jid
267 cdd495ae Iustin Pop
              let rargs = map fromJSString jargs
268 cdd495ae Iustin Pop
              return $ QueryJobs rid rargs
269 cdd495ae Iustin Pop
    ReqQueryInstances -> do
270 cdd495ae Iustin Pop
              (names, fields, locking) <- fromJVal args
271 cdd495ae Iustin Pop
              return $ QueryInstances names fields locking
272 cdd495ae Iustin Pop
    ReqQueryNodes -> do
273 cdd495ae Iustin Pop
              (names, fields, locking) <- fromJVal args
274 cdd495ae Iustin Pop
              return $ QueryNodes names fields locking
275 cdd495ae Iustin Pop
    ReqQueryGroups -> do
276 cdd495ae Iustin Pop
              (names, fields, locking) <- fromJVal args
277 cdd495ae Iustin Pop
              return $ QueryGroups names fields locking
278 cdd495ae Iustin Pop
    ReqQueryClusterInfo -> do
279 cdd495ae Iustin Pop
              return QueryClusterInfo
280 cdd495ae Iustin Pop
    ReqQuery -> do
281 cdd495ae Iustin Pop
              (what, fields, _) <-
282 cdd495ae Iustin Pop
                fromJVal args::Result (QrViaLuxi, [String], JSValue)
283 cdd495ae Iustin Pop
              return $ Query what fields ()
284 cdd495ae Iustin Pop
    ReqSubmitJob -> do
285 cdd495ae Iustin Pop
              [ops1] <- fromJVal args
286 cdd495ae Iustin Pop
              ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1
287 cdd495ae Iustin Pop
              return $ SubmitJob ops2
288 cdd495ae Iustin Pop
    ReqSubmitManyJobs -> do
289 cdd495ae Iustin Pop
              [ops1] <- fromJVal args
290 cdd495ae Iustin Pop
              ops2 <- mapM (fromJResult (luxiReqToRaw call) . J.readJSON) ops1
291 cdd495ae Iustin Pop
              return $ SubmitManyJobs ops2
292 cdd495ae Iustin Pop
    ReqWaitForJobChange -> do
293 cdd495ae Iustin Pop
              (jid, fields, pinfo, pidx, wtmout) <-
294 cdd495ae Iustin Pop
                -- No instance for 5-tuple, code copied from the
295 cdd495ae Iustin Pop
                -- json sources and adapted
296 cdd495ae Iustin Pop
                fromJResult "Parsing WaitForJobChange message" $
297 cdd495ae Iustin Pop
                case args of
298 cdd495ae Iustin Pop
                  JSArray [a, b, c, d, e] ->
299 cdd495ae Iustin Pop
                    (,,,,) `fmap`
300 cdd495ae Iustin Pop
                    J.readJSON a `ap`
301 cdd495ae Iustin Pop
                    J.readJSON b `ap`
302 cdd495ae Iustin Pop
                    J.readJSON c `ap`
303 cdd495ae Iustin Pop
                    J.readJSON d `ap`
304 cdd495ae Iustin Pop
                    J.readJSON e
305 cdd495ae Iustin Pop
                  _ -> J.Error "Not enough values"
306 cdd495ae Iustin Pop
              rid <- tryRead "parsing job ID" jid
307 cdd495ae Iustin Pop
              return $ WaitForJobChange rid fields pinfo pidx wtmout
308 cdd495ae Iustin Pop
    ReqArchiveJob -> do
309 cdd495ae Iustin Pop
              [jid] <- fromJVal args
310 cdd495ae Iustin Pop
              rid <- tryRead "parsing job ID" jid
311 cdd495ae Iustin Pop
              return $ ArchiveJob rid
312 cdd495ae Iustin Pop
    ReqAutoArchiveJobs -> do
313 cdd495ae Iustin Pop
              (age, tmout) <- fromJVal args
314 cdd495ae Iustin Pop
              return $ AutoArchiveJobs age tmout
315 cdd495ae Iustin Pop
    ReqQueryExports -> do
316 cdd495ae Iustin Pop
              (nodes, lock) <- fromJVal args
317 cdd495ae Iustin Pop
              return $ QueryExports nodes lock
318 cdd495ae Iustin Pop
    ReqQueryConfigValues -> do
319 cdd495ae Iustin Pop
              [fields] <- fromJVal args
320 cdd495ae Iustin Pop
              return $ QueryConfigValues fields
321 cdd495ae Iustin Pop
    ReqQueryTags -> do
322 cdd495ae Iustin Pop
              (kind, name) <- fromJVal args
323 cdd495ae Iustin Pop
              return $ QueryTags kind name
324 cdd495ae Iustin Pop
    ReqCancelJob -> do
325 cdd495ae Iustin Pop
              [job] <- fromJVal args
326 cdd495ae Iustin Pop
              rid <- tryRead "parsing job ID" job
327 cdd495ae Iustin Pop
              return $ CancelJob rid
328 cdd495ae Iustin Pop
    ReqSetDrainFlag -> do
329 cdd495ae Iustin Pop
              [flag] <- fromJVal args
330 cdd495ae Iustin Pop
              return $ SetDrainFlag flag
331 cdd495ae Iustin Pop
    ReqSetWatcherPause -> do
332 cdd495ae Iustin Pop
              [duration] <- fromJVal args
333 cdd495ae Iustin Pop
              return $ SetWatcherPause duration
334 cdd495ae Iustin Pop
335 6583e677 Iustin Pop
-- | Check that luxi responses contain the required keys and that the
336 6583e677 Iustin Pop
-- call was successful.
337 6583e677 Iustin Pop
validateResult :: String -> Result JSValue
338 6583e677 Iustin Pop
validateResult s = do
339 c96d44df Iustin Pop
  oarr <- fromJResult "Parsing LUXI response"
340 c96d44df Iustin Pop
          (decodeStrict s)::Result (JSObject JSValue)
341 262f3e6c Iustin Pop
  let arr = J.fromJSObject oarr
342 e8230242 Iustin Pop
  status <- fromObj arr (strOfKey Success)::Result Bool
343 6583e677 Iustin Pop
  let rkey = strOfKey Result
344 3603605a Iustin Pop
  if status
345 3603605a Iustin Pop
    then fromObj arr rkey
346 3603605a Iustin Pop
    else fromObj arr rkey >>= fail
347 6583e677 Iustin Pop
348 6583e677 Iustin Pop
-- | Generic luxi method call.
349 683b1ca7 Iustin Pop
callMethod :: LuxiOp -> Client -> IO (Result JSValue)
350 683b1ca7 Iustin Pop
callMethod method s = do
351 683b1ca7 Iustin Pop
  sendMsg s $ buildCall method
352 6583e677 Iustin Pop
  result <- recvMsg s
353 6583e677 Iustin Pop
  let rval = validateResult result
354 6583e677 Iustin Pop
  return rval
355 9a2ff880 Iustin Pop
356 9a2ff880 Iustin Pop
-- | Specialized submitManyJobs call.
357 683b1ca7 Iustin Pop
submitManyJobs :: Client -> [[OpCode]] -> IO (Result [String])
358 9a2ff880 Iustin Pop
submitManyJobs s jobs = do
359 683b1ca7 Iustin Pop
  rval <- callMethod (SubmitManyJobs jobs) s
360 9a2ff880 Iustin Pop
  -- map each result (status, payload) pair into a nice Result ADT
361 9a2ff880 Iustin Pop
  return $ case rval of
362 9a2ff880 Iustin Pop
             Bad x -> Bad x
363 9a2ff880 Iustin Pop
             Ok (JSArray r) ->
364 9a2ff880 Iustin Pop
                 mapM (\v -> case v of
365 9a2ff880 Iustin Pop
                               JSArray [JSBool True, JSString x] ->
366 9a2ff880 Iustin Pop
                                   Ok (fromJSString x)
367 9a2ff880 Iustin Pop
                               JSArray [JSBool False, JSString x] ->
368 9a2ff880 Iustin Pop
                                   Bad (fromJSString x)
369 9a2ff880 Iustin Pop
                               _ -> Bad "Unknown result from the master daemon"
370 9a2ff880 Iustin Pop
                      ) r
371 9a2ff880 Iustin Pop
             x -> Bad ("Cannot parse response from Ganeti: " ++ show x)
372 9a2ff880 Iustin Pop
373 9a2ff880 Iustin Pop
-- | Custom queryJobs call.
374 9a2ff880 Iustin Pop
queryJobsStatus :: Client -> [String] -> IO (Result [JobStatus])
375 9a2ff880 Iustin Pop
queryJobsStatus s jids = do
376 683b1ca7 Iustin Pop
  rval <- callMethod (QueryJobs (map read jids) ["status"]) s
377 9a2ff880 Iustin Pop
  return $ case rval of
378 9a2ff880 Iustin Pop
             Bad x -> Bad x
379 9a2ff880 Iustin Pop
             Ok y -> case J.readJSON y::(J.Result [[JobStatus]]) of
380 9a2ff880 Iustin Pop
                       J.Ok vals -> if any null vals
381 9a2ff880 Iustin Pop
                                    then Bad "Missing job status field"
382 9a2ff880 Iustin Pop
                                    else Ok (map head vals)
383 9a2ff880 Iustin Pop
                       J.Error x -> Bad x