module Ganeti.HTools.Cluster.Metrics
( compCV
, compCVfromStats
, compCVNodes
, compClusterStatistics
, updateClusterStatisticsTwice
, optimalCVScore
, printStats
) where
import Control.Monad (guard)
import Data.List (partition, transpose)
import Data.Maybe (fromMaybe)
import Text.Printf (printf)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.PeerMap as P
import Ganeti.HTools.Types
import Ganeti.Utils (printTable)
import Ganeti.Utils.Statistics
reservedMemRtotalCoeff :: Double
reservedMemRtotalCoeff = 0.25
detailedCVInfoExt :: [((Double, String)
, ([AggregateComponent] -> Statistics, Bool))]
detailedCVInfoExt = [ ((0.5, "free_mem_cv"), (getStdDevStatistics, True))
, ((0.5, "free_disk_cv"), (getStdDevStatistics, True))
, ((1, "n1_cnt"), (getSumStatistics, True))
, ((1, "reserved_mem_cv"), (getStdDevStatistics, True))
, ((4, "offline_all_cnt"), (getSumStatistics, False))
, ((16, "offline_pri_cnt"), (getSumStatistics, False))
, ( (0.5, "vcpu_ratio_cv")
, (getStdDevStatistics, True))
, ((1, "cpu_load_cv"), (getStdDevStatistics, True))
, ((1, "mem_load_cv"), (getStdDevStatistics, True))
, ((1, "disk_load_cv"), (getStdDevStatistics, True))
, ((1, "net_load_cv"), (getStdDevStatistics, True))
, ((2, "pri_tags_score"), (getSumStatistics, True))
, ((0.5, "spindles_cv"), (getStdDevStatistics, True))
, ((0.5, "free_mem_cv_forth"), (getStdDevStatistics, True))
, ( (0.5, "free_disk_cv_forth")
, (getStdDevStatistics, True))
, ( (0.5, "vcpu_ratio_cv_forth")
, (getStdDevStatistics, True))
, ((0.5, "spindles_cv_forth"), (getStdDevStatistics, True))
, ((1, "location_score"), (getSumStatistics, True))
, ( (1, "location_exclusion_score")
, (getMapStatistics, True))
, ( (reservedMemRtotalCoeff, "reserved_mem_rtotal")
, (getSumStatistics, True))
]
optimalCVScore :: Node.List -> Double
optimalCVScore nodelist = fromMaybe 0 $ do
let nodes = Container.elems nodelist
guard $ length nodes > 1
let nodeMems = map Node.tMem nodes
totalMem = sum nodeMems
totalMemOneLessNode = totalMem maximum nodeMems
guard $ totalMemOneLessNode > 0
let totalDrbdMem = fromIntegral . sum $ map (P.sumElems . Node.peers) nodes
optimalUsage = totalDrbdMem / totalMem
optimalUsageOneLessNode = totalDrbdMem / totalMemOneLessNode
relativeReserved = optimalUsageOneLessNode optimalUsage
return $ reservedMemRtotalCoeff * relativeReserved
detailedCVInfo :: [(Double, String)]
detailedCVInfo = map fst detailedCVInfoExt
detailedCVWeights :: [Double]
detailedCVWeights = map fst detailedCVInfo
detailedCVAggregation :: [([AggregateComponent] -> Statistics, Bool)]
detailedCVAggregation = map snd detailedCVInfoExt
detailedCVOnlineStatus :: [Bool]
detailedCVOnlineStatus = map snd detailedCVAggregation
compDetailedCVNode :: Node.Node -> [AggregateComponent]
compDetailedCVNode node =
let mem = Node.pMem node
memF = Node.pMemForth node
dsk = Node.pDsk node
dskF = Node.pDskForth node
n1 = fromIntegral
$ if Node.failN1 node
then length (Node.sList node) + length (Node.pList node)
else 0
res = Node.pRem node
ipri = fromIntegral . length $ Node.pList node
isec = fromIntegral . length $ Node.sList node
ioff = ipri + isec
cpu = Node.pCpuEff node
cpuF = Node.pCpuEffForth node
DynUtil c1 m1 d1 nn1 = Node.utilLoad node
DynUtil c2 m2 d2 nn2 = Node.utilPool node
(c_load, m_load, d_load, n_load) = (c1/c2, m1/m2, d1/d2, nn1/nn2)
pri_tags = fromIntegral $ Node.conflictingPrimaries node
spindles = Node.instSpindles node / Node.hiSpindles node
spindlesF = Node.instSpindlesForth node / Node.hiSpindles node
location_score = fromIntegral $ Node.locationScore node
location_exclusion_score = Node.instanceMap node
in [ SimpleNumber mem, SimpleNumber dsk, SimpleNumber n1, SimpleNumber res
, SimpleNumber ioff, SimpleNumber ipri, SimpleNumber cpu
, SimpleNumber c_load, SimpleNumber m_load, SimpleNumber d_load
, SimpleNumber n_load
, SimpleNumber pri_tags, SimpleNumber spindles
, SimpleNumber memF, SimpleNumber dskF, SimpleNumber cpuF
, SimpleNumber spindlesF
, SimpleNumber location_score
, SpreadValues location_exclusion_score
, SimpleNumber res
]
compClusterStatistics :: [Node.Node] -> [Statistics]
compClusterStatistics all_nodes =
let (offline, nodes) = partition Node.offline all_nodes
offline_values = transpose (map compDetailedCVNode offline)
++ repeat []
online_values = transpose $ map compDetailedCVNode nodes
aggregate (f, True) (onNodes, _) = f onNodes
aggregate (f, False) (_, offNodes) = f offNodes
in zipWith aggregate detailedCVAggregation
$ zip online_values offline_values
updateClusterStatistics :: [Statistics]
-> (Node.Node, Node.Node) -> [Statistics]
updateClusterStatistics stats (old, new) =
let update = zip (compDetailedCVNode old) (compDetailedCVNode new)
online = not $ Node.offline old
updateStat forOnline stat upd = if forOnline == online
then updateStatistics stat upd
else stat
in zipWith3 updateStat detailedCVOnlineStatus stats update
updateClusterStatisticsTwice :: [Statistics]
-> (Node.Node, Node.Node)
-> (Node.Node, Node.Node)
-> [Statistics]
updateClusterStatisticsTwice s a =
updateClusterStatistics (updateClusterStatistics s a)
compDetailedCV :: [Node.Node] -> [Double]
compDetailedCV = map getStatisticValue . compClusterStatistics
compCVfromStats :: [Statistics] -> Double
compCVfromStats = sum . zipWith (*) detailedCVWeights . map getStatisticValue
compCVNodes :: [Node.Node] -> Double
compCVNodes = sum . zipWith (*) detailedCVWeights . compDetailedCV
compCV :: Node.List -> Double
compCV = compCVNodes . Container.elems
printStats :: String -> Node.List -> String
printStats lp nl =
let dcvs = compDetailedCV $ Container.elems nl
(weights, names) = unzip detailedCVInfo
hd = zip3 (weights ++ repeat 1) (names ++ repeat "unknown") dcvs
header = [ "Field", "Value", "Weight" ]
formatted = map (\(w, h, val) ->
[ h
, printf "%.8f" val
, printf "x%.2f" w
]) hd
in printTable lp header formatted $ False:repeat True