Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.1 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 8bc34c7b Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012 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 4cab6703 Iustin Pop
import qualified Ganeti.Query.Language as Qlang
37 53ec9022 Iustin Pop
import Ganeti.HTools.Loader
38 53ec9022 Iustin Pop
import Ganeti.HTools.Types
39 a679e9dc Iustin Pop
import qualified Ganeti.HTools.Group as Group
40 53ec9022 Iustin Pop
import qualified Ganeti.HTools.Node as Node
41 53ec9022 Iustin Pop
import qualified Ganeti.HTools.Instance as Instance
42 f3baf5ef Iustin Pop
import Ganeti.JSON
43 53ec9022 Iustin Pop
44 3603605a Iustin Pop
{-# ANN module "HLint: ignore Eta reduce" #-}
45 3603605a Iustin Pop
46 53ec9022 Iustin Pop
-- * Utility functions
47 53ec9022 Iustin Pop
48 92678b3c Iustin Pop
-- | Get values behind \"data\" part of the result.
49 92678b3c Iustin Pop
getData :: (Monad m) => JSValue -> m JSValue
50 d12f50b2 Iustin Pop
getData (JSObject o) = fromObj (fromJSObject o) "data"
51 d12f50b2 Iustin Pop
getData x = fail $ "Invalid input, expected dict entry but got " ++ show x
52 d12f50b2 Iustin Pop
53 d12f50b2 Iustin Pop
-- | Converts a (status, value) into m value, if possible.
54 d12f50b2 Iustin Pop
parseQueryField :: (Monad m) => JSValue -> m (JSValue, JSValue)
55 d12f50b2 Iustin Pop
parseQueryField (JSArray [status, result]) = return (status, result)
56 d12f50b2 Iustin Pop
parseQueryField o =
57 ebf38064 Iustin Pop
  fail $ "Invalid query field, expected (status, value) but got " ++ show o
58 d12f50b2 Iustin Pop
59 d12f50b2 Iustin Pop
-- | Parse a result row.
60 d12f50b2 Iustin Pop
parseQueryRow :: (Monad m) => JSValue -> m [(JSValue, JSValue)]
61 d12f50b2 Iustin Pop
parseQueryRow (JSArray arr) = mapM parseQueryField arr
62 d12f50b2 Iustin Pop
parseQueryRow o =
63 ebf38064 Iustin Pop
  fail $ "Invalid query row result, expected array but got " ++ show o
64 d12f50b2 Iustin Pop
65 d12f50b2 Iustin Pop
-- | Parse an overall query result and get the [(status, value)] list
66 d12f50b2 Iustin Pop
-- for each element queried.
67 d12f50b2 Iustin Pop
parseQueryResult :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
68 d12f50b2 Iustin Pop
parseQueryResult (JSArray arr) = mapM parseQueryRow arr
69 d12f50b2 Iustin Pop
parseQueryResult o =
70 ebf38064 Iustin Pop
  fail $ "Invalid query result, expected array but got " ++ show o
71 92678b3c Iustin Pop
72 92678b3c Iustin Pop
-- | Prepare resulting output as parsers expect it.
73 260d0bda Agata Murawska
extractArray :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
74 d12f50b2 Iustin Pop
extractArray v =
75 260d0bda Agata Murawska
  getData v >>= parseQueryResult
76 260d0bda Agata Murawska
77 260d0bda Agata Murawska
-- | Testing result status for more verbose error message.
78 260d0bda Agata Murawska
fromJValWithStatus :: (Text.JSON.JSON a, Monad m) => (JSValue, JSValue) -> m a
79 260d0bda Agata Murawska
fromJValWithStatus (st, v) = do
80 ebf38064 Iustin Pop
  st' <- fromJVal st
81 62377cf5 Iustin Pop
  Qlang.checkRS st' v >>= fromJVal
82 92678b3c Iustin Pop
83 39420403 Iustin Pop
-- | Annotate errors when converting values with owner/attribute for
84 39420403 Iustin Pop
-- better debugging.
85 39420403 Iustin Pop
genericConvert :: (Text.JSON.JSON a) =>
86 260d0bda Agata Murawska
                  String             -- ^ The object type
87 260d0bda Agata Murawska
               -> String             -- ^ The object name
88 260d0bda Agata Murawska
               -> String             -- ^ The attribute we're trying to convert
89 260d0bda Agata Murawska
               -> (JSValue, JSValue) -- ^ The value we're trying to convert
90 260d0bda Agata Murawska
               -> Result a           -- ^ The annotated result
91 39420403 Iustin Pop
genericConvert otype oname oattr =
92 ebf38064 Iustin Pop
  annotateResult (otype ++ " '" ++ oname ++
93 ebf38064 Iustin Pop
                  "', error while reading attribute '" ++
94 ebf38064 Iustin Pop
                  oattr ++ "'") . fromJValWithStatus
95 39420403 Iustin Pop
96 53ec9022 Iustin Pop
-- * Data querying functionality
97 53ec9022 Iustin Pop
98 53ec9022 Iustin Pop
-- | The input data for node query.
99 683b1ca7 Iustin Pop
queryNodesMsg :: L.LuxiOp
100 53ec9022 Iustin Pop
queryNodesMsg =
101 8a9ee1e9 Iustin Pop
  L.Query Qlang.QRNode ["name", "mtotal", "mnode", "mfree", "dtotal", "dfree",
102 8a9ee1e9 Iustin Pop
                        "ctotal", "offline", "drained", "vm_capable",
103 8a9ee1e9 Iustin Pop
                        "ndp/spindle_count", "group.uuid"] Qlang.EmptyFilter
104 53ec9022 Iustin Pop
105 53ec9022 Iustin Pop
-- | The input data for instance query.
106 683b1ca7 Iustin Pop
queryInstancesMsg :: L.LuxiOp
107 53ec9022 Iustin Pop
queryInstancesMsg =
108 8a9ee1e9 Iustin Pop
  L.Query Qlang.QRInstance ["name", "disk_usage", "be/memory", "be/vcpus",
109 8a9ee1e9 Iustin Pop
                            "status", "pnode", "snodes", "tags", "oper_ram",
110 8a9ee1e9 Iustin Pop
                            "be/auto_balance", "disk_template",
111 8a9ee1e9 Iustin Pop
                            "be/spindle_use"] Qlang.EmptyFilter
112 53ec9022 Iustin Pop
113 a679e9dc Iustin Pop
-- | The input data for cluster query.
114 683b1ca7 Iustin Pop
queryClusterInfoMsg :: L.LuxiOp
115 683b1ca7 Iustin Pop
queryClusterInfoMsg = L.QueryClusterInfo
116 f89235f1 Iustin Pop
117 a679e9dc Iustin Pop
-- | The input data for node group query.
118 a679e9dc Iustin Pop
queryGroupsMsg :: L.LuxiOp
119 a679e9dc Iustin Pop
queryGroupsMsg =
120 8a9ee1e9 Iustin Pop
  L.Query Qlang.QRGroup ["uuid", "name", "alloc_policy", "ipolicy"]
121 dc6a0f82 Iustin Pop
   Qlang.EmptyFilter
122 a679e9dc Iustin Pop
123 179c0828 Iustin Pop
-- | Wraper over 'callMethod' doing node query.
124 6583e677 Iustin Pop
queryNodes :: L.Client -> IO (Result JSValue)
125 683b1ca7 Iustin Pop
queryNodes = L.callMethod queryNodesMsg
126 53ec9022 Iustin Pop
127 179c0828 Iustin Pop
-- | Wraper over 'callMethod' doing instance query.
128 6583e677 Iustin Pop
queryInstances :: L.Client -> IO (Result JSValue)
129 683b1ca7 Iustin Pop
queryInstances = L.callMethod queryInstancesMsg
130 53ec9022 Iustin Pop
131 179c0828 Iustin Pop
-- | Wrapper over 'callMethod' doing cluster information query.
132 f89235f1 Iustin Pop
queryClusterInfo :: L.Client -> IO (Result JSValue)
133 683b1ca7 Iustin Pop
queryClusterInfo = L.callMethod queryClusterInfoMsg
134 f89235f1 Iustin Pop
135 a679e9dc Iustin Pop
-- | Wrapper over callMethod doing group query.
136 a679e9dc Iustin Pop
queryGroups :: L.Client -> IO (Result JSValue)
137 a679e9dc Iustin Pop
queryGroups = L.callMethod queryGroupsMsg
138 a679e9dc Iustin Pop
139 53ec9022 Iustin Pop
-- | Parse a instance list in JSON format.
140 53ec9022 Iustin Pop
getInstances :: NameAssoc
141 53ec9022 Iustin Pop
             -> JSValue
142 53ec9022 Iustin Pop
             -> Result [(String, Instance.Instance)]
143 92678b3c Iustin Pop
getInstances ktn arr = extractArray arr >>= mapM (parseInstance ktn)
144 53ec9022 Iustin Pop
145 53ec9022 Iustin Pop
-- | Construct an instance from a JSON object.
146 6ff78049 Iustin Pop
parseInstance :: NameAssoc
147 260d0bda Agata Murawska
              -> [(JSValue, JSValue)]
148 53ec9022 Iustin Pop
              -> Result (String, Instance.Instance)
149 260d0bda Agata Murawska
parseInstance ktn [ name, disk, mem, vcpus
150 260d0bda Agata Murawska
                  , status, pnode, snodes, tags, oram
151 b003b8c0 René Nussbaumer
                  , auto_balance, disk_template, su ] = do
152 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new instance" (fromJValWithStatus name)
153 39420403 Iustin Pop
  let convert a = genericConvert "Instance" xname a
154 39420403 Iustin Pop
  xdisk <- convert "disk_usage" disk
155 3603605a Iustin Pop
  xmem <- case oram of -- FIXME: remove the "guessing"
156 3603605a Iustin Pop
            (_, JSRational _ _) -> convert "oper_ram" oram
157 3603605a Iustin Pop
            _ -> convert "be/memory" mem
158 39420403 Iustin Pop
  xvcpus <- convert "be/vcpus" vcpus
159 39420403 Iustin Pop
  xpnode <- convert "pnode" pnode >>= lookupNode ktn xname
160 39420403 Iustin Pop
  xsnodes <- convert "snodes" snodes::Result [JSString]
161 3603605a Iustin Pop
  snode <- if null xsnodes
162 3603605a Iustin Pop
             then return Node.noSecondary
163 3603605a Iustin Pop
             else lookupNode ktn xname (fromJSString $ head xsnodes)
164 39420403 Iustin Pop
  xrunning <- convert "status" status
165 39420403 Iustin Pop
  xtags <- convert "tags" tags
166 6880526c Iustin Pop
  xauto_balance <- convert "auto_balance" auto_balance
167 b3c5e8de Iustin Pop
  xdt <- convert "disk_template" disk_template
168 ec629280 René Nussbaumer
  xsu <- convert "be/spindle_use" su
169 17e7af2b Iustin Pop
  let inst = Instance.create xname xmem xdisk xvcpus
170 b003b8c0 René Nussbaumer
             xrunning xtags xauto_balance xpnode snode xdt xsu
171 53ec9022 Iustin Pop
  return (xname, inst)
172 53ec9022 Iustin Pop
173 53ec9022 Iustin Pop
parseInstance _ v = fail ("Invalid instance query result: " ++ show v)
174 53ec9022 Iustin Pop
175 53ec9022 Iustin Pop
-- | Parse a node list in JSON format.
176 10ef6b4e Iustin Pop
getNodes :: NameAssoc -> JSValue -> Result [(String, Node.Node)]
177 92678b3c Iustin Pop
getNodes ktg arr = extractArray arr >>= mapM (parseNode ktg)
178 53ec9022 Iustin Pop
179 53ec9022 Iustin Pop
-- | Construct a node from a JSON object.
180 260d0bda Agata Murawska
parseNode :: NameAssoc -> [(JSValue, JSValue)] -> Result (String, Node.Node)
181 260d0bda Agata Murawska
parseNode ktg [ name, mtotal, mnode, mfree, dtotal, dfree
182 8bc34c7b Iustin Pop
              , ctotal, offline, drained, vm_capable, spindles, g_uuid ]
183 53ec9022 Iustin Pop
    = do
184 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new node" (fromJValWithStatus name)
185 39420403 Iustin Pop
  let convert a = genericConvert "Node" xname a
186 39420403 Iustin Pop
  xoffline <- convert "offline" offline
187 39420403 Iustin Pop
  xdrained <- convert "drained" drained
188 39420403 Iustin Pop
  xvm_capable <- convert "vm_capable" vm_capable
189 8bc34c7b Iustin Pop
  xspindles <- convert "spindles" spindles
190 39420403 Iustin Pop
  xgdx   <- convert "group.uuid" g_uuid >>= lookupGroup ktg xname
191 3603605a Iustin Pop
  node <- if xoffline || xdrained || not xvm_capable
192 8bc34c7b Iustin Pop
            then return $ Node.create xname 0 0 0 0 0 0 True xspindles xgdx
193 3603605a Iustin Pop
            else do
194 3603605a Iustin Pop
              xmtotal  <- convert "mtotal" mtotal
195 3603605a Iustin Pop
              xmnode   <- convert "mnode" mnode
196 3603605a Iustin Pop
              xmfree   <- convert "mfree" mfree
197 3603605a Iustin Pop
              xdtotal  <- convert "dtotal" dtotal
198 3603605a Iustin Pop
              xdfree   <- convert "dfree" dfree
199 3603605a Iustin Pop
              xctotal  <- convert "ctotal" ctotal
200 3603605a Iustin Pop
              return $ Node.create xname xmtotal xmnode xmfree
201 8bc34c7b Iustin Pop
                     xdtotal xdfree xctotal False xspindles xgdx
202 53ec9022 Iustin Pop
  return (xname, node)
203 53ec9022 Iustin Pop
204 10ef6b4e Iustin Pop
parseNode _ v = fail ("Invalid node query result: " ++ show v)
205 53ec9022 Iustin Pop
206 179c0828 Iustin Pop
-- | Parses the cluster tags.
207 e77bc89b Iustin Pop
getClusterData :: JSValue -> Result ([String], IPolicy)
208 e77bc89b Iustin Pop
getClusterData (JSObject obj) = do
209 f89235f1 Iustin Pop
  let errmsg = "Parsing cluster info"
210 e77bc89b Iustin Pop
      obj' = fromJSObject obj
211 e77bc89b Iustin Pop
  ctags <- tryFromObj errmsg obj' "tags"
212 e77bc89b Iustin Pop
  cpol <- tryFromObj errmsg obj' "ipolicy"
213 e77bc89b Iustin Pop
  return (ctags, cpol)
214 e77bc89b Iustin Pop
215 5b11f8db Iustin Pop
getClusterData _ = Bad "Cannot parse cluster info, not a JSON record"
216 f89235f1 Iustin Pop
217 179c0828 Iustin Pop
-- | Parses the cluster groups.
218 a679e9dc Iustin Pop
getGroups :: JSValue -> Result [(String, Group.Group)]
219 92678b3c Iustin Pop
getGroups jsv = extractArray jsv >>= mapM parseGroup
220 a679e9dc Iustin Pop
221 179c0828 Iustin Pop
-- | Parses a given group information.
222 260d0bda Agata Murawska
parseGroup :: [(JSValue, JSValue)] -> Result (String, Group.Group)
223 6cff91f5 Iustin Pop
parseGroup [uuid, name, apol, ipol] = do
224 260d0bda Agata Murawska
  xname <- annotateResult "Parsing new group" (fromJValWithStatus name)
225 39420403 Iustin Pop
  let convert a = genericConvert "Group" xname a
226 39420403 Iustin Pop
  xuuid <- convert "uuid" uuid
227 39420403 Iustin Pop
  xapol <- convert "alloc_policy" apol
228 6cff91f5 Iustin Pop
  xipol <- convert "ipolicy" ipol
229 6cff91f5 Iustin Pop
  return (xuuid, Group.create xname xuuid xapol xipol)
230 a679e9dc Iustin Pop
231 a679e9dc Iustin Pop
parseGroup v = fail ("Invalid group query result: " ++ show v)
232 a679e9dc Iustin Pop
233 53ec9022 Iustin Pop
-- * Main loader functionality
234 53ec9022 Iustin Pop
235 525bfb36 Iustin Pop
-- | Builds the cluster data by querying a given socket name.
236 b3f0710c Iustin Pop
readData :: String -- ^ Unix socket to use as source
237 a679e9dc Iustin Pop
         -> IO (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
238 b3f0710c Iustin Pop
readData master =
239 53ec9022 Iustin Pop
  E.bracket
240 6583e677 Iustin Pop
       (L.getClient master)
241 6583e677 Iustin Pop
       L.closeClient
242 53ec9022 Iustin Pop
       (\s -> do
243 53ec9022 Iustin Pop
          nodes <- queryNodes s
244 53ec9022 Iustin Pop
          instances <- queryInstances s
245 f89235f1 Iustin Pop
          cinfo <- queryClusterInfo s
246 a679e9dc Iustin Pop
          groups <- queryGroups s
247 a679e9dc Iustin Pop
          return (groups, nodes, instances, cinfo)
248 53ec9022 Iustin Pop
       )
249 b3f0710c Iustin Pop
250 525bfb36 Iustin Pop
-- | Converts the output of 'readData' into the internal cluster
251 525bfb36 Iustin Pop
-- representation.
252 a679e9dc Iustin Pop
parseData :: (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
253 f4f6eb0b Iustin Pop
          -> Result ClusterData
254 a679e9dc Iustin Pop
parseData (groups, nodes, instances, cinfo) = do
255 a679e9dc Iustin Pop
  group_data <- groups >>= getGroups
256 10ef6b4e Iustin Pop
  let (group_names, group_idx) = assignIndices group_data
257 10ef6b4e Iustin Pop
  node_data <- nodes >>= getNodes group_names
258 b3f0710c Iustin Pop
  let (node_names, node_idx) = assignIndices node_data
259 b3f0710c Iustin Pop
  inst_data <- instances >>= getInstances node_names
260 b3f0710c Iustin Pop
  let (_, inst_idx) = assignIndices inst_data
261 e77bc89b Iustin Pop
  (ctags, cpol) <- cinfo >>= getClusterData
262 e77bc89b Iustin Pop
  return (ClusterData group_idx node_idx inst_idx ctags cpol)
263 b3f0710c Iustin Pop
264 525bfb36 Iustin Pop
-- | Top level function for data loading.
265 b3f0710c Iustin Pop
loadData :: String -- ^ Unix socket to use as source
266 f4f6eb0b Iustin Pop
         -> IO (Result ClusterData)
267 2a8e2dc9 Iustin Pop
loadData = fmap parseData . readData