Revision cd08cfa4 htools/Ganeti/HTools/CLI.hs
b/htools/Ganeti/HTools/CLI.hs | ||
---|---|---|
28 | 28 |
-} |
29 | 29 |
|
30 | 30 |
module Ganeti.HTools.CLI |
31 |
( Options(..)
|
|
32 |
, OptType
|
|
33 |
, parseOpts
|
|
34 |
, shTemplate
|
|
35 |
, defaultLuxiSocket
|
|
36 |
, maybePrintNodes
|
|
37 |
, maybePrintInsts
|
|
38 |
, maybeShowWarnings
|
|
39 |
, setNodeStatus
|
|
40 |
-- * The options
|
|
41 |
, oDataFile
|
|
42 |
, oDiskMoves
|
|
43 |
, oDiskTemplate
|
|
44 |
, oDynuFile
|
|
45 |
, oEvacMode
|
|
46 |
, oExInst
|
|
47 |
, oExTags
|
|
48 |
, oExecJobs
|
|
49 |
, oGroup
|
|
50 |
, oIDisk
|
|
51 |
, oIMem
|
|
52 |
, oIVcpus
|
|
53 |
, oInstMoves
|
|
54 |
, oLuxiSocket
|
|
55 |
, oMachineReadable
|
|
56 |
, oMaxCpu
|
|
57 |
, oMaxSolLength
|
|
58 |
, oMinDisk
|
|
59 |
, oMinGain
|
|
60 |
, oMinGainLim
|
|
61 |
, oMinScore
|
|
62 |
, oNoHeaders
|
|
63 |
, oNodeSim
|
|
64 |
, oOfflineNode
|
|
65 |
, oOutputDir
|
|
66 |
, oPrintCommands
|
|
67 |
, oPrintInsts
|
|
68 |
, oPrintNodes
|
|
69 |
, oQuiet
|
|
70 |
, oRapiMaster
|
|
71 |
, oReplay
|
|
72 |
, oSaveCluster
|
|
73 |
, oSelInst
|
|
74 |
, oShowHelp
|
|
75 |
, oShowVer
|
|
76 |
, oTieredSpec
|
|
77 |
, oVerbose
|
|
78 |
) where
|
|
31 |
( Options(..) |
|
32 |
, OptType |
|
33 |
, parseOpts |
|
34 |
, shTemplate |
|
35 |
, defaultLuxiSocket |
|
36 |
, maybePrintNodes |
|
37 |
, maybePrintInsts |
|
38 |
, maybeShowWarnings |
|
39 |
, setNodeStatus |
|
40 |
-- * The options |
|
41 |
, oDataFile |
|
42 |
, oDiskMoves |
|
43 |
, oDiskTemplate |
|
44 |
, oDynuFile |
|
45 |
, oEvacMode |
|
46 |
, oExInst |
|
47 |
, oExTags |
|
48 |
, oExecJobs |
|
49 |
, oGroup |
|
50 |
, oIDisk |
|
51 |
, oIMem |
|
52 |
, oIVcpus |
|
53 |
, oInstMoves |
|
54 |
, oLuxiSocket |
|
55 |
, oMachineReadable |
|
56 |
, oMaxCpu |
|
57 |
, oMaxSolLength |
|
58 |
, oMinDisk |
|
59 |
, oMinGain |
|
60 |
, oMinGainLim |
|
61 |
, oMinScore |
|
62 |
, oNoHeaders |
|
63 |
, oNodeSim |
|
64 |
, oOfflineNode |
|
65 |
, oOutputDir |
|
66 |
, oPrintCommands |
|
67 |
, oPrintInsts |
|
68 |
, oPrintNodes |
|
69 |
, oQuiet |
|
70 |
, oRapiMaster |
|
71 |
, oReplay |
|
72 |
, oSaveCluster |
|
73 |
, oSelInst |
|
74 |
, oShowHelp |
|
75 |
, oShowVer |
|
76 |
, oTieredSpec |
|
77 |
, oVerbose |
|
78 |
) where |
|
79 | 79 |
|
80 | 80 |
import Control.Monad |
81 | 81 |
import Data.Maybe (fromMaybe) |
... | ... | |
106 | 106 |
|
107 | 107 |
-- | Command line options structure. |
108 | 108 |
data Options = Options |
109 |
{ optDataFile :: Maybe FilePath -- ^ Path to the cluster data file
|
|
110 |
, optDiskMoves :: Bool -- ^ Allow disk moves
|
|
111 |
, optInstMoves :: Bool -- ^ Allow instance moves
|
|
112 |
, optDiskTemplate :: DiskTemplate -- ^ The requested disk template
|
|
113 |
, optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data
|
|
114 |
, optEvacMode :: Bool -- ^ Enable evacuation mode
|
|
115 |
, optExInst :: [String] -- ^ Instances to be excluded
|
|
116 |
, optExTags :: Maybe [String] -- ^ Tags to use for exclusion
|
|
117 |
, optExecJobs :: Bool -- ^ Execute the commands via Luxi
|
|
118 |
, optGroup :: Maybe GroupID -- ^ The UUID of the group to process
|
|
119 |
, optSelInst :: [String] -- ^ Instances to be excluded
|
|
120 |
, optISpec :: RSpec -- ^ Requested instance specs
|
|
121 |
, optLuxi :: Maybe FilePath -- ^ Collect data from Luxi
|
|
122 |
, optMachineReadable :: Bool -- ^ Output machine-readable format
|
|
123 |
, optMaster :: String -- ^ Collect data from RAPI
|
|
124 |
, optMaxLength :: Int -- ^ Stop after this many steps
|
|
125 |
, optMcpu :: Double -- ^ Max cpu ratio for nodes
|
|
126 |
, optMdsk :: Double -- ^ Max disk usage ratio for nodes
|
|
127 |
, optMinGain :: Score -- ^ Min gain we aim for in a step
|
|
128 |
, optMinGainLim :: Score -- ^ Limit below which we apply mingain
|
|
129 |
, optMinScore :: Score -- ^ The minimum score we aim for
|
|
130 |
, optNoHeaders :: Bool -- ^ Do not show a header line
|
|
131 |
, optNodeSim :: [String] -- ^ Cluster simulation mode
|
|
132 |
, optOffline :: [String] -- ^ Names of offline nodes
|
|
133 |
, optOutPath :: FilePath -- ^ Path to the output directory
|
|
134 |
, optSaveCluster :: Maybe FilePath -- ^ Save cluster state to this file
|
|
135 |
, optShowCmds :: Maybe FilePath -- ^ Whether to show the command list
|
|
136 |
, optShowHelp :: Bool -- ^ Just show the help
|
|
137 |
, optShowInsts :: Bool -- ^ Whether to show the instance map
|
|
138 |
, optShowNodes :: Maybe [String] -- ^ Whether to show node status
|
|
139 |
, optShowVer :: Bool -- ^ Just show the program version
|
|
140 |
, optTieredSpec :: Maybe RSpec -- ^ Requested specs for tiered mode
|
|
141 |
, optReplay :: Maybe String -- ^ Unittests: RNG state
|
|
142 |
, optVerbose :: Int -- ^ Verbosity level
|
|
143 |
} deriving Show
|
|
109 |
{ optDataFile :: Maybe FilePath -- ^ Path to the cluster data file |
|
110 |
, optDiskMoves :: Bool -- ^ Allow disk moves |
|
111 |
, optInstMoves :: Bool -- ^ Allow instance moves |
|
112 |
, optDiskTemplate :: DiskTemplate -- ^ The requested disk template |
|
113 |
, optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data |
|
114 |
, optEvacMode :: Bool -- ^ Enable evacuation mode |
|
115 |
, optExInst :: [String] -- ^ Instances to be excluded |
|
116 |
, optExTags :: Maybe [String] -- ^ Tags to use for exclusion |
|
117 |
, optExecJobs :: Bool -- ^ Execute the commands via Luxi |
|
118 |
, optGroup :: Maybe GroupID -- ^ The UUID of the group to process |
|
119 |
, optSelInst :: [String] -- ^ Instances to be excluded |
|
120 |
, optISpec :: RSpec -- ^ Requested instance specs |
|
121 |
, optLuxi :: Maybe FilePath -- ^ Collect data from Luxi |
|
122 |
, optMachineReadable :: Bool -- ^ Output machine-readable format |
|
123 |
, optMaster :: String -- ^ Collect data from RAPI |
|
124 |
, optMaxLength :: Int -- ^ Stop after this many steps |
|
125 |
, optMcpu :: Double -- ^ Max cpu ratio for nodes |
|
126 |
, optMdsk :: Double -- ^ Max disk usage ratio for nodes |
|
127 |
, optMinGain :: Score -- ^ Min gain we aim for in a step |
|
128 |
, optMinGainLim :: Score -- ^ Limit below which we apply mingain |
|
129 |
, optMinScore :: Score -- ^ The minimum score we aim for |
|
130 |
, optNoHeaders :: Bool -- ^ Do not show a header line |
|
131 |
, optNodeSim :: [String] -- ^ Cluster simulation mode |
|
132 |
, optOffline :: [String] -- ^ Names of offline nodes |
|
133 |
, optOutPath :: FilePath -- ^ Path to the output directory |
|
134 |
, optSaveCluster :: Maybe FilePath -- ^ Save cluster state to this file |
|
135 |
, optShowCmds :: Maybe FilePath -- ^ Whether to show the command list |
|
136 |
, optShowHelp :: Bool -- ^ Just show the help |
|
137 |
, optShowInsts :: Bool -- ^ Whether to show the instance map |
|
138 |
, optShowNodes :: Maybe [String] -- ^ Whether to show node status |
|
139 |
, optShowVer :: Bool -- ^ Just show the program version |
|
140 |
, optTieredSpec :: Maybe RSpec -- ^ Requested specs for tiered mode |
|
141 |
, optReplay :: Maybe String -- ^ Unittests: RNG state |
|
142 |
, optVerbose :: Int -- ^ Verbosity level |
|
143 |
} deriving Show |
|
144 | 144 |
|
145 | 145 |
-- | Default values for the command line options. |
146 | 146 |
defaultOptions :: Options |
147 | 147 |
defaultOptions = Options |
148 |
{ optDataFile = Nothing |
|
149 |
, optDiskMoves = True |
|
150 |
, optInstMoves = True |
|
151 |
, optDiskTemplate = DTDrbd8 |
|
152 |
, optDynuFile = Nothing |
|
153 |
, optEvacMode = False |
|
154 |
, optExInst = [] |
|
155 |
, optExTags = Nothing |
|
156 |
, optExecJobs = False |
|
157 |
, optGroup = Nothing |
|
158 |
, optSelInst = [] |
|
159 |
, optISpec = RSpec 1 4096 102400 |
|
160 |
, optLuxi = Nothing |
|
161 |
, optMachineReadable = False |
|
162 |
, optMaster = "" |
|
163 |
, optMaxLength = -1 |
|
164 |
, optMcpu = defVcpuRatio |
|
165 |
, optMdsk = defReservedDiskRatio |
|
166 |
, optMinGain = 1e-2 |
|
167 |
, optMinGainLim = 1e-1 |
|
168 |
, optMinScore = 1e-9 |
|
169 |
, optNoHeaders = False |
|
170 |
, optNodeSim = [] |
|
171 |
, optOffline = [] |
|
172 |
, optOutPath = "." |
|
173 |
, optSaveCluster = Nothing |
|
174 |
, optShowCmds = Nothing |
|
175 |
, optShowHelp = False |
|
176 |
, optShowInsts = False |
|
177 |
, optShowNodes = Nothing |
|
178 |
, optShowVer = False |
|
179 |
, optTieredSpec = Nothing |
|
180 |
, optReplay = Nothing |
|
181 |
, optVerbose = 1 |
|
182 |
} |
|
148 |
{ optDataFile = Nothing
|
|
149 |
, optDiskMoves = True
|
|
150 |
, optInstMoves = True
|
|
151 |
, optDiskTemplate = DTDrbd8
|
|
152 |
, optDynuFile = Nothing
|
|
153 |
, optEvacMode = False
|
|
154 |
, optExInst = []
|
|
155 |
, optExTags = Nothing
|
|
156 |
, optExecJobs = False
|
|
157 |
, optGroup = Nothing
|
|
158 |
, optSelInst = []
|
|
159 |
, optISpec = RSpec 1 4096 102400
|
|
160 |
, optLuxi = Nothing
|
|
161 |
, optMachineReadable = False
|
|
162 |
, optMaster = ""
|
|
163 |
, optMaxLength = -1
|
|
164 |
, optMcpu = defVcpuRatio
|
|
165 |
, optMdsk = defReservedDiskRatio
|
|
166 |
, optMinGain = 1e-2
|
|
167 |
, optMinGainLim = 1e-1
|
|
168 |
, optMinScore = 1e-9
|
|
169 |
, optNoHeaders = False
|
|
170 |
, optNodeSim = []
|
|
171 |
, optOffline = []
|
|
172 |
, optOutPath = "."
|
|
173 |
, optSaveCluster = Nothing
|
|
174 |
, optShowCmds = Nothing
|
|
175 |
, optShowHelp = False
|
|
176 |
, optShowInsts = False
|
|
177 |
, optShowNodes = Nothing
|
|
178 |
, optShowVer = False
|
|
179 |
, optTieredSpec = Nothing
|
|
180 |
, optReplay = Nothing
|
|
181 |
, optVerbose = 1
|
|
182 |
}
|
|
183 | 183 |
|
184 | 184 |
-- | Abrreviation for the option type. |
185 | 185 |
type OptType = OptDescr (Options -> Result Options) |
... | ... | |
283 | 283 |
|
284 | 284 |
oMachineReadable :: OptType |
285 | 285 |
oMachineReadable = Option "" ["machine-readable"] |
286 |
(OptArg (\ f opts -> do |
|
286 |
(OptArg (\ f opts -> do
|
|
287 | 287 |
flag <- parseYesNo True f |
288 | 288 |
return $ opts { optMachineReadable = flag }) "CHOICE") |
289 | 289 |
"enable machine readable output (pass either 'yes' or 'no' to\ |
... | ... | |
360 | 360 |
oPrintNodes :: OptType |
361 | 361 |
oPrintNodes = Option "p" ["print-nodes"] |
362 | 362 |
(OptArg ((\ f opts -> |
363 |
let (prefix, realf) = case f of
|
|
364 |
'+':rest -> (["+"], rest) |
|
365 |
_ -> ([], f) |
|
366 |
splitted = prefix ++ sepSplit ',' realf
|
|
367 |
in Ok opts { optShowNodes = Just splitted }) .
|
|
363 |
let (prefix, realf) = case f of |
|
364 |
'+':rest -> (["+"], rest)
|
|
365 |
_ -> ([], f)
|
|
366 |
splitted = prefix ++ sepSplit ',' realf |
|
367 |
in Ok opts { optShowNodes = Just splitted }) . |
|
368 | 368 |
fromMaybe []) "FIELDS") |
369 | 369 |
"print the final node list" |
370 | 370 |
|
... | ... | |
396 | 396 |
oTieredSpec :: OptType |
397 | 397 |
oTieredSpec = Option "" ["tiered-alloc"] |
398 | 398 |
(ReqArg (\ inp opts -> do |
399 |
let sp = sepSplit ',' inp
|
|
400 |
prs <- mapM (\(fn, val) -> fn val) $
|
|
401 |
zip [ annotateResult "tiered specs memory" .
|
|
402 |
parseUnit
|
|
403 |
, annotateResult "tiered specs disk" .
|
|
404 |
parseUnit
|
|
405 |
, tryRead "tiered specs cpus"
|
|
406 |
] sp
|
|
407 |
tspec <-
|
|
408 |
case prs of
|
|
409 |
[dsk, ram, cpu] -> return $ RSpec cpu ram dsk
|
|
410 |
_ -> Bad $ "Invalid specification: " ++ inp ++
|
|
411 |
", expected disk,ram,cpu"
|
|
412 |
return $ opts { optTieredSpec = Just tspec } )
|
|
399 |
let sp = sepSplit ',' inp |
|
400 |
prs <- mapM (\(fn, val) -> fn val) $ |
|
401 |
zip [ annotateResult "tiered specs memory" . |
|
402 |
parseUnit |
|
403 |
, annotateResult "tiered specs disk" . |
|
404 |
parseUnit |
|
405 |
, tryRead "tiered specs cpus" |
|
406 |
] sp |
|
407 |
tspec <- |
|
408 |
case prs of |
|
409 |
[dsk, ram, cpu] -> return $ RSpec cpu ram dsk |
|
410 |
_ -> Bad $ "Invalid specification: " ++ inp ++ |
|
411 |
", expected disk,ram,cpu" |
|
412 |
return $ opts { optTieredSpec = Just tspec } ) |
|
413 | 413 |
"TSPEC") |
414 | 414 |
"enable tiered specs allocation, given as 'disk,ram,cpu'" |
415 | 415 |
|
... | ... | |
438 | 438 |
-- | Usage info. |
439 | 439 |
usageHelp :: String -> [OptType] -> String |
440 | 440 |
usageHelp progname = |
441 |
usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
|
|
442 |
progname Version.version progname)
|
|
441 |
usageInfo (printf "%s %s\nUsage: %s [OPTION...]" |
|
442 |
progname Version.version progname) |
|
443 | 443 |
|
444 | 444 |
-- | Command line parser, using the 'Options' structure. |
445 | 445 |
parseOpts :: [String] -- ^ The command line arguments |
... | ... | |
448 | 448 |
-> IO (Options, [String]) -- ^ The resulting options and leftover |
449 | 449 |
-- arguments |
450 | 450 |
parseOpts argv progname options = |
451 |
case getOpt Permute options argv of
|
|
452 |
(o, n, []) ->
|
|
453 |
do
|
|
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)
|
|
462 |
when (optShowHelp po) $ do
|
|
463 |
putStr $ usageHelp progname options
|
|
464 |
exitWith ExitSuccess
|
|
465 |
when (optShowVer po) $ do
|
|
466 |
printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
|
|
467 |
progname Version.version
|
|
468 |
compilerName (Data.Version.showVersion compilerVersion)
|
|
469 |
os arch :: IO ()
|
|
470 |
exitWith ExitSuccess
|
|
471 |
return (po, args)
|
|
472 |
(_, _, errs) -> do
|
|
473 |
hPutStrLn stderr $ "Command line error: " ++ concat errs
|
|
474 |
hPutStrLn stderr $ usageHelp progname options
|
|
475 |
exitWith $ ExitFailure 2
|
|
451 |
case getOpt Permute options argv of |
|
452 |
(o, n, []) -> |
|
453 |
do |
|
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) |
|
462 |
when (optShowHelp po) $ do |
|
463 |
putStr $ usageHelp progname options |
|
464 |
exitWith ExitSuccess |
|
465 |
when (optShowVer po) $ do |
|
466 |
printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n" |
|
467 |
progname Version.version |
|
468 |
compilerName (Data.Version.showVersion compilerVersion) |
|
469 |
os arch :: IO () |
|
470 |
exitWith ExitSuccess |
|
471 |
return (po, args) |
|
472 |
(_, _, errs) -> do |
|
473 |
hPutStrLn stderr $ "Command line error: " ++ concat errs |
|
474 |
hPutStrLn stderr $ usageHelp progname options |
|
475 |
exitWith $ ExitFailure 2 |
|
476 | 476 |
|
477 | 477 |
-- | A shell script template for autogenerated scripts. |
478 | 478 |
shTemplate :: String |
479 | 479 |
shTemplate = |
480 |
printf "#!/bin/sh\n\n\
|
|
481 |
\# Auto-generated script for executing cluster rebalancing\n\n\
|
|
482 |
\# To stop, touch the file /tmp/stop-htools\n\n\
|
|
483 |
\set -e\n\n\
|
|
484 |
\check() {\n\
|
|
485 |
\ if [ -f /tmp/stop-htools ]; then\n\
|
|
486 |
\ echo 'Stop requested, exiting'\n\
|
|
487 |
\ exit 0\n\
|
|
488 |
\ fi\n\
|
|
489 |
\}\n\n"
|
|
480 |
printf "#!/bin/sh\n\n\ |
|
481 |
\# Auto-generated script for executing cluster rebalancing\n\n\ |
|
482 |
\# To stop, touch the file /tmp/stop-htools\n\n\ |
|
483 |
\set -e\n\n\ |
|
484 |
\check() {\n\ |
|
485 |
\ if [ -f /tmp/stop-htools ]; then\n\ |
|
486 |
\ echo 'Stop requested, exiting'\n\ |
|
487 |
\ exit 0\n\ |
|
488 |
\ fi\n\ |
|
489 |
\}\n\n" |
|
490 | 490 |
|
491 | 491 |
-- | Optionally print the node list. |
492 | 492 |
maybePrintNodes :: Maybe [String] -- ^ The field list |
Also available in: Unified diff