Add a new data collector collecting for the kvm processes of instances the resident set size. This can serve as an indicator of memory pressure on an over-committed node.
Signed-off-by: Klaus Aehlig <[email protected]> --- Makefile.am | 1 + src/Ganeti/DataCollectors/KvmRSS.hs | 114 ++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/Ganeti/DataCollectors/KvmRSS.hs diff --git a/Makefile.am b/Makefile.am index b5a353d..53eb30e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -909,6 +909,7 @@ HS_LIB_SRCS = \ src/Ganeti/DataCollectors/Drbd.hs \ src/Ganeti/DataCollectors/InstStatus.hs \ src/Ganeti/DataCollectors/InstStatusTypes.hs \ + src/Ganeti/DataCollectors/KvmRSS.hs \ src/Ganeti/DataCollectors/Lv.hs \ src/Ganeti/DataCollectors/Program.hs \ src/Ganeti/DataCollectors/Types.hs \ diff --git a/src/Ganeti/DataCollectors/KvmRSS.hs b/src/Ganeti/DataCollectors/KvmRSS.hs new file mode 100644 index 0000000..486d273 --- /dev/null +++ b/src/Ganeti/DataCollectors/KvmRSS.hs @@ -0,0 +1,114 @@ +{-| kvm resident set size collector + +-} + +{- + +Copyright (C) 2015 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.KvmRSS + ( dcName + , dcVersion + , dcFormatVersion + , dcCategory + , dcKind + , dcReport + ) where + +import Control.Monad (liftM) +import Data.Char (isSpace) +import Data.Maybe (mapMaybe) +import Network.BSD (getHostName) +import System.FilePath ((</>)) +import qualified Text.JSON as J +import Text.Printf (printf) + +import Ganeti.BasicTypes +import Ganeti.Confd.ClientFunctions (getInstances) +import qualified Ganeti.Constants as C +import Ganeti.DataCollectors.Types +import Ganeti.Objects +import Ganeti.Path (kvmPidDir) + +-- | The name of this data collector. +dcName :: String +dcName = C.dataCollectorKvmRSS + +-- | The version number for the data format of this data collector. +dcFormatVersion :: Int +dcFormatVersion = 1 + +-- | The version of this data collector. +dcVersion :: DCVersion +dcVersion = DCVerBuiltin + +-- | The category of this data collector. +dcCategory :: Maybe DCCategory +dcCategory = Nothing + +-- | The kind of this data collector. +dcKind :: DCKind +dcKind = DCKPerf + +-- | Parse the contents of a pid file. +parsePid :: Monad m => String -> m Int +parsePid s = case reads s of + [(pid, r)] | all isSpace r -> return pid + _ -> fail $ "Couldn't parse pid " ++ s + +-- | From the contents of a memstat file get the resident set size, +-- in pages. +parseRss :: Monad m => String -> m Int +parseRss s = + let drop1 = dropWhile isSpace . dropWhile (not . isSpace) . dropWhile isSpace + in case reads (drop1 s) of + [(n, _)] -> return n + _ -> fail $ "Failed to parse memstat " ++ s + +-- | For an instance, collect the resident set size, if available. +collectInstanceRSS :: String -> IO (Result (String, J.JSValue)) +collectInstanceRSS inst = runResultT $ do + piddir <- liftIO kvmPidDir + let pidfile = piddir </> inst + pidstring <- liftIO $ readFile pidfile + pid <- parsePid pidstring + let procfspath = printf "/proc/%d/statm" pid + memstat <- liftIO $ readFile procfspath + rss <- parseRss memstat + return (inst, J.showJSON rss) + +-- | The data exported by the data collector. +dcReport :: IO DCReport +dcReport = do + node <- getHostName + instances <- liftM (genericResult (const []) (mapMaybe instName . fst)) + . runResultT $ getInstances node Nothing Nothing + reports <- liftM justOk $ mapM collectInstanceRSS instances + buildReport dcName dcVersion dcFormatVersion dcCategory dcKind + . J.JSObject $ J.toJSObject reports -- 2.6.0.rc2.230.g3dd15c0
