1 {-| Implementation of command-line functions.
3 This module holds the common cli-related functions for the binaries,
4 separated into this module since Utils.hs is used in many other places
5 and this is more IO oriented.
11 Copyright (C) 2009 Google Inc.
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30 module Ganeti.HTools.CLI
62 import Data.Maybe (fromMaybe)
63 import qualified Data.Version
65 import System.Console.GetOpt
69 import Text.Printf (printf)
71 import qualified Ganeti.HTools.Version as Version(version)
72 import qualified Ganeti.HTools.Cluster as Cluster
74 -- | The default value for the luxi socket
75 defaultLuxiSocket :: FilePath
76 defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master"
78 -- | Command line options structure.
79 data Options = Options
80 { optShowNodes :: Bool -- ^ Whether to show node status
81 , optShowCmds :: Maybe FilePath -- ^ Whether to show the command list
82 , optOneline :: Bool -- ^ Switch output to a single line
83 , optOutPath :: FilePath -- ^ Path to the output directory
84 , optNoHeaders :: Bool -- ^ Do not show a header line
85 , optNodeFile :: FilePath -- ^ Path to the nodes file
86 , optNodeSet :: Bool -- ^ The nodes have been set by options
87 , optInstFile :: FilePath -- ^ Path to the instances file
88 , optInstSet :: Bool -- ^ The insts have been set by options
89 , optNodeSim :: Maybe String -- ^ Cluster simulation mode
90 , optMaxLength :: Int -- ^ Stop after this many steps
91 , optMaster :: String -- ^ Collect data from RAPI
92 , optLuxi :: Maybe FilePath -- ^ Collect data from Luxi
93 , optOffline :: [String] -- ^ Names of offline nodes
94 , optIMem :: Int -- ^ Instance memory
95 , optIDsk :: Int -- ^ Instance disk
96 , optIVCPUs :: Int -- ^ Instance VCPUs
97 , optINodes :: Int -- ^ Nodes required for an instance
98 , optMinScore :: Cluster.Score -- ^ The minimum score we aim for
99 , optMcpu :: Double -- ^ Max cpu ratio for nodes
100 , optMdsk :: Double -- ^ Max disk usage ratio for nodes
101 , optDiskMoves :: Bool -- ^ Allow disk moves
102 , optVerbose :: Int -- ^ Verbosity level
103 , optShowVer :: Bool -- ^ Just show the program version
104 , optShowHelp :: Bool -- ^ Just show the help
107 -- | Default values for the command line options.
108 defaultOptions :: Options
109 defaultOptions = Options
110 { optShowNodes = False
111 , optShowCmds = Nothing
113 , optNoHeaders = False
115 , optNodeFile = "nodes"
117 , optInstFile = "instances"
119 , optNodeSim = Nothing
131 , optDiskMoves = True
134 , optShowHelp = False
137 -- | Abrreviation for the option type
138 type OptType = OptDescr (Options -> Options)
140 oPrintNodes :: OptType
141 oPrintNodes = Option "p" ["print-nodes"]
142 (NoArg (\ opts -> opts { optShowNodes = True }))
143 "print the final node list"
145 oPrintCommands :: OptType
146 oPrintCommands = Option "C" ["print-commands"]
147 (OptArg ((\ f opts -> opts { optShowCmds = Just f }) .
150 "print the ganeti command list for reaching the solution,\
151 \ if an argument is passed then write the commands to a\
152 \ file named as such"
155 oOneline = Option "o" ["oneline"]
156 (NoArg (\ opts -> opts { optOneline = True }))
157 "print the ganeti command list for reaching the solution"
159 oNoHeaders :: OptType
160 oNoHeaders = Option "" ["no-headers"]
161 (NoArg (\ opts -> opts { optNoHeaders = True }))
162 "do not show a header line"
164 oOutputDir :: OptType
165 oOutputDir = Option "d" ["output-dir"]
166 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
167 "directory in which to write output files"
170 oNodeFile = Option "n" ["nodes"]
171 (ReqArg (\ f o -> o { optNodeFile = f, optNodeSet = True }) "FILE")
175 oInstFile = Option "i" ["instances"]
176 (ReqArg (\ f o -> o { optInstFile = f, optInstSet = True }) "FILE")
177 "the instance list FILE"
180 oNodeSim = Option "" ["simulate"]
181 (ReqArg (\ f o -> o { optNodeSim = Just f }) "SPEC")
182 "simulate an empty cluster, given as 'num_nodes,disk,memory,cpus'"
184 oRapiMaster :: OptType
185 oRapiMaster = Option "m" ["master"]
186 (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
187 "collect data via RAPI at the given ADDRESS"
189 oLuxiSocket :: OptType
190 oLuxiSocket = Option "L" ["luxi"]
191 (OptArg ((\ f opts -> opts { optLuxi = Just f }) .
192 fromMaybe defaultLuxiSocket) "SOCKET")
193 "collect data via Luxi, optionally using the given SOCKET path"
196 oVerbose = Option "v" ["verbose"]
197 (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
198 "increase the verbosity level"
201 oQuiet = Option "q" ["quiet"]
202 (NoArg (\ opts -> opts { optVerbose = optVerbose opts - 1 }))
203 "decrease the verbosity level"
205 oOfflineNode :: OptType
206 oOfflineNode = Option "O" ["offline"]
207 (ReqArg (\ n o -> o { optOffline = n:optOffline o }) "NODE")
208 "set node as offline"
210 oMaxSolLength :: OptType
211 oMaxSolLength = Option "l" ["max-length"]
212 (ReqArg (\ i opts -> opts { optMaxLength = read i::Int }) "N")
213 "cap the solution at this many moves (useful for very\
214 \ unbalanced clusters)"
217 oMinScore = Option "e" ["min-score"]
218 (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
219 " mininum score to aim for"
222 oIMem = Option "" ["memory"]
223 (ReqArg (\ m opts -> opts { optIMem = read m }) "MEMORY")
224 "memory size for instances"
227 oIDisk = Option "" ["disk"]
228 (ReqArg (\ d opts -> opts { optIDsk = read d }) "DISK")
229 "disk size for instances"
232 oIVcpus = Option "" ["vcpus"]
233 (ReqArg (\ p opts -> opts { optIVCPUs = read p }) "NUM")
234 "number of virtual cpus for instances"
237 oINodes = Option "" ["req-nodes"]
238 (ReqArg (\ n opts -> opts { optINodes = read n }) "NODES")
239 "number of nodes for the new instances (1=plain, 2=mirrored)"
242 oMaxCpu = Option "" ["max-cpu"]
243 (ReqArg (\ n opts -> opts { optMcpu = read n }) "RATIO")
244 "maximum virtual-to-physical cpu ratio for nodes"
247 oMinDisk = Option "" ["min-disk"]
248 (ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
249 "minimum free disk space for nodes (between 0 and 1)"
251 oDiskMoves :: OptType
252 oDiskMoves = Option "" ["no-disk-moves"]
253 (NoArg (\ opts -> opts { optDiskMoves = False}))
254 "disallow disk moves from the list of allowed instance changes,\
255 \ thus allowing only the 'cheap' failover/migrate operations"
258 oShowVer = Option "V" ["version"]
259 (NoArg (\ opts -> opts { optShowVer = True}))
260 "show the version of the program"
263 oShowHelp = Option "h" ["help"]
264 (NoArg (\ opts -> opts { optShowHelp = True}))
268 usageHelp :: String -> [OptType] -> String
270 usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
271 progname Version.version progname)
273 -- | Command line parser, using the 'options' structure.
274 parseOpts :: [String] -- ^ The command line arguments
275 -> String -- ^ The program name
276 -> [OptType] -- ^ The supported command line options
277 -> IO (Options, [String]) -- ^ The resulting options and leftover
279 parseOpts argv progname options =
280 case getOpt Permute options argv of
283 let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
284 when (optShowHelp po) $ do
285 putStr $ usageHelp progname options
287 when (optShowVer po) $ do
288 printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
289 progname Version.version
290 compilerName (Data.Version.showVersion compilerVersion)
295 hPutStrLn stderr $ "Command line error: " ++ concat errs
296 hPutStrLn stderr $ usageHelp progname options
297 exitWith $ ExitFailure 2
299 -- | A shell script template for autogenerated scripts.
302 printf "#!/bin/sh\n\n\
303 \# Auto-generated script for executing cluster rebalancing\n\n\
304 \# To stop, touch the file /tmp/stop-htools\n\n\
307 \ if [ -f /tmp/stop-htools ]; then\n\
308 \ echo 'Stop requested, exiting'\n\