Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / Luxi.hs @ a334d536

History | View | Annotate | Download (6.1 kB)

1
{-| Implementation of the LUXI loader.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2009, 2010 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.Luxi
27
    (
28
      loadData
29
    , parseData
30
    ) where
31

    
32
import qualified Control.Exception as E
33
import Text.JSON.Types
34

    
35
import qualified Ganeti.Luxi as L
36
import Ganeti.HTools.Loader
37
import Ganeti.HTools.Types
38
import qualified Ganeti.HTools.Node as Node
39
import qualified Ganeti.HTools.Instance as Instance
40
import Ganeti.HTools.Utils (fromJVal, annotateResult, tryFromObj, asJSObject)
41

    
42
-- * Utility functions
43

    
44
-- | Ensure a given JSValue is actually a JSArray.
45
toArray :: (Monad m) => JSValue -> m [JSValue]
46
toArray v =
47
    case v of
48
      JSArray arr -> return arr
49
      o -> fail ("Invalid input, expected array but got " ++ show o)
50

    
51
-- * Data querying functionality
52

    
53
-- | The input data for node query.
54
queryNodesMsg :: L.LuxiOp
55
queryNodesMsg =
56
  L.QueryNodes [] ["name", "mtotal", "mnode", "mfree", "dtotal", "dfree",
57
                   "ctotal", "offline", "drained", "vm_capable",
58
                   "group.uuid"] False
59

    
60
-- | The input data for instance query.
61
queryInstancesMsg :: L.LuxiOp
62
queryInstancesMsg =
63
  L.QueryInstances [] ["name", "disk_usage", "be/memory", "be/vcpus",
64
                       "status", "pnode", "snodes", "tags", "oper_ram"] False
65

    
66
-- | The input data for cluster query
67
queryClusterInfoMsg :: L.LuxiOp
68
queryClusterInfoMsg = L.QueryClusterInfo
69

    
70
-- | Wraper over callMethod doing node query.
71
queryNodes :: L.Client -> IO (Result JSValue)
72
queryNodes = L.callMethod queryNodesMsg
73

    
74
-- | Wraper over callMethod doing instance query.
75
queryInstances :: L.Client -> IO (Result JSValue)
76
queryInstances = L.callMethod queryInstancesMsg
77

    
78
queryClusterInfo :: L.Client -> IO (Result JSValue)
79
queryClusterInfo = L.callMethod queryClusterInfoMsg
80

    
81
-- | Parse a instance list in JSON format.
82
getInstances :: NameAssoc
83
             -> JSValue
84
             -> Result [(String, Instance.Instance)]
85
getInstances ktn arr = toArray arr >>= mapM (parseInstance ktn)
86

    
87
-- | Construct an instance from a JSON object.
88
parseInstance :: NameAssoc
89
              -> JSValue
90
              -> Result (String, Instance.Instance)
91
parseInstance ktn (JSArray [ name, disk, mem, vcpus
92
                           , status, pnode, snodes, tags, oram ]) = do
93
  xname <- annotateResult "Parsing new instance" (fromJVal name)
94
  let convert v = annotateResult ("Instance '" ++ xname ++ "'") (fromJVal v)
95
  xdisk <- convert disk
96
  xmem <- (case oram of
97
             JSRational _ _ -> convert oram
98
             _ -> convert mem)
99
  xvcpus <- convert vcpus
100
  xpnode <- convert pnode >>= lookupNode ktn xname
101
  xsnodes <- convert snodes::Result [JSString]
102
  snode <- (if null xsnodes then return Node.noSecondary
103
            else lookupNode ktn xname (fromJSString $ head xsnodes))
104
  xrunning <- convert status
105
  xtags <- convert tags
106
  let inst = Instance.create xname xmem xdisk xvcpus
107
             xrunning xtags xpnode snode
108
  return (xname, inst)
109

    
110
parseInstance _ v = fail ("Invalid instance query result: " ++ show v)
111

    
112
-- | Parse a node list in JSON format.
113
getNodes :: JSValue -> Result [(String, Node.Node)]
114
getNodes arr = toArray arr >>= mapM parseNode
115

    
116
-- | Construct a node from a JSON object.
117
parseNode :: JSValue -> Result (String, Node.Node)
118
parseNode (JSArray [ name, mtotal, mnode, mfree, dtotal, dfree
119
                   , ctotal, offline, drained, vm_capable, g_uuid ])
120
    = do
121
  xname <- annotateResult "Parsing new node" (fromJVal name)
122
  let convert v = annotateResult ("Node '" ++ xname ++ "'") (fromJVal v)
123
  xoffline <- convert offline
124
  xdrained <- convert drained
125
  xvm_capable <- convert vm_capable
126
  xguuid   <- convert g_uuid
127
  node <- (if xoffline || xdrained || not xvm_capable
128
           then return $ Node.create xname 0 0 0 0 0 0 True xguuid
129
           else do
130
             xmtotal  <- convert mtotal
131
             xmnode   <- convert mnode
132
             xmfree   <- convert mfree
133
             xdtotal  <- convert dtotal
134
             xdfree   <- convert dfree
135
             xctotal  <- convert ctotal
136
             return $ Node.create xname xmtotal xmnode xmfree
137
                    xdtotal xdfree xctotal False xguuid)
138
  return (xname, node)
139

    
140
parseNode v = fail ("Invalid node query result: " ++ show v)
141

    
142
getClusterTags :: JSValue -> Result [String]
143
getClusterTags v = do
144
  let errmsg = "Parsing cluster info"
145
  obj <- annotateResult errmsg $ asJSObject v
146
  tryFromObj errmsg (fromJSObject obj) "tags"
147

    
148
-- * Main loader functionality
149

    
150
-- | Builds the cluster data from an URL.
151
readData :: String -- ^ Unix socket to use as source
152
         -> IO (Result JSValue, Result JSValue, Result JSValue)
153
readData master =
154
  E.bracket
155
       (L.getClient master)
156
       L.closeClient
157
       (\s -> do
158
          nodes <- queryNodes s
159
          instances <- queryInstances s
160
          cinfo <- queryClusterInfo s
161
          return (nodes, instances, cinfo)
162
       )
163

    
164
parseData :: (Result JSValue, Result JSValue, Result JSValue)
165
          -> Result (Node.List, Instance.List, [String])
166
parseData (nodes, instances, cinfo) = do
167
  node_data <- nodes >>= getNodes
168
  let (node_names, node_idx) = assignIndices node_data
169
  inst_data <- instances >>= getInstances node_names
170
  let (_, inst_idx) = assignIndices inst_data
171
  ctags <- cinfo >>= getClusterTags
172
  return (node_idx, inst_idx, ctags)
173

    
174
-- | Top level function for data loading
175
loadData :: String -- ^ Unix socket to use as source
176
            -> IO (Result (Node.List, Instance.List, [String]))
177
loadData master = readData master >>= return . parseData