metrics: detect rrd schema mismatch and recreate with backup

This commit is contained in:
2026-09-15 16:20:37 +03:00
parent af46776bb0
commit a19e5afd5e
2 changed files with 115 additions and 7 deletions
+50 -6
View File
@@ -8,6 +8,8 @@ module HomeAssistant.Runtime.Metrics
, buildSchema
, buildCreateArgs
, buildUpdateArgs
, parseInfoDs
, schemaMatches
, ensureRrd
, sampleAndUpdate
, metricsAction
@@ -17,19 +19,22 @@ import Control.Concurrent (threadDelay)
import Control.Monad (forever, unless)
import Data.Char (ord)
import Data.Int (Int64)
import Data.List (intercalate, sortBy)
import Data.List (intercalate, sort, sortBy, stripPrefix)
import Data.Maybe (mapMaybe)
import Data.Ord (comparing)
import Data.Text (Text)
import Data.Time (defaultTimeLocale, formatTime, getCurrentTime)
import Data.Void (Void)
import Numeric (showHex)
import qualified Data.Text as T
import qualified Data.HashMap.Strict as HM
import qualified System.Metrics as M (Value (..), Sample, Store, sampleAll)
import System.Directory (doesFileExist)
import System.Process (callProcess)
import System.Directory (doesFileExist, renameFile)
import System.Exit (ExitCode (..))
import System.Process (callProcess, readProcessWithExitCode)
data DsType = Derive | Gauge
deriving (Eq, Show)
deriving (Eq, Ord, Show)
data DsSpec = DsSpec
{ dsEkgName :: Text
@@ -89,18 +94,57 @@ buildUpdateArgs path names values =
renderValue Nothing = "U"
renderValue (Just n) = show n
dsTypeFromString :: String -> Maybe DsType
dsTypeFromString "DERIVE" = Just Derive
dsTypeFromString "GAUGE" = Just Gauge
dsTypeFromString _ = Nothing
parseInfoDs :: String -> [(String, DsType)]
parseInfoDs = mapMaybe parseLine . lines
where
parseLine line = do
rest0 <- stripPrefix "ds[" line
let (name, rest1) = break (== ']') rest0
rest2 <- stripPrefix "].type = \"" rest1
let (ty, _) = break (== '"') rest2
dt <- dsTypeFromString ty
pure (name, dt)
schemaMatches :: [DsSpec] -> [(String, DsType)] -> Bool
schemaMatches schema existing =
sort (map (\s -> (dsName s, dsType s)) schema) == sort existing
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
readExistingDs :: FilePath -> FilePath -> IO [(String, DsType)]
readExistingDs rrdtool rrdPath = do
(code, out, _) <- readProcessWithExitCode rrdtool ["info", rrdPath] ""
pure $ case code of
ExitSuccess -> parseInfoDs out
_ -> []
backupPath :: FilePath -> IO FilePath
backupPath path = do
now <- getCurrentTime
pure $ path ++ ".bak-" ++ formatTime defaultTimeLocale "%Y%m%dT%H%M%S" now
ensureRrd :: FilePath -> FilePath -> [DsSpec] -> IO ()
ensureRrd rrdtool rrdPath schema = do
exists <- doesFileExist rrdPath
unless exists $
callProcess rrdtool (buildCreateArgs rrdPath 10 (map toPair schema))
if not exists
then create
else do
current <- readExistingDs rrdtool rrdPath
unless (schemaMatches schema current) $ do
backup <- backupPath rrdPath
renameFile rrdPath backup
create
where
create = callProcess rrdtool (buildCreateArgs rrdPath 10 (map toPair schema))
toPair s = (dsName s, dsType s)
sampleAndUpdate :: M.Store -> FilePath -> FilePath -> [DsSpec] -> IO ()