Allowing rebalance to run silently
[ganeti-local] / htools / Ganeti / HTools / CLI.hs
index 405b51b..308fe6d 100644 (file)
@@ -39,11 +39,14 @@ module Ganeti.HTools.CLI
   , maybePrintNodes
   , maybePrintInsts
   , maybeShowWarnings
+  , printKeys
+  , printFinal
   , setNodeStatus
   -- * The options
   , oDataFile
   , oDiskMoves
   , oDiskTemplate
+  , oSpindleUse
   , oDynuFile
   , oEvacMode
   , oExInst
@@ -61,6 +64,7 @@ module Ganeti.HTools.CLI
   , oMinGainLim
   , oMinScore
   , oNoHeaders
+  , oNoSimulation
   , oNodeSim
   , oOfflineNode
   , oOutputDir
@@ -81,13 +85,14 @@ module Ganeti.HTools.CLI
   ) where
 
 import Control.Monad
+import Data.Char (toUpper)
 import Data.Maybe (fromMaybe)
 import qualified Data.Version
 import System.Console.GetOpt
 import System.IO
 import System.Info
 import System.Exit
-import Text.Printf (printf, hPrintf)
+import Text.Printf (printf)
 
 import qualified Ganeti.HTools.Version as Version(version)
 import qualified Ganeti.HTools.Container as Container
@@ -113,6 +118,7 @@ data Options = Options
   , optDiskMoves   :: Bool           -- ^ Allow disk moves
   , optInstMoves   :: Bool           -- ^ Allow instance moves
   , optDiskTemplate :: Maybe DiskTemplate  -- ^ Override for the disk template
+  , optSpindleUse  :: Maybe Int      -- ^ Override for the spindle usage
   , optDynuFile    :: Maybe FilePath -- ^ Optional file with dynamic use data
   , optEvacMode    :: Bool           -- ^ Enable evacuation mode
   , optExInst      :: [String]       -- ^ Instances to be excluded
@@ -131,6 +137,7 @@ data Options = Options
   , optMinGainLim  :: Score          -- ^ Limit below which we apply mingain
   , optMinScore    :: Score          -- ^ The minimum score we aim for
   , optNoHeaders   :: Bool           -- ^ Do not show a header line
+  , optNoSimulation :: Bool          -- ^ Skip the rebalancing dry-run
   , optNodeSim     :: [String]       -- ^ Cluster simulation mode
   , optOffline     :: [String]       -- ^ Names of offline nodes
   , optOutPath     :: FilePath       -- ^ Path to the output directory
@@ -154,6 +161,7 @@ defaultOptions  = Options
   , optDiskMoves   = True
   , optInstMoves   = True
   , optDiskTemplate = Nothing
+  , optSpindleUse  = Nothing
   , optDynuFile    = Nothing
   , optEvacMode    = False
   , optExInst      = []
@@ -172,6 +180,7 @@ defaultOptions  = Options
   , optMinGainLim  = 1e-1
   , optMinScore    = 1e-9
   , optNoHeaders   = False
+  , optNoSimulation = False
   , optNodeSim     = []
   , optOffline     = []
   , optOutPath     = "."
@@ -228,6 +237,17 @@ oDiskTemplate = Option "" ["disk-template"]
                            return $ opts { optDiskTemplate = Just dt })
                  "TEMPLATE") "select the desired disk template"
 
+oSpindleUse :: OptType
+oSpindleUse = Option "" ["spindle-use"]
+              (ReqArg (\ n opts -> do
+                         su <- tryRead "parsing spindle-use" n
+                         when (su < 0) $
+                              fail "Invalid value of the spindle-use\
+                                   \ (expected >= 0)"
+                         return $ opts { optSpindleUse = Just su })
+               "SPINDLES") "select how many virtual spindle instances use\
+                           \ [default read from cluster]"
+
 oSelInst :: OptType
 oSelInst = Option "" ["select-instances"]
           (ReqArg (\ f opts -> Ok opts { optSelInst = sepSplit ',' f }) "INSTS")
@@ -335,6 +355,11 @@ oNoHeaders = Option "" ["no-headers"]
              (NoArg (\ opts -> Ok opts { optNoHeaders = True }))
              "do not show a header line"
 
+oNoSimulation :: OptType
+oNoSimulation = Option "" ["no-simulation"]
+                (NoArg (\opts -> Ok opts {optNoSimulation = True}))
+                "do not perform rebalancing simulation"
+
 oNodeSim :: OptType
 oNodeSim = Option "" ["simulate"]
             (ReqArg (\ f o -> Ok o { optNodeSim = f:optNodeSim o }) "SPEC")
@@ -541,6 +566,19 @@ maybeShowWarnings fix_msgs =
     hPutStrLn stderr "Warning: cluster has inconsistent data:"
     hPutStrLn stderr . unlines . map (printf "  - %s") $ fix_msgs
 
+-- | Format a list of key, value as a shell fragment.
+printKeys :: String -> [(String, String)] -> IO ()
+printKeys prefix = mapM_ (\(k, v) ->
+                       printf "%s_%s=%s\n" prefix (map toUpper k) (ensureQuoted v))
+
+-- | Prints the final @OK@ marker in machine readable output.
+printFinal :: String -> Bool -> IO ()
+printFinal prefix True =
+  -- this should be the final entry
+  printKeys prefix [("OK", "1")]
+
+printFinal _ False = return ()
+
 -- | Potentially set the node as offline based on passed offline list.
 setNodeOffline :: [Ndx] -> Node.Node -> Node.Node
 setNodeOffline offline_indices n =
@@ -563,9 +601,8 @@ setNodeStatus opts fixed_nl = do
       m_dsk = optMdsk opts
 
   unless (null offline_wrong) $ do
-         hPrintf stderr "Error: Wrong node name(s) set as offline: %s\n"
-                     (commaJoin (map lrContent offline_wrong)) :: IO ()
-         exitWith $ ExitFailure 1
+         exitErr $ printf "wrong node name(s) set as offline: %s\n"
+                   (commaJoin (map lrContent offline_wrong))
   let setMCpuFn = case m_cpu of
                     Nothing -> id
                     Just new_mcpu -> flip Node.setMcpu new_mcpu