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 | 60 ++++++++++++++++++++++++++++++------ src/Ganeti/HTools/Program/Hbal.hs | 1 + src/Ganeti/HTools/Program/Hinfo.hs | 1 + 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/Ganeti/HTools/CLI.hs b/src/Ganeti/HTools/CLI.hs index b01df61..560c73d 100644 --- a/src/Ganeti/HTools/CLI.hs +++ b/src/Ganeti/HTools/CLI.hs @@ -49,6 +49,7 @@ module Ganeti.HTools.CLI , oSpindleUse , oDynuFile , oMonD + , oMonDDataFile , oEvacMode , oExInst , oExTags @@ -125,6 +126,8 @@ data Options = Options , optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data , optIgnoreDynu :: Bool -- ^ Do not use dynamic use data , optMonD :: Bool -- ^ Query MonDs + , optMonDFile :: 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 @@ -181,6 +184,7 @@ defaultOptions = Options , optIgnoreDynu = False , optDynuFile = Nothing , optMonD = False + , optMonDFile = Nothing , optEvacMode = False , optExInst = [] , optExTags = Nothing @@ -290,6 +294,13 @@ oMonD = "Query MonDs", OptComplNone) +oMonDDataFile :: OptType +oMonDDataFile = + (Option "" ["mond-data"] + (ReqArg (\ f opts -> Ok opts { optMonDFile = 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 4030536..972dfec 100644 --- a/src/Ganeti/HTools/ExtLoader.hs +++ b/src/Ganeti/HTools/ExtLoader.hs @@ -46,6 +46,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 @@ -167,18 +169,46 @@ collectors opts = then [] else [ 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 optMonDFile 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 opts) + (nl', il') <- foldM (queryAllMonDs map_mDD) (nl, il) (collectors opts) 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 @@ -218,10 +248,20 @@ 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 = + let matchDCName dcr = dName dc == dcReportName dcr + in maybe Nothing (L.find matchDCName) $ Map.lookup (Node.name node) m + -- | 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/Hbal.hs b/src/Ganeti/HTools/Program/Hbal.hs index 776b10f..3fdc30a 100644 --- a/src/Ganeti/HTools/Program/Hbal.hs +++ b/src/Ganeti/HTools/Program/Hbal.hs @@ -93,6 +93,7 @@ options = do , oDynuFile , oIgnoreDyn , oMonD + , oMonDDataFile , oExTags , oExInst , oSaveCluster diff --git a/src/Ganeti/HTools/Program/Hinfo.hs b/src/Ganeti/HTools/Program/Hinfo.hs index 1b45225..08db777 100644 --- a/src/Ganeti/HTools/Program/Hinfo.hs +++ b/src/Ganeti/HTools/Program/Hinfo.hs @@ -63,6 +63,7 @@ options = do , oOfflineNode , oIgnoreDyn , oMonD + , oMonDDataFile ] -- | The list of arguments supported by the program. -- 1.7.10.4
