Revision 3603605a
b/Makefile.am | ||
---|---|---|
1170 | 1170 |
.PHONY: hlint |
1171 | 1171 |
hlint: $(HS_BUILT_SRCS) |
1172 | 1172 |
if tty -s; then C="-c"; else C=""; fi; \ |
1173 |
hlint --report=doc/hs-lint.html $$C htools |
|
1173 |
hlint --report=doc/hs-lint.html --cross $$C \ |
|
1174 |
--ignore "Use first" \ |
|
1175 |
--ignore "Use comparing" \ |
|
1176 |
--ignore "Use on" \ |
|
1177 |
--ignore "Use Control.Exception.catch" \ |
|
1178 |
--ignore "Reduce duplication" \ |
|
1179 |
$(filter-out htools/Ganeti/THH.hs,$(HS_LIB_SRCS)) |
|
1174 | 1180 |
|
1175 | 1181 |
# a dist hook rule for updating the vcs-version file; this is |
1176 | 1182 |
# hardcoded due to where it needs to build the file... |
b/doc/devnotes.rst | ||
---|---|---|
36 | 36 |
- `HsColour <http://hackage.haskell.org/package/hscolour>`_, again |
37 | 37 |
used for documentation (it's source-code pretty-printing) |
38 | 38 |
- `hlint <http://community.haskell.org/~ndm/hlint/>`_, a source code |
39 |
linter (equivalent to pylint for Python) |
|
39 |
linter (equivalent to pylint for Python), recommended version 1.8 or |
|
40 |
above (tested with 1.8.15) |
|
40 | 41 |
- the `QuickCheck <http://hackage.haskell.org/package/QuickCheck>`_ |
41 | 42 |
library, version 2.x |
42 | 43 |
- ``hpc``, which comes with the compiler, so you should already have |
... | ... | |
69 | 70 |
|
70 | 71 |
make hlint |
71 | 72 |
|
72 |
This is not enabled by default as it gets many false positives, and
|
|
73 |
thus the normal output is not “clean”. The above command will generate
|
|
74 |
both output on the terminal and also a HTML report at
|
|
73 |
This is not enabled by default (as the htools component is
|
|
74 |
optional). The above command will generate both output on the terminal
|
|
75 |
and, if any warnings are found, also an HTML report at
|
|
75 | 76 |
``doc/hs-lint.html``. |
76 | 77 |
|
77 | 78 |
When writing or debugging TemplateHaskell code, it's useful to see |
b/htools/Ganeti/HTools/CLI.hs | ||
---|---|---|
452 | 452 |
(o, n, []) -> |
453 | 453 |
do |
454 | 454 |
let (pr, args) = (foldM (flip id) defaultOptions o, n) |
455 |
po <- (case pr of
|
|
456 |
Bad msg -> do
|
|
457 |
hPutStrLn stderr "Error while parsing command\
|
|
458 |
\line arguments:"
|
|
459 |
hPutStrLn stderr msg
|
|
460 |
exitWith $ ExitFailure 1
|
|
461 |
Ok val -> return val)
|
|
455 |
po <- case pr of |
|
456 |
Bad msg -> do |
|
457 |
hPutStrLn stderr "Error while parsing command\ |
|
458 |
\line arguments:" |
|
459 |
hPutStrLn stderr msg |
|
460 |
exitWith $ ExitFailure 1 |
|
461 |
Ok val -> return val
|
|
462 | 462 |
when (optShowHelp po) $ do |
463 | 463 |
putStr $ usageHelp progname options |
464 | 464 |
exitWith ExitSuccess |
... | ... | |
534 | 534 |
m_cpu = optMcpu opts |
535 | 535 |
m_dsk = optMdsk opts |
536 | 536 |
|
537 |
when (not (null offline_wrong)) $ do
|
|
537 |
unless (null offline_wrong) $ do
|
|
538 | 538 |
hPrintf stderr "Error: Wrong node name(s) set as offline: %s\n" |
539 | 539 |
(commaJoin (map lrContent offline_wrong)) :: IO () |
540 | 540 |
exitWith $ ExitFailure 1 |
b/htools/Ganeti/HTools/Cluster.hs | ||
---|---|---|
373 | 373 |
let (old_pdx, old_sdx, old_p, old_s) = instanceNodes nl inst |
374 | 374 |
int_p = Node.removePri old_p inst |
375 | 375 |
int_s = Node.removeSec old_s inst |
376 |
force_p = Node.offline old_p |
|
377 | 376 |
new_nl = do -- Maybe monad |
378 |
new_p <- Node.addPriEx force_p int_s inst
|
|
377 |
new_p <- Node.addPriEx (Node.offline old_p) int_s inst
|
|
379 | 378 |
new_s <- Node.addSec int_p inst old_sdx |
380 | 379 |
let new_inst = Instance.setBoth inst old_sdx old_pdx |
381 | 380 |
return (Container.addTwo old_pdx new_s old_sdx new_p nl, |
... | ... | |
526 | 525 |
checkInstanceMove nodes_idx disk_moves inst_moves ini_tbl target = |
527 | 526 |
let opdx = Instance.pNode target |
528 | 527 |
osdx = Instance.sNode target |
529 |
nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx |
|
528 |
bad_nodes = [opdx, osdx] |
|
529 |
nodes = filter (`notElem` bad_nodes) nodes_idx |
|
530 | 530 |
use_secondary = elem osdx nodes_idx && inst_moves |
531 | 531 |
aft_failover = if use_secondary -- if allowed to failover |
532 | 532 |
then checkSingleStep ini_tbl target ini_tbl Failover |
... | ... | |
1308 | 1308 |
_ -> fs |
1309 | 1309 |
snl = sortBy (comparing Node.idx) (Container.elems nl) |
1310 | 1310 |
(header, isnum) = unzip $ map Node.showHeader fields |
1311 |
in unlines . map ((:) ' ' . intercalate " ") $
|
|
1311 |
in unlines . map ((:) ' ' . unwords) $
|
|
1312 | 1312 |
formatTable (header:map (Node.list fields) snl) isnum |
1313 | 1313 |
|
1314 | 1314 |
-- | Print the instance list. |
... | ... | |
1335 | 1335 |
header = [ "F", "Name", "Pri_node", "Sec_node", "Auto_bal" |
1336 | 1336 |
, "vcpu", "mem" , "dsk", "lCpu", "lMem", "lDsk", "lNet" ] |
1337 | 1337 |
isnum = False:False:False:False:False:repeat True |
1338 |
in unlines . map ((:) ' ' . intercalate " ") $
|
|
1338 |
in unlines . map ((:) ' ' . unwords) $
|
|
1339 | 1339 |
formatTable (header:map helper sil) isnum |
1340 | 1340 |
|
1341 | 1341 |
-- | Shows statistics for a given node list. |
b/htools/Ganeti/HTools/ExtLoader.hs | ||
---|---|---|
95 | 95 |
" files options should be given.") |
96 | 96 |
exitWith $ ExitFailure 1 |
97 | 97 |
|
98 |
util_contents <- (case optDynuFile opts of |
|
99 |
Just path -> readFile path |
|
100 |
Nothing -> return "") |
|
98 |
util_contents <- maybe (return "") readFile (optDynuFile opts) |
|
101 | 99 |
let util_data = mapM parseUtilisation $ lines util_contents |
102 |
util_data' <- (case util_data of
|
|
103 |
Ok x -> return x
|
|
104 |
Bad y -> do
|
|
105 |
hPutStrLn stderr ("Error: can't parse utilisation" ++
|
|
106 |
" data: " ++ show y)
|
|
107 |
exitWith $ ExitFailure 1)
|
|
100 |
util_data' <- case util_data of |
|
101 |
Ok x -> return x |
|
102 |
Bad y -> do |
|
103 |
hPutStrLn stderr ("Error: can't parse utilisation" ++ |
|
104 |
" data: " ++ show y) |
|
105 |
exitWith $ ExitFailure 1
|
|
108 | 106 |
input_data <- |
109 | 107 |
case () of |
110 | 108 |
_ | setRapi -> wrapIO $ Rapi.loadData mhost |
... | ... | |
115 | 113 |
|
116 | 114 |
let ldresult = input_data >>= mergeData util_data' exTags selInsts exInsts |
117 | 115 |
cdata <- |
118 |
(case ldresult of |
|
119 |
Ok x -> return x |
|
120 |
Bad s -> do |
|
121 |
hPrintf stderr |
|
122 |
"Error: failed to load data, aborting. Details:\n%s\n" s:: IO () |
|
123 |
exitWith $ ExitFailure 1 |
|
124 |
) |
|
116 |
case ldresult of |
|
117 |
Ok x -> return x |
|
118 |
Bad s -> do |
|
119 |
hPrintf stderr |
|
120 |
"Error: failed to load data, aborting. Details:\n%s\n" s:: IO () |
|
121 |
exitWith $ ExitFailure 1 |
|
125 | 122 |
let (fix_msgs, nl) = checkData (cdNodes cdata) (cdInstances cdata) |
126 | 123 |
|
127 | 124 |
unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs |
b/htools/Ganeti/HTools/IAlloc.hs | ||
---|---|---|
50 | 50 |
import Ganeti.HTools.Utils |
51 | 51 |
import Ganeti.HTools.Types |
52 | 52 |
|
53 |
{-# ANN module "HLint: ignore Eta reduce" #-} |
|
54 |
|
|
53 | 55 |
-- | Type alias for the result of an IAllocator call. |
54 | 56 |
type IAllocResult = (String, JSValue, Node.List, Instance.List) |
55 | 57 |
|
... | ... | |
83 | 85 |
else readEitherString $ head nodes |
84 | 86 |
pidx <- lookupNode ktn n pnode |
85 | 87 |
let snodes = tail nodes |
86 |
sidx <- (if null snodes then return Node.noSecondary |
|
87 |
else readEitherString (head snodes) >>= lookupNode ktn n) |
|
88 |
sidx <- if null snodes |
|
89 |
then return Node.noSecondary |
|
90 |
else readEitherString (head snodes) >>= lookupNode ktn n |
|
88 | 91 |
return (n, Instance.setBoth (snd base) pidx sidx) |
89 | 92 |
|
90 | 93 |
-- | Parses a node as found in the cluster node list. |
... | ... | |
101 | 104 |
vm_capable <- annotateResult desc $ maybeFromObj a "vm_capable" |
102 | 105 |
let vm_capable' = fromMaybe True vm_capable |
103 | 106 |
gidx <- lookupGroup ktg n guuid |
104 |
node <- (if offline || drained || not vm_capable'
|
|
105 |
then return $ Node.create n 0 0 0 0 0 0 True gidx |
|
106 |
else do |
|
107 |
mtotal <- extract "total_memory" |
|
108 |
mnode <- extract "reserved_memory" |
|
109 |
mfree <- extract "free_memory" |
|
110 |
dtotal <- extract "total_disk" |
|
111 |
dfree <- extract "free_disk" |
|
112 |
ctotal <- extract "total_cpus" |
|
113 |
return $ Node.create n mtotal mnode mfree |
|
114 |
dtotal dfree ctotal False gidx)
|
|
107 |
node <- if offline || drained || not vm_capable' |
|
108 |
then return $ Node.create n 0 0 0 0 0 0 True gidx
|
|
109 |
else do
|
|
110 |
mtotal <- extract "total_memory"
|
|
111 |
mnode <- extract "reserved_memory"
|
|
112 |
mfree <- extract "free_memory"
|
|
113 |
dtotal <- extract "total_disk"
|
|
114 |
dfree <- extract "free_disk"
|
|
115 |
ctotal <- extract "total_cpus"
|
|
116 |
return $ Node.create n mtotal mnode mfree
|
|
117 |
dtotal dfree ctotal False gidx
|
|
115 | 118 |
return (n, node) |
116 | 119 |
|
117 | 120 |
-- | Parses a group as found in the cluster group list. |
... | ... | |
330 | 333 |
hPutStrLn stderr $ "Error: " ++ err |
331 | 334 |
exitWith $ ExitFailure 1 |
332 | 335 |
Ok (fix_msgs, rq) -> maybeShowWarnings fix_msgs >> return rq |
333 |
(if isJust (optDataFile opts) || (not . null . optNodeSim) opts
|
|
334 |
then do |
|
335 |
cdata <- loadExternalData opts |
|
336 |
let Request rqt _ = r1 |
|
337 |
return $ Request rqt cdata |
|
338 |
else return r1)
|
|
336 |
if isJust (optDataFile opts) || (not . null . optNodeSim) opts |
|
337 |
then do
|
|
338 |
cdata <- loadExternalData opts
|
|
339 |
let Request rqt _ = r1
|
|
340 |
return $ Request rqt cdata
|
|
341 |
else return r1
|
|
339 | 342 |
|
340 | 343 |
-- | Main iallocator pipeline. |
341 | 344 |
runIAllocator :: Request -> (Maybe (Node.List, Instance.List), String) |
b/htools/Ganeti/HTools/Loader.hs | ||
---|---|---|
317 | 317 |
(Node.fMem node - adj_mem) |
318 | 318 |
umsg1 = |
319 | 319 |
if delta_mem > 512 || delta_dsk > 1024 |
320 |
then (printf "node %s is missing %d MB ram \ |
|
321 |
\and %d GB disk" |
|
322 |
nname delta_mem (delta_dsk `div` 1024)): |
|
323 |
msgs |
|
320 |
then printf "node %s is missing %d MB ram \ |
|
321 |
\and %d GB disk" |
|
322 |
nname delta_mem (delta_dsk `div` 1024):msgs |
|
324 | 323 |
else msgs |
325 | 324 |
in (umsg1, newn) |
326 | 325 |
) [] nl |
b/htools/Ganeti/HTools/Luxi.hs | ||
---|---|---|
41 | 41 |
import Ganeti.HTools.Utils (fromJVal, annotateResult, tryFromObj, asJSObject, |
42 | 42 |
fromObj) |
43 | 43 |
|
44 |
{-# ANN module "HLint: ignore Eta reduce" #-} |
|
45 |
|
|
44 | 46 |
-- * Utility functions |
45 | 47 |
|
46 | 48 |
-- | Get values behind \"data\" part of the result. |
... | ... | |
148 | 150 |
xname <- annotateResult "Parsing new instance" (fromJValWithStatus name) |
149 | 151 |
let convert a = genericConvert "Instance" xname a |
150 | 152 |
xdisk <- convert "disk_usage" disk |
151 |
xmem <- (case oram of -- FIXME: remove the "guessing"
|
|
152 |
(_, JSRational _ _) -> convert "oper_ram" oram
|
|
153 |
_ -> convert "be/memory" mem)
|
|
153 |
xmem <- case oram of -- FIXME: remove the "guessing" |
|
154 |
(_, JSRational _ _) -> convert "oper_ram" oram |
|
155 |
_ -> convert "be/memory" mem
|
|
154 | 156 |
xvcpus <- convert "be/vcpus" vcpus |
155 | 157 |
xpnode <- convert "pnode" pnode >>= lookupNode ktn xname |
156 | 158 |
xsnodes <- convert "snodes" snodes::Result [JSString] |
157 |
snode <- (if null xsnodes then return Node.noSecondary |
|
158 |
else lookupNode ktn xname (fromJSString $ head xsnodes)) |
|
159 |
snode <- if null xsnodes |
|
160 |
then return Node.noSecondary |
|
161 |
else lookupNode ktn xname (fromJSString $ head xsnodes) |
|
159 | 162 |
xrunning <- convert "status" status |
160 | 163 |
xtags <- convert "tags" tags |
161 | 164 |
xauto_balance <- convert "auto_balance" auto_balance |
... | ... | |
181 | 184 |
xdrained <- convert "drained" drained |
182 | 185 |
xvm_capable <- convert "vm_capable" vm_capable |
183 | 186 |
xgdx <- convert "group.uuid" g_uuid >>= lookupGroup ktg xname |
184 |
node <- (if xoffline || xdrained || not xvm_capable
|
|
185 |
then return $ Node.create xname 0 0 0 0 0 0 True xgdx |
|
186 |
else do |
|
187 |
xmtotal <- convert "mtotal" mtotal |
|
188 |
xmnode <- convert "mnode" mnode |
|
189 |
xmfree <- convert "mfree" mfree |
|
190 |
xdtotal <- convert "dtotal" dtotal |
|
191 |
xdfree <- convert "dfree" dfree |
|
192 |
xctotal <- convert "ctotal" ctotal |
|
193 |
return $ Node.create xname xmtotal xmnode xmfree |
|
194 |
xdtotal xdfree xctotal False xgdx)
|
|
187 |
node <- if xoffline || xdrained || not xvm_capable |
|
188 |
then return $ Node.create xname 0 0 0 0 0 0 True xgdx
|
|
189 |
else do
|
|
190 |
xmtotal <- convert "mtotal" mtotal
|
|
191 |
xmnode <- convert "mnode" mnode
|
|
192 |
xmfree <- convert "mfree" mfree
|
|
193 |
xdtotal <- convert "dtotal" dtotal
|
|
194 |
xdfree <- convert "dfree" dfree
|
|
195 |
xctotal <- convert "ctotal" ctotal
|
|
196 |
return $ Node.create xname xmtotal xmnode xmfree
|
|
197 |
xdtotal xdfree xctotal False xgdx
|
|
195 | 198 |
return (xname, node) |
196 | 199 |
|
197 | 200 |
parseNode _ v = fail ("Invalid node query result: " ++ show v) |
b/htools/Ganeti/HTools/Node.hs | ||
---|---|---|
328 | 328 |
removeSec :: Node -> Instance.Instance -> Node |
329 | 329 |
removeSec t inst = |
330 | 330 |
let iname = Instance.idx inst |
331 |
uses_disk = Instance.usesLocalStorage inst |
|
332 | 331 |
cur_dsk = fDsk t |
333 | 332 |
pnode = Instance.pNode inst |
334 | 333 |
new_slist = delete iname (sList t) |
335 |
new_dsk = if uses_disk
|
|
334 |
new_dsk = if Instance.usesLocalStorage inst
|
|
336 | 335 |
then cur_dsk + Instance.dsk inst |
337 | 336 |
else cur_dsk |
338 | 337 |
old_peers = peers t |
b/htools/Ganeti/HTools/Program/Hail.hs | ||
---|---|---|
26 | 26 |
module Ganeti.HTools.Program.Hail (main) where |
27 | 27 |
|
28 | 28 |
import Control.Monad |
29 |
import Data.Maybe (fromMaybe) |
|
29 | 30 |
import System.Environment (getArgs) |
30 | 31 |
import System.IO |
31 | 32 |
|
... | ... | |
74 | 75 |
maybeSaveData savecluster "pre-ialloc" "before iallocator run" cdata |
75 | 76 |
|
76 | 77 |
let (maybe_ni, resp) = runIAllocator request |
77 |
(fin_nl, fin_il) = maybe (cdNodes cdata, cdInstances cdata) id maybe_ni
|
|
78 |
(fin_nl, fin_il) = fromMaybe (cdNodes cdata, cdInstances cdata) maybe_ni
|
|
78 | 79 |
putStrLn resp |
79 | 80 |
|
80 | 81 |
maybePrintNodes shownodes "Final cluster" (Cluster.printNodes fin_nl) |
b/htools/Ganeti/HTools/Program/Hbal.hs | ||
---|---|---|
143 | 143 |
saveBalanceCommands opts cmd_data = do |
144 | 144 |
let out_path = fromJust $ optShowCmds opts |
145 | 145 |
putStrLn "" |
146 |
(if out_path == "-" then
|
|
147 |
printf "Commands to run to reach the above solution:\n%s"
|
|
148 |
(unlines . map (" " ++) .
|
|
149 |
filter (/= " check") .
|
|
150 |
lines $ cmd_data)
|
|
151 |
else do |
|
152 |
writeFile out_path (shTemplate ++ cmd_data) |
|
153 |
printf "The commands have been written to file '%s'\n" out_path)
|
|
146 |
if out_path == "-"
|
|
147 |
then printf "Commands to run to reach the above solution:\n%s"
|
|
148 |
(unlines . map (" " ++) . |
|
149 |
filter (/= " check") . |
|
150 |
lines $ cmd_data) |
|
151 |
else do
|
|
152 |
writeFile out_path (shTemplate ++ cmd_data)
|
|
153 |
printf "The commands have been written to file '%s'\n" out_path
|
|
154 | 154 |
|
155 | 155 |
-- | Polls a set of jobs at a fixed interval until all are finished |
156 | 156 |
-- one way or another. |
... | ... | |
176 | 176 |
execWrapper _ _ _ _ [] = return True |
177 | 177 |
execWrapper master nl il cref alljss = do |
178 | 178 |
cancel <- readIORef cref |
179 |
(if cancel > 0
|
|
180 |
then do |
|
181 |
hPrintf stderr "Exiting early due to user request, %d\ |
|
182 |
\ jobset(s) remaining." (length alljss)::IO () |
|
183 |
return False |
|
184 |
else execJobSet master nl il cref alljss)
|
|
179 |
if cancel > 0 |
|
180 |
then do
|
|
181 |
hPrintf stderr "Exiting early due to user request, %d\
|
|
182 |
\ jobset(s) remaining." (length alljss)::IO ()
|
|
183 |
return False
|
|
184 |
else execJobSet master nl il cref alljss
|
|
185 | 185 |
|
186 | 186 |
-- | Execute an entire jobset. |
187 | 187 |
execJobSet :: String -> Node.List |
... | ... | |
202 | 202 |
putStrLn $ "Got job IDs " ++ commaJoin x |
203 | 203 |
waitForJobs client x |
204 | 204 |
) |
205 |
(case jrs of
|
|
206 |
Bad x -> do
|
|
207 |
hPutStrLn stderr $ "Cannot compute job status, aborting: " ++ show x
|
|
208 |
return False
|
|
209 |
Ok x -> if checkJobsStatus x
|
|
210 |
then execWrapper master nl il cref jss |
|
211 |
else do |
|
212 |
hPutStrLn stderr $ "Not all jobs completed successfully: " ++ |
|
213 |
show x |
|
214 |
hPutStrLn stderr "Aborting." |
|
215 |
return False)
|
|
205 |
case jrs of |
|
206 |
Bad x -> do |
|
207 |
hPutStrLn stderr $ "Cannot compute job status, aborting: " ++ show x |
|
208 |
return False |
|
209 |
Ok x -> if checkJobsStatus x |
|
210 |
then execWrapper master nl il cref jss
|
|
211 |
else do
|
|
212 |
hPutStrLn stderr $ "Not all jobs completed successfully: " ++
|
|
213 |
show x
|
|
214 |
hPutStrLn stderr "Aborting."
|
|
215 |
return False
|
|
216 | 216 |
|
217 | 217 |
-- | Executes the jobs, if possible and desired. |
218 | 218 |
maybeExecJobs :: Options |
... | ... | |
279 | 279 |
exitWith $ ExitFailure 1 |
280 | 280 |
Just grp -> |
281 | 281 |
case lookup (Group.idx grp) ngroups of |
282 |
Nothing -> do
|
|
282 |
Nothing -> |
|
283 | 283 |
-- This will only happen if there are no nodes assigned |
284 | 284 |
-- to this group |
285 | 285 |
return (Group.name grp, (Container.empty, Container.empty)) |
... | ... | |
375 | 375 |
|
376 | 376 |
checkNeedRebalance opts ini_cv |
377 | 377 |
|
378 |
(if verbose > 2
|
|
379 |
then printf "Initial coefficients: overall %.8f, %s\n" |
|
380 |
ini_cv (Cluster.printStats nl)::IO () |
|
381 |
else printf "Initial score: %.8f\n" ini_cv)
|
|
378 |
if verbose > 2 |
|
379 |
then printf "Initial coefficients: overall %.8f, %s\n"
|
|
380 |
ini_cv (Cluster.printStats nl)::IO ()
|
|
381 |
else printf "Initial score: %.8f\n" ini_cv
|
|
382 | 382 |
|
383 | 383 |
putStrLn "Trying to minimize the CV..." |
384 | 384 |
let imlen = maximum . map (length . Instance.alias) $ Container.elems il |
b/htools/Ganeti/HTools/Program/Hspace.hs | ||
---|---|---|
268 | 268 |
printAllocationMap verbose msg nl ixes = |
269 | 269 |
when (verbose > 1) $ do |
270 | 270 |
hPutStrLn stderr (msg ++ " map") |
271 |
hPutStr stderr . unlines . map ((:) ' ' . intercalate " ") $
|
|
271 |
hPutStr stderr . unlines . map ((:) ' ' . unwords) $
|
|
272 | 272 |
formatTable (map (printInstance nl) (reverse ixes)) |
273 | 273 |
-- This is the numberic-or-not field |
274 | 274 |
-- specification; the first three fields are |
... | ... | |
315 | 315 |
-> Node.List -> Node.List -> [(FailMode, Int)] -> IO () |
316 | 316 |
printTiered True spec_map m_cpu nl trl_nl _ = do |
317 | 317 |
printKeys $ printStats PTiered (Cluster.totalResources trl_nl) |
318 |
printKeys [("TSPEC", intercalate " " (formatSpecMap spec_map))]
|
|
318 |
printKeys [("TSPEC", unwords (formatSpecMap spec_map))]
|
|
319 | 319 |
printAllocationStats m_cpu nl trl_nl |
320 | 320 |
|
321 | 321 |
printTiered False spec_map _ ini_nl fin_nl sreason = do |
... | ... | |
433 | 433 |
|
434 | 434 |
-- Run the tiered allocation, if enabled |
435 | 435 |
|
436 |
(case optTieredSpec opts of
|
|
437 |
Nothing -> return ()
|
|
438 |
Just tspec -> do
|
|
439 |
(treason, trl_nl, _, spec_map) <- |
|
436 |
case optTieredSpec opts of |
|
437 |
Nothing -> return () |
|
438 |
Just tspec -> do |
|
439 |
(treason, trl_nl, _, spec_map) <-
|
|
440 | 440 |
runAllocation cdata stop_allocation |
441 |
(Cluster.tieredAlloc nl il alloclimit (iofspec tspec)
|
|
442 |
allocnodes [] []) tspec SpecTiered opts
|
|
441 |
(Cluster.tieredAlloc nl il alloclimit (iofspec tspec) |
|
442 |
allocnodes [] []) tspec SpecTiered opts |
|
443 | 443 |
|
444 |
printTiered machine_r spec_map (optMcpu opts) nl trl_nl treason |
|
445 |
) |
|
444 |
printTiered machine_r spec_map (optMcpu opts) nl trl_nl treason |
|
446 | 445 |
|
447 | 446 |
-- Run the standard (avg-mode) allocation |
448 | 447 |
|
b/htools/Ganeti/HTools/QC.hs | ||
---|---|---|
133 | 133 |
(_, nlst) = Loader.assignIndices namelst |
134 | 134 |
in nlst |
135 | 135 |
|
136 |
-- | Make a small cluster, both nodes and instances. |
|
137 |
makeSmallEmptyCluster :: Node.Node -> Int -> Instance.Instance |
|
138 |
-> (Node.List, Instance.List, Instance.Instance) |
|
139 |
makeSmallEmptyCluster node count inst = |
|
140 |
(makeSmallCluster node count, Container.empty, |
|
141 |
setInstanceSmallerThanNode node inst) |
|
142 |
|
|
136 | 143 |
-- | Checks if a node is "big" enough. |
137 | 144 |
isNodeBig :: Node.Node -> Int -> Bool |
138 | 145 |
isNodeBig node size = Node.availDisk node > size * Types.unitDsk |
... | ... | |
246 | 253 |
, "OP_INSTANCE_FAILOVER" |
247 | 254 |
, "OP_INSTANCE_MIGRATE" |
248 | 255 |
] |
249 |
(case op_id of
|
|
250 |
"OP_TEST_DELAY" ->
|
|
251 |
liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary
|
|
252 |
"OP_INSTANCE_REPLACE_DISKS" ->
|
|
253 |
liftM5 OpCodes.OpInstanceReplaceDisks arbitrary arbitrary
|
|
254 |
arbitrary arbitrary arbitrary |
|
255 |
"OP_INSTANCE_FAILOVER" ->
|
|
256 |
liftM3 OpCodes.OpInstanceFailover arbitrary arbitrary
|
|
257 |
arbitrary
|
|
258 |
"OP_INSTANCE_MIGRATE" ->
|
|
259 |
liftM5 OpCodes.OpInstanceMigrate arbitrary arbitrary
|
|
260 |
arbitrary arbitrary arbitrary
|
|
261 |
_ -> fail "Wrong opcode")
|
|
256 |
case op_id of |
|
257 |
"OP_TEST_DELAY" -> |
|
258 |
liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary |
|
259 |
"OP_INSTANCE_REPLACE_DISKS" -> |
|
260 |
liftM5 OpCodes.OpInstanceReplaceDisks arbitrary arbitrary |
|
261 |
arbitrary arbitrary arbitrary
|
|
262 |
"OP_INSTANCE_FAILOVER" -> |
|
263 |
liftM3 OpCodes.OpInstanceFailover arbitrary arbitrary |
|
264 |
arbitrary |
|
265 |
"OP_INSTANCE_MIGRATE" -> |
|
266 |
liftM5 OpCodes.OpInstanceMigrate arbitrary arbitrary |
|
267 |
arbitrary arbitrary arbitrary |
|
268 |
_ -> fail "Wrong opcode"
|
|
262 | 269 |
|
263 | 270 |
instance Arbitrary Jobs.OpStatus where |
264 | 271 |
arbitrary = elements [minBound..maxBound] |
... | ... | |
283 | 290 |
|
284 | 291 |
instance Arbitrary a => Arbitrary (Types.OpResult a) where |
285 | 292 |
arbitrary = arbitrary >>= \c -> |
286 |
case c of
|
|
287 |
False -> liftM Types.OpFail arbitrary
|
|
288 |
True -> liftM Types.OpGood arbitrary
|
|
293 |
if c
|
|
294 |
then liftM Types.OpGood arbitrary
|
|
295 |
else liftM Types.OpFail arbitrary
|
|
289 | 296 |
|
290 | 297 |
-- * Actual tests |
291 | 298 |
|
... | ... | |
295 | 302 |
-- not contain commas, then join+split should be idempotent. |
296 | 303 |
prop_Utils_commaJoinSplit = |
297 | 304 |
forAll (arbitrary `suchThat` |
298 |
(\l -> l /= [""] && all (not . elem ',') l )) $ \lst ->
|
|
305 |
(\l -> l /= [""] && all (notElem ',') l )) $ \lst ->
|
|
299 | 306 |
Utils.sepSplit ',' (Utils.commaJoin lst) ==? lst |
300 | 307 |
|
301 | 308 |
-- | Split and join should always be idempotent. |
... | ... | |
323 | 330 |
-> [Int] -- ^ List of True values |
324 | 331 |
-> Gen Prop -- ^ Test result |
325 | 332 |
prop_Utils_select def lst1 lst2 = |
326 |
Utils.select def cndlist ==? expectedresult
|
|
333 |
Utils.select def (flist ++ tlist) ==? expectedresult
|
|
327 | 334 |
where expectedresult = Utils.if' (null lst2) def (head lst2) |
328 | 335 |
flist = map (\e -> (False, e)) lst1 |
329 | 336 |
tlist = map (\e -> (True, e)) lst2 |
330 |
cndlist = flist ++ tlist |
|
331 | 337 |
|
332 | 338 |
-- | Test basic select functionality with undefined default |
333 | 339 |
prop_Utils_select_undefd :: [Int] -- ^ List of False values |
334 | 340 |
-> NonEmptyList Int -- ^ List of True values |
335 | 341 |
-> Gen Prop -- ^ Test result |
336 | 342 |
prop_Utils_select_undefd lst1 (NonEmpty lst2) = |
337 |
Utils.select undefined cndlist ==? head lst2
|
|
343 |
Utils.select undefined (flist ++ tlist) ==? head lst2
|
|
338 | 344 |
where flist = map (\e -> (False, e)) lst1 |
339 | 345 |
tlist = map (\e -> (True, e)) lst2 |
340 |
cndlist = flist ++ tlist |
|
341 | 346 |
|
342 | 347 |
-- | Test basic select functionality with undefined list values |
343 | 348 |
prop_Utils_select_undefv :: [Int] -- ^ List of False values |
... | ... | |
422 | 427 |
|
423 | 428 |
-- ** Container tests |
424 | 429 |
|
430 |
-- we silence the following due to hlint bug fixed in later versions |
|
431 |
{-# ANN prop_Container_addTwo "HLint: ignore Avoid lambda" #-} |
|
425 | 432 |
prop_Container_addTwo cdata i1 i2 = |
426 | 433 |
fn i1 i2 cont == fn i2 i1 cont && |
427 | 434 |
fn i1 i2 cont == fn i1 i2 (fn i1 i2 cont) |
... | ... | |
444 | 451 |
forAll (vector cnt) $ \ names -> |
445 | 452 |
(length . nub) (map fst names ++ map snd names) == |
446 | 453 |
length names * 2 && |
447 |
not (othername `elem` (map fst names ++ map snd names)) ==>
|
|
454 |
othername `notElem` (map fst names ++ map snd names) ==>
|
|
448 | 455 |
let nl = makeSmallCluster node cnt |
449 | 456 |
nodes = Container.elems nl |
450 | 457 |
nodes' = map (\((name, alias), nn) -> (Node.idx nn, |
... | ... | |
455 | 462 |
target = snd (nodes' !! fidx) |
456 | 463 |
in Container.findByName nl' (Node.name target) == Just target && |
457 | 464 |
Container.findByName nl' (Node.alias target) == Just target && |
458 |
Container.findByName nl' othername == Nothing
|
|
465 |
isNothing (Container.findByName nl' othername)
|
|
459 | 466 |
|
460 | 467 |
testSuite "Container" |
461 | 468 |
[ 'prop_Container_addTwo |
... | ... | |
765 | 772 |
-- this is not related to rMem, but as good a place to |
766 | 773 |
-- test as any |
767 | 774 |
inst_idx `elem` Node.sList a_ab && |
768 |
not (inst_idx `elem` Node.sList d_ab)
|
|
775 |
inst_idx `notElem` Node.sList d_ab
|
|
769 | 776 |
x -> printTestCase ("Failed to add/remove instances: " ++ show x) False |
770 | 777 |
|
771 | 778 |
-- | Check mdsk setting. |
... | ... | |
858 | 865 |
&& Node.availDisk node > 0 |
859 | 866 |
&& Node.availMem node > 0 |
860 | 867 |
==> |
861 |
let nl = makeSmallCluster node count |
|
862 |
il = Container.empty |
|
863 |
inst' = setInstanceSmallerThanNode node inst |
|
868 |
let (nl, il, inst') = makeSmallEmptyCluster node count inst |
|
864 | 869 |
in case Cluster.genAllocNodes defGroupList nl 2 True >>= |
865 | 870 |
Cluster.tryAlloc nl il inst' of |
866 | 871 |
Types.Bad _ -> False |
... | ... | |
900 | 905 |
&& not (Node.failN1 node) |
901 | 906 |
&& isNodeBig node 4 |
902 | 907 |
==> |
903 |
let nl = makeSmallCluster node count |
|
904 |
il = Container.empty |
|
905 |
inst' = setInstanceSmallerThanNode node inst |
|
908 |
let (nl, il, inst') = makeSmallEmptyCluster node count inst |
|
906 | 909 |
in case Cluster.genAllocNodes defGroupList nl 2 True >>= |
907 | 910 |
Cluster.tryAlloc nl il inst' of |
908 | 911 |
Types.Bad _ -> False |
b/htools/Ganeti/HTools/Rapi.hs | ||
---|---|---|
48 | 48 |
import qualified Ganeti.HTools.Instance as Instance |
49 | 49 |
import qualified Ganeti.Constants as C |
50 | 50 |
|
51 |
{-# ANN module "HLint: ignore Eta reduce" #-} |
|
52 |
|
|
51 | 53 |
-- | Read an URL via curl and return the body if successful. |
52 | 54 |
getUrl :: (Monad m) => String -> IO (m String) |
53 | 55 |
|
... | ... | |
108 | 110 |
disk <- extract "disk_usage" a |
109 | 111 |
beparams <- liftM fromJSObject (extract "beparams" a) |
110 | 112 |
omem <- extract "oper_ram" a |
111 |
mem <- (case omem of
|
|
112 |
JSRational _ _ -> annotateResult owner_name (fromJVal omem)
|
|
113 |
_ -> extract "memory" beparams)
|
|
113 |
mem <- case omem of |
|
114 |
JSRational _ _ -> annotateResult owner_name (fromJVal omem) |
|
115 |
_ -> extract "memory" beparams
|
|
114 | 116 |
vcpus <- extract "vcpus" beparams |
115 | 117 |
pnode <- extract "pnode" a >>= lookupNode ktn name |
116 | 118 |
snodes <- extract "snodes" a |
117 |
snode <- (if null snodes then return Node.noSecondary |
|
118 |
else readEitherString (head snodes) >>= lookupNode ktn name) |
|
119 |
snode <- if null snodes |
|
120 |
then return Node.noSecondary |
|
121 |
else readEitherString (head snodes) >>= lookupNode ktn name |
|
119 | 122 |
running <- extract "status" a |
120 | 123 |
tags <- extract "tags" a |
121 | 124 |
auto_balance <- extract "auto_balance" beparams |
... | ... | |
136 | 139 |
let vm_cap' = fromMaybe True vm_cap |
137 | 140 |
guuid <- annotateResult desc $ maybeFromObj a "group.uuid" |
138 | 141 |
guuid' <- lookupGroup ktg name (fromMaybe defaultGroupID guuid) |
139 |
node <- (if offline || drained || not vm_cap'
|
|
140 |
then return $ Node.create name 0 0 0 0 0 0 True guuid' |
|
141 |
else do |
|
142 |
mtotal <- extract "mtotal" |
|
143 |
mnode <- extract "mnode" |
|
144 |
mfree <- extract "mfree" |
|
145 |
dtotal <- extract "dtotal" |
|
146 |
dfree <- extract "dfree" |
|
147 |
ctotal <- extract "ctotal" |
|
148 |
return $ Node.create name mtotal mnode mfree |
|
149 |
dtotal dfree ctotal False guuid')
|
|
142 |
node <- if offline || drained || not vm_cap' |
|
143 |
then return $ Node.create name 0 0 0 0 0 0 True guuid'
|
|
144 |
else do
|
|
145 |
mtotal <- extract "mtotal"
|
|
146 |
mnode <- extract "mnode"
|
|
147 |
mfree <- extract "mfree"
|
|
148 |
dtotal <- extract "dtotal"
|
|
149 |
dfree <- extract "dfree"
|
|
150 |
ctotal <- extract "ctotal"
|
|
151 |
return $ Node.create name mtotal mnode mfree
|
|
152 |
dtotal dfree ctotal False guuid'
|
|
150 | 153 |
return (name, node) |
151 | 154 |
|
152 | 155 |
-- | Construct a group from a JSON object. |
b/htools/Ganeti/HTools/Text.hs | ||
---|---|---|
154 | 154 |
loadInst ktn [ name, mem, dsk, vcpus, status, auto_bal, pnode, snode |
155 | 155 |
, dt, tags ] = do |
156 | 156 |
pidx <- lookupNode ktn name pnode |
157 |
sidx <- (if null snode then return Node.noSecondary |
|
158 |
else lookupNode ktn name snode) |
|
157 |
sidx <- if null snode |
|
158 |
then return Node.noSecondary |
|
159 |
else lookupNode ktn name snode |
|
159 | 160 |
vmem <- tryRead name mem |
160 | 161 |
vdsk <- tryRead name dsk |
161 | 162 |
vvcpus <- tryRead name vcpus |
b/htools/Ganeti/Luxi.hs | ||
---|---|---|
60 | 60 |
withTimeout :: Int -> String -> IO a -> IO a |
61 | 61 |
withTimeout secs descr action = do |
62 | 62 |
result <- timeout (secs * 1000000) action |
63 |
(case result of
|
|
64 |
Nothing -> fail $ "Timeout in " ++ descr
|
|
65 |
Just v -> return v)
|
|
63 |
case result of |
|
64 |
Nothing -> fail $ "Timeout in " ++ descr |
|
65 |
Just v -> return v
|
|
66 | 66 |
|
67 | 67 |
-- * Generic protocol functionality |
68 | 68 |
|
... | ... | |
213 | 213 |
nbuf <- withTimeout queryTimeout "reading luxi response" $ |
214 | 214 |
S.recv (socket s) 4096 |
215 | 215 |
let (msg, remaining) = break (eOM ==) nbuf |
216 |
(if null remaining
|
|
217 |
then _recv (obuf ++ msg) |
|
218 |
else return (obuf ++ msg, tail remaining))
|
|
216 |
if null remaining |
|
217 |
then _recv (obuf ++ msg)
|
|
218 |
else return (obuf ++ msg, tail remaining)
|
|
219 | 219 |
cbuf <- readIORef $ rbuf s |
220 | 220 |
let (imsg, ibuf) = break (eOM ==) cbuf |
221 | 221 |
(msg, nbuf) <- |
222 |
(if null ibuf -- if old buffer didn't contain a full message
|
|
223 |
then _recv cbuf -- then we read from network
|
|
224 |
else return (imsg, tail ibuf)) -- else we return data from our buffer
|
|
222 |
if null ibuf -- if old buffer didn't contain a full message |
|
223 |
then _recv cbuf -- then we read from network |
|
224 |
else return (imsg, tail ibuf) -- else we return data from our buffer
|
|
225 | 225 |
writeIORef (rbuf s) nbuf |
226 | 226 |
return msg |
227 | 227 |
|
... | ... | |
244 | 244 |
let arr = J.fromJSObject oarr |
245 | 245 |
status <- fromObj arr (strOfKey Success)::Result Bool |
246 | 246 |
let rkey = strOfKey Result |
247 |
(if status
|
|
248 |
then fromObj arr rkey |
|
249 |
else fromObj arr rkey >>= fail)
|
|
247 |
if status |
|
248 |
then fromObj arr rkey
|
|
249 |
else fromObj arr rkey >>= fail
|
|
250 | 250 |
|
251 | 251 |
-- | Generic luxi method call. |
252 | 252 |
callMethod :: LuxiOp -> Client -> IO (Result JSValue) |
b/htools/test.hs | ||
---|---|---|
136 | 136 |
Nothing -> return Nothing |
137 | 137 |
Just str -> do |
138 | 138 |
let vs = sepSplit ',' str |
139 |
(case vs of
|
|
140 |
[rng, size] -> return $ Just (read rng, read size)
|
|
141 |
_ -> fail "Invalid state given")
|
|
139 |
case vs of |
|
140 |
[rng, size] -> return $ Just (read rng, read size) |
|
141 |
_ -> fail "Invalid state given"
|
|
142 | 142 |
return args { chatty = optVerbose opts > 1, |
143 | 143 |
replay = r |
144 | 144 |
} |
... | ... | |
149 | 149 |
let wrap = map (wrapTest errs) |
150 | 150 |
cmd_args <- getArgs |
151 | 151 |
(opts, args) <- parseOpts cmd_args "test" options |
152 |
tests <- (if null args
|
|
153 |
then return allTests
|
|
154 |
else (let args' = map lower args
|
|
155 |
selected = filter ((`elem` args') . lower .
|
|
156 |
extractName) allTests
|
|
157 |
in if null selected
|
|
158 |
then do
|
|
159 |
hPutStrLn stderr $ "No tests matching '"
|
|
160 |
++ intercalate " " args ++ "', available tests: "
|
|
161 |
++ intercalate ", " (map extractName allTests)
|
|
162 |
exitWith $ ExitFailure 1
|
|
163 |
else return selected))
|
|
152 |
tests <- if null args |
|
153 |
then return allTests |
|
154 |
else let args' = map lower args
|
|
155 |
selected = filter ((`elem` args') . lower . |
|
156 |
extractName) allTests |
|
157 |
in if null selected |
|
158 |
then do |
|
159 |
hPutStrLn stderr $ "No tests matching '" |
|
160 |
++ unwords args ++ "', available tests: "
|
|
161 |
++ intercalate ", " (map extractName allTests) |
|
162 |
exitWith $ ExitFailure 1 |
|
163 |
else return selected
|
|
164 | 164 |
|
165 | 165 |
let max_count = maximum $ map (\(_, (_, t)) -> length t) tests |
166 | 166 |
mapM_ (\(targs, (name, tl)) -> |
167 | 167 |
transformTestOpts targs opts >>= \newargs -> |
168 | 168 |
runTests name newargs (wrap tl) max_count) tests |
169 | 169 |
terr <- readIORef errs |
170 |
(if terr > 0 |
|
171 |
then do |
|
172 |
hPutStrLn stderr $ "A total of " ++ show terr ++ " tests failed." |
|
173 |
exitWith $ ExitFailure 1 |
|
174 |
else putStrLn "All tests succeeded.") |
|
170 |
if terr > 0 |
|
171 |
then do |
|
172 |
hPutStrLn stderr $ "A total of " ++ show terr ++ " tests failed." |
|
173 |
exitWith $ ExitFailure 1 |
|
174 |
else putStrLn "All tests succeeded." |
Also available in: Unified diff