Implement functionality to import MonD data from a mock file to HTools (mainly for testing purposes) with the --mond-data option.
Signed-off-by: Spyros Trigazis <[email protected]> --- src/Ganeti/HTools/CLI.hs | 11 +++++++ src/Ganeti/HTools/ExtLoader.hs | 65 ++++++++++++++++++++++++++++++------- src/Ganeti/HTools/Program/Hail.hs | 2 +- src/Ganeti/HTools/Program/Hbal.hs | 1 + 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/Ganeti/HTools/CLI.hs b/src/Ganeti/HTools/CLI.hs index 12c3914..f4ca9fe 100644 --- a/src/Ganeti/HTools/CLI.hs +++ b/src/Ganeti/HTools/CLI.hs @@ -48,6 +48,7 @@ module Ganeti.HTools.CLI , oDiskTemplate , oSpindleUse , oDynuFile + , oMonDDataFile , oEvacMode , oExInst , oExTags @@ -123,6 +124,8 @@ data Options = Options , optSpindleUse :: Maybe Int -- ^ Override for the spindle usage , optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data , optIgnoreDynu :: Bool -- ^ Do not use dynamic use data + , optMonDDataFile :: Maybe FilePath -- ^ Optional file with data provided + -- ^ by MonDs , optEvacMode :: Bool -- ^ Enable evacuation mode , optExInst :: [String] -- ^ Instances to be excluded , optExTags :: Maybe [String] -- ^ Tags to use for exclusion @@ -178,6 +181,7 @@ defaultOptions = Options , optSpindleUse = Nothing , optIgnoreDynu = False , optDynuFile = Nothing + , optMonDDataFile = Nothing , optEvacMode = False , optExInst = [] , optExTags = Nothing @@ -280,6 +284,13 @@ oDiskMoves = \ thus allowing only the 'cheap' failover/migrate operations", OptComplNone) +oMonDDataFile :: OptType +oMonDDataFile = + (Option "" ["mond-data"] + (ReqArg (\ f opts -> Ok opts { optMonDDataFile = Just f }) "FILE") + "Import data provided by MonDs from the given FILE", + OptComplFile) + oDiskTemplate :: OptType oDiskTemplate = (Option "" ["disk-template"] diff --git a/src/Ganeti/HTools/ExtLoader.hs b/src/Ganeti/HTools/ExtLoader.hs index d42c833..9086e91 100644 --- a/src/Ganeti/HTools/ExtLoader.hs +++ b/src/Ganeti/HTools/ExtLoader.hs @@ -47,6 +47,8 @@ import System.Time (getClockTime) import Text.Printf (hPrintf) import qualified Text.JSON as J +import qualified Data.Map as Map +import qualified Data.List as L import qualified Ganeti.Constants as C import qualified Ganeti.DataCollectors.CPUload as CPUload @@ -131,7 +133,7 @@ loadExternalData opts = do ldresult = input_data >>= (if ignoreDynU then clearDynU else return) >>= mergeData eff_u exTags selInsts exInsts now cdata <- exitIfBad "failed to load data, aborting" ldresult - cdata' <- if ignoreDynU then return cdata else queryAllMonDDCs cdata + cdata' <- if ignoreDynU then return cdata else queryAllMonDDCs cdata opts let (fix_msgs, nl) = checkData (cdNodes cdata') (cdInstances cdata') unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs @@ -166,18 +168,46 @@ collectors :: [DataCollector] collectors = [ DataCollector CPUload.dcName CPUload.dcCategory ] +-- | MonDs Data parsed by a mock file. Representing (node name, list of reports +-- produced by MonDs Data Collectors). +type MonDData = (String, [DCReport]) + +-- | A map storing MonDs data. +type MapMonDData = Map.Map String [DCReport] + +-- | Parse MonD data file contents. +pMonDData :: String -> Result [MonDData] +pMonDData input = + loadJSArray "Parsing MonD's answer" input >>= + mapM (pMonDN . J.fromJSObject) + +-- | Parse a node's JSON record. +pMonDN :: JSRecord -> Result MonDData +pMonDN a = do + node <- tryFromObj "Parsing node's name" a "node" + reports <- tryFromObj "Parsing node's reports" a "reports" + return (node, reports) + -- | Query all MonDs for all Data Collector. -queryAllMonDDCs :: ClusterData -> IO ClusterData -queryAllMonDDCs cdata = do +queryAllMonDDCs :: ClusterData -> Options -> IO ClusterData +queryAllMonDDCs cdata opts = do + map_mDD <- + case optMonDDataFile opts of + Nothing -> return Nothing + Just fp -> do + monDData_contents <- readFile fp + monDData <- exitIfBad "can't parse MonD data" + . pMonDData $ monDData_contents + return . Just $ Map.fromList monDData let (ClusterData _ nl il _ _) = cdata - (nl', il') <- foldM queryAllMonDs (nl, il) collectors + (nl', il') <- foldM (queryAllMonDs map_mDD) (nl, il) collectors return $ cdata {cdNodes = nl', cdInstances = il'} -- | Query all MonDs for a single Data Collector. -queryAllMonDs :: (Node.List, Instance.List) -> DataCollector - -> IO (Node.List, Instance.List) -queryAllMonDs (nl, il) dc = do - elems <- mapM (queryAMonD dc) (Container.elems nl) +queryAllMonDs :: Maybe MapMonDData -> (Node.List, Instance.List) + -> DataCollector -> IO (Node.List, Instance.List) +queryAllMonDs m (nl, il) dc = do + elems <- mapM (queryAMonD m dc) (Container.elems nl) let elems' = catMaybes elems if length elems == length elems' then do @@ -214,10 +244,23 @@ mkReport dc dcr = Bad _ -> Nothing | otherwise -> Nothing +-- | Get data report for the specified Data Collector and Node from the map. +fromFile :: DataCollector -> Node.Node -> MapMonDData -> Maybe DCReport +fromFile dc node m = + case Map.lookup (Node.name node) m of + Nothing -> Nothing + Just reports -> + let matchDCName dcr = dName dc == dcReportName dcr + in L.find matchDCName reports + -- | Query a MonD for a single Data Collector. -queryAMonD :: DataCollector -> Node.Node -> IO (Maybe Node.Node) -queryAMonD dc node = do - dcReport <- fromCurl dc node +queryAMonD :: Maybe MapMonDData -> DataCollector -> Node.Node + -> IO (Maybe Node.Node) +queryAMonD m dc node = do + dcReport <- + case m of + Nothing -> fromCurl dc node + Just m' -> return $ fromFile dc node m' case mkReport dc dcReport of Nothing -> return Nothing Just report -> diff --git a/src/Ganeti/HTools/Program/Hail.hs b/src/Ganeti/HTools/Program/Hail.hs index 507192c..b1e3653 100644 --- a/src/Ganeti/HTools/Program/Hail.hs +++ b/src/Ganeti/HTools/Program/Hail.hs @@ -74,7 +74,7 @@ wrapReadRequest opts args = do else do let Request rqt cdata = r1 cdata' <- - if optIgnoreDynu opts then return cdata else queryAllMonDDCs cdata + if optIgnoreDynu opts then return cdata else queryAllMonDDCs cdata opts return $ Request rqt cdata' -- | Main function. diff --git a/src/Ganeti/HTools/Program/Hbal.hs b/src/Ganeti/HTools/Program/Hbal.hs index f863ad1..c385642 100644 --- a/src/Ganeti/HTools/Program/Hbal.hs +++ b/src/Ganeti/HTools/Program/Hbal.hs @@ -92,6 +92,7 @@ options = do , oInstMoves , oDynuFile , oIgnoreDyn + , oMonDDataFile , oExTags , oExInst , oSaveCluster -- 1.7.10.4
