Statistics
| Branch: | Tag: | Revision:

root / src / Rapi.hs @ 9b9a5931

History | View | Annotate | Download (5 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
fromObj :: JSON a => String -> JSObject JSValue -> Either String a
51
fromObj k o =
52
    case lookup k (fromJSObject o) of
53
      Nothing -> Left $ printf "key '%s' not found" k
54
      Just val -> resultToEither $ readJSON val
55

    
56
getStringElement :: String -> JSObject JSValue -> Either String String
57
getStringElement = fromObj
58

    
59
getIntElement :: String -> JSObject JSValue -> Either String Int
60
getIntElement = fromObj
61

    
62
getListElement :: String -> JSObject JSValue
63
               -> Either String [JSValue]
64
getListElement = fromObj
65

    
66
readString :: JSValue -> Either String String
67
readString v =
68
    case v of
69
      JSString s -> Right $ fromJSString s
70
      _ -> Left "Wrong JSON type"
71

    
72
concatElems :: Either String String
73
            -> Either String String
74
            -> Either String String
75
concatElems = apply2 (\x y -> x ++ "|" ++ y)
76

    
77
apply1 :: (a -> b) -> Either String a -> Either String b
78
apply1 fn a =
79
    case a of
80
      Left x -> Left x
81
      Right y -> Right $ fn y
82

    
83
apply2 :: (a -> b -> c)
84
       -> Either String a
85
       -> Either String b
86
       -> Either String c
87
apply2 fn a b =
88
    case (a, b) of
89
      (Right x, Right y) -> Right $ fn x y
90
      (Left x, _) -> Left x
91
      (_, Left y) -> Left y
92

    
93
getUrl :: String -> IO (Either String String)
94
getUrl url = do
95
  (code, body) <- curlGetString url [CurlSSLVerifyPeer False,
96
                                     CurlSSLVerifyHost 0]
97
  return (case code of
98
            CurlOK -> Right body
99
            _ -> Left $ printf "Curl error for '%s', error %s"
100
                 url (show code))
101

    
102
tryRapi :: String -> String -> IO (Either String String)
103
tryRapi url1 url2 =
104
    do
105
      body1 <- getUrl url1
106
      (case body1 of
107
         Left _ -> getUrl url2
108
         Right _ -> return body1)
109

    
110
getInstances :: String -> IO (Either String String)
111
getInstances master =
112
    let
113
        url2 = printf "https://%s:5080/2/instances?bulk=1" master
114
        url1 = printf "http://%s:5080/instances?bulk=1" master
115
    in do
116
      body <- tryRapi url1 url2
117
      let inst = body `combine` loadJSArray `combine` (parseList parseInstance)
118
      return inst
119

    
120
getNodes :: String -> IO (Either String String)
121
getNodes master =
122
    let
123
        url2 = printf "https://%s:5080/2/nodes?bulk=1" master
124
        url1 = printf "http://%s:5080/nodes?bulk=1" master
125
    in do
126
      body <- tryRapi url1 url2
127
      let inst = body `combine` loadJSArray `combine` (parseList parseNode)
128
      return inst
129

    
130
parseList :: (JSObject JSValue -> Either String String)
131
          -> [JSObject JSValue]
132
          ->Either String String
133
parseList fn idata =
134
    let ml = ensureList $ map fn idata
135
    in ml `combine` (Right . unlines)
136

    
137
parseInstance :: JSObject JSValue -> Either String String
138
parseInstance a =
139
    let name = getStringElement "name" a
140
        disk = case getIntElement "disk_usage" a of
141
                 Left _ -> apply2 (+)
142
                           (getIntElement "sda_size" a)
143
                           (getIntElement "sdb_size" a)
144
                 Right x -> Right x
145
        bep = fromObj "beparams" a
146
        pnode = getStringElement "pnode" a
147
        snode = (listHead $ getListElement "snodes" a) `combine` readString
148
        mem = case bep of
149
                Left _ -> getIntElement "admin_ram" a
150
                Right o -> getIntElement "memory" o
151
    in
152
      concatElems name $
153
                  concatElems (show `apply1` mem) $
154
                  concatElems (show `apply1` disk) $
155
                  concatElems pnode snode
156

    
157
parseNode :: JSObject JSValue -> Either String String
158
parseNode a =
159
    let name = getStringElement "name" a
160
        mtotal = getIntElement "mtotal" a
161
        mfree = getIntElement "mfree" a
162
        dtotal = getIntElement "dtotal" a
163
        dfree = getIntElement "dfree" a
164
    in concatElems name $
165
       concatElems (show `apply1` mtotal) $
166
       concatElems (show `apply1` mfree) $
167
       concatElems (show `apply1` dtotal) (show `apply1` dfree)