and add it to the data collector list. Add the new file to HS_LIB_SRCS in Makefile.am
Signed-off-by: BSRK Aditya <[email protected]> --- Makefile.am | 1 + src/Ganeti/DataCollectors.hs | 5 ++ src/Ganeti/DataCollectors/Diagnose.hs | 147 +++++++++++++++++++++++++++++++++ src/Ganeti/DataCollectors/Types.hs | 2 +- src/Ganeti/Monitoring/Server.hs | 1 + 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/Ganeti/DataCollectors/Diagnose.hs diff --git a/Makefile.am b/Makefile.am index a39e15c..920a4da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -899,6 +899,7 @@ HS_LIB_SRCS = \ src/Ganeti/DataCollectors.hs \ src/Ganeti/DataCollectors/CLI.hs \ src/Ganeti/DataCollectors/CPUload.hs \ + src/Ganeti/DataCollectors/Diagnose.hs \ src/Ganeti/DataCollectors/Diskstats.hs \ src/Ganeti/DataCollectors/Drbd.hs \ src/Ganeti/DataCollectors/InstStatus.hs \ diff --git a/src/Ganeti/DataCollectors.hs b/src/Ganeti/DataCollectors.hs index bca6848..9c2cc3b 100644 --- a/src/Ganeti/DataCollectors.hs +++ b/src/Ganeti/DataCollectors.hs @@ -38,6 +38,7 @@ import Data.Map (findWithDefault) import Data.Monoid (mempty) import qualified Ganeti.DataCollectors.CPUload as CPUload +import qualified Ganeti.DataCollectors.Diagnose as Diagnose import qualified Ganeti.DataCollectors.Diskstats as Diskstats import qualified Ganeti.DataCollectors.Drbd as Drbd import qualified Ganeti.DataCollectors.InstStatus as InstStatus @@ -57,6 +58,7 @@ collectors = , drdbCollector , instStatusCollector , lvCollector + , diagnoseCollector ] where f .&&. g = \x y -> f x y && g x y @@ -82,6 +84,9 @@ collectors = lvCollector = DataCollector Lv.dcName Lv.dcCategory Lv.dcKind (StatelessR Lv.dcReport) Nothing activeConfig updateInterval + diagnoseCollector = + DataCollector Diagnose.dcName Diagnose.dcCategory Diagnose.dcKind + (StatelessR Diagnose.dcReport) Nothing activeConfig updateInterval cpuLoadCollector = DataCollector CPUload.dcName CPUload.dcCategory CPUload.dcKind (StatefulR CPUload.dcReport) (Just CPUload.dcUpdate) activeConfig diff --git a/src/Ganeti/DataCollectors/Diagnose.hs b/src/Ganeti/DataCollectors/Diagnose.hs new file mode 100644 index 0000000..7491978 --- /dev/null +++ b/src/Ganeti/DataCollectors/Diagnose.hs @@ -0,0 +1,147 @@ +{-| Self-diagnose data collector + +-} + +{- + +Copyright (C) 2013 Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-} + +module Ganeti.DataCollectors.Diagnose + ( dcName + , dcCategory + , dcKind + , dcReport + ) where + +import Control.Monad.Trans.Class (lift) +import System.Directory (doesFileExist) +import System.FilePath.Posix (isValid, takeFileName, (</>)) +import System.Posix.Files (getFileStatus, fileOwner, fileGroup, + fileMode, ownerModes, groupReadMode, + groupExecuteMode, otherReadMode, + otherExecuteMode, intersectFileModes, + unionFileModes, ownerExecuteMode, + isRegularFile, regularFileMode) +import System.Process (readProcess) +import Text.JSON (JSValue(..), toJSObject, toJSString, decode, Result(..)) + +import Ganeti.BasicTypes (runResultT, ResultT(..), genericResult) +import Ganeti.Config (loadConfig) +import Ganeti.Constants (dataCollectorDiagnose, dataCollectorDiagnoseDirectory) +import Ganeti.DataCollectors.Types (DCCategory(..), DCKind(..), DCVersion(..), + DCReport(..), buildReport) +import Ganeti.Objects (configCluster, clusterDiagnoseDataCollectorFilename) +import Ganeti.Path (clusterConfFile) + +-- | The name of this data collector. +dcName :: String +dcName = dataCollectorDiagnose + +-- | The category of this data collector. +dcCategory :: Maybe DCCategory +dcCategory = Just DCNode + +-- | The kind of this data collector. +dcKind :: DCKind +dcKind = DCKStatus + +-- | The version of this data collector. +dcVersion :: DCVersion +dcVersion = DCVerBuiltin + +-- | The version number for the data format of this data collector. +dcFormatVersion :: Int +dcFormatVersion = 1 + +okWithDetails :: String -> JSValue +okWithDetails details = JSObject $ toJSObject + [ ("status", JSString $ toJSString "Ok") + , ("details", JSString $ toJSString details) + ] + + +fnToVal :: String -> IO JSValue +fnToVal fn + | null fn = return $ okWithDetails + "No file specified for diagnose data collector" + | not $ isValid fn = return $ okWithDetails + "Invalid filename specified for diagnose data collector" + | takeFileName fn /= fn = return $ okWithDetails + "Filepaths cannot be specified for diagnose data collector" + | otherwise = do + let fp = dataCollectorDiagnoseDirectory </> fn + exists <- doesFileExist fp + if exists + then do + fs <- getFileStatus fp + let maxFileMode = foldl1 unionFileModes [ ownerModes + , groupReadMode + , groupExecuteMode + , otherReadMode + , otherExecuteMode + , regularFileMode + ] + isSubSetOf m1 m2 = m1 `intersectFileModes` m2 == m1 + case () of _ + | fileOwner fs /= 0 -> return . okWithDetails $ + "File for diagnose data collector " ++ + "must be owned by root" + | fileGroup fs /= 0 -> return . okWithDetails $ + "File for diagnose data collector " ++ + "must have group root" + | not $ isRegularFile fs -> return . okWithDetails $ + "File for diagnose data collector " ++ + "must be a regular file" + | not $ isSubSetOf (fileMode fs) maxFileMode -> + return . okWithDetails $ + "File for diagnose data collector " ++ + "must have permissions 755 or stricter" + | not $ isSubSetOf ownerExecuteMode (fileMode fs) -> + return . okWithDetails $ + "File for diagnose data collector " ++ + "must be executable by owner" + | otherwise -> do + r <- fmap decode (readProcess fp [] "") + case r of + Ok val -> return val + Error str -> return . okWithDetails $ + "Could not parse result: " ++ str + else return $ okWithDetails + "File specified for diagnose data collector does not exist" + +buildJsonReport :: IO JSValue +buildJsonReport = fmap (genericResult okWithDetails id) . runResultT $ do + configData <- ResultT (clusterConfFile >>= loadConfig) + lift . fnToVal . clusterDiagnoseDataCollectorFilename $ + configCluster configData + +-- | The data exported by the data collector, taken from the default location. +dcReport :: IO DCReport +dcReport = buildJsonReport >>= + buildReport dcName dcVersion dcFormatVersion dcCategory dcKind diff --git a/src/Ganeti/DataCollectors/Types.hs b/src/Ganeti/DataCollectors/Types.hs index 8b60be1..3bd31b3 100644 --- a/src/Ganeti/DataCollectors/Types.hs +++ b/src/Ganeti/DataCollectors/Types.hs @@ -68,7 +68,7 @@ import Ganeti.THH import Ganeti.Utils (getCurrentTimeUSec) -- | The possible classes a data collector can belong to. -data DCCategory = DCInstance | DCStorage | DCDaemon | DCHypervisor +data DCCategory = DCInstance | DCStorage | DCDaemon | DCHypervisor | DCNode deriving (Show, Eq, Read, Enum, Bounded) -- | Get the category name and return it as a string. diff --git a/src/Ganeti/Monitoring/Server.hs b/src/Ganeti/Monitoring/Server.hs index 333d0d1..530fd5b 100644 --- a/src/Ganeti/Monitoring/Server.hs +++ b/src/Ganeti/Monitoring/Server.hs @@ -190,6 +190,7 @@ catFromName "instance" = BT.Ok $ Just DCInstance catFromName "storage" = BT.Ok $ Just DCStorage catFromName "daemon" = BT.Ok $ Just DCDaemon catFromName "hypervisor" = BT.Ok $ Just DCHypervisor +catFromName "node" = BT.Ok $ Just DCNode catFromName "default" = BT.Ok Nothing catFromName _ = BT.Bad "No such category" -- 1.7.10.4
