nagios-check (empty) → 0.1.0.0
raw patch · 6 files changed
+335/−0 lines, 6 filesdep +QuickCheckdep +basedep +bifunctorssetup-changed
Dependencies added: QuickCheck, base, bifunctors, exceptions, hspec, mtl, nagios-check, nagios-perfdata, text
Files
- LICENSE +20/−0
- README.md +4/−0
- Setup.hs +2/−0
- lib/System/Nagios/Plugin.hs +223/−0
- nagios-check.cabal +45/−0
- tests/NagiosCheckTest.hs +41/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Sharif Olorin++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,4 @@+nagios-check+============++Write Nagios (or Icinga, Shinken, et cetera) plugins in Haskell.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ lib/System/Nagios/Plugin.hs view
@@ -0,0 +1,223 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}++module System.Nagios.Plugin+(+ CheckStatus(..),+ CheckResult,+ UOM(..),+ PerfValue(..),+ NagiosPlugin,+ runNagiosPlugin,+ runNagiosPlugin',+ addPerfDatum,+ addResult,+ checkStatus,+ checkInfo,+ worstResult,+ PerfDatum,+ finishState+) where++import Control.Applicative+import Control.Monad.Catch+import Control.Monad.State.Lazy+import Data.Bifunctor+import Data.Int+import Data.Monoid+import Data.Nagios.Perfdata.Metric (UOM (..))+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import System.Exit++-- | Nagios plugin exit statuses. Ordered by priority -+-- OK < Warning < Critical < Unknown, which correspond to plugin exit+-- statuses of 0, 1, 2, and 3 respectively.+data CheckStatus = OK -- ^ Check executed successfully and+ -- detected no service problems.+ | Warning -- ^ Nothing's actually broken but this+ -- should be followed up.+ | Critical -- ^ Check executed successfully and detected+ -- a service failure.+ | Unknown -- ^ Check unable to determine service+ -- status.+ deriving (Enum, Eq, Ord)++instance Show CheckStatus where+ show OK = "OK"+ show Warning = "WARNING"+ show Critical = "CRITICAL"+ show Unknown = "UNKNOWN"++-- | A CheckResult is the exit status of the plugin combined with the+-- plugin's info text. A `NagiosPlugin` which exits with+--+-- > CheckResult (Critical "entropy decreasing in closed system")+--+-- as its peak-badness CheckResult (and no 'PerfDatum's) will a) exit with+-- status 2 and b) output the text "CRITICAL: entropy decreasing in closed+-- system".+newtype CheckResult = CheckResult+ { unCheckResult :: (CheckStatus, Text) }+ deriving (Eq, Ord, Show)++-- | Extract the return status from a 'CheckResult'+checkStatus :: CheckResult -> CheckStatus+checkStatus = fst . unCheckResult++-- | Extract the infotext from a 'CheckResult'.+checkInfo :: CheckResult -> Text+checkInfo = snd . unCheckResult++-- | Value of a performance metric.+data PerfValue = RealValue Double | IntegralValue Int64+ deriving Eq++instance Show PerfValue where+ show (RealValue x) = show x+ show (IntegralValue x) = show x++-- | One performance metric. A plugin will output zero or more of these,+-- whereupon Nagios generally passes them off to an external system such+-- as <http://oss.oetiker.ch/rrdtool/ RRDTool> or+-- <https://github.com/anchor/vaultaire Vaultaire>.+-- The thresholds are purely informative (designed to be graphed), and+-- do not affect alerting; likewise with `_min` and `_max`.+data PerfDatum = PerfDatum+ { _label :: Text -- ^ Name of quantity being measured.+ , _value :: PerfValue -- ^ Measured value, integral or real.+ , _uom :: UOM -- ^ Unit of measure; 'NullUOM' is fine here.+ , _min :: Maybe PerfValue -- ^ Measured quantity cannot be lower than this.+ , _max :: Maybe PerfValue -- ^ Measured quantity cannot be higher than this.+ , _warn :: Maybe PerfValue -- ^ Warning threshold for graphing.+ , _crit :: Maybe PerfValue -- ^ Critical threshold for graphing.+ }+ deriving (Eq, Show)++-- | Current check results/perfdata. If the check suddenly dies, the+-- 'worst' of the CheckResults (and all the PerfDatums) will be used+-- to determine the exit state.+type CheckState = ([CheckResult], [PerfDatum])++newtype NagiosPlugin a = NagiosPlugin+ {+ unNagiosPlugin :: StateT CheckState IO a+ } deriving (Functor, Applicative, Monad, MonadIO, MonadState CheckState, MonadCatch, MonadThrow)++-- | Execute a Nagios check. The program will terminate at the check's+-- completion. A default status will provided if none is given.+runNagiosPlugin :: NagiosPlugin a -> IO ()+runNagiosPlugin check = do+ (_, st) <- runNagiosPlugin' $ catch check panic+ finishWith st+ where+ panic :: SomeException -> NagiosPlugin a+ panic = liftIO . finishWith . panicState++-- | Execute a Nagios check as with 'runNagiosPlugin', but return its+-- final state rather than terminating.+runNagiosPlugin' :: NagiosPlugin a -> IO (a, CheckState)+runNagiosPlugin' a = runStateT (unNagiosPlugin a) mempty++-- | Insert a result. Only the 'CheckStatus' with the most 'badness'+-- will determine the check's exit status.+addResult :: CheckStatus -> Text -> NagiosPlugin ()+addResult s t =+ modify (first (CheckResult (s, t) :))++-- | Insert a performance metric into the list the check will output.+addPerfDatum ::+ Text -- ^ Name of the quantity being measured.+ -> PerfValue -- ^ Measured value.+ -> UOM -- ^ Unit of the measured value.+ -> Maybe PerfValue -- ^ Minimum threshold.+ -> Maybe PerfValue -- ^ Maximum threshold.+ -> Maybe PerfValue -- ^ Warning threshold.+ -> Maybe PerfValue -- ^ Critical threshold.+ -> NagiosPlugin ()+addPerfDatum info val uom min' max' warn crit =+ addPerfDatum' $ PerfDatum info val uom min' max' warn crit+ where+ addPerfDatum' pd = do+ (rs, pds) <- get+ put (rs, pd : pds)++-- | The result which will be used if no other results have been+-- provided.+defaultResult :: CheckResult+defaultResult = CheckResult (Unknown, T.pack "no check result specified")++-- | The state the plugin will exit with if an uncaught exception occurs.+-- within the plugin.+panicState :: SomeException -> CheckState+panicState = (,[]) . return . CheckResult . panicResult+ where+ panicResult e = (Critical,+ T.pack ("unhandled exception: " ++ show e))++-- | Returns result with greatest badness, or a default UNKNOWN result+-- if no results have been specified.+worstResult :: [CheckResult] -> CheckResult+worstResult rs+ | null rs = defaultResult+ | otherwise = maximum rs++-- | Render a plugin's performance data according to the+-- <https://nagios-plugins.org/doc/guidelines.html Nagios plugin development guidelines>.+fmtPerfData :: [PerfDatum] -> Text+fmtPerfData = T.intercalate " " . map fmtPerfDatum+ where+ fmtPerfDatum PerfDatum{..} = T.concat+ [ _label+ , "="+ , T.pack (show _value)+ , T.pack (show _uom)+ , fmtThreshold _min+ , fmtThreshold _max+ , fmtThreshold _warn+ , fmtThreshold _crit+ ]++ fmtThreshold Nothing = ";"+ fmtThreshold (Just t) = T.pack $ ";" <> show t++-- | Render a plugin's result according to the+-- <https://nagios-plugins.org/doc/guidelines.html Nagios plugin development guidelines>.+--+-- FIXME: not actually true yet, need to implement verbose output.+fmtResults :: [CheckResult] -> Text+fmtResults = fmtResult . worstResult+ where+ fmtResult (CheckResult (s,t)) =+ T.pack (show s) <> ": " <> t++-- | Given a check's final state, return the status and output it would+-- exit with.+finishState :: CheckState -> (CheckStatus, Text)+finishState (rs, pds) =+ let worst = worstResult rs+ output = fmtResults rs <> perfdataPart pds+ in (checkStatus worst, output)+ where+ perfdataPart [] = ""+ perfdataPart xs = " | " <> fmtPerfData xs++-- | Calculate our final result, print output and then exit with the+-- appropriate status.+finishWith :: MonadIO m => CheckState -> m a+finishWith = liftIO . exitWithStatus . finishState++-- | Output the final check result to stdout and then terminate the+-- check program with the appropriate exit status.+exitWithStatus :: (CheckStatus, Text) -> IO a+exitWithStatus (OK, t) = T.putStrLn t >> exitSuccess+exitWithStatus (r, t) = T.putStrLn t >> exitWith (ExitFailure $ fromEnum r)++-- | A 'Range' is a combination of a lower boundary and an upper boundary (x,y).+-- An 'AcceptableRange' asserts that measured values between x and y+-- imply that nothing is wrong; an UnacceptableRange implies the inverse.+data Range = AcceptableRange PerfValue PerfValue+ | UnacceptableRange PerfValue PerfValue
+ nagios-check.cabal view
@@ -0,0 +1,45 @@+name: nagios-check+version: 0.1.0.0+synopsis: Package for writing monitoring plugins+description: Implements Nagios plugin development guidelines+ within a Haskell framework for writing Nagios+ checks.+homepage: https://github.com/fractalcat/nagios-check+license: MIT+license-file: LICENSE+author: Sharif Olorin+maintainer: sio@tesser.org+copyright: 2014 Sharif Olorin+category: System+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++source-repository head+ type: git + location: git@github.com:fractalcat/nagios-check.git++library+ exposed-modules: System.Nagios.Plugin+ build-depends: base >=4.7 && <5,+ mtl,+ text,+ bifunctors,+ exceptions,+ nagios-perfdata+ hs-source-dirs: lib+ default-language: Haskell2010+ ghc-options: -Wall+ -Wwarn+ -fwarn-tabs++test-suite nagios-check-test+ hs-source-dirs: tests+ main-is: NagiosCheckTest.hs+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ build-depends: base >=4.7 && <5,+ hspec,+ QuickCheck,+ text,+ nagios-check
+ tests/NagiosCheckTest.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Data.Monoid+import Data.Text (Text)+import Test.Hspec++import System.Nagios.Plugin++main :: IO ()+main = hspec suite++suite :: Spec+suite = describe "runNagiosPlugin'" $ do+ it "returns default result if no results are specified" $ do+ (_,s) <- runNagiosPlugin' (return ())+ snd (finishState s) `shouldBe` "UNKNOWN: no check result specified"+ fst (finishState s) `shouldBe` Unknown++ it "returns explicitly added result in favor of default" $ do+ (_,s) <- runNagiosPlugin' (universeCheck pi)+ snd (finishState s) `shouldBe` ("OK: " <> universeGoodResult)+ fst (finishState s) `shouldBe` OK++ it "returns result with greatest badness" $ do+ (_,s) <- runNagiosPlugin' (universeCheck 1.0)+ snd (finishState s) `shouldBe` ("CRITICAL: " <> universeBadResult)+ fst (finishState s) `shouldBe` Critical+++universeCheck :: Double -> NagiosPlugin ()+universeCheck pi' = do+ addResult OK universeGoodResult+ if (pi' < (3.0 :: Double)) then addResult Critical universeBadResult else return ()++universeGoodResult :: Text+universeGoodResult = "universe passes basic consistency tests"++universeBadResult :: Text+universeBadResult = "universe broken, π < 3"