Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / CLI.hs @ 0df5a1b4

History | View | Annotate | Download (10.9 kB)

1
{-| Implementation of command-line functions.
2

    
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.
6

    
7
-}
8

    
9
{-
10

    
11
Copyright (C) 2009 Google Inc.
12

    
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.
17

    
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.
22

    
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
26
02110-1301, USA.
27

    
28
-}
29

    
30
module Ganeti.HTools.CLI
31
    ( Options(..)
32
    , OptType
33
    , parseOpts
34
    , shTemplate
35
    -- * The options
36
    , oPrintNodes
37
    , oPrintCommands
38
    , oOneline
39
    , oNoHeaders
40
    , oOutputDir
41
    , oNodeFile
42
    , oInstFile
43
    , oNodeSim
44
    , oRapiMaster
45
    , oLuxiSocket
46
    , oExecJobs
47
    , oMaxSolLength
48
    , oVerbose
49
    , oQuiet
50
    , oOfflineNode
51
    , oMinScore
52
    , oIMem
53
    , oIDisk
54
    , oIVcpus
55
    , oINodes
56
    , oMaxCpu
57
    , oMinDisk
58
    , oDiskMoves
59
    , oShowVer
60
    , oShowHelp
61
    ) where
62

    
63
import Data.Maybe (fromMaybe)
64
import qualified Data.Version
65
import Monad
66
import System.Console.GetOpt
67
import System.IO
68
import System.Info
69
import System
70
import Text.Printf (printf)
71

    
72
import qualified Ganeti.HTools.Version as Version(version)
73
import Ganeti.HTools.Types
74

    
75
-- | The default value for the luxi socket
76
defaultLuxiSocket :: FilePath
77
defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master"
78

    
79
-- | Command line options structure.
80
data Options = Options
81
    { optShowNodes :: Bool           -- ^ Whether to show node status
82
    , optShowCmds  :: Maybe FilePath -- ^ Whether to show the command list
83
    , optOneline   :: Bool           -- ^ Switch output to a single line
84
    , optOutPath   :: FilePath       -- ^ Path to the output directory
85
    , optNoHeaders :: Bool           -- ^ Do not show a header line
86
    , optNodeFile  :: FilePath       -- ^ Path to the nodes file
87
    , optNodeSet   :: Bool           -- ^ The nodes have been set by options
88
    , optInstFile  :: FilePath       -- ^ Path to the instances file
89
    , optInstSet   :: Bool           -- ^ The insts have been set by options
90
    , optNodeSim   :: Maybe String   -- ^ Cluster simulation mode
91
    , optMaxLength :: Int            -- ^ Stop after this many steps
92
    , optMaster    :: String         -- ^ Collect data from RAPI
93
    , optLuxi      :: Maybe FilePath -- ^ Collect data from Luxi
94
    , optExecJobs  :: Bool           -- ^ Execute the commands via Luxi
95
    , optOffline   :: [String]       -- ^ Names of offline nodes
96
    , optIMem      :: Int            -- ^ Instance memory
97
    , optIDsk      :: Int            -- ^ Instance disk
98
    , optIVCPUs    :: Int            -- ^ Instance VCPUs
99
    , optINodes    :: Int            -- ^ Nodes required for an instance
100
    , optMinScore  :: Score          -- ^ The minimum score we aim for
101
    , optMcpu      :: Double         -- ^ Max cpu ratio for nodes
102
    , optMdsk      :: Double         -- ^ Max disk usage ratio for nodes
103
    , optDiskMoves :: Bool           -- ^ Allow disk moves
104
    , optVerbose   :: Int            -- ^ Verbosity level
105
    , optShowVer   :: Bool           -- ^ Just show the program version
106
    , optShowHelp  :: Bool           -- ^ Just show the help
107
    } deriving Show
108

    
109
-- | Default values for the command line options.
110
defaultOptions :: Options
111
defaultOptions  = Options
112
 { optShowNodes = False
113
 , optShowCmds  = Nothing
114
 , optOneline   = False
115
 , optNoHeaders = False
116
 , optOutPath   = "."
117
 , optNodeFile  = "nodes"
118
 , optNodeSet   = False
119
 , optInstFile  = "instances"
120
 , optInstSet   = False
121
 , optNodeSim   = Nothing
122
 , optMaxLength = -1
123
 , optMaster    = ""
124
 , optLuxi      = Nothing
125
 , optExecJobs  = False
126
 , optOffline   = []
127
 , optIMem      = 4096
128
 , optIDsk      = 102400
129
 , optIVCPUs    = 1
130
 , optINodes    = 2
131
 , optMinScore  = 1e-9
132
 , optMcpu      = -1
133
 , optMdsk      = -1
134
 , optDiskMoves = True
135
 , optVerbose   = 1
136
 , optShowVer   = False
137
 , optShowHelp  = False
138
 }
139

    
140
-- | Abrreviation for the option type
141
type OptType = OptDescr (Options -> Options)
142

    
143
oPrintNodes :: OptType
144
oPrintNodes = Option "p" ["print-nodes"]
145
              (NoArg (\ opts -> opts { optShowNodes = True }))
146
              "print the final node list"
147

    
148
oPrintCommands :: OptType
149
oPrintCommands = Option "C" ["print-commands"]
150
                 (OptArg ((\ f opts -> opts { optShowCmds = Just f }) .
151
                          fromMaybe "-")
152
                  "FILE")
153
                 "print the ganeti command list for reaching the solution,\
154
                 \ if an argument is passed then write the commands to a\
155
                 \ file named as such"
156

    
157
oOneline :: OptType
158
oOneline = Option "o" ["oneline"]
159
           (NoArg (\ opts -> opts { optOneline = True }))
160
           "print the ganeti command list for reaching the solution"
161

    
162
oNoHeaders :: OptType
163
oNoHeaders = Option "" ["no-headers"]
164
             (NoArg (\ opts -> opts { optNoHeaders = True }))
165
             "do not show a header line"
166

    
167
oOutputDir :: OptType
168
oOutputDir = Option "d" ["output-dir"]
169
             (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
170
             "directory in which to write output files"
171

    
172
oNodeFile :: OptType
173
oNodeFile = Option "n" ["nodes"]
174
            (ReqArg (\ f o -> o { optNodeFile = f, optNodeSet = True }) "FILE")
175
            "the node list FILE"
176

    
177
oInstFile :: OptType
178
oInstFile = Option "i" ["instances"]
179
            (ReqArg (\ f o -> o { optInstFile = f, optInstSet = True }) "FILE")
180
            "the instance list FILE"
181

    
182
oNodeSim :: OptType
183
oNodeSim = Option "" ["simulate"]
184
            (ReqArg (\ f o -> o { optNodeSim = Just f }) "SPEC")
185
            "simulate an empty cluster, given as 'num_nodes,disk,memory,cpus'"
186

    
187
oRapiMaster :: OptType
188
oRapiMaster = Option "m" ["master"]
189
              (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
190
              "collect data via RAPI at the given ADDRESS"
191

    
192
oLuxiSocket :: OptType
193
oLuxiSocket = Option "L" ["luxi"]
194
              (OptArg ((\ f opts -> opts { optLuxi = Just f }) .
195
                       fromMaybe defaultLuxiSocket) "SOCKET")
196
              "collect data via Luxi, optionally using the given SOCKET path"
197

    
198
oExecJobs :: OptType
199
oExecJobs = Option "X" ["exec"]
200
             (NoArg (\ opts -> opts { optExecJobs = True}))
201
             "execute the suggested moves via Luxi (only available when using\
202
             \ it for data gathering"
203

    
204
oVerbose :: OptType
205
oVerbose = Option "v" ["verbose"]
206
           (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
207
           "increase the verbosity level"
208

    
209
oQuiet :: OptType
210
oQuiet = Option "q" ["quiet"]
211
         (NoArg (\ opts -> opts { optVerbose = optVerbose opts - 1 }))
212
         "decrease the verbosity level"
213

    
214
oOfflineNode :: OptType
215
oOfflineNode = Option "O" ["offline"]
216
               (ReqArg (\ n o -> o { optOffline = n:optOffline o }) "NODE")
217
               "set node as offline"
218

    
219
oMaxSolLength :: OptType
220
oMaxSolLength = Option "l" ["max-length"]
221
                (ReqArg (\ i opts -> opts { optMaxLength =  read i::Int }) "N")
222
                "cap the solution at this many moves (useful for very\
223
                \ unbalanced clusters)"
224

    
225
oMinScore :: OptType
226
oMinScore = Option "e" ["min-score"]
227
            (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
228
            " mininum score to aim for"
229

    
230
oIMem :: OptType
231
oIMem = Option "" ["memory"]
232
        (ReqArg (\ m opts -> opts { optIMem = read m }) "MEMORY")
233
        "memory size for instances"
234

    
235
oIDisk :: OptType
236
oIDisk = Option "" ["disk"]
237
         (ReqArg (\ d opts -> opts { optIDsk = read d }) "DISK")
238
         "disk size for instances"
239

    
240
oIVcpus :: OptType
241
oIVcpus = Option "" ["vcpus"]
242
          (ReqArg (\ p opts -> opts { optIVCPUs = read p }) "NUM")
243
          "number of virtual cpus for instances"
244

    
245
oINodes :: OptType
246
oINodes = Option "" ["req-nodes"]
247
          (ReqArg (\ n opts -> opts { optINodes = read n }) "NODES")
248
          "number of nodes for the new instances (1=plain, 2=mirrored)"
249

    
250
oMaxCpu :: OptType
251
oMaxCpu = Option "" ["max-cpu"]
252
          (ReqArg (\ n opts -> opts { optMcpu = read n }) "RATIO")
253
          "maximum virtual-to-physical cpu ratio for nodes"
254

    
255
oMinDisk :: OptType
256
oMinDisk = Option "" ["min-disk"]
257
           (ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
258
           "minimum free disk space for nodes (between 0 and 1)"
259

    
260
oDiskMoves :: OptType
261
oDiskMoves = Option "" ["no-disk-moves"]
262
             (NoArg (\ opts -> opts { optDiskMoves = False}))
263
             "disallow disk moves from the list of allowed instance changes,\
264
             \ thus allowing only the 'cheap' failover/migrate operations"
265

    
266
oShowVer :: OptType
267
oShowVer = Option "V" ["version"]
268
           (NoArg (\ opts -> opts { optShowVer = True}))
269
           "show the version of the program"
270

    
271
oShowHelp :: OptType
272
oShowHelp = Option "h" ["help"]
273
            (NoArg (\ opts -> opts { optShowHelp = True}))
274
            "show help"
275

    
276
-- | Usage info
277
usageHelp :: String -> [OptType] -> String
278
usageHelp progname =
279
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
280
               progname Version.version progname)
281

    
282
-- | Command line parser, using the 'options' structure.
283
parseOpts :: [String]               -- ^ The command line arguments
284
          -> String                 -- ^ The program name
285
          -> [OptType]              -- ^ The supported command line options
286
          -> IO (Options, [String]) -- ^ The resulting options and leftover
287
                                    -- arguments
288
parseOpts argv progname options =
289
    case getOpt Permute options argv of
290
      (o, n, []) ->
291
          do
292
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
293
            when (optShowHelp po) $ do
294
              putStr $ usageHelp progname options
295
              exitWith ExitSuccess
296
            when (optShowVer po) $ do
297
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
298
                     progname Version.version
299
                     compilerName (Data.Version.showVersion compilerVersion)
300
                     os arch
301
              exitWith ExitSuccess
302
            return resu
303
      (_, _, errs) -> do
304
        hPutStrLn stderr $ "Command line error: "  ++ concat errs
305
        hPutStrLn stderr $ usageHelp progname options
306
        exitWith $ ExitFailure 2
307

    
308
-- | A shell script template for autogenerated scripts.
309
shTemplate :: String
310
shTemplate =
311
    printf "#!/bin/sh\n\n\
312
           \# Auto-generated script for executing cluster rebalancing\n\n\
313
           \# To stop, touch the file /tmp/stop-htools\n\n\
314
           \set -e\n\n\
315
           \check() {\n\
316
           \  if [ -f /tmp/stop-htools ]; then\n\
317
           \    echo 'Stop requested, exiting'\n\
318
           \    exit 0\n\
319
           \  fi\n\
320
           \}\n\n"