Revision f23f21c3
b/htools/Ganeti/HTools/Cluster.hs | ||
---|---|---|
27 | 27 |
-} |
28 | 28 |
|
29 | 29 |
module Ganeti.HTools.Cluster |
30 |
(
|
|
31 |
-- * Types
|
|
32 |
AllocSolution(..)
|
|
33 |
, EvacSolution(..)
|
|
34 |
, Table(..)
|
|
35 |
, CStats(..)
|
|
36 |
, AllocStats
|
|
37 |
, AllocResult
|
|
38 |
, AllocMethod
|
|
39 |
-- * Generic functions
|
|
40 |
, totalResources
|
|
41 |
, computeAllocationDelta
|
|
42 |
-- * First phase functions
|
|
43 |
, computeBadItems
|
|
44 |
-- * Second phase functions
|
|
45 |
, printSolutionLine
|
|
46 |
, formatCmds
|
|
47 |
, involvedNodes
|
|
48 |
, splitJobs
|
|
49 |
-- * Display functions
|
|
50 |
, printNodes
|
|
51 |
, printInsts
|
|
52 |
-- * Balacing functions
|
|
53 |
, checkMove
|
|
54 |
, doNextBalance
|
|
55 |
, tryBalance
|
|
56 |
, compCV
|
|
57 |
, compCVNodes
|
|
58 |
, compDetailedCV
|
|
59 |
, printStats
|
|
60 |
, iMoveToJob
|
|
61 |
-- * IAllocator functions
|
|
62 |
, genAllocNodes
|
|
63 |
, tryAlloc
|
|
64 |
, tryMGAlloc
|
|
65 |
, tryReloc
|
|
66 |
, tryNodeEvac
|
|
67 |
, tryChangeGroup
|
|
68 |
, collapseFailures
|
|
69 |
-- * Allocation functions
|
|
70 |
, iterateAlloc
|
|
71 |
, tieredAlloc
|
|
72 |
-- * Node group functions
|
|
73 |
, instanceGroup
|
|
74 |
, findSplitInstances
|
|
75 |
, splitCluster
|
|
76 |
) where
|
|
30 |
( |
|
31 |
-- * Types |
|
32 |
AllocSolution(..) |
|
33 |
, EvacSolution(..) |
|
34 |
, Table(..) |
|
35 |
, CStats(..) |
|
36 |
, AllocStats |
|
37 |
, AllocResult |
|
38 |
, AllocMethod |
|
39 |
-- * Generic functions |
|
40 |
, totalResources |
|
41 |
, computeAllocationDelta |
|
42 |
-- * First phase functions |
|
43 |
, computeBadItems |
|
44 |
-- * Second phase functions |
|
45 |
, printSolutionLine |
|
46 |
, formatCmds |
|
47 |
, involvedNodes |
|
48 |
, splitJobs |
|
49 |
-- * Display functions |
|
50 |
, printNodes |
|
51 |
, printInsts |
|
52 |
-- * Balacing functions |
|
53 |
, checkMove |
|
54 |
, doNextBalance |
|
55 |
, tryBalance |
|
56 |
, compCV |
|
57 |
, compCVNodes |
|
58 |
, compDetailedCV |
|
59 |
, printStats |
|
60 |
, iMoveToJob |
|
61 |
-- * IAllocator functions |
|
62 |
, genAllocNodes |
|
63 |
, tryAlloc |
|
64 |
, tryMGAlloc |
|
65 |
, tryReloc |
|
66 |
, tryNodeEvac |
|
67 |
, tryChangeGroup |
|
68 |
, collapseFailures |
|
69 |
-- * Allocation functions |
|
70 |
, iterateAlloc |
|
71 |
, tieredAlloc |
|
72 |
-- * Node group functions |
|
73 |
, instanceGroup |
|
74 |
, findSplitInstances |
|
75 |
, splitCluster |
|
76 |
) where |
|
77 | 77 |
|
78 | 78 |
import qualified Data.IntSet as IntSet |
79 | 79 |
import Data.List |
... | ... | |
104 | 104 |
-- type consists of actual opcodes (a restricted subset) that are |
105 | 105 |
-- transmitted back to Ganeti. |
106 | 106 |
data EvacSolution = EvacSolution |
107 |
{ esMoved :: [(Idx, Gdx, [Ndx])] -- ^ Instances moved successfully
|
|
108 |
, esFailed :: [(Idx, String)] -- ^ Instances which were not
|
|
109 |
-- relocated
|
|
110 |
, esOpCodes :: [[OpCodes.OpCode]] -- ^ List of jobs
|
|
111 |
}
|
|
107 |
{ esMoved :: [(Idx, Gdx, [Ndx])] -- ^ Instances moved successfully |
|
108 |
, esFailed :: [(Idx, String)] -- ^ Instances which were not |
|
109 |
-- relocated |
|
110 |
, esOpCodes :: [[OpCodes.OpCode]] -- ^ List of jobs |
|
111 |
} |
|
112 | 112 |
|
113 | 113 |
-- | Allocation results, as used in 'iterateAlloc' and 'tieredAlloc'. |
114 | 114 |
type AllocResult = (FailStats, Node.List, Instance.List, |
... | ... | |
207 | 207 |
-- | Update stats with data from a new node. |
208 | 208 |
updateCStats :: CStats -> Node.Node -> CStats |
209 | 209 |
updateCStats cs node = |
210 |
let CStats { csFmem = x_fmem, csFdsk = x_fdsk, |
|
211 |
csAmem = x_amem, csAcpu = x_acpu, csAdsk = x_adsk, |
|
212 |
csMmem = x_mmem, csMdsk = x_mdsk, csMcpu = x_mcpu, |
|
213 |
csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu, |
|
214 |
csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu, |
|
215 |
csVcpu = x_vcpu, |
|
216 |
csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst |
|
217 |
} |
|
218 |
= cs |
|
219 |
inc_amem = Node.fMem node - Node.rMem node |
|
220 |
inc_amem' = if inc_amem > 0 then inc_amem else 0 |
|
221 |
inc_adsk = Node.availDisk node |
|
222 |
inc_imem = truncate (Node.tMem node) - Node.nMem node |
|
223 |
- Node.xMem node - Node.fMem node |
|
224 |
inc_icpu = Node.uCpu node |
|
225 |
inc_idsk = truncate (Node.tDsk node) - Node.fDsk node |
|
226 |
inc_vcpu = Node.hiCpu node |
|
227 |
inc_acpu = Node.availCpu node |
|
228 |
|
|
229 |
in cs { csFmem = x_fmem + fromIntegral (Node.fMem node) |
|
230 |
, csFdsk = x_fdsk + fromIntegral (Node.fDsk node) |
|
231 |
, csAmem = x_amem + fromIntegral inc_amem' |
|
232 |
, csAdsk = x_adsk + fromIntegral inc_adsk |
|
233 |
, csAcpu = x_acpu + fromIntegral inc_acpu |
|
234 |
, csMmem = max x_mmem (fromIntegral inc_amem') |
|
235 |
, csMdsk = max x_mdsk (fromIntegral inc_adsk) |
|
236 |
, csMcpu = max x_mcpu (fromIntegral inc_acpu) |
|
237 |
, csImem = x_imem + fromIntegral inc_imem |
|
238 |
, csIdsk = x_idsk + fromIntegral inc_idsk |
|
239 |
, csIcpu = x_icpu + fromIntegral inc_icpu |
|
240 |
, csTmem = x_tmem + Node.tMem node |
|
241 |
, csTdsk = x_tdsk + Node.tDsk node |
|
242 |
, csTcpu = x_tcpu + Node.tCpu node |
|
243 |
, csVcpu = x_vcpu + fromIntegral inc_vcpu |
|
244 |
, csXmem = x_xmem + fromIntegral (Node.xMem node) |
|
245 |
, csNmem = x_nmem + fromIntegral (Node.nMem node) |
|
246 |
, csNinst = x_ninst + length (Node.pList node) |
|
247 |
} |
|
210 |
let CStats { csFmem = x_fmem, csFdsk = x_fdsk, |
|
211 |
csAmem = x_amem, csAcpu = x_acpu, csAdsk = x_adsk, |
|
212 |
csMmem = x_mmem, csMdsk = x_mdsk, csMcpu = x_mcpu, |
|
213 |
csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu, |
|
214 |
csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu, |
|
215 |
csVcpu = x_vcpu, |
|
216 |
csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst |
|
217 |
} |
|
218 |
= cs |
|
219 |
inc_amem = Node.fMem node - Node.rMem node |
|
220 |
inc_amem' = if inc_amem > 0 then inc_amem else 0 |
|
221 |
inc_adsk = Node.availDisk node |
|
222 |
inc_imem = truncate (Node.tMem node) - Node.nMem node |
|
223 |
- Node.xMem node - Node.fMem node |
|
224 |
inc_icpu = Node.uCpu node |
|
225 |
inc_idsk = truncate (Node.tDsk node) - Node.fDsk node |
|
226 |
inc_vcpu = Node.hiCpu node |
|
227 |
inc_acpu = Node.availCpu node |
|
228 |
in cs { csFmem = x_fmem + fromIntegral (Node.fMem node) |
|
229 |
, csFdsk = x_fdsk + fromIntegral (Node.fDsk node) |
|
230 |
, csAmem = x_amem + fromIntegral inc_amem' |
|
231 |
, csAdsk = x_adsk + fromIntegral inc_adsk |
|
232 |
, csAcpu = x_acpu + fromIntegral inc_acpu |
|
233 |
, csMmem = max x_mmem (fromIntegral inc_amem') |
|
234 |
, csMdsk = max x_mdsk (fromIntegral inc_adsk) |
|
235 |
, csMcpu = max x_mcpu (fromIntegral inc_acpu) |
|
236 |
, csImem = x_imem + fromIntegral inc_imem |
|
237 |
, csIdsk = x_idsk + fromIntegral inc_idsk |
|
238 |
, csIcpu = x_icpu + fromIntegral inc_icpu |
|
239 |
, csTmem = x_tmem + Node.tMem node |
|
240 |
, csTdsk = x_tdsk + Node.tDsk node |
|
241 |
, csTcpu = x_tcpu + Node.tCpu node |
|
242 |
, csVcpu = x_vcpu + fromIntegral inc_vcpu |
|
243 |
, csXmem = x_xmem + fromIntegral (Node.xMem node) |
|
244 |
, csNmem = x_nmem + fromIntegral (Node.nMem node) |
|
245 |
, csNinst = x_ninst + length (Node.pList node) |
|
246 |
} |
|
248 | 247 |
|
249 | 248 |
-- | Compute the total free disk and memory in the cluster. |
250 | 249 |
totalResources :: Node.List -> CStats |
251 | 250 |
totalResources nl = |
252 |
let cs = foldl' updateCStats emptyCStats . Container.elems $ nl
|
|
253 |
in cs { csScore = compCV nl }
|
|
251 |
let cs = foldl' updateCStats emptyCStats . Container.elems $ nl |
|
252 |
in cs { csScore = compCV nl } |
|
254 | 253 |
|
255 | 254 |
-- | Compute the delta between two cluster state. |
256 | 255 |
-- |
... | ... | |
260 | 259 |
-- was left unallocated. |
261 | 260 |
computeAllocationDelta :: CStats -> CStats -> AllocStats |
262 | 261 |
computeAllocationDelta cini cfin = |
263 |
let CStats {csImem = i_imem, csIdsk = i_idsk, csIcpu = i_icpu} = cini
|
|
264 |
CStats {csImem = f_imem, csIdsk = f_idsk, csIcpu = f_icpu,
|
|
265 |
csTmem = t_mem, csTdsk = t_dsk, csVcpu = v_cpu } = cfin
|
|
266 |
rini = RSpec (fromIntegral i_icpu) (fromIntegral i_imem)
|
|
267 |
(fromIntegral i_idsk)
|
|
268 |
rfin = RSpec (fromIntegral (f_icpu - i_icpu))
|
|
269 |
(fromIntegral (f_imem - i_imem))
|
|
270 |
(fromIntegral (f_idsk - i_idsk))
|
|
271 |
un_cpu = fromIntegral (v_cpu - f_icpu)::Int
|
|
272 |
runa = RSpec un_cpu (truncate t_mem - fromIntegral f_imem)
|
|
273 |
(truncate t_dsk - fromIntegral f_idsk)
|
|
274 |
in (rini, rfin, runa)
|
|
262 |
let CStats {csImem = i_imem, csIdsk = i_idsk, csIcpu = i_icpu} = cini |
|
263 |
CStats {csImem = f_imem, csIdsk = f_idsk, csIcpu = f_icpu, |
|
264 |
csTmem = t_mem, csTdsk = t_dsk, csVcpu = v_cpu } = cfin |
|
265 |
rini = RSpec (fromIntegral i_icpu) (fromIntegral i_imem) |
|
266 |
(fromIntegral i_idsk) |
|
267 |
rfin = RSpec (fromIntegral (f_icpu - i_icpu)) |
|
268 |
(fromIntegral (f_imem - i_imem)) |
|
269 |
(fromIntegral (f_idsk - i_idsk)) |
|
270 |
un_cpu = fromIntegral (v_cpu - f_icpu)::Int |
|
271 |
runa = RSpec un_cpu (truncate t_mem - fromIntegral f_imem) |
|
272 |
(truncate t_dsk - fromIntegral f_idsk) |
|
273 |
in (rini, rfin, runa) |
|
275 | 274 |
|
276 | 275 |
-- | The names and weights of the individual elements in the CV list. |
277 | 276 |
detailedCVInfo :: [(Double, String)] |
... | ... | |
296 | 295 |
-- | Compute the mem and disk covariance. |
297 | 296 |
compDetailedCV :: [Node.Node] -> [Double] |
298 | 297 |
compDetailedCV all_nodes = |
299 |
let |
|
300 |
(offline, nodes) = partition Node.offline all_nodes |
|
301 |
mem_l = map Node.pMem nodes |
|
302 |
dsk_l = map Node.pDsk nodes |
|
303 |
-- metric: memory covariance |
|
304 |
mem_cv = stdDev mem_l |
|
305 |
-- metric: disk covariance |
|
306 |
dsk_cv = stdDev dsk_l |
|
307 |
-- metric: count of instances living on N1 failing nodes |
|
308 |
n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) + |
|
309 |
length (Node.pList n)) . |
|
310 |
filter Node.failN1 $ nodes :: Double |
|
311 |
res_l = map Node.pRem nodes |
|
312 |
-- metric: reserved memory covariance |
|
313 |
res_cv = stdDev res_l |
|
314 |
-- offline instances metrics |
|
315 |
offline_ipri = sum . map (length . Node.pList) $ offline |
|
316 |
offline_isec = sum . map (length . Node.sList) $ offline |
|
317 |
-- metric: count of instances on offline nodes |
|
318 |
off_score = fromIntegral (offline_ipri + offline_isec)::Double |
|
319 |
-- metric: count of primary instances on offline nodes (this |
|
320 |
-- helps with evacuation/failover of primary instances on |
|
321 |
-- 2-node clusters with one node offline) |
|
322 |
off_pri_score = fromIntegral offline_ipri::Double |
|
323 |
cpu_l = map Node.pCpu nodes |
|
324 |
-- metric: covariance of vcpu/pcpu ratio |
|
325 |
cpu_cv = stdDev cpu_l |
|
326 |
-- metrics: covariance of cpu, memory, disk and network load |
|
327 |
(c_load, m_load, d_load, n_load) = unzip4 $ |
|
328 |
map (\n -> |
|
329 |
let DynUtil c1 m1 d1 n1 = Node.utilLoad n |
|
330 |
DynUtil c2 m2 d2 n2 = Node.utilPool n |
|
331 |
in (c1/c2, m1/m2, d1/d2, n1/n2) |
|
332 |
) nodes |
|
333 |
-- metric: conflicting instance count |
|
334 |
pri_tags_inst = sum $ map Node.conflictingPrimaries nodes |
|
335 |
pri_tags_score = fromIntegral pri_tags_inst::Double |
|
336 |
in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv |
|
337 |
, stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load |
|
338 |
, pri_tags_score ] |
|
298 |
let (offline, nodes) = partition Node.offline all_nodes |
|
299 |
mem_l = map Node.pMem nodes |
|
300 |
dsk_l = map Node.pDsk nodes |
|
301 |
-- metric: memory covariance |
|
302 |
mem_cv = stdDev mem_l |
|
303 |
-- metric: disk covariance |
|
304 |
dsk_cv = stdDev dsk_l |
|
305 |
-- metric: count of instances living on N1 failing nodes |
|
306 |
n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) + |
|
307 |
length (Node.pList n)) . |
|
308 |
filter Node.failN1 $ nodes :: Double |
|
309 |
res_l = map Node.pRem nodes |
|
310 |
-- metric: reserved memory covariance |
|
311 |
res_cv = stdDev res_l |
|
312 |
-- offline instances metrics |
|
313 |
offline_ipri = sum . map (length . Node.pList) $ offline |
|
314 |
offline_isec = sum . map (length . Node.sList) $ offline |
|
315 |
-- metric: count of instances on offline nodes |
|
316 |
off_score = fromIntegral (offline_ipri + offline_isec)::Double |
|
317 |
-- metric: count of primary instances on offline nodes (this |
|
318 |
-- helps with evacuation/failover of primary instances on |
|
319 |
-- 2-node clusters with one node offline) |
|
320 |
off_pri_score = fromIntegral offline_ipri::Double |
|
321 |
cpu_l = map Node.pCpu nodes |
|
322 |
-- metric: covariance of vcpu/pcpu ratio |
|
323 |
cpu_cv = stdDev cpu_l |
|
324 |
-- metrics: covariance of cpu, memory, disk and network load |
|
325 |
(c_load, m_load, d_load, n_load) = |
|
326 |
unzip4 $ map (\n -> |
|
327 |
let DynUtil c1 m1 d1 n1 = Node.utilLoad n |
|
328 |
DynUtil c2 m2 d2 n2 = Node.utilPool n |
|
329 |
in (c1/c2, m1/m2, d1/d2, n1/n2)) nodes |
|
330 |
-- metric: conflicting instance count |
|
331 |
pri_tags_inst = sum $ map Node.conflictingPrimaries nodes |
|
332 |
pri_tags_score = fromIntegral pri_tags_inst::Double |
|
333 |
in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv |
|
334 |
, stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load |
|
335 |
, pri_tags_score ] |
|
339 | 336 |
|
340 | 337 |
-- | Compute the /total/ variance. |
341 | 338 |
compCVNodes :: [Node.Node] -> Double |
... | ... | |
354 | 351 |
-- | Compute best table. Note that the ordering of the arguments is important. |
355 | 352 |
compareTables :: Table -> Table -> Table |
356 | 353 |
compareTables a@(Table _ _ a_cv _) b@(Table _ _ b_cv _ ) = |
357 |
if a_cv > b_cv then b else a
|
|
354 |
if a_cv > b_cv then b else a |
|
358 | 355 |
|
359 | 356 |
-- | Applies an instance move to a given node list and instance. |
360 | 357 |
applyMove :: Node.List -> Instance.Instance |
361 | 358 |
-> IMove -> OpResult (Node.List, Instance.Instance, Ndx, Ndx) |
362 | 359 |
-- Failover (f) |
363 | 360 |
applyMove nl inst Failover = |
364 |
let old_pdx = Instance.pNode inst
|
|
365 |
old_sdx = Instance.sNode inst
|
|
366 |
old_p = Container.find old_pdx nl
|
|
367 |
old_s = Container.find old_sdx nl
|
|
368 |
int_p = Node.removePri old_p inst
|
|
369 |
int_s = Node.removeSec old_s inst
|
|
370 |
force_p = Node.offline old_p
|
|
371 |
new_nl = do -- Maybe monad
|
|
372 |
new_p <- Node.addPriEx force_p int_s inst
|
|
373 |
new_s <- Node.addSec int_p inst old_sdx
|
|
374 |
let new_inst = Instance.setBoth inst old_sdx old_pdx
|
|
375 |
return (Container.addTwo old_pdx new_s old_sdx new_p nl,
|
|
376 |
new_inst, old_sdx, old_pdx)
|
|
377 |
in new_nl
|
|
361 |
let old_pdx = Instance.pNode inst |
|
362 |
old_sdx = Instance.sNode inst |
|
363 |
old_p = Container.find old_pdx nl |
|
364 |
old_s = Container.find old_sdx nl |
|
365 |
int_p = Node.removePri old_p inst |
|
366 |
int_s = Node.removeSec old_s inst |
|
367 |
force_p = Node.offline old_p |
|
368 |
new_nl = do -- Maybe monad |
|
369 |
new_p <- Node.addPriEx force_p int_s inst |
|
370 |
new_s <- Node.addSec int_p inst old_sdx |
|
371 |
let new_inst = Instance.setBoth inst old_sdx old_pdx |
|
372 |
return (Container.addTwo old_pdx new_s old_sdx new_p nl, |
|
373 |
new_inst, old_sdx, old_pdx) |
|
374 |
in new_nl |
|
378 | 375 |
|
379 | 376 |
-- Replace the primary (f:, r:np, f) |
380 | 377 |
applyMove nl inst (ReplacePrimary new_pdx) = |
381 |
let old_pdx = Instance.pNode inst
|
|
382 |
old_sdx = Instance.sNode inst
|
|
383 |
old_p = Container.find old_pdx nl
|
|
384 |
old_s = Container.find old_sdx nl
|
|
385 |
tgt_n = Container.find new_pdx nl
|
|
386 |
int_p = Node.removePri old_p inst
|
|
387 |
int_s = Node.removeSec old_s inst
|
|
388 |
force_p = Node.offline old_p
|
|
389 |
new_nl = do -- Maybe monad
|
|
390 |
-- check that the current secondary can host the instance |
|
391 |
-- during the migration |
|
392 |
tmp_s <- Node.addPriEx force_p int_s inst
|
|
393 |
let tmp_s' = Node.removePri tmp_s inst
|
|
394 |
new_p <- Node.addPriEx force_p tgt_n inst
|
|
395 |
new_s <- Node.addSecEx force_p tmp_s' inst new_pdx
|
|
396 |
let new_inst = Instance.setPri inst new_pdx
|
|
397 |
return (Container.add new_pdx new_p $
|
|
398 |
Container.addTwo old_pdx int_p old_sdx new_s nl,
|
|
399 |
new_inst, new_pdx, old_sdx)
|
|
400 |
in new_nl
|
|
378 |
let old_pdx = Instance.pNode inst |
|
379 |
old_sdx = Instance.sNode inst |
|
380 |
old_p = Container.find old_pdx nl |
|
381 |
old_s = Container.find old_sdx nl |
|
382 |
tgt_n = Container.find new_pdx nl |
|
383 |
int_p = Node.removePri old_p inst |
|
384 |
int_s = Node.removeSec old_s inst |
|
385 |
force_p = Node.offline old_p |
|
386 |
new_nl = do -- Maybe monad |
|
387 |
-- check that the current secondary can host the instance
|
|
388 |
-- during the migration
|
|
389 |
tmp_s <- Node.addPriEx force_p int_s inst |
|
390 |
let tmp_s' = Node.removePri tmp_s inst |
|
391 |
new_p <- Node.addPriEx force_p tgt_n inst |
|
392 |
new_s <- Node.addSecEx force_p tmp_s' inst new_pdx |
|
393 |
let new_inst = Instance.setPri inst new_pdx |
|
394 |
return (Container.add new_pdx new_p $ |
|
395 |
Container.addTwo old_pdx int_p old_sdx new_s nl, |
|
396 |
new_inst, new_pdx, old_sdx) |
|
397 |
in new_nl |
|
401 | 398 |
|
402 | 399 |
-- Replace the secondary (r:ns) |
403 | 400 |
applyMove nl inst (ReplaceSecondary new_sdx) = |
404 |
let old_pdx = Instance.pNode inst
|
|
405 |
old_sdx = Instance.sNode inst
|
|
406 |
old_s = Container.find old_sdx nl
|
|
407 |
tgt_n = Container.find new_sdx nl
|
|
408 |
int_s = Node.removeSec old_s inst
|
|
409 |
force_s = Node.offline old_s
|
|
410 |
new_inst = Instance.setSec inst new_sdx
|
|
411 |
new_nl = Node.addSecEx force_s tgt_n inst old_pdx >>=
|
|
412 |
\new_s -> return (Container.addTwo new_sdx
|
|
413 |
new_s old_sdx int_s nl,
|
|
414 |
new_inst, old_pdx, new_sdx)
|
|
415 |
in new_nl
|
|
401 |
let old_pdx = Instance.pNode inst |
|
402 |
old_sdx = Instance.sNode inst |
|
403 |
old_s = Container.find old_sdx nl |
|
404 |
tgt_n = Container.find new_sdx nl |
|
405 |
int_s = Node.removeSec old_s inst |
|
406 |
force_s = Node.offline old_s |
|
407 |
new_inst = Instance.setSec inst new_sdx |
|
408 |
new_nl = Node.addSecEx force_s tgt_n inst old_pdx >>= |
|
409 |
\new_s -> return (Container.addTwo new_sdx |
|
410 |
new_s old_sdx int_s nl, |
|
411 |
new_inst, old_pdx, new_sdx) |
|
412 |
in new_nl |
|
416 | 413 |
|
417 | 414 |
-- Replace the secondary and failover (r:np, f) |
418 | 415 |
applyMove nl inst (ReplaceAndFailover new_pdx) = |
419 |
let old_pdx = Instance.pNode inst
|
|
420 |
old_sdx = Instance.sNode inst
|
|
421 |
old_p = Container.find old_pdx nl
|
|
422 |
old_s = Container.find old_sdx nl
|
|
423 |
tgt_n = Container.find new_pdx nl
|
|
424 |
int_p = Node.removePri old_p inst
|
|
425 |
int_s = Node.removeSec old_s inst
|
|
426 |
force_s = Node.offline old_s
|
|
427 |
new_nl = do -- Maybe monad
|
|
428 |
new_p <- Node.addPri tgt_n inst
|
|
429 |
new_s <- Node.addSecEx force_s int_p inst new_pdx
|
|
430 |
let new_inst = Instance.setBoth inst new_pdx old_pdx
|
|
431 |
return (Container.add new_pdx new_p $
|
|
432 |
Container.addTwo old_pdx new_s old_sdx int_s nl,
|
|
433 |
new_inst, new_pdx, old_pdx)
|
|
434 |
in new_nl
|
|
416 |
let old_pdx = Instance.pNode inst |
|
417 |
old_sdx = Instance.sNode inst |
|
418 |
old_p = Container.find old_pdx nl |
|
419 |
old_s = Container.find old_sdx nl |
|
420 |
tgt_n = Container.find new_pdx nl |
|
421 |
int_p = Node.removePri old_p inst |
|
422 |
int_s = Node.removeSec old_s inst |
|
423 |
force_s = Node.offline old_s |
|
424 |
new_nl = do -- Maybe monad |
|
425 |
new_p <- Node.addPri tgt_n inst |
|
426 |
new_s <- Node.addSecEx force_s int_p inst new_pdx |
|
427 |
let new_inst = Instance.setBoth inst new_pdx old_pdx |
|
428 |
return (Container.add new_pdx new_p $ |
|
429 |
Container.addTwo old_pdx new_s old_sdx int_s nl, |
|
430 |
new_inst, new_pdx, old_pdx) |
|
431 |
in new_nl |
|
435 | 432 |
|
436 | 433 |
-- Failver and replace the secondary (f, r:ns) |
437 | 434 |
applyMove nl inst (FailoverAndReplace new_sdx) = |
438 |
let old_pdx = Instance.pNode inst
|
|
439 |
old_sdx = Instance.sNode inst
|
|
440 |
old_p = Container.find old_pdx nl
|
|
441 |
old_s = Container.find old_sdx nl
|
|
442 |
tgt_n = Container.find new_sdx nl
|
|
443 |
int_p = Node.removePri old_p inst
|
|
444 |
int_s = Node.removeSec old_s inst
|
|
445 |
force_p = Node.offline old_p
|
|
446 |
new_nl = do -- Maybe monad
|
|
447 |
new_p <- Node.addPriEx force_p int_s inst
|
|
448 |
new_s <- Node.addSecEx force_p tgt_n inst old_sdx
|
|
449 |
let new_inst = Instance.setBoth inst old_sdx new_sdx
|
|
450 |
return (Container.add new_sdx new_s $
|
|
451 |
Container.addTwo old_sdx new_p old_pdx int_p nl,
|
|
452 |
new_inst, old_sdx, new_sdx)
|
|
453 |
in new_nl
|
|
435 |
let old_pdx = Instance.pNode inst |
|
436 |
old_sdx = Instance.sNode inst |
|
437 |
old_p = Container.find old_pdx nl |
|
438 |
old_s = Container.find old_sdx nl |
|
439 |
tgt_n = Container.find new_sdx nl |
|
440 |
int_p = Node.removePri old_p inst |
|
441 |
int_s = Node.removeSec old_s inst |
|
442 |
force_p = Node.offline old_p |
|
443 |
new_nl = do -- Maybe monad |
|
444 |
new_p <- Node.addPriEx force_p int_s inst |
|
445 |
new_s <- Node.addSecEx force_p tgt_n inst old_sdx |
|
446 |
let new_inst = Instance.setBoth inst old_sdx new_sdx |
|
447 |
return (Container.add new_sdx new_s $ |
|
448 |
Container.addTwo old_sdx new_p old_pdx int_p nl, |
|
449 |
new_inst, old_sdx, new_sdx) |
|
450 |
in new_nl |
|
454 | 451 |
|
455 | 452 |
-- | Tries to allocate an instance on one given node. |
456 | 453 |
allocateOnSingle :: Node.List -> Instance.Instance -> Ndx |
457 | 454 |
-> OpResult Node.AllocElement |
458 | 455 |
allocateOnSingle nl inst new_pdx = |
459 |
let p = Container.find new_pdx nl
|
|
460 |
new_inst = Instance.setBoth inst new_pdx Node.noSecondary
|
|
461 |
in Node.addPri p inst >>= \new_p -> do
|
|
462 |
let new_nl = Container.add new_pdx new_p nl
|
|
463 |
new_score = compCV nl
|
|
464 |
return (new_nl, new_inst, [new_p], new_score)
|
|
456 |
let p = Container.find new_pdx nl |
|
457 |
new_inst = Instance.setBoth inst new_pdx Node.noSecondary |
|
458 |
in Node.addPri p inst >>= \new_p -> do |
|
459 |
let new_nl = Container.add new_pdx new_p nl |
|
460 |
new_score = compCV nl |
|
461 |
return (new_nl, new_inst, [new_p], new_score) |
|
465 | 462 |
|
466 | 463 |
-- | Tries to allocate an instance on a given pair of nodes. |
467 | 464 |
allocateOnPair :: Node.List -> Instance.Instance -> Ndx -> Ndx |
468 | 465 |
-> OpResult Node.AllocElement |
469 | 466 |
allocateOnPair nl inst new_pdx new_sdx = |
470 |
let tgt_p = Container.find new_pdx nl
|
|
471 |
tgt_s = Container.find new_sdx nl
|
|
472 |
in do
|
|
473 |
new_p <- Node.addPri tgt_p inst
|
|
474 |
new_s <- Node.addSec tgt_s inst new_pdx
|
|
475 |
let new_inst = Instance.setBoth inst new_pdx new_sdx
|
|
476 |
new_nl = Container.addTwo new_pdx new_p new_sdx new_s nl
|
|
477 |
return (new_nl, new_inst, [new_p, new_s], compCV new_nl)
|
|
467 |
let tgt_p = Container.find new_pdx nl |
|
468 |
tgt_s = Container.find new_sdx nl |
|
469 |
in do |
|
470 |
new_p <- Node.addPri tgt_p inst |
|
471 |
new_s <- Node.addSec tgt_s inst new_pdx |
|
472 |
let new_inst = Instance.setBoth inst new_pdx new_sdx |
|
473 |
new_nl = Container.addTwo new_pdx new_p new_sdx new_s nl |
|
474 |
return (new_nl, new_inst, [new_p, new_s], compCV new_nl) |
|
478 | 475 |
|
479 | 476 |
-- | Tries to perform an instance move and returns the best table |
480 | 477 |
-- between the original one and the new one. |
... | ... | |
484 | 481 |
-> IMove -- ^ The move to apply |
485 | 482 |
-> Table -- ^ The final best table |
486 | 483 |
checkSingleStep ini_tbl target cur_tbl move = |
487 |
let |
|
488 |
Table ini_nl ini_il _ ini_plc = ini_tbl |
|
489 |
tmp_resu = applyMove ini_nl target move |
|
490 |
in |
|
491 |
case tmp_resu of |
|
492 |
OpFail _ -> cur_tbl |
|
493 |
OpGood (upd_nl, new_inst, pri_idx, sec_idx) -> |
|
494 |
let tgt_idx = Instance.idx target |
|
495 |
upd_cvar = compCV upd_nl |
|
496 |
upd_il = Container.add tgt_idx new_inst ini_il |
|
497 |
upd_plc = (tgt_idx, pri_idx, sec_idx, move, upd_cvar):ini_plc |
|
498 |
upd_tbl = Table upd_nl upd_il upd_cvar upd_plc |
|
499 |
in |
|
500 |
compareTables cur_tbl upd_tbl |
|
484 |
let Table ini_nl ini_il _ ini_plc = ini_tbl |
|
485 |
tmp_resu = applyMove ini_nl target move |
|
486 |
in case tmp_resu of |
|
487 |
OpFail _ -> cur_tbl |
|
488 |
OpGood (upd_nl, new_inst, pri_idx, sec_idx) -> |
|
489 |
let tgt_idx = Instance.idx target |
|
490 |
upd_cvar = compCV upd_nl |
|
491 |
upd_il = Container.add tgt_idx new_inst ini_il |
|
492 |
upd_plc = (tgt_idx, pri_idx, sec_idx, move, upd_cvar):ini_plc |
|
493 |
upd_tbl = Table upd_nl upd_il upd_cvar upd_plc |
|
494 |
in compareTables cur_tbl upd_tbl |
|
501 | 495 |
|
502 | 496 |
-- | Given the status of the current secondary as a valid new node and |
503 | 497 |
-- the current candidate target node, generate the possible moves for |
... | ... | |
508 | 502 |
-> [IMove] -- ^ List of valid result moves |
509 | 503 |
|
510 | 504 |
possibleMoves _ False tdx = |
511 |
[ReplaceSecondary tdx]
|
|
505 |
[ReplaceSecondary tdx] |
|
512 | 506 |
|
513 | 507 |
possibleMoves True True tdx = |
514 |
[ReplaceSecondary tdx, |
|
515 |
ReplaceAndFailover tdx, |
|
516 |
ReplacePrimary tdx, |
|
517 |
FailoverAndReplace tdx] |
|
508 |
[ ReplaceSecondary tdx |
|
509 |
, ReplaceAndFailover tdx |
|
510 |
, ReplacePrimary tdx |
|
511 |
, FailoverAndReplace tdx |
|
512 |
] |
|
518 | 513 |
|
519 | 514 |
possibleMoves False True tdx = |
520 |
[ReplaceSecondary tdx, |
|
521 |
ReplaceAndFailover tdx] |
|
515 |
[ ReplaceSecondary tdx |
|
516 |
, ReplaceAndFailover tdx |
|
517 |
] |
|
522 | 518 |
|
523 | 519 |
-- | Compute the best move for a given instance. |
524 | 520 |
checkInstanceMove :: [Ndx] -- ^ Allowed target node indices |
... | ... | |
528 | 524 |
-> Instance.Instance -- ^ Instance to move |
529 | 525 |
-> Table -- ^ Best new table for this instance |
530 | 526 |
checkInstanceMove nodes_idx disk_moves inst_moves ini_tbl target = |
531 |
let |
|
532 |
opdx = Instance.pNode target |
|
533 |
osdx = Instance.sNode target |
|
534 |
nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx |
|
535 |
use_secondary = elem osdx nodes_idx && inst_moves |
|
536 |
aft_failover = if use_secondary -- if allowed to failover |
|
527 |
let opdx = Instance.pNode target |
|
528 |
osdx = Instance.sNode target |
|
529 |
nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx |
|
530 |
use_secondary = elem osdx nodes_idx && inst_moves |
|
531 |
aft_failover = if use_secondary -- if allowed to failover |
|
537 | 532 |
then checkSingleStep ini_tbl target ini_tbl Failover |
538 | 533 |
else ini_tbl |
539 |
all_moves = if disk_moves
|
|
534 |
all_moves = if disk_moves |
|
540 | 535 |
then concatMap |
541 |
(possibleMoves use_secondary inst_moves) nodes |
|
536 |
(possibleMoves use_secondary inst_moves) nodes
|
|
542 | 537 |
else [] |
543 | 538 |
in |
544 | 539 |
-- iterate over the possible nodes for this instance |
... | ... | |
552 | 547 |
-> [Instance.Instance] -- ^ List of instances still to move |
553 | 548 |
-> Table -- ^ The new solution |
554 | 549 |
checkMove nodes_idx disk_moves inst_moves ini_tbl victims = |
555 |
let Table _ _ _ ini_plc = ini_tbl
|
|
556 |
-- we're using rwhnf from the Control.Parallel.Strategies
|
|
557 |
-- package; we don't need to use rnf as that would force too
|
|
558 |
-- much evaluation in single-threaded cases, and in
|
|
559 |
-- multi-threaded case the weak head normal form is enough to
|
|
560 |
-- spark the evaluation
|
|
561 |
tables = parMap rwhnf (checkInstanceMove nodes_idx disk_moves
|
|
562 |
inst_moves ini_tbl)
|
|
563 |
victims
|
|
564 |
-- iterate over all instances, computing the best move
|
|
565 |
best_tbl = foldl' compareTables ini_tbl tables
|
|
566 |
Table _ _ _ best_plc = best_tbl
|
|
567 |
in if length best_plc == length ini_plc
|
|
550 |
let Table _ _ _ ini_plc = ini_tbl |
|
551 |
-- we're using rwhnf from the Control.Parallel.Strategies |
|
552 |
-- package; we don't need to use rnf as that would force too |
|
553 |
-- much evaluation in single-threaded cases, and in |
|
554 |
-- multi-threaded case the weak head normal form is enough to |
|
555 |
-- spark the evaluation |
|
556 |
tables = parMap rwhnf (checkInstanceMove nodes_idx disk_moves |
|
557 |
inst_moves ini_tbl) |
|
558 |
victims |
|
559 |
-- iterate over all instances, computing the best move |
|
560 |
best_tbl = foldl' compareTables ini_tbl tables |
|
561 |
Table _ _ _ best_plc = best_tbl |
|
562 |
in if length best_plc == length ini_plc |
|
568 | 563 |
then ini_tbl -- no advancement |
569 | 564 |
else best_tbl |
570 | 565 |
|
... | ... | |
574 | 569 |
-> Score -- ^ Score at which to stop |
575 | 570 |
-> Bool -- ^ The resulting table and commands |
576 | 571 |
doNextBalance ini_tbl max_rounds min_score = |
577 |
let Table _ _ ini_cv ini_plc = ini_tbl
|
|
578 |
ini_plc_len = length ini_plc
|
|
579 |
in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score
|
|
572 |
let Table _ _ ini_cv ini_plc = ini_tbl |
|
573 |
ini_plc_len = length ini_plc |
|
574 |
in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score |
|
580 | 575 |
|
581 | 576 |
-- | Run a balance move. |
582 | 577 |
tryBalance :: Table -- ^ The starting table |
Also available in: Unified diff