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
64 import Data.Maybe (fromMaybe)
65 import qualified Data.Version
67 import System.Console.GetOpt
71 import Text.Printf (printf)
73 import qualified Ganeti.HTools.Version as Version(version)
74 import Ganeti.HTools.Types
76 -- | The default value for the luxi socket
77 defaultLuxiSocket :: FilePath
78 defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master"
80 -- | Command line options structure.
81 data Options = Options
82 { optShowNodes :: Bool -- ^ Whether to show node status
83 , optShowInsts :: Bool -- ^ Whether to show the instance map
84 , optShowCmds :: Maybe FilePath -- ^ Whether to show the command list
85 , optOneline :: Bool -- ^ Switch output to a single line
86 , optOutPath :: FilePath -- ^ Path to the output directory
87 , optNoHeaders :: Bool -- ^ Do not show a header line
88 , optNodeFile :: FilePath -- ^ Path to the nodes file
89 , optNodeSet :: Bool -- ^ The nodes have been set by options
90 , optInstFile :: FilePath -- ^ Path to the instances file
91 , optInstSet :: Bool -- ^ The insts have been set by options
92 , optNodeSim :: Maybe String -- ^ Cluster simulation mode
93 , optMaxLength :: Int -- ^ Stop after this many steps
94 , optMaster :: String -- ^ Collect data from RAPI
95 , optLuxi :: Maybe FilePath -- ^ Collect data from Luxi
96 , optExecJobs :: Bool -- ^ Execute the commands via Luxi
97 , optOffline :: [String] -- ^ Names of offline nodes
98 , optIMem :: Int -- ^ Instance memory
99 , optIDsk :: Int -- ^ Instance disk
100 , optIVCPUs :: Int -- ^ Instance VCPUs
101 , optINodes :: Int -- ^ Nodes required for an instance
102 , optMinScore :: Score -- ^ The minimum score we aim for
103 , optMcpu :: Double -- ^ Max cpu ratio for nodes
104 , optMdsk :: Double -- ^ Max disk usage ratio for nodes
105 , optDiskMoves :: Bool -- ^ Allow disk moves
106 , optVerbose :: Int -- ^ Verbosity level
107 , optShowVer :: Bool -- ^ Just show the program version
108 , optShowHelp :: Bool -- ^ Just show the help
111 -- | Default values for the command line options.
112 defaultOptions :: Options
113 defaultOptions = Options
114 { optShowNodes = False
115 , optShowInsts = False
116 , optShowCmds = Nothing
118 , optNoHeaders = False
120 , optNodeFile = "nodes"
122 , optInstFile = "instances"
124 , optNodeSim = Nothing
128 , optExecJobs = False
137 , optDiskMoves = True
140 , optShowHelp = False
143 -- | Abrreviation for the option type
144 type OptType = OptDescr (Options -> Options)
146 oPrintNodes :: OptType
147 oPrintNodes = Option "p" ["print-nodes"]
148 (NoArg (\ opts -> opts { optShowNodes = True }))
149 "print the final node list"
151 oPrintInsts :: OptType
152 oPrintInsts = Option "" ["print-instances"]
153 (NoArg (\ opts -> opts { optShowInsts = True }))
154 "print the final instance map"
156 oPrintCommands :: OptType
157 oPrintCommands = Option "C" ["print-commands"]
158 (OptArg ((\ f opts -> opts { optShowCmds = Just f }) .
161 "print the ganeti command list for reaching the solution,\
162 \ if an argument is passed then write the commands to a\
163 \ file named as such"
166 oOneline = Option "o" ["oneline"]
167 (NoArg (\ opts -> opts { optOneline = True }))
168 "print the ganeti command list for reaching the solution"
170 oNoHeaders :: OptType
171 oNoHeaders = Option "" ["no-headers"]
172 (NoArg (\ opts -> opts { optNoHeaders = True }))
173 "do not show a header line"
175 oOutputDir :: OptType
176 oOutputDir = Option "d" ["output-dir"]
177 (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
178 "directory in which to write output files"
181 oNodeFile = Option "n" ["nodes"]
182 (ReqArg (\ f o -> o { optNodeFile = f, optNodeSet = True }) "FILE")
186 oInstFile = Option "i" ["instances"]
187 (ReqArg (\ f o -> o { optInstFile = f, optInstSet = True }) "FILE")
188 "the instance list FILE"
191 oNodeSim = Option "" ["simulate"]
192 (ReqArg (\ f o -> o { optNodeSim = Just f }) "SPEC")
193 "simulate an empty cluster, given as 'num_nodes,disk,memory,cpus'"
195 oRapiMaster :: OptType
196 oRapiMaster = Option "m" ["master"]
197 (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
198 "collect data via RAPI at the given ADDRESS"
200 oLuxiSocket :: OptType
201 oLuxiSocket = Option "L" ["luxi"]
202 (OptArg ((\ f opts -> opts { optLuxi = Just f }) .
203 fromMaybe defaultLuxiSocket) "SOCKET")
204 "collect data via Luxi, optionally using the given SOCKET path"
207 oExecJobs = Option "X" ["exec"]
208 (NoArg (\ opts -> opts { optExecJobs = True}))
209 "execute the suggested moves via Luxi (only available when using\
210 \ it for data gathering"
213 oVerbose = Option "v" ["verbose"]
214 (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
215 "increase the verbosity level"
218 oQuiet = Option "q" ["quiet"]
219 (NoArg (\ opts -> opts { optVerbose = optVerbose opts - 1 }))
220 "decrease the verbosity level"
222 oOfflineNode :: OptType
223 oOfflineNode = Option "O" ["offline"]
224 (ReqArg (\ n o -> o { optOffline = n:optOffline o }) "NODE")
225 "set node as offline"
227 oMaxSolLength :: OptType
228 oMaxSolLength = Option "l" ["max-length"]
229 (ReqArg (\ i opts -> opts { optMaxLength = read i::Int }) "N")
230 "cap the solution at this many moves (useful for very\
231 \ unbalanced clusters)"
234 oMinScore = Option "e" ["min-score"]
235 (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
236 " mininum score to aim for"
239 oIMem = Option "" ["memory"]
240 (ReqArg (\ m opts -> opts { optIMem = read m }) "MEMORY")
241 "memory size for instances"
244 oIDisk = Option "" ["disk"]
245 (ReqArg (\ d opts -> opts { optIDsk = read d }) "DISK")
246 "disk size for instances"
249 oIVcpus = Option "" ["vcpus"]
250 (ReqArg (\ p opts -> opts { optIVCPUs = read p }) "NUM")
251 "number of virtual cpus for instances"
254 oINodes = Option "" ["req-nodes"]
255 (ReqArg (\ n opts -> opts { optINodes = read n }) "NODES")
256 "number of nodes for the new instances (1=plain, 2=mirrored)"
259 oMaxCpu = Option "" ["max-cpu"]
260 (ReqArg (\ n opts -> opts { optMcpu = read n }) "RATIO")
261 "maximum virtual-to-physical cpu ratio for nodes"
264 oMinDisk = Option "" ["min-disk"]
265 (ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
266 "minimum free disk space for nodes (between 0 and 1)"
268 oDiskMoves :: OptType
269 oDiskMoves = Option "" ["no-disk-moves"]
270 (NoArg (\ opts -> opts { optDiskMoves = False}))
271 "disallow disk moves from the list of allowed instance changes,\
272 \ thus allowing only the 'cheap' failover/migrate operations"
275 oShowVer = Option "V" ["version"]
276 (NoArg (\ opts -> opts { optShowVer = True}))
277 "show the version of the program"
280 oShowHelp = Option "h" ["help"]
281 (NoArg (\ opts -> opts { optShowHelp = True}))
285 usageHelp :: String -> [OptType] -> String
287 usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
288 progname Version.version progname)
290 -- | Command line parser, using the 'options' structure.
291 parseOpts :: [String] -- ^ The command line arguments
292 -> String -- ^ The program name
293 -> [OptType] -- ^ The supported command line options
294 -> IO (Options, [String]) -- ^ The resulting options and leftover
296 parseOpts argv progname options =
297 case getOpt Permute options argv of
300 let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
301 when (optShowHelp po) $ do
302 putStr $ usageHelp progname options
304 when (optShowVer po) $ do
305 printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
306 progname Version.version
307 compilerName (Data.Version.showVersion compilerVersion)
312 hPutStrLn stderr $ "Command line error: " ++ concat errs
313 hPutStrLn stderr $ usageHelp progname options
314 exitWith $ ExitFailure 2
316 -- | A shell script template for autogenerated scripts.
319 printf "#!/bin/sh\n\n\
320 \# Auto-generated script for executing cluster rebalancing\n\n\
321 \# To stop, touch the file /tmp/stop-htools\n\n\
324 \ if [ -f /tmp/stop-htools ]; then\n\
325 \ echo 'Stop requested, exiting'\n\