Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / Luxi.hs @ 8b5a517a

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