Add metricsAction: supervised loop sampling ekg store to rrd via rrdtool
This commit is contained in:
@@ -8,15 +8,23 @@ module HomeAssistant.Runtime.Metrics
|
|||||||
, buildSchema
|
, buildSchema
|
||||||
, buildCreateArgs
|
, buildCreateArgs
|
||||||
, buildUpdateArgs
|
, buildUpdateArgs
|
||||||
|
, ensureRrd
|
||||||
|
, sampleAndUpdate
|
||||||
|
, metricsAction
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
|
import Control.Monad (forever, unless)
|
||||||
import Data.Int (Int64)
|
import Data.Int (Int64)
|
||||||
import Data.List (intercalate, sortBy)
|
import Data.List (intercalate, sortBy)
|
||||||
import Data.Ord (comparing)
|
import Data.Ord (comparing)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Data.Void (Void)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.HashMap.Strict as HM
|
import qualified Data.HashMap.Strict as HM
|
||||||
import qualified System.Metrics as M (Value (..), Sample)
|
import qualified System.Metrics as M (Value (..), Sample, Store, sampleAll)
|
||||||
|
import System.Directory (doesFileExist)
|
||||||
|
import System.Process (callProcess)
|
||||||
|
|
||||||
data DsType = Derive | Gauge
|
data DsType = Derive | Gauge
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
@@ -70,3 +78,32 @@ buildUpdateArgs path names values =
|
|||||||
where
|
where
|
||||||
renderValue Nothing = "U"
|
renderValue Nothing = "U"
|
||||||
renderValue (Just n) = show n
|
renderValue (Just n) = show n
|
||||||
|
|
||||||
|
lookupValue :: M.Sample -> Text -> Maybe Int64
|
||||||
|
lookupValue sample name = case HM.lookup name sample of
|
||||||
|
Just (M.Counter n) -> Just n
|
||||||
|
Just (M.Gauge n) -> Just n
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
ensureRrd :: FilePath -> FilePath -> [DsSpec] -> IO ()
|
||||||
|
ensureRrd rrdtool rrdPath schema = do
|
||||||
|
exists <- doesFileExist rrdPath
|
||||||
|
unless exists $
|
||||||
|
callProcess rrdtool (buildCreateArgs rrdPath 10 (map toPair schema))
|
||||||
|
where
|
||||||
|
toPair s = (dsName s, dsType s)
|
||||||
|
|
||||||
|
sampleAndUpdate :: M.Store -> FilePath -> FilePath -> [DsSpec] -> IO ()
|
||||||
|
sampleAndUpdate store rrdtool rrdPath schema = do
|
||||||
|
sample <- M.sampleAll store
|
||||||
|
let names = map dsName schema
|
||||||
|
values = map (lookupValue sample . dsEkgName) schema
|
||||||
|
callProcess rrdtool (buildUpdateArgs rrdPath names values)
|
||||||
|
|
||||||
|
metricsAction :: M.Store -> FilePath -> FilePath -> IO Void
|
||||||
|
metricsAction store rrdPath rrdtool = do
|
||||||
|
schema <- buildSchema <$> M.sampleAll store
|
||||||
|
ensureRrd rrdtool rrdPath schema
|
||||||
|
forever $ do
|
||||||
|
sampleAndUpdate store rrdtool rrdPath schema
|
||||||
|
threadDelay 10000000
|
||||||
|
|||||||
@@ -14,9 +14,19 @@ import HomeAssistant.Runtime.Metrics
|
|||||||
, buildSchema
|
, buildSchema
|
||||||
, buildCreateArgs
|
, buildCreateArgs
|
||||||
, buildUpdateArgs
|
, buildUpdateArgs
|
||||||
|
, ensureRrd
|
||||||
|
, sampleAndUpdate
|
||||||
|
, metricsAction
|
||||||
)
|
)
|
||||||
import qualified System.Metrics as M (Value (..))
|
import qualified System.Metrics as M (Value (..))
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import Control.Exception (try, SomeException)
|
||||||
|
import System.Directory (findExecutable, getTemporaryDirectory, removeFile)
|
||||||
|
import System.Exit (ExitCode (ExitSuccess))
|
||||||
|
import System.Process (readProcessWithExitCode)
|
||||||
|
import qualified System.Metrics as Metrics
|
||||||
|
import qualified System.Metrics.Counter as Counter
|
||||||
|
import qualified System.Metrics.Gauge as Gauge
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
@@ -96,3 +106,26 @@ spec = do
|
|||||||
, "ds1"
|
, "ds1"
|
||||||
, "N:U"
|
, "N:U"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
describe "end-to-end (rrdtool-gated)" $ do
|
||||||
|
it "creates an rrd, samples, and updates it" $ do
|
||||||
|
mRrdtool <- findExecutable "rrdtool"
|
||||||
|
case mRrdtool of
|
||||||
|
Nothing -> pendingWith "rrdtool not on PATH"
|
||||||
|
Just rrdtool -> do
|
||||||
|
store <- Metrics.newStore
|
||||||
|
c <- Metrics.createCounter "test.counter" store
|
||||||
|
g <- Metrics.createGauge "test.gauge" store
|
||||||
|
Counter.inc c
|
||||||
|
Gauge.set g 42
|
||||||
|
tmp <- getTemporaryDirectory
|
||||||
|
let rrdPath = tmp ++ "/hass-controller-metrics-test.rrd"
|
||||||
|
_ <- try (removeFile rrdPath) :: IO (Either SomeException ())
|
||||||
|
schema <- buildSchema <$> Metrics.sampleAll store
|
||||||
|
ensureRrd rrdtool rrdPath schema
|
||||||
|
sampleAndUpdate store rrdtool rrdPath schema
|
||||||
|
(rc, out, _) <- readProcessWithExitCode rrdtool ["fetch", rrdPath, "AVERAGE"] ""
|
||||||
|
rc `shouldBe` ExitSuccess
|
||||||
|
length out `shouldSatisfy` (> 0)
|
||||||
|
_ <- try (removeFile rrdPath) :: IO (Either SomeException ())
|
||||||
|
return ()
|
||||||
|
|||||||
Reference in New Issue
Block a user