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