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