1 {-| Implementation of the RAPI client interface.
5 module Ganeti.HTools.Rapi
11 import Network.Curl.Types ()
12 import Network.Curl.Code
15 import Text.JSON (JSObject, JSValue)
16 import Text.Printf (printf)
18 import Ganeti.HTools.Utils
19 import Ganeti.HTools.Loader
20 import Ganeti.HTools.Types
21 import qualified Ganeti.HTools.Node as Node
22 import qualified Ganeti.HTools.Instance as Instance
24 -- | Read an URL via curl and return the body if successful
25 getUrl :: (Monad m) => String -> IO (m String)
27 (code, body) <- curlGetString url [CurlSSLVerifyPeer False,
31 _ -> fail $ printf "Curl error for '%s', error %s"
34 -- | Append the default port if not passed in
35 formatHost :: String -> String
37 if elem ':' master then master
38 else "https://" ++ master ++ ":5080"
40 getInstances :: NameAssoc
42 -> Result [(String, Instance.Instance)]
43 getInstances ktn body = do
44 arr <- loadJSArray body
45 ilist <- mapM (parseInstance ktn) arr
48 getNodes :: String -> Result [(String, Node.Node)]
50 arr <- loadJSArray body
51 nlist <- mapM parseNode arr
54 parseInstance :: [(String, Int)]
56 -> Result (String, Instance.Instance)
57 parseInstance ktn a = do
58 name <- fromObj "name" a
59 disk <- fromObj "disk_usage" a
60 mem <- fromObj "beparams" a >>= fromObj "memory"
61 pnode <- fromObj "pnode" a >>= lookupNode ktn name
62 snodes <- fromObj "snodes" a
63 snode <- (if null snodes then return Node.noSecondary
64 else readEitherString (head snodes) >>= lookupNode ktn name)
65 running <- fromObj "status" a
66 let inst = Instance.create name mem disk running pnode snode
69 parseNode :: JSObject JSValue -> Result (String, Node.Node)
71 name <- fromObj "name" a
72 offline <- fromObj "offline" a
73 node <- (case offline of
74 True -> return $ Node.create name 0 0 0 0 0 True
76 drained <- fromObj "drained" a
77 mtotal <- fromObj "mtotal" a
78 mnode <- fromObj "mnode" a
79 mfree <- fromObj "mfree" a
80 dtotal <- fromObj "dtotal" a
81 dfree <- fromObj "dfree" a
82 return $ Node.create name mtotal mnode mfree
83 dtotal dfree (offline || drained))
86 loadData :: String -- ^ Cluster or URL to use as source
87 -> IO (Result (Node.AssocList, Instance.AssocList))
88 loadData master = do -- IO monad
89 let url = formatHost master
90 node_body <- getUrl $ printf "%s/2/nodes?bulk=1" url
91 inst_body <- getUrl $ printf "%s/2/instances?bulk=1" url
92 return $ do -- Result monad
93 node_data <- node_body >>= getNodes
94 let (node_names, node_idx) = assignIndices node_data
95 inst_data <- inst_body >>= getInstances node_names
96 let (_, inst_idx) = assignIndices inst_data
97 return (node_idx, inst_idx)