Statistics
| Branch: | Tag: | Revision:

root / src / Rapi.hs @ 9b9a5931

History | View | Annotate | Download (5 kB)

1 a7654563 Iustin Pop
{-| Implementation of the RAPI client interface.
2 a7654563 Iustin Pop
3 a7654563 Iustin Pop
-}
4 a7654563 Iustin Pop
5 a7654563 Iustin Pop
module Rapi
6 a7654563 Iustin Pop
    where
7 a7654563 Iustin Pop
8 a7654563 Iustin Pop
import Network.Curl
9 b8b9a53c Iustin Pop
import Network.Curl.Types ()
10 a7654563 Iustin Pop
import Network.Curl.Code
11 b8b9a53c Iustin Pop
import Data.Either ()
12 aab26f2d Iustin Pop
import Data.Maybe
13 a7654563 Iustin Pop
import Control.Monad
14 a7654563 Iustin Pop
import Text.JSON
15 a7654563 Iustin Pop
import Text.Printf (printf)
16 b8b9a53c Iustin Pop
import Utils ()
17 a7654563 Iustin Pop
18 a7654563 Iustin Pop
19 a7654563 Iustin Pop
{-- Our cheap monad-like stuff.
20 a7654563 Iustin Pop
21 a7654563 Iustin Pop
Thi is needed since Either e a is already a monad instance somewhere
22 a7654563 Iustin Pop
in the standard libraries (Control.Monad.Error) and we don't need that
23 a7654563 Iustin Pop
entire thing.
24 a7654563 Iustin Pop
25 a7654563 Iustin Pop
-}
26 a7654563 Iustin Pop
combine :: (Either String a) -> (a -> Either String b)  -> (Either String b)
27 a7654563 Iustin Pop
combine (Left s) _ = Left s
28 a7654563 Iustin Pop
combine (Right s) f = f s
29 a7654563 Iustin Pop
30 a7654563 Iustin Pop
ensureList :: [Either String a] -> Either String [a]
31 a7654563 Iustin Pop
ensureList lst =
32 a7654563 Iustin Pop
    foldr (\elem accu ->
33 a7654563 Iustin Pop
               case (elem, accu) of
34 a7654563 Iustin Pop
                 (Left x, _) -> Left x
35 a7654563 Iustin Pop
                 (_, Left x) -> Left x -- should never happen
36 a7654563 Iustin Pop
                 (Right e, Right a) -> Right (e:a)
37 a7654563 Iustin Pop
          )
38 a7654563 Iustin Pop
    (Right []) lst
39 a7654563 Iustin Pop
40 b8b9a53c Iustin Pop
listHead :: Either String [a] -> Either String a
41 b8b9a53c Iustin Pop
listHead lst =
42 b8b9a53c Iustin Pop
    case lst of
43 b8b9a53c Iustin Pop
      Left x -> Left x
44 b8b9a53c Iustin Pop
      Right (x:_) -> Right x
45 b8b9a53c Iustin Pop
      Right [] -> Left "List empty"
46 b8b9a53c Iustin Pop
47 a7654563 Iustin Pop
loadJSArray :: String -> Either String [JSObject JSValue]
48 a7654563 Iustin Pop
loadJSArray s = resultToEither $ decodeStrict s
49 a7654563 Iustin Pop
50 aab26f2d Iustin Pop
fromObj :: JSON a => String -> JSObject JSValue -> Either String a
51 aab26f2d Iustin Pop
fromObj k o =
52 aab26f2d Iustin Pop
    case lookup k (fromJSObject o) of
53 aab26f2d Iustin Pop
      Nothing -> Left $ printf "key '%s' not found" k
54 aab26f2d Iustin Pop
      Just val -> resultToEither $ readJSON val
55 aab26f2d Iustin Pop
56 a7654563 Iustin Pop
getStringElement :: String -> JSObject JSValue -> Either String String
57 d7cf83bf Iustin Pop
getStringElement = fromObj
58 a7654563 Iustin Pop
59 d7cf83bf Iustin Pop
getIntElement :: String -> JSObject JSValue -> Either String Int
60 d7cf83bf Iustin Pop
getIntElement = fromObj
61 a7654563 Iustin Pop
62 b8b9a53c Iustin Pop
getListElement :: String -> JSObject JSValue
63 b8b9a53c Iustin Pop
               -> Either String [JSValue]
64 d7cf83bf Iustin Pop
getListElement = fromObj
65 b8b9a53c Iustin Pop
66 b8b9a53c Iustin Pop
readString :: JSValue -> Either String String
67 b8b9a53c Iustin Pop
readString v =
68 b8b9a53c Iustin Pop
    case v of
69 b8b9a53c Iustin Pop
      JSString s -> Right $ fromJSString s
70 b8b9a53c Iustin Pop
      _ -> Left "Wrong JSON type"
71 b8b9a53c Iustin Pop
72 aab26f2d Iustin Pop
concatElems :: Either String String
73 aab26f2d Iustin Pop
            -> Either String String
74 aab26f2d Iustin Pop
            -> Either String String
75 aab26f2d Iustin Pop
concatElems = apply2 (\x y -> x ++ "|" ++ y)
76 aab26f2d Iustin Pop
77 d7cf83bf Iustin Pop
apply1 :: (a -> b) -> Either String a -> Either String b
78 d7cf83bf Iustin Pop
apply1 fn a =
79 d7cf83bf Iustin Pop
    case a of
80 d7cf83bf Iustin Pop
      Left x -> Left x
81 d7cf83bf Iustin Pop
      Right y -> Right $ fn y
82 d7cf83bf Iustin Pop
83 aab26f2d Iustin Pop
apply2 :: (a -> b -> c)
84 aab26f2d Iustin Pop
       -> Either String a
85 aab26f2d Iustin Pop
       -> Either String b
86 aab26f2d Iustin Pop
       -> Either String c
87 aab26f2d Iustin Pop
apply2 fn a b =
88 aab26f2d Iustin Pop
    case (a, b) of
89 aab26f2d Iustin Pop
      (Right x, Right y) -> Right $ fn x y
90 aab26f2d Iustin Pop
      (Left x, _) -> Left x
91 aab26f2d Iustin Pop
      (_, Left y) -> Left y
92 a7654563 Iustin Pop
93 a7654563 Iustin Pop
getUrl :: String -> IO (Either String String)
94 a7654563 Iustin Pop
getUrl url = do
95 a7654563 Iustin Pop
  (code, body) <- curlGetString url [CurlSSLVerifyPeer False,
96 a7654563 Iustin Pop
                                     CurlSSLVerifyHost 0]
97 a7654563 Iustin Pop
  return (case code of
98 a7654563 Iustin Pop
            CurlOK -> Right body
99 aab26f2d Iustin Pop
            _ -> Left $ printf "Curl error for '%s', error %s"
100 aab26f2d Iustin Pop
                 url (show code))
101 aab26f2d Iustin Pop
102 aab26f2d Iustin Pop
tryRapi :: String -> String -> IO (Either String String)
103 aab26f2d Iustin Pop
tryRapi url1 url2 =
104 aab26f2d Iustin Pop
    do
105 aab26f2d Iustin Pop
      body1 <- getUrl url1
106 9b9a5931 Iustin Pop
      (case body1 of
107 9b9a5931 Iustin Pop
         Left _ -> getUrl url2
108 9b9a5931 Iustin Pop
         Right _ -> return body1)
109 a7654563 Iustin Pop
110 a7654563 Iustin Pop
getInstances :: String -> IO (Either String String)
111 a7654563 Iustin Pop
getInstances master =
112 aab26f2d Iustin Pop
    let
113 aab26f2d Iustin Pop
        url2 = printf "https://%s:5080/2/instances?bulk=1" master
114 aab26f2d Iustin Pop
        url1 = printf "http://%s:5080/instances?bulk=1" master
115 a7654563 Iustin Pop
    in do
116 aab26f2d Iustin Pop
      body <- tryRapi url1 url2
117 a7654563 Iustin Pop
      let inst = body `combine` loadJSArray `combine` (parseList parseInstance)
118 a7654563 Iustin Pop
      return inst
119 a7654563 Iustin Pop
120 a7654563 Iustin Pop
getNodes :: String -> IO (Either String String)
121 a7654563 Iustin Pop
getNodes master =
122 aab26f2d Iustin Pop
    let
123 aab26f2d Iustin Pop
        url2 = printf "https://%s:5080/2/nodes?bulk=1" master
124 aab26f2d Iustin Pop
        url1 = printf "http://%s:5080/nodes?bulk=1" master
125 a7654563 Iustin Pop
    in do
126 aab26f2d Iustin Pop
      body <- tryRapi url1 url2
127 a7654563 Iustin Pop
      let inst = body `combine` loadJSArray `combine` (parseList parseNode)
128 a7654563 Iustin Pop
      return inst
129 a7654563 Iustin Pop
130 a7654563 Iustin Pop
parseList :: (JSObject JSValue -> Either String String)
131 a7654563 Iustin Pop
          -> [JSObject JSValue]
132 a7654563 Iustin Pop
          ->Either String String
133 a7654563 Iustin Pop
parseList fn idata =
134 a7654563 Iustin Pop
    let ml = ensureList $ map fn idata
135 a7654563 Iustin Pop
    in ml `combine` (Right . unlines)
136 a7654563 Iustin Pop
137 a7654563 Iustin Pop
parseInstance :: JSObject JSValue -> Either String String
138 a7654563 Iustin Pop
parseInstance a =
139 a7654563 Iustin Pop
    let name = getStringElement "name" a
140 a7654563 Iustin Pop
        disk = case getIntElement "disk_usage" a of
141 d7cf83bf Iustin Pop
                 Left _ -> apply2 (+)
142 aab26f2d Iustin Pop
                           (getIntElement "sda_size" a)
143 aab26f2d Iustin Pop
                           (getIntElement "sdb_size" a)
144 a7654563 Iustin Pop
                 Right x -> Right x
145 aab26f2d Iustin Pop
        bep = fromObj "beparams" a
146 b8b9a53c Iustin Pop
        pnode = getStringElement "pnode" a
147 b8b9a53c Iustin Pop
        snode = (listHead $ getListElement "snodes" a) `combine` readString
148 aab26f2d Iustin Pop
        mem = case bep of
149 aab26f2d Iustin Pop
                Left _ -> getIntElement "admin_ram" a
150 d7cf83bf Iustin Pop
                Right o -> getIntElement "memory" o
151 a7654563 Iustin Pop
    in
152 d7cf83bf Iustin Pop
      concatElems name $
153 d7cf83bf Iustin Pop
                  concatElems (show `apply1` mem) $
154 d7cf83bf Iustin Pop
                  concatElems (show `apply1` disk) $
155 d7cf83bf Iustin Pop
                  concatElems pnode snode
156 a7654563 Iustin Pop
157 a7654563 Iustin Pop
parseNode :: JSObject JSValue -> Either String String
158 a7654563 Iustin Pop
parseNode a =
159 a7654563 Iustin Pop
    let name = getStringElement "name" a
160 a7654563 Iustin Pop
        mtotal = getIntElement "mtotal" a
161 a7654563 Iustin Pop
        mfree = getIntElement "mfree" a
162 a7654563 Iustin Pop
        dtotal = getIntElement "dtotal" a
163 a7654563 Iustin Pop
        dfree = getIntElement "dfree" a
164 d7cf83bf Iustin Pop
    in concatElems name $
165 d7cf83bf Iustin Pop
       concatElems (show `apply1` mtotal) $
166 d7cf83bf Iustin Pop
       concatElems (show `apply1` mfree) $
167 d7cf83bf Iustin Pop
       concatElems (show `apply1` dtotal) (show `apply1` dfree)