-- This file is part of nagios-perfdata. -- -- Copyright 2014 Anchor Systems Pty Ltd and others. -- -- The code in this file, and the program it is a part of, is made -- available to you by its authors as open source software: you can -- redistribute it and/or modify it under the terms of the BSD license. {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Data.Nagios.Perfdata.Metric( Perfdata(..), MetricList, Metric(..), MetricValue(..), HostOrService(..), ServicePerfdata(..), uomFromString, parseReturnCode, parseReturnState, parseMetricString, UOM(..), ReturnState(..), Threshold(..), perfdataServiceDescription, metricValueDefault, unknownMetricValue, isMetricBase, convertMetricToBase, convertPerfdataToBase ) where import Data.Nagios.Perfdata.Error import Control.Applicative import Control.Monad import Data.Attoparsec.ByteString.Char8 import Data.Bifunctor (second) import qualified Data.ByteString as S import Data.Int import Prelude hiding (takeWhile) -- |Value of a performance metric. We may lose some data converting -- to doubles here; this may change in the future. data MetricValue = DoubleValue Double | UnknownValue deriving (Show, Eq) -- |Value of a min/max/warn/crit threshold, subject to the same -- constraints as MetricValue. data Threshold = DoubleThreshold Double | NoThreshold deriving (Show) -- |Encapsulates the data in a Nagios performance metric. A service can -- have several of these. data Metric = Metric { metricValue :: MetricValue, metricUOM :: UOM, warnValue :: Threshold, critValue :: Threshold, minValue :: Threshold, maxValue :: Threshold } deriving (Show) metricValueDefault :: Metric -> Double -> Double metricValueDefault Metric{..} d = case metricValue of UnknownValue -> d DoubleValue x -> x unknownMetricValue :: Metric -> Bool unknownMetricValue m = case metricValue m of UnknownValue -> True _ -> False -- |List of metrics by metric name. type MetricList = [(String, Metric)] -- |Nagios unit of measurement. NullUnit is an empty string in the -- check result; UnknownUOM indicates a failure to parse. data UOM = Second | Millisecond | Microsecond | Percent | Byte | Kilobyte | Megabyte | Gigabyte | Terabyte | Counter | NullUnit | UnknownUOM deriving (Eq) instance Show UOM where show Second = "s" show Percent = "%" show Byte = "B" show Counter = "c" show NullUnit = "" show UnknownUOM = "?" show uom = show (uomToPrefix uom) ++ show (uomToBase uom) uomFromString :: String -> UOM uomFromString "s" = Second uomFromString "ms" = Millisecond uomFromString "us" = Microsecond uomFromString "%" = Percent uomFromString "B" = Byte uomFromString "KB" = Kilobyte uomFromString "MB" = Megabyte uomFromString "GB" = Gigabyte uomFromString "TB" = Terabyte uomFromString "c" = Counter uomFromString "" = NullUnit uomFromString _ = UnknownUOM data Prefix = Base | Milli | Micro | Kilo | Mega | Giga | Tera deriving (Eq) instance Show Prefix where show Base = "" show Milli = "m" show Micro = "u" show Kilo = "K" show Mega = "M" show Giga = "G" show Tera = "T" uomToPrefix :: UOM -> Prefix uomToPrefix Second = Base uomToPrefix Millisecond = Milli uomToPrefix Microsecond = Micro uomToPrefix Percent = Base uomToPrefix Byte = Base uomToPrefix Kilobyte = Kilo uomToPrefix Megabyte = Mega uomToPrefix Gigabyte = Giga uomToPrefix Terabyte = Tera uomToPrefix Counter = Base uomToPrefix NullUnit = Base uomToPrefix UnknownUOM = Base uomToBase :: UOM -> UOM uomToBase Second = Second uomToBase Millisecond = Second uomToBase Microsecond = Second uomToBase Percent = Percent uomToBase Byte = Byte uomToBase Kilobyte = Byte uomToBase Megabyte = Byte uomToBase Gigabyte = Byte uomToBase Terabyte = Byte uomToBase Counter = Counter uomToBase NullUnit = NullUnit uomToBase UnknownUOM = UnknownUOM prefixToScale :: Prefix -> Double prefixToScale Base = 1 prefixToScale Milli = 0.001 prefixToScale Micro = 0.000001 prefixToScale Kilo = 1000 prefixToScale Mega = 1000000 prefixToScale Giga = 1000000000 prefixToScale Tera = 1000000000000 uomToScale :: UOM -> Double uomToScale = prefixToScale . uomToPrefix convertUnitToBase :: MetricValue -> UOM -> (MetricValue, UOM) convertUnitToBase UnknownValue uom = (UnknownValue, uom) convertUnitToBase (DoubleValue v) uom = (DoubleValue $ uomToScale uom * v, uomToBase uom) convertMetricToBase :: Metric -> Metric convertMetricToBase m@Metric{..} = m{metricValue = v, metricUOM = uom} where (v, uom) = convertUnitToBase metricValue metricUOM isMetricBase :: Metric -> Bool isMetricBase Metric{..} = metricUOM == uomToBase metricUOM convertPerfdataToBase :: Perfdata -> Perfdata convertPerfdataToBase p@Perfdata{..} = p{perfdataMetrics = map (second convertMetricToBase ) perfdataMetrics} -- |The part of the check result that's specific to service checks, -- and doesn't appear in host checks. data ServicePerfdata = ServicePerfdata { serviceDescription :: S.ByteString, serviceState :: ReturnState } deriving (Show) -- |The check type, either Service with associated ServiceData or Host. data HostOrService = Service ServicePerfdata | Host deriving (Show) data ReturnState = OKState | WarningState | CriticalState | UnknownState deriving (Show,Enum) parseReturnCode :: Integral a => a -> Maybe ReturnState parseReturnCode 0 = Just OKState parseReturnCode 1 = Just WarningState parseReturnCode 2 = Just CriticalState parseReturnCode 3 = Just UnknownState parseReturnCode _ = Nothing parseReturnState :: S.ByteString -> Maybe ReturnState parseReturnState "OK" = Just OKState parseReturnState "WARNING" = Just WarningState parseReturnState "CRITICAL" = Just CriticalState parseReturnState "UNKNOWN" = Just UnknownState parseReturnState _ = Nothing -- |Encapsulates all the data in a check result that's relevant to -- metrics (we throw away things like the state type of HARD/SOFT). data Perfdata = Perfdata { perfdataType :: HostOrService, perfdataTimestamp :: Int64, perfdataHostname :: String, perfdataHostState :: Maybe S.ByteString, perfdataMetrics :: MetricList } deriving (Show) perfdataServiceDescription :: Perfdata -> S.ByteString perfdataServiceDescription datum = case perfdataType datum of Host -> "host" Service serviceData -> serviceDescription serviceData uomParser :: Parser UOM uomParser = liftM uomFromString . option "" $ many (satisfy uomChar) where uomChar = inClass "A-Za-z%" metricName :: Parser String metricName = option quote (char quote) *> many (satisfy nameChar) <* option quote (char quote) where quote = '\'' nameChar '\'' = False nameChar '=' = False nameChar _ = True value :: Parser MetricValue value = option UnknownValue $ liftM DoubleValue double threshold :: Parser Threshold threshold = char8 ';' *> thresholdValue <|> thresholdValue where thresholdValue = option NoThreshold (liftM DoubleThreshold double) metric :: Parser (String, Metric) metric = do name <- metricName void $ char8 '=' m <- Metric `fmap` value <*> uomParser <*> threshold <*> threshold <*> threshold <*> threshold return (name, m) metricLine :: Parser MetricList metricLine = many (metric <* (skipMany (char8 ';') <* skipSpace)) -- |Parse the component of the check output which contains the -- performance metrics (HOSTPERFDATA or SERVICEPERFDATA). parseMetricString :: S.ByteString -> Either ParserError MetricList parseMetricString = completeParse . parse metricLine where completeParse r = case r of Done _ m -> Right m Fail _ ctxs err -> Left $ fmtParseError ctxs err Partial parseRest -> completeParse (parseRest "")