Abstract the individual query functions
authorIustin Pop <iustin@google.com>
Mon, 25 Feb 2013 12:59:01 +0000 (13:59 +0100)
committerIustin Pop <iustin@google.com>
Mon, 4 Mar 2013 16:09:09 +0000 (17:09 +0100)
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: Iustin Pop <iustin@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>

src/Ganeti/Query/Group.hs
src/Ganeti/Query/Network.hs
src/Ganeti/Query/Query.hs

index 53ba1fd..211d66f 100644 (file)
@@ -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))
index 52c6f8b..4acfdc7 100644 (file)
@@ -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))
index a6a5231..a3ea143 100644 (file)
@@ -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"