Commit d5b2753a authored by Iustin Pop's avatar Iustin Pop
Browse files

Abstract the individual query functions



After implementing a few of the query executor functions, it turns out
that we have the same general pattern:

- compile the filter
- extract the selected fields
- determine whether we need to run collectors
- do a first pass filtering
- run the collector if needed
- compute the final fields

For pure config queries, the collector/final computation is not
needed, but otherwise the code flow is the same.

We can therefore abstract all the queries that originate in the config
(i.e. except the job query) and have a single code path for all of
them, just with different parameters.

To do this, we add some stub collectors for group/network queries,
which don't have live data.
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarGuido Trotter <ultrotter@google.com>
parent dce08ad3
......@@ -24,8 +24,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-}
module Ganeti.Query.Group
( Runtime(..)
( Runtime
, fieldsMap
, collectLiveData
) where
import qualified Data.Map as Map
......@@ -89,3 +90,8 @@ groupFields =
fieldsMap :: FieldMap NodeGroup Runtime
fieldsMap =
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) groupFields
-- | Dummy function for collecting live data (which groups don't have).
collectLiveData :: Bool -> ConfigData -> [NodeGroup]
-> IO [(NodeGroup, Runtime)]
collectLiveData _ _ = return . map (\n -> (n, Runtime))
......@@ -27,8 +27,9 @@ module Ganeti.Query.Network
( getGroupConnection
, getNetworkUuid
, instIsConnected
, Runtime(..)
, Runtime
, fieldsMap
, collectLiveData
) where
-- FIXME: everything except Runtime(..) and fieldsMap
......@@ -145,3 +146,7 @@ getNetworkUuid cfg name =
let net = find (\n -> name == fromNonEmpty (networkName n))
((Map.elems . fromContainer . configNetworks) cfg)
in fmap networkUuid net
-- | Dummy function for collecting live data (which networks don't have).
collectLiveData :: Bool -> ConfigData -> [Network] -> IO [(Network, Runtime)]
collectLiveData _ _ = return . map (\n -> (n, Runtime))
......@@ -150,6 +150,42 @@ getRequestedJobIDs qfilter =
NumericValue i -> makeJobId $ fromIntegral i
) vals
-- | Generic query implementation for resources that are backed by
-- some configuration objects.
genericQuery :: FieldMap a b -- ^ Field map
-> (Bool -> ConfigData -> [a] -> IO [(a, b)]) -- ^ Collector
-> (a -> String) -- ^ Object to name function
-> (ConfigData -> Container a) -- ^ Get all objects from config
-> (ConfigData -> String -> ErrorResult a) -- ^ Lookup object
-> ConfigData -- ^ The config to run the query against
-> Bool -- ^ Whether the query should be run live
-> [String] -- ^ List of requested fields
-> Filter FilterField -- ^ Filter field
-> [String] -- ^ List of requested names
-> IO (ErrorResult QueryResult)
genericQuery fieldsMap collector nameFn configFn getFn cfg
live fields qfilter wanted =
runResultT $ do
cfilter <- resultT $ compileFilter fieldsMap qfilter
let selected = getSelectedFields fieldsMap fields
(fdefs, fgetters, _) = unzip3 selected
live' = live && needsLiveData fgetters
objects <- resultT $ case wanted of
[] -> Ok . niceSortKey nameFn .
Map.elems . fromContainer $ configFn cfg
_ -> mapM (getFn cfg) wanted
-- runs first pass of the filter, without a runtime context; this
-- will limit the objects that we'll contact for exports
fobjects <- resultT $ filterM (\n -> evaluateFilter cfg Nothing n cfilter)
objects
-- here run the runtime data gathering...
runtimes <- lift $ collector live' cfg fobjects
-- ... then filter again the results, based on gathered runtime data
let fdata = map (\(obj, runtime) ->
map (execGetter cfg runtime obj) fgetters)
runtimes
return QueryResult { qresFields = fdefs, qresData = fdata }
-- | Main query execution function.
query :: ConfigData -- ^ The current configuration
-> Bool -- ^ Whether to collect live data
......@@ -167,80 +203,21 @@ queryInner :: ConfigData -- ^ The current configuration
-> IO (ErrorResult QueryResult) -- ^ Result
queryInner cfg live (Query (ItemTypeOpCode QRNode) fields qfilter) wanted =
runResultT $ do
cfilter <- resultT $ compileFilter Node.fieldsMap qfilter
let selected = getSelectedFields Node.fieldsMap fields
(fdefs, fgetters, _) = unzip3 selected
live' = live && needsLiveData fgetters
nodes <- resultT $ case wanted of
[] -> Ok . niceSortKey nodeName .
Map.elems . fromContainer $ configNodes cfg
_ -> mapM (getNode cfg) wanted
-- runs first pass of the filter, without a runtime context; this
-- will limit the nodes that we'll contact for runtime data
fnodes <- resultT $ filterM (\n -> evaluateFilter cfg Nothing n cfilter)
nodes
-- here we would run the runtime data gathering, then filter again
-- the nodes, based on existing runtime data
nruntimes <- lift $ Node.collectLiveData live' cfg fnodes
let fdata = map (\(node, nrt) -> map (execGetter cfg nrt node) fgetters)
nruntimes
return QueryResult { qresFields = fdefs, qresData = fdata }
genericQuery Node.fieldsMap Node.collectLiveData nodeName configNodes getNode
cfg live fields qfilter wanted
queryInner cfg _ (Query (ItemTypeOpCode QRGroup) fields qfilter) wanted =
return $ do
cfilter <- compileFilter Group.fieldsMap qfilter
let selected = getSelectedFields Group.fieldsMap fields
(fdefs, fgetters, _) = unzip3 selected
groups <- case wanted of
[] -> Ok . niceSortKey groupName .
Map.elems . fromContainer $ configNodegroups cfg
_ -> mapM (getGroup cfg) wanted
-- there is no live data for groups, so filtering is much simpler
fgroups <- filterM (\n -> evaluateFilter cfg Nothing n cfilter) groups
let fdata = map (\node ->
map (execGetter cfg Group.Runtime node) fgetters) fgroups
return QueryResult { qresFields = fdefs, qresData = fdata }
queryInner cfg live (Query (ItemTypeOpCode QRGroup) fields qfilter) wanted =
genericQuery Group.fieldsMap Group.collectLiveData groupName configNodegroups
getGroup cfg live fields qfilter wanted
queryInner cfg _ (Query (ItemTypeOpCode QRNetwork) fields qfilter) wanted =
return $ do
cfilter <- compileFilter Network.fieldsMap qfilter
let selected = getSelectedFields Network.fieldsMap fields
(fdefs, fgetters, _) = unzip3 selected
networks <- case wanted of
[] -> Ok . niceSortKey (fromNonEmpty . networkName) .
Map.elems . fromContainer $ configNetworks cfg
_ -> mapM (getNetwork cfg) wanted
fnetworks <- filterM (\n -> evaluateFilter cfg Nothing n cfilter) networks
let fdata = map (\network ->
map (execGetter cfg Network.Runtime network) fgetters)
fnetworks
return QueryResult { qresFields = fdefs, qresData = fdata }
queryInner cfg live (Query (ItemTypeOpCode QRNetwork) fields qfilter) wanted =
genericQuery Network.fieldsMap Network.collectLiveData
(fromNonEmpty . networkName)
configNetworks getNetwork cfg live fields qfilter wanted
queryInner cfg live (Query (ItemTypeOpCode QRExport) fields qfilter) wanted =
runResultT $ do
cfilter <- resultT $ compileFilter Export.fieldsMap qfilter
let selected = getSelectedFields Export.fieldsMap fields
(fdefs, fgetters, _) = unzip3 selected
-- we alwyas have live queries in exports, but we keep this for
-- standard style (in case we add static fields in the future)
live' = live && needsLiveData fgetters
nodes <- resultT $ case wanted of
[] -> Ok . niceSortKey nodeName .
Map.elems . fromContainer $ configNodes cfg
_ -> mapM (getNode cfg) wanted
-- runs first pass of the filter, without a runtime context; this
-- will limit the nodes that we'll contact for exports
fnodes <- resultT $ filterM (\n -> evaluateFilter cfg Nothing n cfilter)
nodes
-- here we would run the runtime data gathering...
nruntimes <- lift $ Export.collectLiveData live' cfg fnodes
-- ... then filter again the results, based on existing export
-- names, but note that no client sends filters on the export list
-- today, so it's likely a no-oop
let fdata = map (\(node, nrt) -> map (execGetter cfg nrt node) fgetters)
nruntimes
return QueryResult { qresFields = fdefs, qresData = fdata }
genericQuery Export.fieldsMap Export.collectLiveData nodeName configNodes
getNode cfg live fields qfilter wanted
queryInner _ _ (Query qkind _ _) _ =
return . Bad . GenericError $ "Query '" ++ show qkind ++ "' not supported"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment