Revision 525bfb36 htools/Ganeti/HTools/Cluster.hs
b/htools/Ganeti/HTools/Cluster.hs | ||
---|---|---|
1 | 1 |
{-| Implementation of cluster-wide logic. |
2 | 2 |
|
3 | 3 |
This module holds all pure cluster-logic; I\/O related functionality |
4 |
goes into the "Main" module for the individual binaries.
|
|
4 |
goes into the /Main/ module for the individual binaries.
|
|
5 | 5 |
|
6 | 6 |
-} |
7 | 7 |
|
... | ... | |
106 | 106 |
|
107 | 107 |
|
108 | 108 |
-- | A type denoting the valid allocation mode/pairs. |
109 |
-- |
|
109 | 110 |
-- For a one-node allocation, this will be a @Left ['Node.Node']@, |
110 | 111 |
-- whereas for a two-node allocation, this will be a @Right |
111 | 112 |
-- [('Node.Node', 'Node.Node')]@. |
112 | 113 |
type AllocNodes = Either [Ndx] [(Ndx, Ndx)] |
113 | 114 |
|
114 |
-- | The empty solution we start with when computing allocations |
|
115 |
-- | The empty solution we start with when computing allocations.
|
|
115 | 116 |
emptySolution :: AllocSolution |
116 | 117 |
emptySolution = AllocSolution { asFailures = [], asAllocs = 0 |
117 | 118 |
, asSolutions = [], asLog = [] } |
118 | 119 |
|
119 |
-- | The complete state for the balancing solution |
|
120 |
-- | The complete state for the balancing solution.
|
|
120 | 121 |
data Table = Table Node.List Instance.List Score [Placement] |
121 | 122 |
deriving (Show, Read) |
122 | 123 |
|
... | ... | |
144 | 145 |
} |
145 | 146 |
deriving (Show, Read) |
146 | 147 |
|
147 |
-- | Currently used, possibly to allocate, unallocable |
|
148 |
-- | Currently used, possibly to allocate, unallocable.
|
|
148 | 149 |
type AllocStats = (RSpec, RSpec, RSpec) |
149 | 150 |
|
150 | 151 |
-- * Utility functions |
... | ... | |
170 | 171 |
in |
171 | 172 |
(bad_nodes, bad_instances) |
172 | 173 |
|
173 |
-- | Zero-initializer for the CStats type |
|
174 |
-- | Zero-initializer for the CStats type.
|
|
174 | 175 |
emptyCStats :: CStats |
175 | 176 |
emptyCStats = CStats 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
176 | 177 |
|
177 |
-- | Update stats with data from a new node |
|
178 |
-- | Update stats with data from a new node.
|
|
178 | 179 |
updateCStats :: CStats -> Node.Node -> CStats |
179 | 180 |
updateCStats cs node = |
180 | 181 |
let CStats { csFmem = x_fmem, csFdsk = x_fdsk, |
... | ... | |
243 | 244 |
(truncate t_dsk - fromIntegral f_idsk) |
244 | 245 |
in (rini, rfin, runa) |
245 | 246 |
|
246 |
-- | The names and weights of the individual elements in the CV list |
|
247 |
-- | The names and weights of the individual elements in the CV list.
|
|
247 | 248 |
detailedCVInfo :: [(Double, String)] |
248 | 249 |
detailedCVInfo = [ (1, "free_mem_cv") |
249 | 250 |
, (1, "free_disk_cv") |
... | ... | |
311 | 312 |
compCV :: Node.List -> Double |
312 | 313 |
compCV = sum . zipWith (*) detailedCVWeights . compDetailedCV |
313 | 314 |
|
314 |
-- | Compute online nodes from a Node.List
|
|
315 |
-- | Compute online nodes from a 'Node.List'.
|
|
315 | 316 |
getOnline :: Node.List -> [Node.Node] |
316 | 317 |
getOnline = filter (not . Node.offline) . Container.elems |
317 | 318 |
|
318 |
-- * hbal functions
|
|
319 |
-- * Balancing functions
|
|
319 | 320 |
|
320 | 321 |
-- | Compute best table. Note that the ordering of the arguments is important. |
321 | 322 |
compareTables :: Table -> Table -> Table |
... | ... | |
534 | 535 |
then ini_tbl -- no advancement |
535 | 536 |
else best_tbl |
536 | 537 |
|
537 |
-- | Check if we are allowed to go deeper in the balancing |
|
538 |
-- | Check if we are allowed to go deeper in the balancing.
|
|
538 | 539 |
doNextBalance :: Table -- ^ The starting table |
539 | 540 |
-> Int -- ^ Remaining length |
540 | 541 |
-> Score -- ^ Score at which to stop |
... | ... | |
544 | 545 |
ini_plc_len = length ini_plc |
545 | 546 |
in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score |
546 | 547 |
|
547 |
-- | Run a balance move |
|
548 |
-- | Run a balance move.
|
|
548 | 549 |
tryBalance :: Table -- ^ The starting table |
549 | 550 |
-> Bool -- ^ Allow disk moves |
550 | 551 |
-> Bool -- ^ Allow instance moves |
... | ... | |
574 | 575 |
|
575 | 576 |
-- * Allocation functions |
576 | 577 |
|
577 |
-- | Build failure stats out of a list of failures |
|
578 |
-- | Build failure stats out of a list of failures.
|
|
578 | 579 |
collapseFailures :: [FailMode] -> FailStats |
579 | 580 |
collapseFailures flst = |
580 | 581 |
map (\k -> (k, length $ filter (k ==) flst)) [minBound..maxBound] |
581 | 582 |
|
582 | 583 |
-- | Update current Allocation solution and failure stats with new |
583 |
-- elements |
|
584 |
-- elements.
|
|
584 | 585 |
concatAllocs :: AllocSolution -> OpResult Node.AllocElement -> AllocSolution |
585 | 586 |
concatAllocs as (OpFail reason) = as { asFailures = reason : asFailures as } |
586 | 587 |
|
... | ... | |
611 | 612 |
sumAllocs (AllocSolution af aa as al) (AllocSolution bf ba bs bl) = |
612 | 613 |
AllocSolution (af ++ bf) (aa + ba) (as ++ bs) (al ++ bl) |
613 | 614 |
|
614 |
-- | Given a solution, generates a reasonable description for it |
|
615 |
-- | Given a solution, generates a reasonable description for it.
|
|
615 | 616 |
describeSolution :: AllocSolution -> String |
616 | 617 |
describeSolution as = |
617 | 618 |
let fcnt = asFailures as |
... | ... | |
629 | 630 |
" for node(s) %s") cv (asAllocs as) (length fcnt) freasons |
630 | 631 |
(intercalate "/" . map Node.name $ nodes) |
631 | 632 |
|
632 |
-- | Annotates a solution with the appropriate string |
|
633 |
-- | Annotates a solution with the appropriate string.
|
|
633 | 634 |
annotateSolution :: AllocSolution -> AllocSolution |
634 | 635 |
annotateSolution as = as { asLog = describeSolution as : asLog as } |
635 | 636 |
|
... | ... | |
678 | 679 |
then fail "No online nodes" |
679 | 680 |
else return $ annotateSolution sols |
680 | 681 |
|
681 |
-- | Given a group/result, describe it as a nice (list of) messages |
|
682 |
-- | Given a group/result, describe it as a nice (list of) messages.
|
|
682 | 683 |
solutionDescription :: Group.List -> (Gdx, Result AllocSolution) -> [String] |
683 | 684 |
solutionDescription gl (groupId, result) = |
684 | 685 |
case result of |
... | ... | |
690 | 691 |
|
691 | 692 |
-- | From a list of possibly bad and possibly empty solutions, filter |
692 | 693 |
-- only the groups with a valid result. Note that the result will be |
693 |
-- reversed compared to the original list |
|
694 |
-- reversed compared to the original list.
|
|
694 | 695 |
filterMGResults :: Group.List |
695 | 696 |
-> [(Gdx, Result AllocSolution)] |
696 | 697 |
-> [(Gdx, AllocSolution)] |
... | ... | |
703 | 704 |
| unallocable gdx -> accu |
704 | 705 |
| otherwise -> (gdx, sol):accu |
705 | 706 |
|
706 |
-- | Sort multigroup results based on policy and score |
|
707 |
-- | Sort multigroup results based on policy and score.
|
|
707 | 708 |
sortMGResults :: Group.List |
708 | 709 |
-> [(Gdx, AllocSolution)] |
709 | 710 |
-> [(Gdx, AllocSolution)] |
... | ... | |
782 | 783 |
Just v -> return v |
783 | 784 |
tryReloc nl il xid ncount ex_ndx |
784 | 785 |
|
785 |
-- | Change an instance's secondary node |
|
786 |
-- | Change an instance's secondary node.
|
|
786 | 787 |
evacInstance :: (Monad m) => |
787 | 788 |
[Ndx] -- ^ Excluded nodes |
788 | 789 |
-> Instance.List -- ^ The current instance list |
... | ... | |
854 | 855 |
let sol = foldl' sumAllocs emptySolution results |
855 | 856 |
return $ annotateSolution sol |
856 | 857 |
|
857 |
-- | Recursively place instances on the cluster until we're out of space |
|
858 |
-- | Recursively place instances on the cluster until we're out of space.
|
|
858 | 859 |
iterateAlloc :: Node.List |
859 | 860 |
-> Instance.List |
860 | 861 |
-> Instance.Instance |
... | ... | |
879 | 880 |
_ -> Bad "Internal error: multiple solutions for single\ |
880 | 881 |
\ allocation" |
881 | 882 |
|
882 |
-- | The core of the tiered allocation mode |
|
883 |
-- | The core of the tiered allocation mode.
|
|
883 | 884 |
tieredAlloc :: Node.List |
884 | 885 |
-> Instance.List |
885 | 886 |
-> Instance.Instance |
... | ... | |
1072 | 1073 |
|
1073 | 1074 |
-- * Node group functions |
1074 | 1075 |
|
1075 |
-- | Computes the group of an instance |
|
1076 |
-- | Computes the group of an instance.
|
|
1076 | 1077 |
instanceGroup :: Node.List -> Instance.Instance -> Result Gdx |
1077 | 1078 |
instanceGroup nl i = |
1078 | 1079 |
let sidx = Instance.sNode i |
... | ... | |
1087 | 1088 |
show pgroup ++ ", secondary " ++ show sgroup) |
1088 | 1089 |
else return pgroup |
1089 | 1090 |
|
1090 |
-- | Computes the group of an instance per the primary node |
|
1091 |
-- | Computes the group of an instance per the primary node.
|
|
1091 | 1092 |
instancePriGroup :: Node.List -> Instance.Instance -> Gdx |
1092 | 1093 |
instancePriGroup nl i = |
1093 | 1094 |
let pnode = Container.find (Instance.pNode i) nl |
1094 | 1095 |
in Node.group pnode |
1095 | 1096 |
|
1096 | 1097 |
-- | Compute the list of badly allocated instances (split across node |
1097 |
-- groups) |
|
1098 |
-- groups).
|
|
1098 | 1099 |
findSplitInstances :: Node.List -> Instance.List -> [Instance.Instance] |
1099 | 1100 |
findSplitInstances nl = |
1100 | 1101 |
filter (not . isOk . instanceGroup nl) . Container.elems |
1101 | 1102 |
|
1102 |
-- | Splits a cluster into the component node groups |
|
1103 |
-- | Splits a cluster into the component node groups.
|
|
1103 | 1104 |
splitCluster :: Node.List -> Instance.List -> |
1104 | 1105 |
[(Gdx, (Node.List, Instance.List))] |
1105 | 1106 |
splitCluster nl il = |
Also available in: Unified diff