Revision 129734d3 htools/Ganeti/HTools/Cluster.hs
b/htools/Ganeti/HTools/Cluster.hs | ||
---|---|---|
75 | 75 |
|
76 | 76 |
import qualified Data.IntSet as IntSet |
77 | 77 |
import Data.List |
78 |
import Data.Maybe (fromJust) |
|
78 |
import Data.Maybe (fromJust, isNothing)
|
|
79 | 79 |
import Data.Ord (comparing) |
80 | 80 |
import Text.Printf (printf) |
81 | 81 |
import Control.Monad |
... | ... | |
93 | 93 |
|
94 | 94 |
-- | Allocation\/relocation solution. |
95 | 95 |
data AllocSolution = AllocSolution |
96 |
{ asFailures :: [FailMode] -- ^ Failure counts |
|
97 |
, asAllocs :: Int -- ^ Good allocation count |
|
98 |
, asSolutions :: [Node.AllocElement] -- ^ The actual result, length |
|
99 |
-- of the list depends on the |
|
100 |
-- allocation/relocation mode |
|
101 |
, asLog :: [String] -- ^ A list of informational messages |
|
96 |
{ asFailures :: [FailMode] -- ^ Failure counts |
|
97 |
, asAllocs :: Int -- ^ Good allocation count |
|
98 |
, asSolution :: Maybe Node.AllocElement -- ^ The actual allocation result |
|
99 |
, asLog :: [String] -- ^ Informational messages |
|
102 | 100 |
} |
103 | 101 |
|
104 | 102 |
-- | Node evacuation/group change iallocator result type. This result |
... | ... | |
125 | 123 |
-- | The empty solution we start with when computing allocations. |
126 | 124 |
emptyAllocSolution :: AllocSolution |
127 | 125 |
emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0 |
128 |
, asSolutions = [], asLog = [] }
|
|
126 |
, asSolution = Nothing, asLog = [] }
|
|
129 | 127 |
|
130 | 128 |
-- | The empty evac solution. |
131 | 129 |
emptyEvacSolution :: EvacSolution |
... | ... | |
610 | 608 |
concatAllocs as (OpGood ns@(_, _, _, nscore)) = |
611 | 609 |
let -- Choose the old or new solution, based on the cluster score |
612 | 610 |
cntok = asAllocs as |
613 |
osols = asSolutions as
|
|
611 |
osols = asSolution as |
|
614 | 612 |
nsols = case osols of |
615 |
[] -> [ns]
|
|
616 |
(_, _, _, oscore):[] ->
|
|
613 |
Nothing -> Just ns
|
|
614 |
Just (_, _, _, oscore) ->
|
|
617 | 615 |
if oscore < nscore |
618 | 616 |
then osols |
619 |
else [ns] |
|
620 |
-- FIXME: here we simply concat to lists with more |
|
621 |
-- than one element; we should instead abort, since |
|
622 |
-- this is not a valid usage of this function |
|
623 |
xs -> ns:xs |
|
617 |
else Just ns |
|
624 | 618 |
nsuc = cntok + 1 |
625 | 619 |
-- Note: we force evaluation of nsols here in order to keep the |
626 | 620 |
-- memory profile low - we know that we will need nsols for sure |
627 | 621 |
-- in the next cycle, so we force evaluation of nsols, since the |
628 | 622 |
-- foldl' in the caller will only evaluate the tuple, but not the |
629 | 623 |
-- elements of the tuple |
630 |
in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolutions = nsols }
|
|
624 |
in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols } |
|
631 | 625 |
|
632 | 626 |
-- | Given a solution, generates a reasonable description for it. |
633 | 627 |
describeSolution :: AllocSolution -> String |
634 | 628 |
describeSolution as = |
635 | 629 |
let fcnt = asFailures as |
636 |
sols = asSolutions as
|
|
630 |
sols = asSolution as |
|
637 | 631 |
freasons = |
638 | 632 |
intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) . |
639 | 633 |
filter ((> 0) . snd) . collapseFailures $ fcnt |
640 |
in if null sols |
|
641 |
then "No valid allocation solutions, failure reasons: " ++ |
|
642 |
(if null fcnt |
|
643 |
then "unknown reasons" |
|
644 |
else freasons) |
|
645 |
else let (_, _, nodes, cv) = head sols |
|
646 |
in printf ("score: %.8f, successes %d, failures %d (%s)" ++ |
|
647 |
" for node(s) %s") cv (asAllocs as) (length fcnt) freasons |
|
648 |
(intercalate "/" . map Node.name $ nodes) |
|
634 |
in case sols of |
|
635 |
Nothing -> "No valid allocation solutions, failure reasons: " ++ |
|
636 |
(if null fcnt then "unknown reasons" else freasons) |
|
637 |
Just (_, _, nodes, cv) -> |
|
638 |
printf ("score: %.8f, successes %d, failures %d (%s)" ++ |
|
639 |
" for node(s) %s") cv (asAllocs as) (length fcnt) freasons |
|
640 |
(intercalate "/" . map Node.name $ nodes) |
|
649 | 641 |
|
650 | 642 |
-- | Annotates a solution with the appropriate string. |
651 | 643 |
annotateSolution :: AllocSolution -> AllocSolution |
... | ... | |
725 | 717 |
fn accu (gdx, rasol) = |
726 | 718 |
case rasol of |
727 | 719 |
Bad _ -> accu |
728 |
Ok sol | null (asSolutions sol) -> accu
|
|
720 |
Ok sol | isNothing (asSolution sol) -> accu
|
|
729 | 721 |
| unallocable gdx -> accu |
730 | 722 |
| otherwise -> (gdx, sol):accu |
731 | 723 |
|
... | ... | |
736 | 728 |
sortMGResults gl sols = |
737 | 729 |
let extractScore (_, _, _, x) = x |
738 | 730 |
solScore (gdx, sol) = (Group.allocPolicy (Container.find gdx gl), |
739 |
(extractScore . head . asSolutions) sol)
|
|
731 |
(extractScore . fromJust . asSolution) sol)
|
|
740 | 732 |
in sortBy (comparing solScore) sols |
741 | 733 |
|
742 | 734 |
-- | Finds the best group for an instance on a multi-group cluster. |
... | ... | |
1150 | 1142 |
newlimit = fmap (flip (-) 1) limit |
1151 | 1143 |
in case tryAlloc nl il newi2 allocnodes of |
1152 | 1144 |
Bad s -> Bad s |
1153 |
Ok (AllocSolution { asFailures = errs, asSolutions = sols3 }) ->
|
|
1145 |
Ok (AllocSolution { asFailures = errs, asSolution = sols3 }) -> |
|
1154 | 1146 |
let newsol = Ok (collapseFailures errs, nl, il, ixes, cstats) in |
1155 | 1147 |
case sols3 of |
1156 |
[] -> newsol
|
|
1157 |
(xnl, xi, _, _):[] ->
|
|
1148 |
Nothing -> newsol
|
|
1149 |
Just (xnl, xi, _, _) ->
|
|
1158 | 1150 |
if limit == Just 0 |
1159 | 1151 |
then newsol |
1160 | 1152 |
else iterateAlloc xnl (Container.add newidx xi il) |
1161 | 1153 |
newlimit newinst allocnodes (xi:ixes) |
1162 | 1154 |
(totalResources xnl:cstats) |
1163 |
_ -> Bad "Internal error: multiple solutions for single\ |
|
1164 |
\ allocation" |
|
1165 | 1155 |
|
1166 | 1156 |
-- | The core of the tiered allocation mode. |
1167 | 1157 |
tieredAlloc :: Node.List |
Also available in: Unified diff