Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / HTools / Backend / Luxi.hs @ 241cea1e

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