Revision 51000365 htools/Ganeti/HTools/CLI.hs
b/htools/Ganeti/HTools/CLI.hs | ||
---|---|---|
30 | 30 |
module Ganeti.HTools.CLI |
31 | 31 |
( Options(..) |
32 | 32 |
, OptType |
33 |
, parseOpts |
|
33 |
, defaultOptions |
|
34 |
, Ganeti.HTools.CLI.parseOpts |
|
34 | 35 |
, parseOptsInner |
35 | 36 |
, parseYesNo |
36 | 37 |
, parseISpecString |
... | ... | |
73 | 74 |
, oPrintNodes |
74 | 75 |
, oQuiet |
75 | 76 |
, oRapiMaster |
76 |
, oReplay |
|
77 | 77 |
, oSaveCluster |
78 | 78 |
, oSelInst |
79 | 79 |
, oShowHelp |
80 | 80 |
, oShowVer |
81 | 81 |
, oStdSpec |
82 |
, oTestCount |
|
83 | 82 |
, oTieredSpec |
84 | 83 |
, oVerbose |
85 | 84 |
) where |
... | ... | |
87 | 86 |
import Control.Monad |
88 | 87 |
import Data.Char (toUpper) |
89 | 88 |
import Data.Maybe (fromMaybe) |
90 |
import qualified Data.Version |
|
91 | 89 |
import System.Console.GetOpt |
92 | 90 |
import System.IO |
93 |
import System.Info |
|
94 |
import System.Exit |
|
95 | 91 |
import Text.Printf (printf) |
96 | 92 |
|
97 |
import qualified Ganeti.Version as Version (version) |
|
98 | 93 |
import qualified Ganeti.HTools.Container as Container |
99 | 94 |
import qualified Ganeti.HTools.Node as Node |
100 | 95 |
import qualified Ganeti.Constants as C |
101 | 96 |
import Ganeti.HTools.Types |
102 | 97 |
import Ganeti.HTools.Utils |
103 | 98 |
import Ganeti.BasicTypes |
99 |
import Ganeti.Common as Common |
|
104 | 100 |
|
105 | 101 |
-- * Constants |
106 | 102 |
|
... | ... | |
198 | 194 |
} |
199 | 195 |
|
200 | 196 |
-- | Abrreviation for the option type. |
201 |
type OptType = OptDescr (Options -> Result Options) |
|
197 |
type OptType = GenericOptType Options |
|
198 |
|
|
199 |
instance StandardOptions Options where |
|
200 |
helpRequested = optShowHelp |
|
201 |
verRequested = optShowVer |
|
202 |
requestHelp = \opts -> opts { optShowHelp = True } |
|
203 |
requestVer = \opts -> opts { optShowVer = True } |
|
202 | 204 |
|
203 | 205 |
-- * Helper functions |
204 | 206 |
|
... | ... | |
232 | 234 |
|
233 | 235 |
oDiskTemplate :: OptType |
234 | 236 |
oDiskTemplate = Option "" ["disk-template"] |
235 |
(ReqArg (\ t opts -> do |
|
236 |
dt <- diskTemplateFromRaw t |
|
237 |
return $ opts { optDiskTemplate = Just dt }) |
|
237 |
(reqWithConversion diskTemplateFromRaw |
|
238 |
(\dt opts -> Ok opts { optDiskTemplate = Just dt }) |
|
238 | 239 |
"TEMPLATE") "select the desired disk template" |
239 | 240 |
|
240 | 241 |
oSpindleUse :: OptType |
241 | 242 |
oSpindleUse = Option "" ["spindle-use"] |
242 |
(ReqArg (\ n opts -> do
|
|
243 |
su <- tryRead "parsing spindle-use" n
|
|
244 |
when (su < 0) $
|
|
245 |
fail "Invalid value of the spindle-use\
|
|
246 |
\ (expected >= 0)"
|
|
247 |
return $ opts { optSpindleUse = Just su })
|
|
243 |
(reqWithConversion (tryRead "parsing spindle-use")
|
|
244 |
(\su opts -> do
|
|
245 |
when (su < 0) $ |
|
246 |
fail "Invalid value of the spindle-use\ |
|
247 |
\ (expected >= 0)" |
|
248 |
return $ opts { optSpindleUse = Just su }) |
|
248 | 249 |
"SPINDLES") "select how many virtual spindle instances use\ |
249 | 250 |
\ [default read from cluster]" |
250 | 251 |
|
... | ... | |
314 | 315 |
|
315 | 316 |
oMaxCpu :: OptType |
316 | 317 |
oMaxCpu = Option "" ["max-cpu"] |
317 |
(ReqArg (\ n opts -> do
|
|
318 |
mcpu <- tryRead "parsing max-cpu" n
|
|
319 |
when (mcpu <= 0) $
|
|
320 |
fail "Invalid value of the max-cpu ratio,\
|
|
321 |
\ expected >0"
|
|
322 |
return $ opts { optMcpu = Just mcpu }) "RATIO")
|
|
318 |
(reqWithConversion (tryRead "parsing max-cpu")
|
|
319 |
(\mcpu opts -> do
|
|
320 |
when (mcpu <= 0) $ |
|
321 |
fail "Invalid value of the max-cpu ratio,\ |
|
322 |
\ expected >0" |
|
323 |
return $ opts { optMcpu = Just mcpu }) "RATIO") |
|
323 | 324 |
"maximum virtual-to-physical cpu ratio for nodes (from 0\ |
324 | 325 |
\ upwards) [default read from cluster]" |
325 | 326 |
|
326 | 327 |
oMaxSolLength :: OptType |
327 | 328 |
oMaxSolLength = Option "l" ["max-length"] |
328 |
(ReqArg (\ i opts -> Ok opts { optMaxLength = read i }) "N") |
|
329 |
(reqWithConversion (tryRead "max solution length") |
|
330 |
(\i opts -> Ok opts { optMaxLength = i }) "N") |
|
329 | 331 |
"cap the solution at this many balancing or allocation \ |
330 | 332 |
\ rounds (useful for very unbalanced clusters or empty \ |
331 | 333 |
\ clusters)" |
332 | 334 |
|
333 | 335 |
oMinDisk :: OptType |
334 | 336 |
oMinDisk = Option "" ["min-disk"] |
335 |
(ReqArg (\ n opts -> Ok opts { optMdsk = read n }) "RATIO") |
|
337 |
(reqWithConversion (tryRead "min free disk space") |
|
338 |
(\n opts -> Ok opts { optMdsk = n }) "RATIO") |
|
336 | 339 |
"minimum free disk space for nodes (between 0 and 1) [0]" |
337 | 340 |
|
338 | 341 |
oMinGain :: OptType |
339 | 342 |
oMinGain = Option "g" ["min-gain"] |
340 |
(ReqArg (\ g opts -> Ok opts { optMinGain = read g }) "DELTA") |
|
343 |
(reqWithConversion (tryRead "min gain") |
|
344 |
(\g opts -> Ok opts { optMinGain = g }) "DELTA") |
|
341 | 345 |
"minimum gain to aim for in a balancing step before giving up" |
342 | 346 |
|
343 | 347 |
oMinGainLim :: OptType |
344 | 348 |
oMinGainLim = Option "" ["min-gain-limit"] |
345 |
(ReqArg (\ g opts -> Ok opts { optMinGainLim = read g }) "SCORE") |
|
349 |
(reqWithConversion (tryRead "min gain limit") |
|
350 |
(\g opts -> Ok opts { optMinGainLim = g }) "SCORE") |
|
346 | 351 |
"minimum cluster score for which we start checking the min-gain" |
347 | 352 |
|
348 | 353 |
oMinScore :: OptType |
349 | 354 |
oMinScore = Option "e" ["min-score"] |
350 |
(ReqArg (\ e opts -> Ok opts { optMinScore = read e }) "EPSILON") |
|
355 |
(reqWithConversion (tryRead "min score") |
|
356 |
(\e opts -> Ok opts { optMinScore = e }) "EPSILON") |
|
351 | 357 |
"mininum score to aim for" |
352 | 358 |
|
353 | 359 |
oNoHeaders :: OptType |
... | ... | |
416 | 422 |
(ReqArg (\ f opts -> Ok opts { optSaveCluster = Just f }) "FILE") |
417 | 423 |
"Save cluster state at the end of the processing to FILE" |
418 | 424 |
|
419 |
oShowHelp :: OptType |
|
420 |
oShowHelp = Option "h" ["help"] |
|
421 |
(NoArg (\ opts -> Ok opts { optShowHelp = True})) |
|
422 |
"show help" |
|
423 |
|
|
424 |
oShowVer :: OptType |
|
425 |
oShowVer = Option "V" ["version"] |
|
426 |
(NoArg (\ opts -> Ok opts { optShowVer = True})) |
|
427 |
"show the version of the program" |
|
428 |
|
|
429 | 425 |
oStdSpec :: OptType |
430 | 426 |
oStdSpec = Option "" ["standard-alloc"] |
431 | 427 |
(ReqArg (\ inp opts -> do |
... | ... | |
434 | 430 |
"STDSPEC") |
435 | 431 |
"enable standard specs allocation, given as 'disk,ram,cpu'" |
436 | 432 |
|
437 |
oTestCount :: OptType |
|
438 |
oTestCount = Option "" ["test-count"] |
|
439 |
(ReqArg (\ inp opts -> do |
|
440 |
tcount <- tryRead "parsing test count" inp |
|
441 |
return $ opts { optTestCount = Just tcount } ) |
|
442 |
"COUNT") |
|
443 |
"override the target test count" |
|
444 |
|
|
445 | 433 |
oTieredSpec :: OptType |
446 | 434 |
oTieredSpec = Option "" ["tiered-alloc"] |
447 | 435 |
(ReqArg (\ inp opts -> do |
... | ... | |
450 | 438 |
"TSPEC") |
451 | 439 |
"enable tiered specs allocation, given as 'disk,ram,cpu'" |
452 | 440 |
|
453 |
oReplay :: OptType |
|
454 |
oReplay = Option "" ["replay"] |
|
455 |
(ReqArg (\ stat opts -> Ok opts { optReplay = Just stat } ) "STATE") |
|
456 |
"Pre-seed the random number generator with STATE" |
|
457 |
|
|
458 | 441 |
oVerbose :: OptType |
459 | 442 |
oVerbose = Option "v" ["verbose"] |
460 | 443 |
(NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts + 1 })) |
... | ... | |
462 | 445 |
|
463 | 446 |
-- * Functions |
464 | 447 |
|
465 |
-- | Helper for parsing a yes\/no command line flag. |
|
466 |
parseYesNo :: Bool -- ^ Default value (when we get a @Nothing@) |
|
467 |
-> Maybe String -- ^ Parameter value |
|
468 |
-> Result Bool -- ^ Resulting boolean value |
|
469 |
parseYesNo v Nothing = return v |
|
470 |
parseYesNo _ (Just "yes") = return True |
|
471 |
parseYesNo _ (Just "no") = return False |
|
472 |
parseYesNo _ (Just s) = fail ("Invalid choice '" ++ s ++ |
|
473 |
"', pass one of 'yes' or 'no'") |
|
474 |
|
|
475 |
-- | Usage info. |
|
476 |
usageHelp :: String -> [OptType] -> String |
|
477 |
usageHelp progname = |
|
478 |
usageInfo (printf "%s %s\nUsage: %s [OPTION...]" |
|
479 |
progname Version.version progname) |
|
480 |
|
|
481 |
-- | Show the program version info. |
|
482 |
versionInfo :: String -> String |
|
483 |
versionInfo progname = |
|
484 |
printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n" |
|
485 |
progname Version.version compilerName |
|
486 |
(Data.Version.showVersion compilerVersion) |
|
487 |
os arch |
|
488 |
|
|
489 |
-- | Command line parser, using the 'Options' structure. |
|
448 |
-- | Wrapper over 'Common.parseOpts' with our custom options. |
|
490 | 449 |
parseOpts :: [String] -- ^ The command line arguments |
491 | 450 |
-> String -- ^ The program name |
492 | 451 |
-> [OptType] -- ^ The supported command line options |
493 | 452 |
-> IO (Options, [String]) -- ^ The resulting options and leftover |
494 | 453 |
-- arguments |
495 |
parseOpts argv progname options = |
|
496 |
case parseOptsInner argv progname options of |
|
497 |
Left (code, msg) -> do |
|
498 |
hPutStr (if code == 0 then stdout else stderr) msg |
|
499 |
exitWith (if code == 0 then ExitSuccess else ExitFailure code) |
|
500 |
Right result -> |
|
501 |
return result |
|
502 |
|
|
503 |
-- | Inner parse options. The arguments are similar to 'parseOpts', |
|
504 |
-- but it returns either a 'Left' composed of exit code and message, |
|
505 |
-- or a 'Right' for the success case. |
|
506 |
parseOptsInner :: [String] -> String -> [OptType] |
|
507 |
-> Either (Int, String) (Options, [String]) |
|
508 |
parseOptsInner argv progname options = |
|
509 |
case getOpt Permute options argv of |
|
510 |
(o, n, []) -> |
|
511 |
let (pr, args) = (foldM (flip id) defaultOptions o, n) |
|
512 |
in case pr of |
|
513 |
Bad msg -> Left (1, "Error while parsing command\ |
|
514 |
\line arguments:\n" ++ msg ++ "\n") |
|
515 |
Ok po -> |
|
516 |
select (Right (po, args)) |
|
517 |
[ (optShowHelp po, Left (0, usageHelp progname options)) |
|
518 |
, (optShowVer po, Left (0, versionInfo progname)) |
|
519 |
] |
|
520 |
(_, _, errs) -> |
|
521 |
Left (2, "Command line error: " ++ concat errs ++ "\n" ++ |
|
522 |
usageHelp progname options) |
|
454 |
parseOpts = Common.parseOpts defaultOptions |
|
455 |
|
|
523 | 456 |
|
524 | 457 |
-- | A shell script template for autogenerated scripts. |
525 | 458 |
shTemplate :: String |
... | ... | |
546 | 479 |
hPutStrLn stderr (msg ++ " status:") |
547 | 480 |
hPutStrLn stderr $ fn fields |
548 | 481 |
|
549 |
|
|
550 | 482 |
-- | Optionally print the instance list. |
551 | 483 |
maybePrintInsts :: Bool -- ^ Whether to print the instance list |
552 | 484 |
-> String -- ^ Type of the instance map (e.g. initial) |
... | ... | |
571 | 503 |
printKeys :: String -- ^ Prefix to printed variables |
572 | 504 |
-> [(String, String)] -- ^ List of (key, value) pairs to be printed |
573 | 505 |
-> IO () |
574 |
printKeys prefix = mapM_ (\(k, v) -> |
|
575 |
printf "%s_%s=%s\n" prefix (map toUpper k) (ensureQuoted v)) |
|
506 |
printKeys prefix = |
|
507 |
mapM_ (\(k, v) -> |
|
508 |
printf "%s_%s=%s\n" prefix (map toUpper k) (ensureQuoted v)) |
|
576 | 509 |
|
577 | 510 |
-- | Prints the final @OK@ marker in machine readable output. |
578 | 511 |
printFinal :: String -- ^ Prefix to printed variable |
579 |
-> Bool -- ^ Whether output should be machine readable |
|
580 |
-- Note: if not, there is nothing to print
|
|
512 |
-> Bool -- ^ Whether output should be machine readable;
|
|
513 |
-- note: if not, there is nothing to print
|
|
581 | 514 |
-> IO () |
582 | 515 |
printFinal prefix True = |
583 | 516 |
-- this should be the final entry |
Also available in: Unified diff