Add 'Read' instances for most objects
[ganeti-local] / Ganeti / HTools / CLI.hs
index 11f9330..b000a00 100644 (file)
@@ -8,7 +8,7 @@ and this is more IO oriented.
 
 {-
 
-Copyright (C) 2009 Google Inc.
+Copyright (C) 2009, 2010 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -32,12 +32,18 @@ module Ganeti.HTools.CLI
     , OptType
     , parseOpts
     , shTemplate
+    , defaultLuxiSocket
+    , maybePrintNodes
+    , maybePrintInsts
     -- * The options
     , oDataFile
     , oDiskMoves
     , oDynuFile
+    , oEvacMode
+    , oExInst
     , oExTags
     , oExecJobs
+    , oGroup
     , oIDisk
     , oIMem
     , oINodes
@@ -46,6 +52,8 @@ module Ganeti.HTools.CLI
     , oMaxCpu
     , oMaxSolLength
     , oMinDisk
+    , oMinGain
+    , oMinGainLim
     , oMinScore
     , oNoHeaders
     , oNodeSim
@@ -57,6 +65,7 @@ module Ganeti.HTools.CLI
     , oPrintNodes
     , oQuiet
     , oRapiMaster
+    , oSaveCluster
     , oShowHelp
     , oShowVer
     , oTieredSpec
@@ -85,8 +94,11 @@ data Options = Options
     { optDataFile    :: Maybe FilePath -- ^ Path to the cluster data file
     , optDiskMoves   :: Bool           -- ^ Allow disk moves
     , optDynuFile    :: Maybe FilePath -- ^ Optional file with dynamic use data
+    , optEvacMode    :: Bool           -- ^ Enable evacuation mode
+    , 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
@@ -94,12 +106,15 @@ 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
+    , optSaveCluster :: Maybe FilePath -- ^ Save cluster state to this file
     , optShowCmds    :: Maybe FilePath -- ^ Whether to show the command list
     , optShowHelp    :: Bool           -- ^ Just show the help
     , optShowInsts   :: Bool           -- ^ Whether to show the instance map
@@ -115,21 +130,27 @@ defaultOptions  = Options
  { optDataFile    = Nothing
  , optDiskMoves   = True
  , optDynuFile    = Nothing
+ , optEvacMode    = False
+ , optExInst      = []
  , optExTags      = Nothing
  , optExecJobs    = False
+ , optGroup       = Nothing
  , optINodes      = 2
  , optISpec       = RSpec 1 4096 102400
  , optLuxi        = Nothing
  , optMaster      = ""
  , optMaxLength   = -1
- , optMcpu        = -1
- , optMdsk        = -1
+ , optMcpu        = defVcpuRatio
+ , optMdsk        = defReservedDiskRatio
+ , optMinGain     = 1e-2
+ , optMinGainLim  = 1e-1
  , optMinScore    = 1e-9
  , optNoHeaders   = False
- , optNodeSim     = Nothing
+ , optNodeSim     = []
  , optOffline     = []
  , optOneline     = False
  , optOutPath     = "."
+ , optSaveCluster = Nothing
  , optShowCmds    = Nothing
  , optShowHelp    = False
  , optShowInsts   = False
@@ -158,6 +179,17 @@ oDynuFile = Option "U" ["dynu-file"]
             (ReqArg (\ f opts -> Ok opts { optDynuFile = Just f }) "FILE")
             "Import dynamic utilisation data from the given FILE"
 
+oEvacMode :: OptType
+oEvacMode = Option "E" ["evac-mode"]
+            (NoArg (\opts -> Ok opts { optEvacMode = True }))
+            "enable evacuation mode, where the algorithm only moves \
+            \ instances away from offline and drained nodes"
+
+oExInst :: OptType
+oExInst = Option "" ["exclude-instances"]
+          (ReqArg (\ f opts -> Ok opts { optExInst = sepSplit ',' f }) "INSTS")
+          "exclude given instances  from any moves"
+
 oExTags :: OptType
 oExTags = Option "" ["exclusion-tags"]
             (ReqArg (\ f opts -> Ok opts { optExTags = Just $ sepSplit ',' f })
@@ -169,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 ->
@@ -207,7 +244,8 @@ oLuxiSocket = Option "L" ["luxi"]
 oMaxCpu :: OptType
 oMaxCpu = Option "" ["max-cpu"]
           (ReqArg (\ n opts -> Ok opts { optMcpu = read n }) "RATIO")
-          "maximum virtual-to-physical cpu ratio for nodes"
+          "maximum virtual-to-physical cpu ratio for nodes (from 1\
+          \ upwards) [64]"
 
 oMaxSolLength :: OptType
 oMaxSolLength = Option "l" ["max-length"]
@@ -218,12 +256,22 @@ oMaxSolLength = Option "l" ["max-length"]
 oMinDisk :: OptType
 oMinDisk = Option "" ["min-disk"]
            (ReqArg (\ n opts -> Ok opts { optMdsk = read n }) "RATIO")
-           "minimum free disk space for nodes (between 0 and 1)"
+           "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"]
@@ -232,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
@@ -267,7 +315,10 @@ oPrintInsts = Option "" ["print-instances"]
 oPrintNodes :: OptType
 oPrintNodes = Option "p" ["print-nodes"]
               (OptArg ((\ f opts ->
-                            let splitted = sepSplit ',' f
+                            let (prefix, realf) = case f of
+                                  '+':rest -> (["+"], rest)
+                                  _ -> ([], f)
+                                splitted = prefix ++ sepSplit ',' realf
                             in Ok opts { optShowNodes = Just splitted }) .
                        fromMaybe []) "FIELDS")
               "print the final node list"
@@ -282,6 +333,11 @@ oRapiMaster = Option "m" ["master"]
               (ReqArg (\ m opts -> Ok opts { optMaster = m }) "ADDRESS")
               "collect data via RAPI at the given ADDRESS"
 
+oSaveCluster :: OptType
+oSaveCluster = Option "S" ["save"]
+            (ReqArg (\ f opts -> Ok opts { optSaveCluster = Just f }) "FILE")
+            "Save cluster state at the end of the processing to FILE"
+
 oShowHelp :: OptType
 oShowHelp = Option "h" ["help"]
             (NoArg (\ opts -> Ok opts { optShowHelp = True}))
@@ -300,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'"
@@ -341,7 +398,7 @@ parseOpts argv progname options =
               printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
                      progname Version.version
                      compilerName (Data.Version.showVersion compilerVersion)
-                     os arch
+                     os arch :: IO ()
               exitWith ExitSuccess
             return (po, args)
       (_, _, errs) -> do
@@ -362,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