Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / Luxi.hs @ ebf38064

History | View | Annotate | Download (9.6 kB)

1 6583e677 Iustin Pop
{-| Implementation of the LUXI loader.
2 53ec9022 Iustin Pop
3 53ec9022 Iustin Pop
-}
4 53ec9022 Iustin Pop
5 53ec9022 Iustin Pop
{-
6 53ec9022 Iustin Pop
7 39420403 Iustin Pop
Copyright (C) 2009, 2010, 2011 Google Inc.
8 53ec9022 Iustin Pop
9 53ec9022 Iustin Pop
This program is free software; you can redistribute it and/or modify
10 53ec9022 Iustin Pop
it under the terms of the GNU General Public License as published by
11 53ec9022 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
12 53ec9022 Iustin Pop
(at your option) any later version.
13 53ec9022 Iustin Pop
14 53ec9022 Iustin Pop
This program is distributed in the hope that it will be useful, but
15 53ec9022 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
16 53ec9022 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 53ec9022 Iustin Pop
General Public License for more details.
18 53ec9022 Iustin Pop
19 53ec9022 Iustin Pop
You should have received a copy of the GNU General Public License
20 53ec9022 Iustin Pop
along with this program; if not, write to the Free Software
21 53ec9022 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 53ec9022 Iustin Pop
02110-1301, USA.
23 53ec9022 Iustin Pop
24 53ec9022 Iustin Pop
-}
25 53ec9022 Iustin Pop
26 53ec9022 Iustin Pop
module Ganeti.HTools.Luxi
27 ebf38064 Iustin Pop
  ( loadData
28 ebf38064 Iustin Pop
  , parseData
29 ebf38064 Iustin Pop
  ) where
30 53ec9022 Iustin Pop
31 53ec9022 Iustin Pop
import qualified Control.Exception as E
32 53ec9022 Iustin Pop
import Text.JSON.Types
33 39420403 Iustin Pop
import qualified Text.JSON
34 53ec9022 Iustin Pop
35 6583e677 Iustin Pop
import qualified Ganeti.Luxi as L
36 53ec9022 Iustin Pop
import Ganeti.HTools.Loader
37 53ec9022 Iustin Pop
import Ganeti.HTools.Types
38 a679e9dc Iustin Pop
import qualified Ganeti.HTools.Group as Group
39 53ec9022 Iustin Pop
import qualified Ganeti.HTools.Node as Node
40 53ec9022 Iustin Pop
import qualified Ganeti.HTools.Instance as Instance
41 d12f50b2 Iustin Pop
import Ganeti.HTools.Utils (fromJVal, annotateResult, tryFromObj, asJSObject,
42 d12f50b2 Iustin Pop
                            fromObj)
43 53ec9022 Iustin Pop
44 53ec9022 Iustin Pop
-- * Utility functions
45 53ec9022 Iustin Pop
46 92678b3c Iustin Pop
-- | Get values behind \"data\" part of the result.
47 92678b3c Iustin Pop
getData :: (Monad m) => JSValue -> m JSValue
48 d12f50b2 Iustin Pop
getData (JSObject o) = fromObj (fromJSObject o) "data"
49 d12f50b2 Iustin Pop
getData x = fail $ "Invalid input, expected dict entry but got " ++ show x
50 d12f50b2 Iustin Pop
51 d12f50b2 Iustin Pop
-- | Converts a (status, value) into m value, if possible.
52 d12f50b2 Iustin Pop
parseQueryField :: (Monad m) => JSValue -> m (JSValue, JSValue)
53 d12f50b2 Iustin Pop
parseQueryField (JSArray [status, result]) = return (status, result)
54 d12f50b2 Iustin Pop
parseQueryField o =
55 ebf38064 Iustin Pop
  fail $ "Invalid query field, expected (status, value) but got " ++ show o
56 d12f50b2 Iustin Pop
57 d12f50b2 Iustin Pop
-- | Parse a result row.
58 d12f50b2 Iustin Pop
parseQueryRow :: (Monad m) => JSValue -> m [(JSValue, JSValue)]
59 d12f50b2 Iustin Pop
parseQueryRow (JSArray arr) = mapM parseQueryField arr
60 d12f50b2 Iustin Pop
parseQueryRow o =
61 ebf38064 Iustin Pop
  fail $ "Invalid query row result, expected array but got " ++ show o
62 d12f50b2 Iustin Pop
63 d12f50b2 Iustin Pop
-- | Parse an overall query result and get the [(status, value)] list
64 d12f50b2 Iustin Pop
-- for each element queried.
65 d12f50b2 Iustin Pop
parseQueryResult :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
66 d12f50b2 Iustin Pop
parseQueryResult (JSArray arr) = mapM parseQueryRow arr
67 d12f50b2 Iustin Pop
parseQueryResult o =
68 ebf38064 Iustin Pop
  fail $ "Invalid query result, expected array but got " ++ show o
69 92678b3c Iustin Pop
70 92678b3c Iustin Pop
-- | Prepare resulting output as parsers expect it.
71 260d0bda Agata Murawska
extractArray :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
72 d12f50b2 Iustin Pop
extractArray v =
73 260d0bda Agata Murawska
  getData v >>= parseQueryResult
74 260d0bda Agata Murawska
75 260d0bda Agata Murawska
-- | Testing result status for more verbose error message.
76 260d0bda Agata Murawska
fromJValWithStatus :: (Text.JSON.JSON a, Monad m) => (JSValue, JSValue) -> m a
77 260d0bda Agata Murawska
fromJValWithStatus (st, v) = do
78 ebf38064 Iustin Pop
  st' <- fromJVal st
79 ebf38064 Iustin Pop
  L.checkRS st' v >>= fromJVal
80 92678b3c Iustin Pop
81 39420403 Iustin Pop
-- | Annotate errors when converting values with owner/attribute for
82 39420403 Iustin Pop
-- better debugging.
83 39420403 Iustin Pop
genericConvert :: (Text.JSON.JSON a) =>
84 260d0bda Agata Murawska
                  String             -- ^ The object type
85 260d0bda Agata Murawska
               -> String             -- ^ The object name
86 260d0bda Agata Murawska
               -> String             -- ^ The attribute we're trying to convert
87 260d0bda Agata Murawska
               -> (JSValue, JSValue) -- ^ The value we're trying to convert
88 260d0bda Agata Murawska
               -> Result a           -- ^ The annotated result
89 39420403 Iustin Pop
genericConvert otype oname oattr =
90 ebf38064 Iustin Pop
  annotateResult (otype ++ " '" ++ oname ++
91 ebf38064 Iustin Pop
                  "', error while reading attribute '" ++
92 ebf38064 Iustin Pop
                  oattr ++ "'") . fromJValWithStatus
93 39420403 Iustin Pop
94 53ec9022 Iustin Pop
-- * Data querying functionality
95 53ec9022 Iustin Pop
96 53ec9022 Iustin Pop
-- | The input data for node query.
97 683b1ca7 Iustin Pop
queryNodesMsg :: L.LuxiOp
98 53ec9022 Iustin Pop
queryNodesMsg =
99 92678b3c Iustin Pop
  L.Query L.QRNode ["name", "mtotal", "mnode", "mfree", "dtotal", "dfree",
100 92678b3c Iustin Pop
                    "ctotal", "offline", "drained", "vm_capable",
101 b20cbf06 Iustin Pop
                    "group.uuid"] ()
102 53ec9022 Iustin Pop
103 53ec9022 Iustin Pop
-- | The input data for instance query.
104 683b1ca7 Iustin Pop
queryInstancesMsg :: L.LuxiOp
105 53ec9022 Iustin Pop
queryInstancesMsg =
106 ebf38064 Iustin Pop
  L.Query L.QRInstance ["name", "disk_usage", "be/memory", "be/vcpus",
107 ebf38064 Iustin Pop
                        "status", "pnode", "snodes", "tags", "oper_ram",
108 ebf38064 Iustin Pop
                        "be/auto_balance", "disk_template"] ()
109 53ec9022 Iustin Pop
110 a679e9dc Iustin Pop
-- | The input data for cluster query.
111 683b1ca7 Iustin Pop
queryClusterInfoMsg :: L.LuxiOp
112 683b1ca7 Iustin Pop
queryClusterInfoMsg = L.QueryClusterInfo
113 f89235f1 Iustin Pop
114 a679e9dc Iustin Pop
-- | The input data for node group query.
115 a679e9dc Iustin Pop
queryGroupsMsg :: L.LuxiOp
116 a679e9dc Iustin Pop
queryGroupsMsg =
117 b20cbf06 Iustin Pop
  L.Query L.QRGroup ["uuid", "name", "alloc_policy"] ()
118 a679e9dc Iustin Pop
119 179c0828 Iustin Pop
-- | Wraper over 'callMethod' doing node query.
120 6583e677 Iustin Pop
queryNodes :: L.Client -> IO (Result JSValue)
121 683b1ca7 Iustin Pop
queryNodes = L.callMethod queryNodesMsg
122 53ec9022 Iustin Pop
123 179c0828 Iustin Pop
-- | Wraper over 'callMethod' doing instance query.
124 6583e677 Iustin Pop
queryInstances :: L.Client -> IO (Result JSValue)
125 683b1ca7 Iustin Pop
queryInstances = L.callMethod queryInstancesMsg
126 53ec9022 Iustin Pop
127 179c0828 Iustin Pop
-- | Wrapper over 'callMethod' doing cluster information query.
128 f89235f1 Iustin Pop
queryClusterInfo :: L.Client -> IO (Result JSValue)
129 683b1ca7 Iustin Pop
queryClusterInfo = L.callMethod queryClusterInfoMsg
130 f89235f1 Iustin Pop
131 a679e9dc Iustin Pop
-- | Wrapper over callMethod doing group query.
132 a679e9dc Iustin Pop
queryGroups :: L.Client -> IO (Result JSValue)
133 a679e9dc Iustin Pop
queryGroups = L.callMethod queryGroupsMsg
134 a679e9dc Iustin Pop
135 53ec9022 Iustin Pop
-- | Parse a instance list in JSON format.
136 53ec9022 Iustin Pop
getInstances :: NameAssoc
137 53ec9022 Iustin Pop
             -> JSValue
138 53ec9022 Iustin Pop
             -> Result [(String, Instance.Instance)]
139 92678b3c Iustin Pop
getInstances ktn arr = extractArray arr >>= mapM (parseInstance ktn)
140 53ec9022 Iustin Pop
141 53ec9022 Iustin Pop
-- | Construct an instance from a JSON object.
142 6ff78049 Iustin Pop
parseInstance :: NameAssoc
143 260d0bda Agata Murawska
              -> [(JSValue, JSValue)]
144 53ec9022 Iustin Pop
              -> Result (String, Instance.Instance)
145 260d0bda Agata Murawska
parseInstance ktn [ name, disk, mem, vcpus
146 260d0bda Agata Murawska
                  , status, pnode, snodes, tags, oram
147 260d0bda Agata Murawska
                  , auto_balance, disk_template ] = do
148 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new instance" (fromJValWithStatus name)
149 39420403 Iustin Pop
  let convert a = genericConvert "Instance" xname a
150 39420403 Iustin Pop
  xdisk <- convert "disk_usage" disk
151 260d0bda Agata Murawska
  xmem <- (case oram of -- FIXME: remove the "guessing"
152 260d0bda Agata Murawska
             (_, JSRational _ _) -> convert "oper_ram" oram
153 39420403 Iustin Pop
             _ -> convert "be/memory" mem)
154 39420403 Iustin Pop
  xvcpus <- convert "be/vcpus" vcpus
155 39420403 Iustin Pop
  xpnode <- convert "pnode" pnode >>= lookupNode ktn xname
156 39420403 Iustin Pop
  xsnodes <- convert "snodes" snodes::Result [JSString]
157 53ec9022 Iustin Pop
  snode <- (if null xsnodes then return Node.noSecondary
158 53ec9022 Iustin Pop
            else lookupNode ktn xname (fromJSString $ head xsnodes))
159 39420403 Iustin Pop
  xrunning <- convert "status" status
160 39420403 Iustin Pop
  xtags <- convert "tags" tags
161 6880526c Iustin Pop
  xauto_balance <- convert "auto_balance" auto_balance
162 b3c5e8de Iustin Pop
  xdt <- convert "disk_template" disk_template
163 17e7af2b Iustin Pop
  let inst = Instance.create xname xmem xdisk xvcpus
164 b3c5e8de Iustin Pop
             xrunning xtags xauto_balance xpnode snode xdt
165 53ec9022 Iustin Pop
  return (xname, inst)
166 53ec9022 Iustin Pop
167 53ec9022 Iustin Pop
parseInstance _ v = fail ("Invalid instance query result: " ++ show v)
168 53ec9022 Iustin Pop
169 53ec9022 Iustin Pop
-- | Parse a node list in JSON format.
170 10ef6b4e Iustin Pop
getNodes :: NameAssoc -> JSValue -> Result [(String, Node.Node)]
171 92678b3c Iustin Pop
getNodes ktg arr = extractArray arr >>= mapM (parseNode ktg)
172 53ec9022 Iustin Pop
173 53ec9022 Iustin Pop
-- | Construct a node from a JSON object.
174 260d0bda Agata Murawska
parseNode :: NameAssoc -> [(JSValue, JSValue)] -> Result (String, Node.Node)
175 260d0bda Agata Murawska
parseNode ktg [ name, mtotal, mnode, mfree, dtotal, dfree
176 260d0bda Agata Murawska
              , ctotal, offline, drained, vm_capable, g_uuid ]
177 53ec9022 Iustin Pop
    = do
178 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new node" (fromJValWithStatus name)
179 39420403 Iustin Pop
  let convert a = genericConvert "Node" xname a
180 39420403 Iustin Pop
  xoffline <- convert "offline" offline
181 39420403 Iustin Pop
  xdrained <- convert "drained" drained
182 39420403 Iustin Pop
  xvm_capable <- convert "vm_capable" vm_capable
183 39420403 Iustin Pop
  xgdx   <- convert "group.uuid" g_uuid >>= lookupGroup ktg xname
184 9d775204 Iustin Pop
  node <- (if xoffline || xdrained || not xvm_capable
185 10ef6b4e Iustin Pop
           then return $ Node.create xname 0 0 0 0 0 0 True xgdx
186 53ec9022 Iustin Pop
           else do
187 39420403 Iustin Pop
             xmtotal  <- convert "mtotal" mtotal
188 39420403 Iustin Pop
             xmnode   <- convert "mnode" mnode
189 39420403 Iustin Pop
             xmfree   <- convert "mfree" mfree
190 39420403 Iustin Pop
             xdtotal  <- convert "dtotal" dtotal
191 39420403 Iustin Pop
             xdfree   <- convert "dfree" dfree
192 39420403 Iustin Pop
             xctotal  <- convert "ctotal" ctotal
193 53ec9022 Iustin Pop
             return $ Node.create xname xmtotal xmnode xmfree
194 10ef6b4e Iustin Pop
                    xdtotal xdfree xctotal False xgdx)
195 53ec9022 Iustin Pop
  return (xname, node)
196 53ec9022 Iustin Pop
197 10ef6b4e Iustin Pop
parseNode _ v = fail ("Invalid node query result: " ++ show v)
198 53ec9022 Iustin Pop
199 179c0828 Iustin Pop
-- | Parses the cluster tags.
200 f89235f1 Iustin Pop
getClusterTags :: JSValue -> Result [String]
201 f89235f1 Iustin Pop
getClusterTags v = do
202 f89235f1 Iustin Pop
  let errmsg = "Parsing cluster info"
203 f89235f1 Iustin Pop
  obj <- annotateResult errmsg $ asJSObject v
204 5182e970 Iustin Pop
  tryFromObj errmsg (fromJSObject obj) "tags"
205 f89235f1 Iustin Pop
206 179c0828 Iustin Pop
-- | Parses the cluster groups.
207 a679e9dc Iustin Pop
getGroups :: JSValue -> Result [(String, Group.Group)]
208 92678b3c Iustin Pop
getGroups jsv = extractArray jsv >>= mapM parseGroup
209 a679e9dc Iustin Pop
210 179c0828 Iustin Pop
-- | Parses a given group information.
211 260d0bda Agata Murawska
parseGroup :: [(JSValue, JSValue)] -> Result (String, Group.Group)
212 260d0bda Agata Murawska
parseGroup [uuid, name, apol] = do
213 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new group" (fromJValWithStatus name)
214 39420403 Iustin Pop
  let convert a = genericConvert "Group" xname a
215 39420403 Iustin Pop
  xuuid <- convert "uuid" uuid
216 39420403 Iustin Pop
  xapol <- convert "alloc_policy" apol
217 d5072e4c Iustin Pop
  return (xuuid, Group.create xname xuuid xapol)
218 a679e9dc Iustin Pop
219 a679e9dc Iustin Pop
parseGroup v = fail ("Invalid group query result: " ++ show v)
220 a679e9dc Iustin Pop
221 53ec9022 Iustin Pop
-- * Main loader functionality
222 53ec9022 Iustin Pop
223 525bfb36 Iustin Pop
-- | Builds the cluster data by querying a given socket name.
224 b3f0710c Iustin Pop
readData :: String -- ^ Unix socket to use as source
225 a679e9dc Iustin Pop
         -> IO (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
226 b3f0710c Iustin Pop
readData master =
227 53ec9022 Iustin Pop
  E.bracket
228 6583e677 Iustin Pop
       (L.getClient master)
229 6583e677 Iustin Pop
       L.closeClient
230 53ec9022 Iustin Pop
       (\s -> do
231 53ec9022 Iustin Pop
          nodes <- queryNodes s
232 53ec9022 Iustin Pop
          instances <- queryInstances s
233 f89235f1 Iustin Pop
          cinfo <- queryClusterInfo s
234 a679e9dc Iustin Pop
          groups <- queryGroups s
235 a679e9dc Iustin Pop
          return (groups, nodes, instances, cinfo)
236 53ec9022 Iustin Pop
       )
237 b3f0710c Iustin Pop
238 525bfb36 Iustin Pop
-- | Converts the output of 'readData' into the internal cluster
239 525bfb36 Iustin Pop
-- representation.
240 a679e9dc Iustin Pop
parseData :: (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
241 f4f6eb0b Iustin Pop
          -> Result ClusterData
242 a679e9dc Iustin Pop
parseData (groups, nodes, instances, cinfo) = do
243 a679e9dc Iustin Pop
  group_data <- groups >>= getGroups
244 10ef6b4e Iustin Pop
  let (group_names, group_idx) = assignIndices group_data
245 10ef6b4e Iustin Pop
  node_data <- nodes >>= getNodes group_names
246 b3f0710c Iustin Pop
  let (node_names, node_idx) = assignIndices node_data
247 b3f0710c Iustin Pop
  inst_data <- instances >>= getInstances node_names
248 b3f0710c Iustin Pop
  let (_, inst_idx) = assignIndices inst_data
249 b3f0710c Iustin Pop
  ctags <- cinfo >>= getClusterTags
250 f4f6eb0b Iustin Pop
  return (ClusterData group_idx node_idx inst_idx ctags)
251 b3f0710c Iustin Pop
252 525bfb36 Iustin Pop
-- | Top level function for data loading.
253 b3f0710c Iustin Pop
loadData :: String -- ^ Unix socket to use as source
254 f4f6eb0b Iustin Pop
         -> IO (Result ClusterData)
255 2a8e2dc9 Iustin Pop
loadData = fmap parseData . readData