module Data.Nagios.Perfdata.Template(
perfdataFromDefaultTemplate,
) where
import Data.Nagios.Perfdata.Error
import Data.Nagios.Perfdata.Metric
import Control.Applicative
import Data.Attoparsec.ByteString.Char8
import qualified Data.ByteString as S
import Data.ByteString.Char8 (readInteger)
import qualified Data.ByteString.Char8 as C
import Data.Int
import qualified Data.Map as M
import Data.Word
import Prelude hiding (takeWhile)
data Item = Item {
label :: S.ByteString,
content :: S.ByteString
} deriving (Show)
separator :: Parser [Word8]
separator = count 2 (char8 ':') <?> "separator"
ident :: Parser S.ByteString
ident = takeWhile uppercase <?> "item identifier"
where
uppercase = inClass $ enumFromTo 'A' 'Z'
val :: Parser S.ByteString
val = takeTill isTabOrEol <?> "item value"
where
isTabOrEol c = c == '\t' || c == '\n'
item :: Parser Item
item = Item `fmap` ident <* separator <*> val <* skipWhile isTab <?> "perfdata item"
where
isTab = (==) '\t'
line :: Parser [Item]
line = many item <?> "perfdata line"
type ItemMap = M.Map S.ByteString S.ByteString
mapItems :: [Item] -> ItemMap
mapItems = foldl (\m i -> M.insert (label i) (content i) m) M.empty
parseLine :: S.ByteString -> Result [Item]
parseLine = parse line
extractItems :: Result [Item] -> Either ParserError ItemMap
extractItems (Done _ is) = Right $ mapItems is
extractItems (Fail _ ctxs err) = Left $ fmtParseError ctxs err
extractItems (Partial f) = extractItems (f "")
parseServiceData :: ItemMap -> Either ParserError ServicePerfdata
parseServiceData m = case M.lookup "SERVICEDESC" m of
Nothing -> Left ("SERVICEDESC not found in " ++ show m)
Just desc -> case M.lookup "SERVICESTATE" m of
Nothing -> Left "SERVICESTATE not found"
Just sState -> case parseReturnState sState of
Nothing -> Left ("invalid service state " ++ C.unpack sState)
Just st -> Right $ ServicePerfdata desc st
parseDataType :: ItemMap -> Either ParserError HostOrService
parseDataType m = case M.lookup "DATATYPE" m of
Nothing -> Left "DATATYPE not found"
Just s -> case s of
"HOSTPERFDATA" -> Right Host
"SERVICEPERFDATA" -> case parseServiceData m of
Left err -> Left err
Right d -> Right $ Service d
_ -> Left "Invalid datatype"
parseHostname :: ItemMap -> Either ParserError S.ByteString
parseHostname m = case M.lookup "HOSTNAME" m of
Nothing -> Left "HOSTNAME not found"
Just h -> Right h
parseTimestamp :: ItemMap -> Either ParserError Int64
parseTimestamp m = case M.lookup "TIMET" m of
Nothing -> Left "TIMET not found"
Just t -> case readInteger t of
Nothing -> Left "Invalid timestamp"
Just (n, _) -> Right $ fromInteger (n * nanosecondFactor)
where
nanosecondFactor = 1000000000
parseHostState :: ItemMap -> Either ParserError S.ByteString
parseHostState m = case M.lookup "HOSTSTATE" m of
Nothing -> Left "HOSTSTATE not found"
Just s -> Right s
parseHostMetrics :: ItemMap -> Either ParserError MetricList
parseHostMetrics m = case M.lookup "HOSTPERFDATA" m of
Nothing -> Left "HOSTPERFDATA not found"
Just p -> parseMetricString p
parseServiceMetrics :: ItemMap -> Either ParserError MetricList
parseServiceMetrics m = case M.lookup "SERVICEPERFDATA" m of
Nothing -> Left "SERVICEPERFDATA not found"
Just p -> parseMetricString p
parseMetrics :: HostOrService -> ItemMap -> Either ParserError MetricList
parseMetrics typ m = case typ of
Host -> parseHostMetrics m
Service _ -> parseServiceMetrics m
extractPerfdata :: ItemMap -> Either ParserError Perfdata
extractPerfdata m = do
typ <- parseDataType m
name <- parseHostname m
t <- parseTimestamp m
state <- parseHostState m
ms <- parseMetrics typ m
return $ Perfdata typ t (C.unpack name) (Just state) ms
perfdataFromDefaultTemplate :: S.ByteString -> Either ParserError Perfdata
perfdataFromDefaultTemplate s =
getItems s >>= extractPerfdata
where
getItems = extractItems . parseLine