diff --git a/Ganeti/HTools/CLI.hs b/Ganeti/HTools/CLI.hs index d5831ec4bfc041aae4b546300b19ce3b390a67e7..11f9330cd566cef5f4d9923794bf9b70b675561e 100644 --- a/Ganeti/HTools/CLI.hs +++ b/Ganeti/HTools/CLI.hs @@ -33,34 +33,34 @@ module Ganeti.HTools.CLI , parseOpts , shTemplate -- * The options - , oPrintNodes - , oPrintInsts - , oPrintCommands - , oOneline - , oNoHeaders - , oOutputDir , oDataFile - , oNodeSim - , oRapiMaster - , oLuxiSocket + , oDiskMoves + , oDynuFile + , oExTags , oExecJobs - , oMaxSolLength - , oVerbose - , oQuiet - , oOfflineNode - , oMinScore - , oIMem , oIDisk - , oIVcpus + , oIMem , oINodes + , oIVcpus + , oLuxiSocket , oMaxCpu + , oMaxSolLength , oMinDisk - , oDiskMoves - , oDynuFile - , oTieredSpec - , oExTags - , oShowVer + , oMinScore + , oNoHeaders + , oNodeSim + , oOfflineNode + , oOneline + , oOutputDir + , oPrintCommands + , oPrintInsts + , oPrintNodes + , oQuiet + , oRapiMaster , oShowHelp + , oShowVer + , oTieredSpec + , oVerbose ) where import Data.Maybe (fromMaybe) @@ -82,123 +82,86 @@ defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master" -- | Command line options structure. data Options = Options - { optShowNodes :: Maybe [String] -- ^ Whether to show node status - , optShowInsts :: Bool -- ^ Whether to show the instance map - , optShowCmds :: Maybe FilePath -- ^ Whether to show the command list - , optOneline :: Bool -- ^ Switch output to a single line - , optOutPath :: FilePath -- ^ Path to the output directory - , optNoHeaders :: Bool -- ^ Do not show a header line - , optDataFile :: Maybe FilePath -- ^ Path to the cluster data file - , optNodeSim :: Maybe String -- ^ Cluster simulation mode - , optMaxLength :: Int -- ^ Stop after this many steps - , optMaster :: String -- ^ Collect data from RAPI - , optLuxi :: Maybe FilePath -- ^ Collect data from Luxi + { optDataFile :: Maybe FilePath -- ^ Path to the cluster data file + , optDiskMoves :: Bool -- ^ Allow disk moves + , optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data + , optExTags :: Maybe [String] -- ^ Tags to use for exclusion , optExecJobs :: Bool -- ^ Execute the commands via Luxi - , optOffline :: [String] -- ^ Names of offline nodes , optINodes :: Int -- ^ Nodes required for an instance , optISpec :: RSpec -- ^ Requested instance specs - , optTieredSpec :: Maybe RSpec -- ^ Requested specs for tiered mode - , optMinScore :: Score -- ^ The minimum score we aim for + , optLuxi :: Maybe FilePath -- ^ Collect data from Luxi + , optMaster :: String -- ^ Collect data from RAPI + , optMaxLength :: Int -- ^ Stop after this many steps , optMcpu :: Double -- ^ Max cpu ratio for nodes , optMdsk :: Double -- ^ Max disk usage ratio for nodes - , optDiskMoves :: Bool -- ^ Allow disk moves - , optDynuFile :: Maybe FilePath -- ^ Optional file with dynamic use data - , optExTags :: Maybe [String] -- ^ Tags to use for exclusion - , optVerbose :: Int -- ^ Verbosity level - , optShowVer :: Bool -- ^ Just show the program version + , optMinScore :: Score -- ^ The minimum score we aim for + , optNoHeaders :: Bool -- ^ Do not show a header line + , optNodeSim :: Maybe 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 + , optShowCmds :: Maybe FilePath -- ^ Whether to show the command list , optShowHelp :: Bool -- ^ Just show the help + , optShowInsts :: Bool -- ^ Whether to show the instance map + , optShowNodes :: Maybe [String] -- ^ Whether to show node status + , optShowVer :: Bool -- ^ Just show the program version + , optTieredSpec :: Maybe RSpec -- ^ Requested specs for tiered mode + , optVerbose :: Int -- ^ Verbosity level } deriving Show -- | Default values for the command line options. defaultOptions :: Options defaultOptions = Options - { optShowNodes = Nothing - , optShowInsts = False - , optShowCmds = Nothing - , optOneline = False - , optNoHeaders = False - , optOutPath = "." - , optDataFile = Nothing - , optNodeSim = Nothing - , optMaxLength = -1 - , optMaster = "" - , optLuxi = Nothing + { optDataFile = Nothing + , optDiskMoves = True + , optDynuFile = Nothing + , optExTags = Nothing , optExecJobs = False - , optOffline = [] , optINodes = 2 , optISpec = RSpec 1 4096 102400 - , optTieredSpec = Nothing - , optMinScore = 1e-9 + , optLuxi = Nothing + , optMaster = "" + , optMaxLength = -1 , optMcpu = -1 , optMdsk = -1 - , optDiskMoves = True - , optDynuFile = Nothing - , optExTags = Nothing - , optVerbose = 1 - , optShowVer = False + , optMinScore = 1e-9 + , optNoHeaders = False + , optNodeSim = Nothing + , optOffline = [] + , optOneline = False + , optOutPath = "." + , optShowCmds = Nothing , optShowHelp = False + , optShowInsts = False + , optShowNodes = Nothing + , optShowVer = False + , optTieredSpec = Nothing + , optVerbose = 1 } -- | Abrreviation for the option type type OptType = OptDescr (Options -> Result Options) -oPrintNodes :: OptType -oPrintNodes = Option "p" ["print-nodes"] - (OptArg ((\ f opts -> - let splitted = sepSplit ',' f - in Ok opts { optShowNodes = Just splitted }) . - fromMaybe []) "FIELDS") - "print the final node list" - -oPrintInsts :: OptType -oPrintInsts = Option "" ["print-instances"] - (NoArg (\ opts -> Ok opts { optShowInsts = True })) - "print the final instance map" - -oPrintCommands :: OptType -oPrintCommands = Option "C" ["print-commands"] - (OptArg ((\ f opts -> Ok opts { optShowCmds = Just f }) . - fromMaybe "-") - "FILE") - "print the ganeti command list for reaching the solution,\ - \ if an argument is passed then write the commands to a\ - \ file named as such" - -oOneline :: OptType -oOneline = Option "o" ["oneline"] - (NoArg (\ opts -> Ok opts { optOneline = True })) - "print the ganeti command list for reaching the solution" - -oNoHeaders :: OptType -oNoHeaders = Option "" ["no-headers"] - (NoArg (\ opts -> Ok opts { optNoHeaders = True })) - "do not show a header line" - -oOutputDir :: OptType -oOutputDir = Option "d" ["output-dir"] - (ReqArg (\ d opts -> Ok opts { optOutPath = d }) "PATH") - "directory in which to write output files" - oDataFile :: OptType oDataFile = Option "t" ["text-data"] (ReqArg (\ f o -> Ok o { optDataFile = Just f }) "FILE") "the cluster data FILE" -oNodeSim :: OptType -oNodeSim = Option "" ["simulate"] - (ReqArg (\ f o -> Ok o { optNodeSim = Just f }) "SPEC") - "simulate an empty cluster, given as 'num_nodes,disk,ram,cpu'" +oDiskMoves :: OptType +oDiskMoves = Option "" ["no-disk-moves"] + (NoArg (\ opts -> Ok opts { optDiskMoves = False})) + "disallow disk moves from the list of allowed instance changes,\ + \ thus allowing only the 'cheap' failover/migrate operations" -oRapiMaster :: OptType -oRapiMaster = Option "m" ["master"] - (ReqArg (\ m opts -> Ok opts { optMaster = m }) "ADDRESS") - "collect data via RAPI at the given ADDRESS" +oDynuFile :: OptType +oDynuFile = Option "U" ["dynu-file"] + (ReqArg (\ f opts -> Ok opts { optDynuFile = Just f }) "FILE") + "Import dynamic utilisation data from the given FILE" -oLuxiSocket :: OptType -oLuxiSocket = Option "L" ["luxi"] - (OptArg ((\ f opts -> Ok opts { optLuxi = Just f }) . - fromMaybe defaultLuxiSocket) "SOCKET") - "collect data via Luxi, optionally using the given SOCKET path" +oExTags :: OptType +oExTags = Option "" ["exclusion-tags"] + (ReqArg (\ f opts -> Ok opts { optExTags = Just $ sepSplit ',' f }) + "TAG,...") "Enable instance exclusion based on given tag prefix" oExecJobs :: OptType oExecJobs = Option "X" ["exec"] @@ -206,31 +169,13 @@ oExecJobs = Option "X" ["exec"] "execute the suggested moves via Luxi (only available when using\ \ it for data gathering)" -oVerbose :: OptType -oVerbose = Option "v" ["verbose"] - (NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts + 1 })) - "increase the verbosity level" - -oQuiet :: OptType -oQuiet = Option "q" ["quiet"] - (NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts - 1 })) - "decrease the verbosity level" - -oOfflineNode :: OptType -oOfflineNode = Option "O" ["offline"] - (ReqArg (\ n o -> Ok o { optOffline = n:optOffline o }) "NODE") - "set node as offline" - -oMaxSolLength :: OptType -oMaxSolLength = Option "l" ["max-length"] - (ReqArg (\ i opts -> Ok opts { optMaxLength = read i }) "N") - "cap the solution at this many moves (useful for very\ - \ unbalanced clusters)" - -oMinScore :: OptType -oMinScore = Option "e" ["min-score"] - (ReqArg (\ e opts -> Ok opts { optMinScore = read e }) "EPSILON") - " mininum score to aim for" +oIDisk :: OptType +oIDisk = Option "" ["disk"] + (ReqArg (\ d opts -> + let ospec = optISpec opts + nspec = ospec { rspecDsk = read d } + in Ok opts { optISpec = nspec }) "DISK") + "disk size for instances" oIMem :: OptType oIMem = Option "" ["memory"] @@ -240,13 +185,10 @@ oIMem = Option "" ["memory"] in Ok opts { optISpec = nspec }) "MEMORY") "memory size for instances" -oIDisk :: OptType -oIDisk = Option "" ["disk"] - (ReqArg (\ d opts -> - let ospec = optISpec opts - nspec = ospec { rspecDsk = read d } - in Ok opts { optISpec = nspec }) "DISK") - "disk size for instances" +oINodes :: OptType +oINodes = Option "" ["req-nodes"] + (ReqArg (\ n opts -> Ok opts { optINodes = read n }) "NODES") + "number of nodes for the new instances (1=plain, 2=mirrored)" oIVcpus :: OptType oIVcpus = Option "" ["vcpus"] @@ -256,36 +198,99 @@ oIVcpus = Option "" ["vcpus"] in Ok opts { optISpec = nspec }) "NUM") "number of virtual cpus for instances" -oINodes :: OptType -oINodes = Option "" ["req-nodes"] - (ReqArg (\ n opts -> Ok opts { optINodes = read n }) "NODES") - "number of nodes for the new instances (1=plain, 2=mirrored)" +oLuxiSocket :: OptType +oLuxiSocket = Option "L" ["luxi"] + (OptArg ((\ f opts -> Ok opts { optLuxi = Just f }) . + fromMaybe defaultLuxiSocket) "SOCKET") + "collect data via Luxi, optionally using the given SOCKET path" oMaxCpu :: OptType oMaxCpu = Option "" ["max-cpu"] (ReqArg (\ n opts -> Ok opts { optMcpu = read n }) "RATIO") "maximum virtual-to-physical cpu ratio for nodes" +oMaxSolLength :: OptType +oMaxSolLength = Option "l" ["max-length"] + (ReqArg (\ i opts -> Ok opts { optMaxLength = read i }) "N") + "cap the solution at this many moves (useful for very\ + \ unbalanced clusters)" + 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)" -oDiskMoves :: OptType -oDiskMoves = Option "" ["no-disk-moves"] - (NoArg (\ opts -> Ok opts { optDiskMoves = False})) - "disallow disk moves from the list of allowed instance changes,\ - \ thus allowing only the 'cheap' failover/migrate operations" +oMinScore :: OptType +oMinScore = Option "e" ["min-score"] + (ReqArg (\ e opts -> Ok opts { optMinScore = read e }) "EPSILON") + " mininum score to aim for" -oDynuFile :: OptType -oDynuFile = Option "U" ["dynu-file"] - (ReqArg (\ f opts -> Ok opts { optDynuFile = Just f }) "FILE") - "Import dynamic utilisation data from the given FILE" +oNoHeaders :: OptType +oNoHeaders = Option "" ["no-headers"] + (NoArg (\ opts -> Ok opts { optNoHeaders = True })) + "do not show a header line" -oExTags :: OptType -oExTags = Option "" ["exclusion-tags"] - (ReqArg (\ f opts -> Ok opts { optExTags = Just $ sepSplit ',' f }) - "TAG,...") "Enable instance exclusion based on given tag prefix" +oNodeSim :: OptType +oNodeSim = Option "" ["simulate"] + (ReqArg (\ f o -> Ok o { optNodeSim = Just f }) "SPEC") + "simulate an empty cluster, given as 'num_nodes,disk,ram,cpu'" + +oOfflineNode :: OptType +oOfflineNode = Option "O" ["offline"] + (ReqArg (\ n o -> Ok o { optOffline = n:optOffline o }) "NODE") + "set node as offline" + +oOneline :: OptType +oOneline = Option "o" ["oneline"] + (NoArg (\ opts -> Ok opts { optOneline = True })) + "print the ganeti command list for reaching the solution" + +oOutputDir :: OptType +oOutputDir = Option "d" ["output-dir"] + (ReqArg (\ d opts -> Ok opts { optOutPath = d }) "PATH") + "directory in which to write output files" + +oPrintCommands :: OptType +oPrintCommands = Option "C" ["print-commands"] + (OptArg ((\ f opts -> Ok opts { optShowCmds = Just f }) . + fromMaybe "-") + "FILE") + "print the ganeti command list for reaching the solution,\ + \ if an argument is passed then write the commands to a\ + \ file named as such" + +oPrintInsts :: OptType +oPrintInsts = Option "" ["print-instances"] + (NoArg (\ opts -> Ok opts { optShowInsts = True })) + "print the final instance map" + +oPrintNodes :: OptType +oPrintNodes = Option "p" ["print-nodes"] + (OptArg ((\ f opts -> + let splitted = sepSplit ',' f + in Ok opts { optShowNodes = Just splitted }) . + fromMaybe []) "FIELDS") + "print the final node list" + +oQuiet :: OptType +oQuiet = Option "q" ["quiet"] + (NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts - 1 })) + "decrease the verbosity level" + +oRapiMaster :: OptType +oRapiMaster = Option "m" ["master"] + (ReqArg (\ m opts -> Ok opts { optMaster = m }) "ADDRESS") + "collect data via RAPI at the given ADDRESS" + +oShowHelp :: OptType +oShowHelp = Option "h" ["help"] + (NoArg (\ opts -> Ok opts { optShowHelp = True})) + "show help" + +oShowVer :: OptType +oShowVer = Option "V" ["version"] + (NoArg (\ opts -> Ok opts { optShowVer = True})) + "show the version of the program" oTieredSpec :: OptType oTieredSpec = Option "" ["tiered-alloc"] @@ -300,15 +305,10 @@ oTieredSpec = Option "" ["tiered-alloc"] "TSPEC") "enable tiered specs allocation, given as 'disk,ram,cpu'" -oShowVer :: OptType -oShowVer = Option "V" ["version"] - (NoArg (\ opts -> Ok opts { optShowVer = True})) - "show the version of the program" - -oShowHelp :: OptType -oShowHelp = Option "h" ["help"] - (NoArg (\ opts -> Ok opts { optShowHelp = True})) - "show help" +oVerbose :: OptType +oVerbose = Option "v" ["verbose"] + (NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts + 1 })) + "increase the verbosity level" -- | Usage info usageHelp :: String -> [OptType] -> String