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