Revision f94a9680
b/htest/Test/Ganeti/Query/Query.hs | ||
---|---|---|
156 | 156 |
Ok (QueryFieldsResult v) -> return v |
157 | 157 |
let field_sort = compare `on` fdefName |
158 | 158 |
assertEqual "Mismatch in all fields list" |
159 |
(sortBy field_sort . map fst $ Map.elems nodeFieldsMap)
|
|
159 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems nodeFieldsMap)
|
|
160 | 160 |
(sortBy field_sort fdefs) |
161 | 161 |
|
162 | 162 |
-- * Same as above, but for group |
... | ... | |
228 | 228 |
Ok (QueryFieldsResult v) -> return v |
229 | 229 |
let field_sort = compare `on` fdefName |
230 | 230 |
assertEqual "Mismatch in all fields list" |
231 |
(sortBy field_sort . map fst $ Map.elems groupFieldsMap)
|
|
231 |
(sortBy field_sort . map (\(f, _, _) -> f) $ Map.elems groupFieldsMap)
|
|
232 | 232 |
(sortBy field_sort fdefs) |
233 | 233 |
|
234 | 234 |
|
b/htools/Ganeti/Query/Common.hs | ||
---|---|---|
104 | 104 |
timeStampFields :: (TimeStampObject a) => FieldList a b |
105 | 105 |
timeStampFields = |
106 | 106 |
[ (FieldDefinition "ctime" "CTime" QFTTimestamp "Creation timestamp", |
107 |
FieldSimple (rsNormal . cTimeOf)) |
|
107 |
FieldSimple (rsNormal . cTimeOf), QffNormal)
|
|
108 | 108 |
, (FieldDefinition "mtime" "MTime" QFTTimestamp "Modification timestamp", |
109 |
FieldSimple (rsNormal . mTimeOf)) |
|
109 |
FieldSimple (rsNormal . mTimeOf), QffNormal)
|
|
110 | 110 |
] |
111 | 111 |
|
112 | 112 |
-- | The list of UUID fields. |
113 | 113 |
uuidFields :: (UuidObject a) => String -> FieldList a b |
114 | 114 |
uuidFields name = |
115 | 115 |
[ (FieldDefinition "uuid" "UUID" QFTText (name ++ " UUID"), |
116 |
FieldSimple (rsNormal . uuidOf)) ] |
|
116 |
FieldSimple (rsNormal . uuidOf), QffNormal) ]
|
|
117 | 117 |
|
118 | 118 |
-- | The list of serial number fields. |
119 | 119 |
serialFields :: (SerialNoObject a) => String -> FieldList a b |
120 | 120 |
serialFields name = |
121 | 121 |
[ (FieldDefinition "serial_no" "SerialNo" QFTNumber |
122 | 122 |
(name ++ " object serial number, incremented on each modification"), |
123 |
FieldSimple (rsNormal . serialOf)) ] |
|
123 |
FieldSimple (rsNormal . serialOf), QffNormal) ]
|
|
124 | 124 |
|
125 | 125 |
-- | The list of tag fields. |
126 | 126 |
tagsFields :: (TagsObject a) => FieldList a b |
127 | 127 |
tagsFields = |
128 | 128 |
[ (FieldDefinition "tags" "Tags" QFTOther "Tags", |
129 |
FieldSimple (rsNormal . tagsOf)) ] |
|
129 |
FieldSimple (rsNormal . tagsOf), QffNormal) ]
|
|
130 | 130 |
|
131 | 131 |
-- * Generic parameter functions |
132 | 132 |
|
... | ... | |
169 | 169 |
qft = fromMaybe QFTOther $ field `Map.lookup` ndParamTypes |
170 | 170 |
desc = "The \"" ++ field ++ "\" node parameter" |
171 | 171 |
in (FieldDefinition full_name title qft desc, |
172 |
FieldConfig (ndParamGetter field)) |
|
172 |
FieldConfig (ndParamGetter field), QffNormal) |
b/htools/Ganeti/Query/Filter.hs | ||
---|---|---|
69 | 69 |
-- | Compiles a filter based on field names to one based on getters. |
70 | 70 |
compileFilter :: FieldMap a b |
71 | 71 |
-> Filter FilterField |
72 |
-> ErrorResult (Filter (FieldGetter a b)) |
|
72 |
-> ErrorResult (Filter (FieldGetter a b, QffMode))
|
|
73 | 73 |
compileFilter fm = |
74 | 74 |
traverse (\field -> maybe |
75 | 75 |
(Bad . ParameterError $ "Can't find field named '" ++ |
76 | 76 |
field ++ "'") |
77 |
(Ok . snd) (field `Map.lookup` fm)) |
|
77 |
(\(_, g, q) -> Ok (g, q)) (field `Map.lookup` fm)) |
|
78 |
|
|
79 |
-- | Processes a field value given a QffMode. |
|
80 |
qffField :: QffMode -> JSValue -> ErrorResult JSValue |
|
81 |
qffField QffNormal v = Ok v |
|
82 |
qffField QffTimestamp v = |
|
83 |
case v of |
|
84 |
JSArray [secs@(JSRational _ _), JSRational _ _] -> return secs |
|
85 |
_ -> Bad $ ProgrammerError |
|
86 |
"Internal error: Getter returned non-timestamp for QffTimestamp" |
|
78 | 87 |
|
79 | 88 |
-- | Wraps a getter, filter pair. If the getter is 'FieldRuntime' but |
80 | 89 |
-- we don't have a runtime context, we skip the filtering, returning |
... | ... | |
82 | 91 |
wrapGetter :: ConfigData |
83 | 92 |
-> Maybe b |
84 | 93 |
-> a |
85 |
-> FieldGetter a b
|
|
94 |
-> (FieldGetter a b, QffMode)
|
|
86 | 95 |
-> (JSValue -> ErrorResult Bool) |
87 | 96 |
-> ErrorResult Bool |
88 |
wrapGetter cfg b a getter faction =
|
|
97 |
wrapGetter cfg b a (getter, qff) faction =
|
|
89 | 98 |
case tryGetter cfg b a getter of |
90 | 99 |
Nothing -> Ok True -- runtime missing, accepting the value |
91 | 100 |
Just v -> |
92 | 101 |
case v of |
93 |
ResultEntry RSNormal (Just fval) -> faction fval
|
|
102 |
ResultEntry RSNormal (Just fval) -> qffField qff fval >>= faction
|
|
94 | 103 |
ResultEntry RSNormal Nothing -> |
95 | 104 |
Bad $ ProgrammerError |
96 | 105 |
"Internal error: Getter returned RSNormal/Nothing" |
... | ... | |
149 | 158 |
-- 'any' and 'all' do not play nice with monadic values, resulting in |
150 | 159 |
-- either too much memory use or in too many thunks being created. |
151 | 160 |
evaluateFilter :: ConfigData -> Maybe b -> a |
152 |
-> Filter (FieldGetter a b) |
|
161 |
-> Filter (FieldGetter a b, QffMode)
|
|
153 | 162 |
-> ErrorResult Bool |
154 | 163 |
evaluateFilter _ _ _ EmptyFilter = Ok True |
155 | 164 |
evaluateFilter c mb a (AndFilter flts) = helper flts |
b/htools/Ganeti/Query/Group.hs | ||
---|---|---|
43 | 43 |
groupFields = |
44 | 44 |
[ (FieldDefinition "alloc_policy" "AllocPolicy" QFTText |
45 | 45 |
"Allocation policy for group", |
46 |
FieldSimple (rsNormal . groupAllocPolicy)) |
|
46 |
FieldSimple (rsNormal . groupAllocPolicy), QffNormal)
|
|
47 | 47 |
, (FieldDefinition "custom_diskparams" "CustomDiskParameters" QFTOther |
48 | 48 |
"Custom disk parameters", |
49 |
FieldSimple (rsNormal . groupDiskparams)) |
|
49 |
FieldSimple (rsNormal . groupDiskparams), QffNormal)
|
|
50 | 50 |
, (FieldDefinition "custom_ipolicy" "CustomInstancePolicy" QFTOther |
51 | 51 |
"Custom instance policy limitations", |
52 |
FieldSimple (rsNormal . groupIpolicy)) |
|
52 |
FieldSimple (rsNormal . groupIpolicy), QffNormal)
|
|
53 | 53 |
, (FieldDefinition "custom_ndparams" "CustomNDParams" QFTOther |
54 | 54 |
"Custom node parameters", |
55 |
FieldSimple (rsNormal . groupNdparams)) |
|
55 |
FieldSimple (rsNormal . groupNdparams), QffNormal)
|
|
56 | 56 |
, (FieldDefinition "diskparams" "DiskParameters" QFTOther |
57 | 57 |
"Disk parameters (merged)", |
58 |
FieldConfig (\cfg -> rsNormal . getGroupDiskParams cfg)) |
|
58 |
FieldConfig (\cfg -> rsNormal . getGroupDiskParams cfg), QffNormal)
|
|
59 | 59 |
, (FieldDefinition "ipolicy" "InstancePolicy" QFTOther |
60 | 60 |
"Instance policy limitations (merged)", |
61 |
FieldConfig (\cfg ng -> rsNormal (getGroupIpolicy cfg ng))) |
|
61 |
FieldConfig (\cfg ng -> rsNormal (getGroupIpolicy cfg ng)), QffNormal)
|
|
62 | 62 |
, (FieldDefinition "name" "Group" QFTText "Group name", |
63 |
FieldSimple (rsNormal . groupName)) |
|
63 |
FieldSimple (rsNormal . groupName), QffNormal)
|
|
64 | 64 |
, (FieldDefinition "ndparams" "NDParams" QFTOther "Node parameters", |
65 |
FieldConfig (\cfg ng -> rsNormal (getGroupNdParams cfg ng))) |
|
65 |
FieldConfig (\cfg ng -> rsNormal (getGroupNdParams cfg ng)), QffNormal)
|
|
66 | 66 |
, (FieldDefinition "node_cnt" "Nodes" QFTNumber "Number of nodes", |
67 |
FieldConfig (\cfg -> rsNormal . length . getGroupNodes cfg . groupName)) |
|
67 |
FieldConfig (\cfg -> rsNormal . length . getGroupNodes cfg . groupName), |
|
68 |
QffNormal) |
|
68 | 69 |
, (FieldDefinition "node_list" "NodeList" QFTOther "List of nodes", |
69 | 70 |
FieldConfig (\cfg -> rsNormal . map nodeName . |
70 |
getGroupNodes cfg . groupName)) |
|
71 |
getGroupNodes cfg . groupName), QffNormal)
|
|
71 | 72 |
, (FieldDefinition "pinst_cnt" "Instances" QFTNumber |
72 | 73 |
"Number of primary instances", |
73 | 74 |
FieldConfig |
74 |
(\cfg -> rsNormal . length . fst . getGroupInstances cfg . groupName)) |
|
75 |
(\cfg -> rsNormal . length . fst . getGroupInstances cfg . groupName), |
|
76 |
QffNormal) |
|
75 | 77 |
, (FieldDefinition "pinst_list" "InstanceList" QFTOther |
76 | 78 |
"List of primary instances", |
77 | 79 |
FieldConfig (\cfg -> rsNormal . map instName . fst . |
78 |
getGroupInstances cfg . groupName)) |
|
80 |
getGroupInstances cfg . groupName), QffNormal)
|
|
79 | 81 |
] ++ |
80 | 82 |
map buildNdParamField allNDParamFields ++ |
81 | 83 |
timeStampFields ++ |
... | ... | |
85 | 87 |
|
86 | 88 |
-- | The group fields map. |
87 | 89 |
groupFieldsMap :: FieldMap NodeGroup GroupRuntime |
88 |
groupFieldsMap = Map.fromList $ map (\v -> (fdefName (fst v), v)) groupFields |
|
90 |
groupFieldsMap = |
|
91 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) groupFields |
b/htools/Ganeti/Query/Node.hs | ||
---|---|---|
105 | 105 |
-> FieldData Node NodeRuntime |
106 | 106 |
nodeLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) = |
107 | 107 |
( FieldDefinition fname ftitle ftype fdoc |
108 |
, FieldRuntime $ nodeLiveRpcCall fname) |
|
108 |
, FieldRuntime $ nodeLiveRpcCall fname |
|
109 |
, QffNormal) |
|
109 | 110 |
|
110 | 111 |
-- | The docstring for the node role. Note that we use 'reverse in |
111 | 112 |
-- order to keep the same order as Python. |
... | ... | |
130 | 131 |
nodeFields :: FieldList Node NodeRuntime |
131 | 132 |
nodeFields = |
132 | 133 |
[ (FieldDefinition "drained" "Drained" QFTBool "Whether node is drained", |
133 |
FieldSimple (rsNormal . nodeDrained)) |
|
134 |
FieldSimple (rsNormal . nodeDrained), QffNormal)
|
|
134 | 135 |
, (FieldDefinition "master_candidate" "MasterC" QFTBool |
135 | 136 |
"Whether node is a master candidate", |
136 |
FieldSimple (rsNormal . nodeMasterCandidate)) |
|
137 |
FieldSimple (rsNormal . nodeMasterCandidate), QffNormal)
|
|
137 | 138 |
, (FieldDefinition "master_capable" "MasterCapable" QFTBool |
138 | 139 |
"Whether node can become a master candidate", |
139 |
FieldSimple (rsNormal . nodeMasterCapable)) |
|
140 |
FieldSimple (rsNormal . nodeMasterCapable), QffNormal)
|
|
140 | 141 |
, (FieldDefinition "name" "Node" QFTText "Node name", |
141 |
FieldSimple (rsNormal . nodeName)) |
|
142 |
FieldSimple (rsNormal . nodeName), QffNormal)
|
|
142 | 143 |
, (FieldDefinition "offline" "Offline" QFTBool |
143 | 144 |
"Whether node is marked offline", |
144 |
FieldSimple (rsNormal . nodeOffline)) |
|
145 |
FieldSimple (rsNormal . nodeOffline), QffNormal)
|
|
145 | 146 |
, (FieldDefinition "vm_capable" "VMCapable" QFTBool |
146 | 147 |
"Whether node can host instances", |
147 |
FieldSimple (rsNormal . nodeVmCapable)) |
|
148 |
FieldSimple (rsNormal . nodeVmCapable), QffNormal)
|
|
148 | 149 |
, (FieldDefinition "pip" "PrimaryIP" QFTText "Primary IP address", |
149 |
FieldSimple (rsNormal . nodePrimaryIp)) |
|
150 |
FieldSimple (rsNormal . nodePrimaryIp), QffNormal)
|
|
150 | 151 |
, (FieldDefinition "sip" "SecondaryIP" QFTText "Secondary IP address", |
151 |
FieldSimple (rsNormal . nodeSecondaryIp)) |
|
152 |
FieldSimple (rsNormal . nodeSecondaryIp), QffNormal)
|
|
152 | 153 |
, (FieldDefinition "master" "IsMaster" QFTBool "Whether node is master", |
153 | 154 |
FieldConfig (\cfg node -> |
154 | 155 |
rsNormal (nodeName node == |
155 |
clusterMasterNode (configCluster cfg)))) |
|
156 |
clusterMasterNode (configCluster cfg))), |
|
157 |
QffNormal) |
|
156 | 158 |
, (FieldDefinition "group" "Group" QFTText "Node group", |
157 | 159 |
FieldConfig (\cfg node -> |
158 |
rsMaybe (groupName <$> getGroupOfNode cfg node))) |
|
160 |
rsMaybe (groupName <$> getGroupOfNode cfg node)), |
|
161 |
QffNormal) |
|
159 | 162 |
, (FieldDefinition "group.uuid" "GroupUUID" QFTText "UUID of node group", |
160 |
FieldSimple (rsNormal . nodeGroup)) |
|
163 |
FieldSimple (rsNormal . nodeGroup), QffNormal)
|
|
161 | 164 |
, (FieldDefinition "ndparams" "NodeParameters" QFTOther |
162 | 165 |
"Merged node parameters", |
163 |
FieldConfig ((rsMaybe .) . getNodeNdParams)) |
|
166 |
FieldConfig ((rsMaybe .) . getNodeNdParams), QffNormal)
|
|
164 | 167 |
, (FieldDefinition "custom_ndparams" "CustomNodeParameters" QFTOther |
165 | 168 |
"Custom node parameters", |
166 |
FieldSimple (rsNormal . nodeNdparams)) |
|
169 |
FieldSimple (rsNormal . nodeNdparams), QffNormal)
|
|
167 | 170 |
-- FIXME: the below could be generalised a bit, like in Python |
168 | 171 |
, (FieldDefinition "pinst_cnt" "Pinst" QFTNumber |
169 | 172 |
"Number of instances with this node as primary", |
170 | 173 |
FieldConfig (\cfg -> |
171 |
rsNormal . length . fst . getNodeInstances cfg . nodeName)) |
|
174 |
rsNormal . length . fst . getNodeInstances cfg . nodeName), |
|
175 |
QffNormal) |
|
172 | 176 |
, (FieldDefinition "sinst_cnt" "Sinst" QFTNumber |
173 | 177 |
"Number of instances with this node as secondary", |
174 | 178 |
FieldConfig (\cfg -> |
175 |
rsNormal . length . snd . getNodeInstances cfg . nodeName)) |
|
179 |
rsNormal . length . snd . getNodeInstances cfg . nodeName), |
|
180 |
QffNormal) |
|
176 | 181 |
, (FieldDefinition "pinst_list" "PriInstances" QFTOther |
177 | 182 |
"List of instances with this node as primary", |
178 | 183 |
FieldConfig (\cfg -> rsNormal . map instName . fst . |
179 |
getNodeInstances cfg . nodeName)) |
|
184 |
getNodeInstances cfg . nodeName), QffNormal)
|
|
180 | 185 |
, (FieldDefinition "sinst_list" "SecInstances" QFTOther |
181 | 186 |
"List of instances with this node as secondary", |
182 | 187 |
FieldConfig (\cfg -> rsNormal . map instName . snd . |
183 |
getNodeInstances cfg . nodeName)) |
|
188 |
getNodeInstances cfg . nodeName), QffNormal)
|
|
184 | 189 |
, (FieldDefinition "role" "Role" QFTText nodeRoleDoc, |
185 |
FieldConfig ((rsNormal .) . getNodeRole)) |
|
190 |
FieldConfig ((rsNormal .) . getNodeRole), QffNormal)
|
|
186 | 191 |
, (FieldDefinition "powered" "Powered" QFTBool |
187 | 192 |
"Whether node is thought to be powered on", |
188 |
FieldConfig getNodePower) |
|
193 |
FieldConfig getNodePower, QffNormal)
|
|
189 | 194 |
-- FIXME: the two fields below are incomplete in Python, part of the |
190 | 195 |
-- non-implemented node resource model; they are declared just for |
191 | 196 |
-- parity, but are not functional |
192 | 197 |
, (FieldDefinition "hv_state" "HypervisorState" QFTOther "Hypervisor state", |
193 |
missingRuntime) |
|
198 |
missingRuntime, QffNormal)
|
|
194 | 199 |
, (FieldDefinition "disk_state" "DiskState" QFTOther "Disk state", |
195 |
missingRuntime) |
|
200 |
missingRuntime, QffNormal)
|
|
196 | 201 |
] ++ |
197 | 202 |
map nodeLiveFieldBuilder nodeLiveFieldsDefs ++ |
198 | 203 |
map buildNdParamField allNDParamFields ++ |
... | ... | |
203 | 208 |
|
204 | 209 |
-- | The node fields map. |
205 | 210 |
nodeFieldsMap :: FieldMap Node NodeRuntime |
206 |
nodeFieldsMap = Map.fromList $ map (\v -> (fdefName (fst v), v)) nodeFields |
|
211 |
nodeFieldsMap = |
|
212 |
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) nodeFields |
b/htools/Ganeti/Query/Query.hs | ||
---|---|---|
79 | 79 |
mkUnknownFDef :: String -> FieldData a b |
80 | 80 |
mkUnknownFDef name = |
81 | 81 |
( FieldDefinition name name QFTUnknown ("Unknown field '" ++ name ++ "'") |
82 |
, FieldUnknown ) |
|
82 |
, FieldUnknown |
|
83 |
, QffNormal ) |
|
83 | 84 |
|
84 | 85 |
-- | Runs a field getter on the existing contexts. |
85 | 86 |
execGetter :: ConfigData -> b -> a -> FieldGetter a b -> ResultEntry |
... | ... | |
161 | 162 |
runResultT $ do |
162 | 163 |
cfilter <- resultT $ compileFilter nodeFieldsMap qfilter |
163 | 164 |
let selected = getSelectedFields nodeFieldsMap fields |
164 |
(fdefs, fgetters) = unzip selected
|
|
165 |
(fdefs, fgetters, _) = unzip3 selected
|
|
165 | 166 |
live' = live && needsLiveData fgetters |
166 | 167 |
nodes <- resultT $ case wanted of |
167 | 168 |
[] -> Ok . niceSortKey nodeName . |
... | ... | |
182 | 183 |
return $ do |
183 | 184 |
cfilter <- compileFilter groupFieldsMap qfilter |
184 | 185 |
let selected = getSelectedFields groupFieldsMap fields |
185 |
(fdefs, fgetters) = unzip selected
|
|
186 |
(fdefs, fgetters, _) = unzip3 selected
|
|
186 | 187 |
groups <- case wanted of |
187 | 188 |
[] -> Ok . niceSortKey groupName . |
188 | 189 |
Map.elems . fromContainer $ configNodegroups cfg |
... | ... | |
202 | 203 |
let selected = if null fields |
203 | 204 |
then map snd $ Map.toAscList fieldsMap |
204 | 205 |
else getSelectedFields fieldsMap fields |
205 |
in QueryFieldsResult (map fst selected)
|
|
206 |
in QueryFieldsResult (map (\(defs, _, _) -> defs) selected)
|
|
206 | 207 |
|
207 | 208 |
-- | Query fields call. |
208 | 209 |
queryFields :: QueryFields -> ErrorResult QueryFieldsResult |
b/htools/Ganeti/Query/Types.hs | ||
---|---|---|
28 | 28 |
|
29 | 29 |
module Ganeti.Query.Types |
30 | 30 |
( FieldGetter(..) |
31 |
, QffMode(..) |
|
31 | 32 |
, FieldData |
32 | 33 |
, FieldList |
33 | 34 |
, FieldMap |
... | ... | |
50 | 51 |
| FieldConfig (ConfigData -> a -> ResultEntry) |
51 | 52 |
| FieldUnknown |
52 | 53 |
|
54 |
-- | Type defining how the value of a field is used in filtering. This |
|
55 |
-- implements the equivalent to Python's QFF_ flags, except that we |
|
56 |
-- don't use OR-able values. |
|
57 |
data QffMode = QffNormal -- ^ Value is used as-is in filters |
|
58 |
| QffTimestamp -- ^ Value is a timestamp tuple, convert to float |
|
59 |
deriving (Show, Eq) |
|
60 |
|
|
61 |
|
|
53 | 62 |
-- | Alias for a field data (definition and getter). |
54 |
type FieldData a b = (FieldDefinition, FieldGetter a b) |
|
63 |
type FieldData a b = (FieldDefinition, FieldGetter a b, QffMode)
|
|
55 | 64 |
|
56 | 65 |
-- | Alias for a field data list. |
57 | 66 |
type FieldList a b = [FieldData a b] |
Also available in: Unified diff