Add 'Read' instances for most objects
[ganeti-local] / Ganeti / HTools / CLI.hs
index ff69047..b000a00 100644 (file)
@@ -33,6 +33,8 @@ module Ganeti.HTools.CLI
     , parseOpts
     , shTemplate
     , defaultLuxiSocket
+    , maybePrintNodes
+    , maybePrintInsts
     -- * The options
     , oDataFile
     , oDiskMoves
@@ -41,6 +43,7 @@ module Ganeti.HTools.CLI
     , oExInst
     , oExTags
     , oExecJobs
+    , oGroup
     , oIDisk
     , oIMem
     , oINodes
@@ -49,6 +52,8 @@ module Ganeti.HTools.CLI
     , oMaxCpu
     , oMaxSolLength
     , oMinDisk
+    , oMinGain
+    , oMinGainLim
     , oMinScore
     , oNoHeaders
     , oNodeSim
@@ -93,6 +98,7 @@ data Options = Options
     , optExInst      :: [String]       -- ^ Instances to be excluded
     , optExTags      :: Maybe [String] -- ^ Tags to use for exclusion
     , optExecJobs    :: Bool           -- ^ Execute the commands via Luxi
+    , optGroup       :: Maybe GroupID  -- ^ The UUID of the group to process
     , optINodes      :: Int            -- ^ Nodes required for an instance
     , optISpec       :: RSpec          -- ^ Requested instance specs
     , optLuxi        :: Maybe FilePath -- ^ Collect data from Luxi
@@ -100,9 +106,11 @@ data Options = Options
     , optMaxLength   :: Int            -- ^ Stop after this many steps
     , optMcpu        :: Double         -- ^ Max cpu ratio for nodes
     , optMdsk        :: Double         -- ^ Max disk usage ratio for nodes
+    , optMinGain     :: Score          -- ^ Min gain we aim for in a step
+    , optMinGainLim  :: Score          -- ^ Limit below which we apply mingain
     , optMinScore    :: Score          -- ^ The minimum score we aim for
     , optNoHeaders   :: Bool           -- ^ Do not show a header line
-    , optNodeSim     :: Maybe String   -- ^ Cluster simulation mode
+    , optNodeSim     :: [String]       -- ^ Cluster simulation mode
     , optOffline     :: [String]       -- ^ Names of offline nodes
     , optOneline     :: Bool           -- ^ Switch output to a single line
     , optOutPath     :: FilePath       -- ^ Path to the output directory
@@ -126,6 +134,7 @@ defaultOptions  = Options
  , optExInst      = []
  , optExTags      = Nothing
  , optExecJobs    = False
+ , optGroup       = Nothing
  , optINodes      = 2
  , optISpec       = RSpec 1 4096 102400
  , optLuxi        = Nothing
@@ -133,9 +142,11 @@ defaultOptions  = Options
  , optMaxLength   = -1
  , optMcpu        = defVcpuRatio
  , optMdsk        = defReservedDiskRatio
+ , optMinGain     = 1e-2
+ , optMinGainLim  = 1e-1
  , optMinScore    = 1e-9
  , optNoHeaders   = False
- , optNodeSim     = Nothing
+ , optNodeSim     = []
  , optOffline     = []
  , optOneline     = False
  , optOutPath     = "."
@@ -190,6 +201,11 @@ oExecJobs = Option "X" ["exec"]
              "execute the suggested moves via Luxi (only available when using\
              \ it for data gathering)"
 
+oGroup :: OptType
+oGroup = Option "G" ["group"]
+            (ReqArg (\ f o -> Ok o { optGroup = Just f }) "ID")
+            "the ID of the group to balance"
+
 oIDisk :: OptType
 oIDisk = Option "" ["disk"]
          (ReqArg (\ d opts ->
@@ -242,10 +258,20 @@ oMinDisk = Option "" ["min-disk"]
            (ReqArg (\ n opts -> Ok opts { optMdsk = read n }) "RATIO")
            "minimum free disk space for nodes (between 0 and 1) [0]"
 
+oMinGain :: OptType
+oMinGain = Option "g" ["min-gain"]
+            (ReqArg (\ g opts -> Ok opts { optMinGain = read g }) "DELTA")
+            "minimum gain to aim for in a balancing step before giving up"
+
+oMinGainLim :: OptType
+oMinGainLim = Option "" ["min-gain-limit"]
+            (ReqArg (\ g opts -> Ok opts { optMinGainLim = read g }) "SCORE")
+            "minimum cluster score for which we start checking the min-gain"
+
 oMinScore :: OptType
 oMinScore = Option "e" ["min-score"]
             (ReqArg (\ e opts -> Ok opts { optMinScore = read e }) "EPSILON")
-            " mininum score to aim for"
+            "mininum score to aim for"
 
 oNoHeaders :: OptType
 oNoHeaders = Option "" ["no-headers"]
@@ -254,7 +280,7 @@ oNoHeaders = Option "" ["no-headers"]
 
 oNodeSim :: OptType
 oNodeSim = Option "" ["simulate"]
-            (ReqArg (\ f o -> Ok o { optNodeSim = Just f }) "SPEC")
+            (ReqArg (\ f o -> Ok o { optNodeSim = f:optNodeSim o }) "SPEC")
             "simulate an empty cluster, given as 'num_nodes,disk,ram,cpu'"
 
 oOfflineNode :: OptType
@@ -330,7 +356,8 @@ oTieredSpec = Option "" ["tiered-alloc"]
                           tspec <-
                               case prs of
                                 [dsk, ram, cpu] -> return $ RSpec cpu ram dsk
-                                _ -> Bad $ "Invalid specification: " ++ inp
+                                _ -> Bad $ "Invalid specification: " ++ inp ++
+                                     ", expected disk,ram,cpu"
                           return $ opts { optTieredSpec = Just tspec } )
               "TSPEC")
              "enable tiered specs allocation, given as 'disk,ram,cpu'"
@@ -392,3 +419,26 @@ shTemplate =
            \    exit 0\n\
            \  fi\n\
            \}\n\n"
+
+-- | Optionally print the node list.
+maybePrintNodes :: Maybe [String]       -- ^ The field list
+                -> String               -- ^ Informational message
+                -> ([String] -> String) -- ^ Function to generate the listing
+                -> IO ()
+maybePrintNodes Nothing _ _ = return ()
+maybePrintNodes (Just fields) msg fn = do
+  hPutStrLn stderr ""
+  hPutStrLn stderr (msg ++ " status:")
+  hPutStrLn stderr $ fn fields
+
+
+-- | Optionally print the instance list.
+maybePrintInsts :: Bool   -- ^ Whether to print the instance list
+                -> String -- ^ Type of the instance map (e.g. initial)
+                -> String -- ^ The instance data
+                -> IO ()
+maybePrintInsts do_print msg instdata =
+  when do_print $ do
+    hPutStrLn stderr ""
+    hPutStrLn stderr $ msg ++ " instance map:"
+    hPutStr stderr instdata