Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / CLI.hs @ 4f83a560

History | View | Annotate | Download (11.5 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
    , oPrintInsts
38
    , oPrintCommands
39
    , oOneline
40
    , oNoHeaders
41
    , oOutputDir
42
    , oNodeFile
43
    , oInstFile
44
    , oNodeSim
45
    , oRapiMaster
46
    , oLuxiSocket
47
    , oExecJobs
48
    , oMaxSolLength
49
    , oVerbose
50
    , oQuiet
51
    , oOfflineNode
52
    , oMinScore
53
    , oIMem
54
    , oIDisk
55
    , oIVcpus
56
    , oINodes
57
    , oMaxCpu
58
    , oMinDisk
59
    , oDiskMoves
60
    , oDynuFile
61
    , oShowVer
62
    , oShowHelp
63
    ) where
64

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

    
74
import qualified Ganeti.HTools.Version as Version(version)
75
import Ganeti.HTools.Types
76

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

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

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

    
146
-- | Abrreviation for the option type
147
type OptType = OptDescr (Options -> Options)
148

    
149
oPrintNodes :: OptType
150
oPrintNodes = Option "p" ["print-nodes"]
151
              (NoArg (\ opts -> opts { optShowNodes = True }))
152
              "print the final node list"
153

    
154
oPrintInsts :: OptType
155
oPrintInsts = Option "" ["print-instances"]
156
              (NoArg (\ opts -> opts { optShowInsts = True }))
157
              "print the final instance map"
158

    
159
oPrintCommands :: OptType
160
oPrintCommands = Option "C" ["print-commands"]
161
                 (OptArg ((\ f opts -> opts { optShowCmds = Just f }) .
162
                          fromMaybe "-")
163
                  "FILE")
164
                 "print the ganeti command list for reaching the solution,\
165
                 \ if an argument is passed then write the commands to a\
166
                 \ file named as such"
167

    
168
oOneline :: OptType
169
oOneline = Option "o" ["oneline"]
170
           (NoArg (\ opts -> opts { optOneline = True }))
171
           "print the ganeti command list for reaching the solution"
172

    
173
oNoHeaders :: OptType
174
oNoHeaders = Option "" ["no-headers"]
175
             (NoArg (\ opts -> opts { optNoHeaders = True }))
176
             "do not show a header line"
177

    
178
oOutputDir :: OptType
179
oOutputDir = Option "d" ["output-dir"]
180
             (ReqArg (\ d opts -> opts { optOutPath = d }) "PATH")
181
             "directory in which to write output files"
182

    
183
oNodeFile :: OptType
184
oNodeFile = Option "n" ["nodes"]
185
            (ReqArg (\ f o -> o { optNodeFile = f, optNodeSet = True }) "FILE")
186
            "the node list FILE"
187

    
188
oInstFile :: OptType
189
oInstFile = Option "i" ["instances"]
190
            (ReqArg (\ f o -> o { optInstFile = f, optInstSet = True }) "FILE")
191
            "the instance list FILE"
192

    
193
oNodeSim :: OptType
194
oNodeSim = Option "" ["simulate"]
195
            (ReqArg (\ f o -> o { optNodeSim = Just f }) "SPEC")
196
            "simulate an empty cluster, given as 'num_nodes,disk,memory,cpus'"
197

    
198
oRapiMaster :: OptType
199
oRapiMaster = Option "m" ["master"]
200
              (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
201
              "collect data via RAPI at the given ADDRESS"
202

    
203
oLuxiSocket :: OptType
204
oLuxiSocket = Option "L" ["luxi"]
205
              (OptArg ((\ f opts -> opts { optLuxi = Just f }) .
206
                       fromMaybe defaultLuxiSocket) "SOCKET")
207
              "collect data via Luxi, optionally using the given SOCKET path"
208

    
209
oExecJobs :: OptType
210
oExecJobs = Option "X" ["exec"]
211
             (NoArg (\ opts -> opts { optExecJobs = True}))
212
             "execute the suggested moves via Luxi (only available when using\
213
             \ it for data gathering"
214

    
215
oVerbose :: OptType
216
oVerbose = Option "v" ["verbose"]
217
           (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
218
           "increase the verbosity level"
219

    
220
oQuiet :: OptType
221
oQuiet = Option "q" ["quiet"]
222
         (NoArg (\ opts -> opts { optVerbose = optVerbose opts - 1 }))
223
         "decrease the verbosity level"
224

    
225
oOfflineNode :: OptType
226
oOfflineNode = Option "O" ["offline"]
227
               (ReqArg (\ n o -> o { optOffline = n:optOffline o }) "NODE")
228
               "set node as offline"
229

    
230
oMaxSolLength :: OptType
231
oMaxSolLength = Option "l" ["max-length"]
232
                (ReqArg (\ i opts -> opts { optMaxLength =  read i::Int }) "N")
233
                "cap the solution at this many moves (useful for very\
234
                \ unbalanced clusters)"
235

    
236
oMinScore :: OptType
237
oMinScore = Option "e" ["min-score"]
238
            (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
239
            " mininum score to aim for"
240

    
241
oIMem :: OptType
242
oIMem = Option "" ["memory"]
243
        (ReqArg (\ m opts -> opts { optIMem = read m }) "MEMORY")
244
        "memory size for instances"
245

    
246
oIDisk :: OptType
247
oIDisk = Option "" ["disk"]
248
         (ReqArg (\ d opts -> opts { optIDsk = read d }) "DISK")
249
         "disk size for instances"
250

    
251
oIVcpus :: OptType
252
oIVcpus = Option "" ["vcpus"]
253
          (ReqArg (\ p opts -> opts { optIVCPUs = read p }) "NUM")
254
          "number of virtual cpus for instances"
255

    
256
oINodes :: OptType
257
oINodes = Option "" ["req-nodes"]
258
          (ReqArg (\ n opts -> opts { optINodes = read n }) "NODES")
259
          "number of nodes for the new instances (1=plain, 2=mirrored)"
260

    
261
oMaxCpu :: OptType
262
oMaxCpu = Option "" ["max-cpu"]
263
          (ReqArg (\ n opts -> opts { optMcpu = read n }) "RATIO")
264
          "maximum virtual-to-physical cpu ratio for nodes"
265

    
266
oMinDisk :: OptType
267
oMinDisk = Option "" ["min-disk"]
268
           (ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
269
           "minimum free disk space for nodes (between 0 and 1)"
270

    
271
oDiskMoves :: OptType
272
oDiskMoves = Option "" ["no-disk-moves"]
273
             (NoArg (\ opts -> opts { optDiskMoves = False}))
274
             "disallow disk moves from the list of allowed instance changes,\
275
             \ thus allowing only the 'cheap' failover/migrate operations"
276

    
277
oDynuFile :: OptType
278
oDynuFile = Option "U" ["dynu-file"]
279
            (ReqArg (\ f opts -> opts { optDynuFile = Just f }) "FILE")
280
            "Import dynamic utilisation data from the given FILE"
281

    
282
oShowVer :: OptType
283
oShowVer = Option "V" ["version"]
284
           (NoArg (\ opts -> opts { optShowVer = True}))
285
           "show the version of the program"
286

    
287
oShowHelp :: OptType
288
oShowHelp = Option "h" ["help"]
289
            (NoArg (\ opts -> opts { optShowHelp = True}))
290
            "show help"
291

    
292
-- | Usage info
293
usageHelp :: String -> [OptType] -> String
294
usageHelp progname =
295
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
296
               progname Version.version progname)
297

    
298
-- | Command line parser, using the 'options' structure.
299
parseOpts :: [String]               -- ^ The command line arguments
300
          -> String                 -- ^ The program name
301
          -> [OptType]              -- ^ The supported command line options
302
          -> IO (Options, [String]) -- ^ The resulting options and leftover
303
                                    -- arguments
304
parseOpts argv progname options =
305
    case getOpt Permute options argv of
306
      (o, n, []) ->
307
          do
308
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
309
            when (optShowHelp po) $ do
310
              putStr $ usageHelp progname options
311
              exitWith ExitSuccess
312
            when (optShowVer po) $ do
313
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
314
                     progname Version.version
315
                     compilerName (Data.Version.showVersion compilerVersion)
316
                     os arch
317
              exitWith ExitSuccess
318
            return resu
319
      (_, _, errs) -> do
320
        hPutStrLn stderr $ "Command line error: "  ++ concat errs
321
        hPutStrLn stderr $ usageHelp progname options
322
        exitWith $ ExitFailure 2
323

    
324
-- | A shell script template for autogenerated scripts.
325
shTemplate :: String
326
shTemplate =
327
    printf "#!/bin/sh\n\n\
328
           \# Auto-generated script for executing cluster rebalancing\n\n\
329
           \# To stop, touch the file /tmp/stop-htools\n\n\
330
           \set -e\n\n\
331
           \check() {\n\
332
           \  if [ -f /tmp/stop-htools ]; then\n\
333
           \    echo 'Stop requested, exiting'\n\
334
           \    exit 0\n\
335
           \  fi\n\
336
           \}\n\n"