Revision 36162faf
b/src/Ganeti/Query/Group.hs | ||
---|---|---|
4 | 4 |
|
5 | 5 |
{- |
6 | 6 |
|
7 |
Copyright (C) 2012 Google Inc. |
|
7 |
Copyright (C) 2012, 2013 Google Inc.
|
|
8 | 8 |
|
9 | 9 |
This program is free software; you can redistribute it and/or modify |
10 | 10 |
it under the terms of the GNU General Public License as published by |
... | ... | |
24 | 24 |
-} |
25 | 25 |
|
26 | 26 |
module Ganeti.Query.Group |
27 |
( GroupRuntime(..)
|
|
28 |
, groupFieldsMap
|
|
27 |
( Runtime(..) |
|
28 |
, fieldsMap
|
|
29 | 29 |
) where |
30 | 30 |
|
31 | 31 |
import qualified Data.Map as Map |
... | ... | |
37 | 37 |
import Ganeti.Query.Types |
38 | 38 |
|
39 | 39 |
-- | There is no runtime. |
40 |
data GroupRuntime = GroupRuntime
|
|
40 |
data Runtime = Runtime
|
|
41 | 41 |
|
42 |
groupFields :: FieldList NodeGroup GroupRuntime
|
|
42 |
groupFields :: FieldList NodeGroup Runtime |
|
43 | 43 |
groupFields = |
44 | 44 |
[ (FieldDefinition "alloc_policy" "AllocPolicy" QFTText |
45 | 45 |
"Allocation policy for group", |
... | ... | |
86 | 86 |
tagsFields |
87 | 87 |
|
88 | 88 |
-- | The group fields map. |
89 |
groupFieldsMap :: FieldMap NodeGroup GroupRuntime
|
|
90 |
groupFieldsMap =
|
|
89 |
fieldsMap :: FieldMap NodeGroup Runtime
|
|
90 |
fieldsMap =
|
|
91 | 91 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) groupFields |
b/src/Ganeti/Query/Network.hs | ||
---|---|---|
4 | 4 |
|
5 | 5 |
{- |
6 | 6 |
|
7 |
Copyright (C) 2012 Google Inc. |
|
7 |
Copyright (C) 2012, 2013 Google Inc.
|
|
8 | 8 |
|
9 | 9 |
This program is free software; you can redistribute it and/or modify |
10 | 10 |
it under the terms of the GNU General Public License as published by |
... | ... | |
27 | 27 |
( getGroupConnection |
28 | 28 |
, getNetworkUuid |
29 | 29 |
, instIsConnected |
30 |
, NetworkRuntime(..)
|
|
31 |
, networkFieldsMap
|
|
30 |
, Runtime(..) |
|
31 |
, fieldsMap
|
|
32 | 32 |
) where |
33 | 33 |
|
34 |
-- FIXME: everything except NetworkRuntime(..) and networkFieldsMap
|
|
34 |
-- FIXME: everything except Runtime(..) and fieldsMap
|
|
35 | 35 |
-- is only exported for testing. |
36 | 36 |
|
37 | 37 |
import qualified Data.Map as Map |
... | ... | |
46 | 46 |
import Ganeti.Query.Types |
47 | 47 |
import Ganeti.Types |
48 | 48 |
|
49 |
data NetworkRuntime = NetworkRuntime |
|
49 |
-- | There is no actual runtime. |
|
50 |
data Runtime = Runtime |
|
50 | 51 |
|
51 |
networkFields :: FieldList Network NetworkRuntime
|
|
52 |
networkFields :: FieldList Network Runtime |
|
52 | 53 |
networkFields = |
53 | 54 |
[ (FieldDefinition "name" "Name" QFTText "Network name", |
54 | 55 |
FieldSimple (rsNormal . networkName), QffNormal) |
... | ... | |
90 | 91 |
tagsFields |
91 | 92 |
|
92 | 93 |
-- | The group fields map. |
93 |
networkFieldsMap :: FieldMap Network NetworkRuntime
|
|
94 |
networkFieldsMap =
|
|
94 |
fieldsMap :: FieldMap Network Runtime
|
|
95 |
fieldsMap =
|
|
95 | 96 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) networkFields |
96 | 97 |
|
97 | 98 |
-- TODO: the following fields are not implemented yet: external_reservations |
b/src/Ganeti/Query/Node.hs | ||
---|---|---|
4 | 4 |
|
5 | 5 |
{- |
6 | 6 |
|
7 |
Copyright (C) 2012 Google Inc. |
|
7 |
Copyright (C) 2012, 2013 Google Inc.
|
|
8 | 8 |
|
9 | 9 |
This program is free software; you can redistribute it and/or modify |
10 | 10 |
it under the terms of the GNU General Public License as published by |
... | ... | |
24 | 24 |
-} |
25 | 25 |
|
26 | 26 |
module Ganeti.Query.Node |
27 |
( NodeRuntime
|
|
28 |
, nodeFieldsMap
|
|
29 |
, maybeCollectLiveData
|
|
27 |
( Runtime |
|
28 |
, fieldsMap
|
|
29 |
, collectLiveData
|
|
30 | 30 |
) where |
31 | 31 |
|
32 | 32 |
import Control.Applicative |
... | ... | |
42 | 42 |
import Ganeti.Query.Common |
43 | 43 |
import Ganeti.Query.Types |
44 | 44 |
|
45 |
-- | NodeRuntime is the resulting type for NodeInfo call.
|
|
46 |
type NodeRuntime = Either RpcError RpcResultNodeInfo
|
|
45 |
-- | Runtime is the resulting type for NodeInfo call. |
|
46 |
type Runtime = Either RpcError RpcResultNodeInfo |
|
47 | 47 |
|
48 | 48 |
-- | List of node live fields. |
49 | 49 |
nodeLiveFieldsDefs :: [(FieldName, FieldTitle, FieldType, String, FieldDoc)] |
... | ... | |
93 | 93 |
nodeLiveFieldExtract _ _ = J.JSNull |
94 | 94 |
|
95 | 95 |
-- | Helper for extracting field from RPC result. |
96 |
nodeLiveRpcCall :: FieldName -> NodeRuntime -> Node -> ResultEntry
|
|
96 |
nodeLiveRpcCall :: FieldName -> Runtime -> Node -> ResultEntry |
|
97 | 97 |
nodeLiveRpcCall fname (Right res) _ = |
98 | 98 |
case nodeLiveFieldExtract fname res of |
99 | 99 |
J.JSNull -> rsNoData |
... | ... | |
103 | 103 |
|
104 | 104 |
-- | Builder for node live fields. |
105 | 105 |
nodeLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc) |
106 |
-> FieldData Node NodeRuntime
|
|
106 |
-> FieldData Node Runtime |
|
107 | 107 |
nodeLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) = |
108 | 108 |
( FieldDefinition fname ftitle ftype fdoc |
109 | 109 |
, FieldRuntime $ nodeLiveRpcCall fname |
... | ... | |
129 | 129 |
else rsNormal (nodePowered node) |
130 | 130 |
|
131 | 131 |
-- | List of all node fields. |
132 |
nodeFields :: FieldList Node NodeRuntime
|
|
132 |
nodeFields :: FieldList Node Runtime |
|
133 | 133 |
nodeFields = |
134 | 134 |
[ (FieldDefinition "drained" "Drained" QFTBool "Whether node is drained", |
135 | 135 |
FieldSimple (rsNormal . nodeDrained), QffNormal) |
... | ... | |
208 | 208 |
tagsFields |
209 | 209 |
|
210 | 210 |
-- | The node fields map. |
211 |
nodeFieldsMap :: FieldMap Node NodeRuntime
|
|
212 |
nodeFieldsMap =
|
|
211 |
fieldsMap :: FieldMap Node Runtime
|
|
212 |
fieldsMap =
|
|
213 | 213 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) nodeFields |
214 | 214 |
|
215 | 215 |
-- | Collect live data from RPC query if enabled. |
216 | 216 |
-- |
217 | 217 |
-- FIXME: Check which fields we actually need and possibly send empty |
218 |
-- hvs/vgs if no info from hypervisor/volume group respectively is
|
|
218 |
-- hvs\/vgs if no info from hypervisor\/volume group respectively is
|
|
219 | 219 |
-- required |
220 |
maybeCollectLiveData:: Bool -> ConfigData -> [Node] -> IO [(Node, NodeRuntime)]
|
|
221 |
maybeCollectLiveData False _ nodes =
|
|
220 |
collectLiveData:: Bool -> ConfigData -> [Node] -> IO [(Node, Runtime)]
|
|
221 |
collectLiveData False _ nodes =
|
|
222 | 222 |
return $ zip nodes (repeat $ Left (RpcResultError "Live data disabled")) |
223 |
maybeCollectLiveData True cfg nodes = do
|
|
223 |
collectLiveData True cfg nodes = do
|
|
224 | 224 |
let vgs = [clusterVolumeGroupName $ configCluster cfg] |
225 | 225 |
hvs = [getDefaultHypervisor cfg] |
226 | 226 |
step n (bn, gn, em) = |
b/src/Ganeti/Query/Query.hs | ||
---|---|---|
70 | 70 |
import qualified Ganeti.Query.Export as Export |
71 | 71 |
import Ganeti.Query.Filter |
72 | 72 |
import qualified Ganeti.Query.Job as Query.Job |
73 |
import Ganeti.Query.Group
|
|
73 |
import qualified Ganeti.Query.Group as Group
|
|
74 | 74 |
import Ganeti.Query.Language |
75 |
import Ganeti.Query.Network
|
|
76 |
import Ganeti.Query.Node
|
|
75 |
import qualified Ganeti.Query.Network as Network
|
|
76 |
import qualified Ganeti.Query.Node as Node
|
|
77 | 77 |
import Ganeti.Query.Types |
78 | 78 |
import Ganeti.Path |
79 | 79 |
import Ganeti.Types |
... | ... | |
168 | 168 |
|
169 | 169 |
queryInner cfg live (Query (ItemTypeOpCode QRNode) fields qfilter) wanted = |
170 | 170 |
runResultT $ do |
171 |
cfilter <- resultT $ compileFilter nodeFieldsMap qfilter
|
|
172 |
let selected = getSelectedFields nodeFieldsMap fields
|
|
171 |
cfilter <- resultT $ compileFilter Node.fieldsMap qfilter
|
|
172 |
let selected = getSelectedFields Node.fieldsMap fields
|
|
173 | 173 |
(fdefs, fgetters, _) = unzip3 selected |
174 | 174 |
live' = live && needsLiveData fgetters |
175 | 175 |
nodes <- resultT $ case wanted of |
... | ... | |
182 | 182 |
nodes |
183 | 183 |
-- here we would run the runtime data gathering, then filter again |
184 | 184 |
-- the nodes, based on existing runtime data |
185 |
nruntimes <- lift $ maybeCollectLiveData live' cfg fnodes
|
|
185 |
nruntimes <- lift $ Node.collectLiveData live' cfg fnodes
|
|
186 | 186 |
let fdata = map (\(node, nrt) -> map (execGetter cfg nrt node) fgetters) |
187 | 187 |
nruntimes |
188 | 188 |
return QueryResult { qresFields = fdefs, qresData = fdata } |
189 | 189 |
|
190 | 190 |
queryInner cfg _ (Query (ItemTypeOpCode QRGroup) fields qfilter) wanted = |
191 | 191 |
return $ do |
192 |
cfilter <- compileFilter groupFieldsMap qfilter
|
|
193 |
let selected = getSelectedFields groupFieldsMap fields
|
|
192 |
cfilter <- compileFilter Group.fieldsMap qfilter
|
|
193 |
let selected = getSelectedFields Group.fieldsMap fields
|
|
194 | 194 |
(fdefs, fgetters, _) = unzip3 selected |
195 | 195 |
groups <- case wanted of |
196 | 196 |
[] -> Ok . niceSortKey groupName . |
... | ... | |
199 | 199 |
-- there is no live data for groups, so filtering is much simpler |
200 | 200 |
fgroups <- filterM (\n -> evaluateFilter cfg Nothing n cfilter) groups |
201 | 201 |
let fdata = map (\node -> |
202 |
map (execGetter cfg GroupRuntime node) fgetters) fgroups
|
|
202 |
map (execGetter cfg Group.Runtime node) fgetters) fgroups
|
|
203 | 203 |
return QueryResult { qresFields = fdefs, qresData = fdata } |
204 | 204 |
|
205 | 205 |
queryInner cfg _ (Query (ItemTypeOpCode QRNetwork) fields qfilter) wanted = |
206 | 206 |
return $ do |
207 |
cfilter <- compileFilter networkFieldsMap qfilter
|
|
208 |
let selected = getSelectedFields networkFieldsMap fields
|
|
207 |
cfilter <- compileFilter Network.fieldsMap qfilter
|
|
208 |
let selected = getSelectedFields Network.fieldsMap fields
|
|
209 | 209 |
(fdefs, fgetters, _) = unzip3 selected |
210 | 210 |
networks <- case wanted of |
211 | 211 |
[] -> Ok . niceSortKey (fromNonEmpty . networkName) . |
... | ... | |
213 | 213 |
_ -> mapM (getNetwork cfg) wanted |
214 | 214 |
fnetworks <- filterM (\n -> evaluateFilter cfg Nothing n cfilter) networks |
215 | 215 |
let fdata = map (\network -> |
216 |
map (execGetter cfg NetworkRuntime network) fgetters) |
|
216 |
map (execGetter cfg Network.Runtime network) fgetters)
|
|
217 | 217 |
fnetworks |
218 | 218 |
return QueryResult { qresFields = fdefs, qresData = fdata } |
219 | 219 |
|
... | ... | |
308 | 308 |
-- | Query fields call. |
309 | 309 |
queryFields :: QueryFields -> ErrorResult QueryFieldsResult |
310 | 310 |
queryFields (QueryFields (ItemTypeOpCode QRNode) fields) = |
311 |
Ok $ fieldsExtractor nodeFieldsMap fields
|
|
311 |
Ok $ fieldsExtractor Node.fieldsMap fields
|
|
312 | 312 |
|
313 | 313 |
queryFields (QueryFields (ItemTypeOpCode QRGroup) fields) = |
314 |
Ok $ fieldsExtractor groupFieldsMap fields
|
|
314 |
Ok $ fieldsExtractor Group.fieldsMap fields
|
|
315 | 315 |
|
316 | 316 |
queryFields (QueryFields (ItemTypeLuxi QRJob) fields) = |
317 | 317 |
Ok $ fieldsExtractor Query.Job.fieldsMap fields |
b/test/hs/Test/Ganeti/Query/Query.hs | ||
---|---|---|
7 | 7 |
|
8 | 8 |
{- |
9 | 9 |
|
10 |
Copyright (C) 2009, 2010, 2011, 2012 Google Inc. |
|
10 |
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
|
|
11 | 11 |
|
12 | 12 |
This program is free software; you can redistribute it and/or modify |
13 | 13 |
it under the terms of the GNU General Public License as published by |
... | ... | |
45 | 45 |
import Ganeti.BasicTypes |
46 | 46 |
import Ganeti.Errors |
47 | 47 |
import Ganeti.Query.Filter |
48 |
import Ganeti.Query.Group
|
|
48 |
import qualified Ganeti.Query.Group as Group
|
|
49 | 49 |
import Ganeti.Query.Language |
50 |
import Ganeti.Query.Node
|
|
50 |
import qualified Ganeti.Query.Node as Node
|
|
51 | 51 |
import Ganeti.Query.Query |
52 | 52 |
import qualified Ganeti.Query.Job as Job |
53 | 53 |
|
... | ... | |
68 | 68 |
prop_queryNode_noUnknown :: Property |
69 | 69 |
prop_queryNode_noUnknown = |
70 | 70 |
forAll (choose (0, maxNodes) >>= genEmptyCluster) $ \cluster -> |
71 |
forAll (elements (Map.keys nodeFieldsMap)) $ \field -> monadicIO $ do
|
|
71 |
forAll (elements (Map.keys Node.fieldsMap)) $ \field -> monadicIO $ do
|
|
72 | 72 |
QueryResult fdefs fdata <- |
73 | 73 |
run (query cluster False (Query (ItemTypeOpCode QRNode) |
74 | 74 |
[field] EmptyFilter)) >>= resultProp |
... | ... | |
88 | 88 |
prop_queryNode_Unknown :: Property |
89 | 89 |
prop_queryNode_Unknown = |
90 | 90 |
forAll (choose (0, maxNodes) >>= genEmptyCluster) $ \cluster -> |
91 |
forAll (arbitrary `suchThat` (`notElem` Map.keys nodeFieldsMap))
|
|
91 |
forAll (arbitrary `suchThat` (`notElem` Map.keys Node.fieldsMap))
|
|
92 | 92 |
$ \field -> monadicIO $ do |
93 | 93 |
QueryResult fdefs fdata <- |
94 | 94 |
run (query cluster False (Query (ItemTypeOpCode QRNode) |
... | ... | |
136 | 136 |
prop_queryNode_types = |
137 | 137 |
forAll (choose (0, maxNodes)) $ \numnodes -> |
138 | 138 |
forAll (genEmptyCluster numnodes) $ \cfg -> |
139 |
forAll (elements (Map.keys nodeFieldsMap)) $ \field -> monadicIO $ do
|
|
139 |
forAll (elements (Map.keys Node.fieldsMap)) $ \field -> monadicIO $ do
|
|
140 | 140 |
QueryResult fdefs fdata <- |
141 | 141 |
run (query cfg False (Query (ItemTypeOpCode QRNode) |
142 | 142 |
[field] EmptyFilter)) >>= resultProp |
... | ... | |
160 | 160 |
Ok (QueryFieldsResult v) -> return v |
161 | 161 |
let field_sort = compare `on` fdefName |
162 | 162 |
assertEqual "Mismatch in all fields list" |
163 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems nodeFieldsMap)
|
|
163 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems Node.fieldsMap)
|
|
164 | 164 |
(sortBy field_sort fdefs) |
165 | 165 |
|
166 | 166 |
-- ** Group queries |
... | ... | |
168 | 168 |
prop_queryGroup_noUnknown :: Property |
169 | 169 |
prop_queryGroup_noUnknown = |
170 | 170 |
forAll (choose (0, maxNodes) >>= genEmptyCluster) $ \cluster -> |
171 |
forAll (elements (Map.keys groupFieldsMap)) $ \field -> monadicIO $ do
|
|
171 |
forAll (elements (Map.keys Group.fieldsMap)) $ \field -> monadicIO $ do
|
|
172 | 172 |
QueryResult fdefs fdata <- |
173 | 173 |
run (query cluster False (Query (ItemTypeOpCode QRGroup) |
174 | 174 |
[field] EmptyFilter)) >>= |
... | ... | |
188 | 188 |
prop_queryGroup_Unknown :: Property |
189 | 189 |
prop_queryGroup_Unknown = |
190 | 190 |
forAll (choose (0, maxNodes) >>= genEmptyCluster) $ \cluster -> |
191 |
forAll (arbitrary `suchThat` (`notElem` Map.keys groupFieldsMap))
|
|
191 |
forAll (arbitrary `suchThat` (`notElem` Map.keys Group.fieldsMap))
|
|
192 | 192 |
$ \field -> monadicIO $ do |
193 | 193 |
QueryResult fdefs fdata <- |
194 | 194 |
run (query cluster False (Query (ItemTypeOpCode QRGroup) |
... | ... | |
212 | 212 |
prop_queryGroup_types = |
213 | 213 |
forAll (choose (0, maxNodes)) $ \numnodes -> |
214 | 214 |
forAll (genEmptyCluster numnodes) $ \cfg -> |
215 |
forAll (elements (Map.keys groupFieldsMap)) $ \field -> monadicIO $ do
|
|
215 |
forAll (elements (Map.keys Group.fieldsMap)) $ \field -> monadicIO $ do
|
|
216 | 216 |
QueryResult fdefs fdata <- |
217 | 217 |
run (query cfg False (Query (ItemTypeOpCode QRGroup) |
218 | 218 |
[field] EmptyFilter)) >>= resultProp |
... | ... | |
232 | 232 |
Ok (QueryFieldsResult v) -> return v |
233 | 233 |
let field_sort = compare `on` fdefName |
234 | 234 |
assertEqual "Mismatch in all fields list" |
235 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems groupFieldsMap)
|
|
235 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems Group.fieldsMap)
|
|
236 | 236 |
(sortBy field_sort fdefs) |
237 | 237 |
|
238 | 238 |
-- ** Job queries |
Also available in: Unified diff