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