Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / Rapi.hs @ c8b662f1

History | View | Annotate | Download (4.8 kB)

1
{-| Implementation of the RAPI client interface.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2009 Google Inc.
8

    
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
13

    
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18

    
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Ganeti.HTools.Rapi
27
    (
28
      loadData
29
    ) where
30

    
31
import Network.Curl
32
import Network.Curl.Types ()
33
import Control.Monad
34
import Text.JSON (JSObject, JSValue, fromJSObject, decodeStrict)
35
import Text.JSON.Types (JSValue(..))
36
import Text.Printf (printf)
37

    
38
import Ganeti.HTools.Utils
39
import Ganeti.HTools.Loader
40
import Ganeti.HTools.Types
41
import qualified Ganeti.HTools.Node as Node
42
import qualified Ganeti.HTools.Instance as Instance
43

    
44
-- | Read an URL via curl and return the body if successful.
45
getUrl :: (Monad m) => String -> IO (m String)
46
getUrl url = do
47
  (code, body) <- curlGetString url [CurlSSLVerifyPeer False,
48
                                     CurlSSLVerifyHost 0,
49
                                     CurlTimeout (fromIntegral queryTimeout),
50
                                     CurlConnectTimeout
51
                                     (fromIntegral connTimeout)]
52
  return (case code of
53
            CurlOK -> return body
54
            _ -> fail $ printf "Curl error for '%s', error %s"
55
                 url (show code))
56

    
57
-- | Append the default port if not passed in.
58
formatHost :: String -> String
59
formatHost master =
60
    if ':' `elem` master then  master
61
    else "https://" ++ master ++ ":5080"
62

    
63
-- | Parse a instance list in JSON format.
64
getInstances :: NameAssoc
65
             -> String
66
             -> Result [(String, Instance.Instance)]
67
getInstances ktn body =
68
    loadJSArray "Parsing instance data" body >>=
69
    mapM (parseInstance ktn . fromJSObject)
70

    
71
-- | Parse a node list in JSON format.
72
getNodes :: String -> Result [(String, Node.Node)]
73
getNodes body = loadJSArray "Parsing node data" body >>=
74
                mapM (parseNode . fromJSObject)
75

    
76
-- | Construct an instance from a JSON object.
77
parseInstance :: [(String, Ndx)]
78
              -> [(String, JSValue)]
79
              -> Result (String, Instance.Instance)
80
parseInstance ktn a = do
81
  name <- tryFromObj "Parsing new instance" a "name"
82
  let owner_name = "Instance '" ++ name ++ "'"
83
  let extract s x = tryFromObj owner_name x s
84
  disk <- extract "disk_usage" a
85
  beparams <- liftM fromJSObject (extract "beparams" a)
86
  omem <- extract "oper_ram" a
87
  mem <- (case omem of
88
            JSRational _ _ -> annotateResult owner_name (fromJVal omem)
89
            _ -> extract "memory" beparams)
90
  vcpus <- extract "vcpus" beparams
91
  pnode <- extract "pnode" a >>= lookupNode ktn name
92
  snodes <- extract "snodes" a
93
  snode <- (if null snodes then return Node.noSecondary
94
            else readEitherString (head snodes) >>= lookupNode ktn name)
95
  running <- extract "status" a
96
  tags <- extract "tags" a
97
  let inst = Instance.create name mem disk vcpus running tags pnode snode
98
  return (name, inst)
99

    
100
-- | Construct a node from a JSON object.
101
parseNode :: [(String, JSValue)] -> Result (String, Node.Node)
102
parseNode a = do
103
  name <- tryFromObj "Parsing new node" a "name"
104
  let extract s = tryFromObj ("Node '" ++ name ++ "'") a s
105
  offline <- extract "offline"
106
  drained <- extract "drained"
107
  node <- (if offline || drained
108
           then return $ Node.create name 0 0 0 0 0 0 True
109
           else do
110
             mtotal  <- extract "mtotal"
111
             mnode   <- extract "mnode"
112
             mfree   <- extract "mfree"
113
             dtotal  <- extract "dtotal"
114
             dfree   <- extract "dfree"
115
             ctotal  <- extract "ctotal"
116
             return $ Node.create name mtotal mnode mfree
117
                    dtotal dfree ctotal False)
118
  return (name, node)
119

    
120
-- | Builds the cluster data from an URL.
121
loadData :: String -- ^ Cluster or URL to use as source
122
         -> IO (Result (Node.AssocList, Instance.AssocList, [String]))
123
loadData master = do -- IO monad
124
  let url = formatHost master
125
  node_body <- getUrl $ printf "%s/2/nodes?bulk=1" url
126
  inst_body <- getUrl $ printf "%s/2/instances?bulk=1" url
127
  tags_body <- getUrl $ printf "%s/2/tags" url
128
  return $ do -- Result monad
129
    node_data <- node_body >>= getNodes
130
    let (node_names, node_idx) = assignIndices node_data
131
    inst_data <- inst_body >>= getInstances node_names
132
    let (_, inst_idx) = assignIndices inst_data
133
    tags_data <- tags_body >>= (fromJResult . decodeStrict)
134
    return (node_idx, inst_idx, tags_data)