module Ganeti.HTools.Cluster.AllocationSolution
( GenericAllocElement
, AllocElement
, GenericAllocSolution(..)
, AllocSolution
, emptyAllocSolution
, sumAllocs
, concatAllocs
, updateIl
, extractNl
, collapseFailures
, genericAnnotateSolution
, annotateSolution
, solutionDescription
, AllocSolutionCollection
, emptyAllocCollection
, concatAllocCollections
, collectionToSolution
) where
import Data.Ord (comparing)
import Data.List (intercalate, foldl', sortBy)
import Data.Maybe (listToMaybe)
import Text.Printf (printf)
import Ganeti.BasicTypes (GenericResult(..), Result)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Group as Group
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Types as T
type GenericAllocElement a = (Node.List, Instance.Instance, [Node.Node], a)
allocMetric :: GenericAllocElement a -> a
allocMetric (_, _, _, a) = a
type AllocElement = GenericAllocElement T.Score
data GenericAllocSolution a = AllocSolution
{ asFailures :: [T.FailMode]
, asAllocs :: Int
, asSolution :: Maybe (GenericAllocElement a)
, asLog :: [String]
}
type AllocSolution = GenericAllocSolution T.Score
emptyAllocSolution :: GenericAllocSolution a
emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0
, asSolution = Nothing, asLog = [] }
updateIl :: Instance.List
-> Maybe (GenericAllocElement a)
-> Instance.List
updateIl il Nothing = il
updateIl il (Just (_, xi, _, _)) = Container.add (Container.size il) xi il
extractNl :: Node.List
-> Instance.List
-> Maybe (GenericAllocElement a)
-> Node.List
extractNl nl _ Nothing = nl
extractNl _ il (Just (xnl, _, ns, _)) =
let newIndex = Container.size il
fixIndex = map (\i -> if i < 0 then newIndex else i)
fixIndices nodes node =
let nidx = Node.idx node
n = Container.find nidx nodes
n' = n { Node.pList = fixIndex $ Node.pList n
, Node.sList = fixIndex $ Node.sList n
}
in Container.add nidx n' nodes
in foldl fixIndices xnl ns
bestAllocElement :: Ord a
=> Maybe (GenericAllocElement a)
-> Maybe (GenericAllocElement a)
-> Maybe (GenericAllocElement a)
bestAllocElement a Nothing = a
bestAllocElement Nothing b = b
bestAllocElement a@(Just (_, _, _, ascore)) b@(Just (_, _, _, bscore)) =
if ascore < bscore then a else b
concatAllocs :: Ord a
=> GenericAllocSolution a
-> T.OpResult (GenericAllocElement a)
-> GenericAllocSolution a
concatAllocs as (Bad reason) = as { asFailures = reason : asFailures as }
concatAllocs as (Ok ns) =
let
cntok = asAllocs as
osols = asSolution as
nsols = bestAllocElement osols (Just ns)
nsuc = cntok + 1
in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols }
sumAllocs :: Ord a
=> GenericAllocSolution a
-> GenericAllocSolution a
-> GenericAllocSolution a
sumAllocs (AllocSolution aFails aAllocs aSols aLog)
(AllocSolution bFails bAllocs bSols bLog) =
let nFails = bFails ++ aFails
nAllocs = aAllocs + bAllocs
nSols = bestAllocElement aSols bSols
nLog = bLog ++ aLog
in AllocSolution nFails nAllocs nSols nLog
collapseFailures :: [T.FailMode] -> T.FailStats
collapseFailures flst =
map (\k -> (k, foldl' (\a e -> if e == k then a + 1 else a) 0 flst))
[minBound..maxBound]
genericDescribeSolution :: (a -> String) -> GenericAllocSolution a -> String
genericDescribeSolution formatMetrics as =
let fcnt = asFailures as
sols = asSolution as
freasons =
intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) .
filter ((> 0) . snd) . collapseFailures $ fcnt
in case sols of
Nothing -> "No valid allocation solutions, failure reasons: " ++
(if null fcnt then "unknown reasons" else freasons)
Just (_, _, nodes, cv) ->
printf ("score: %s, successes %d, failures %d (%s)" ++
" for node(s) %s") (formatMetrics cv) (asAllocs as)
(length fcnt) freasons
(intercalate "/" . map Node.name $ nodes)
genericAnnotateSolution :: (a -> String)
->GenericAllocSolution a -> GenericAllocSolution a
genericAnnotateSolution formatMetrics as =
as { asLog = genericDescribeSolution formatMetrics as : asLog as }
annotateSolution :: AllocSolution -> AllocSolution
annotateSolution = genericAnnotateSolution (printf "%.8f")
solutionDescription :: (Group.Group, Result (GenericAllocSolution a))
-> [String]
solutionDescription (grp, result) =
case result of
Ok solution -> map (printf "Group %s (%s): %s" gname pol) (asLog solution)
Bad message -> [printf "Group %s: error %s" gname message]
where gname = Group.name grp
pol = T.allocPolicyToRaw (Group.allocPolicy grp)
data AllocSolutionCollection a = AllocSolutionCollection
{ ascFailures :: [T.FailMode]
, ascAllocs :: Int
, ascSolutions :: [GenericAllocElement a]
, ascLog :: [String]
}
emptyAllocCollection :: AllocSolutionCollection a
emptyAllocCollection = AllocSolutionCollection
{ ascFailures = []
, ascAllocs = 0
, ascSolutions = []
, ascLog = []
}
concatAllocCollections :: Ord a
=> AllocSolutionCollection a
-> T.OpResult (GenericAllocElement a)
-> AllocSolutionCollection a
concatAllocCollections asc (Bad reason) =
asc { ascFailures = reason : ascFailures asc }
concatAllocCollections asc (Ok ns) =
asc { ascAllocs = ascAllocs asc + 1, ascSolutions = ns : ascSolutions asc }
collectionToSolution :: Ord a
=> T.FailMode
-> (GenericAllocElement a -> Bool)
-> AllocSolutionCollection a
-> GenericAllocSolution a
collectionToSolution failmode isgood asc =
let sols = sortBy (comparing allocMetric) $ ascSolutions asc
(dropped, good) = break isgood sols
dropcount = length dropped
nsols = ascAllocs asc dropcount
failures = replicate dropcount failmode ++ ascFailures asc
sol = listToMaybe good
in AllocSolution { asFailures = failures
, asAllocs = nsols
, asSolution = sol
, asLog = ascLog asc
}