Revision 0427285d Ganeti/HTools/CLI.hs

b/Ganeti/HTools/CLI.hs
28 28
-}
29 29

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

  
40
import Data.Maybe (isJust, fromJust)
63
import Data.Maybe (isJust, fromJust, fromMaybe)
41 64
import qualified Data.Version
42 65
import Monad
43 66
import System.Console.GetOpt
......
54 77
import qualified Ganeti.HTools.Loader as Loader
55 78
import qualified Ganeti.HTools.Instance as Instance
56 79
import qualified Ganeti.HTools.Node as Node
80
import qualified Ganeti.HTools.Cluster as Cluster
57 81

  
58 82
import Ganeti.HTools.Types
59 83

  
......
61 85
defaultLuxiSocket :: FilePath
62 86
defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master"
63 87

  
64
-- | Class for types which support show help and show version.
65
class CLIOptions a where
66
    -- | Denotes whether the show help option has been passed.
67
    showHelp    :: a -> Bool
68
    -- | Denotes whether the show version option has been passed.
69
    showVersion :: a -> Bool
70

  
71
-- | Class for types which support the -i\/-n\/-m options.
72
class EToolOptions a where
73
    -- | Returns the node file name.
74
    nodeFile   :: a -> FilePath
75
    -- | Tells whether the node file has been passed as an option.
76
    nodeSet    :: a -> Bool
77
    -- | Returns the instance file name.
78
    instFile   :: a -> FilePath
79
    -- | Tells whether the instance file has been passed as an option.
80
    instSet    :: a -> Bool
81
    -- | Rapi target, if one has been passed.
82
    masterName :: a -> String
83
    -- | Whether to connect to a local luxi socket.
84
    luxiSocket :: a -> Maybe FilePath
85
    -- | Whether to be less verbose.
86
    silent     :: a -> Bool
88
-- | Command line options structure.
89
data Options = Options
90
    { optShowNodes :: Bool           -- ^ Whether to show node status
91
    , optShowCmds  :: Maybe FilePath -- ^ Whether to show the command list
92
    , optOneline   :: Bool           -- ^ Switch output to a single line
93
    , optOutPath   :: FilePath       -- ^ Path to the output directory
94
    , optNoHeaders :: Bool           -- ^ Do not show a header line
95
    , optNodeFile  :: FilePath       -- ^ Path to the nodes file
96
    , optNodeSet   :: Bool           -- ^ The nodes have been set by options
97
    , optInstFile  :: FilePath       -- ^ Path to the instances file
98
    , optInstSet   :: Bool           -- ^ The insts have been set by options
99
    , optMaxLength :: Int            -- ^ Stop after this many steps
100
    , optMaster    :: String         -- ^ Collect data from RAPI
101
    , optLuxi      :: Maybe FilePath -- ^ Collect data from Luxi
102
    , optOffline   :: [String]       -- ^ Names of offline nodes
103
    , optIMem      :: Int            -- ^ Instance memory
104
    , optIDsk      :: Int            -- ^ Instance disk
105
    , optIVCPUs    :: Int            -- ^ Instance VCPUs
106
    , optINodes    :: Int            -- ^ Nodes required for an instance
107
    , optMinScore  :: Cluster.Score  -- ^ The minimum score we aim for
108
    , optMcpu      :: Double         -- ^ Max cpu ratio for nodes
109
    , optMdsk      :: Double         -- ^ Max disk usage ratio for nodes
110
    , optVerbose   :: Int            -- ^ Verbosity level
111
    , optShowVer   :: Bool           -- ^ Just show the program version
112
    , optShowHelp  :: Bool           -- ^ Just show the help
113
    } deriving Show
114

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

  
143
-- | Abrreviation for the option type
144
type OptType = OptDescr (Options -> Options)
145

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

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

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

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

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

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

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

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

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

  
196
oVerbose :: OptType
197
oVerbose = Option "v" ["verbose"]
198
           (NoArg (\ opts -> opts { optVerbose = optVerbose opts + 1 }))
199
           "increase the verbosity level"
200

  
201
oQuiet :: OptType
202
oQuiet = Option "q" ["quiet"]
203
         (NoArg (\ opts -> opts { optVerbose = optVerbose opts - 1 }))
204
         "decrease the verbosity level"
205

  
206
oOfflineNode :: OptType
207
oOfflineNode = Option "O" ["offline"]
208
               (ReqArg (\ n o -> o { optOffline = n:optOffline o }) "NODE")
209
               "set node as offline"
210

  
211
oMaxSolLength :: OptType
212
oMaxSolLength = Option "l" ["max-length"]
213
                (ReqArg (\ i opts -> opts { optMaxLength =  read i::Int }) "N")
214
                "cap the solution at this many moves (useful for very\
215
                \ unbalanced clusters)"
216

  
217
oMinScore :: OptType
218
oMinScore = Option "e" ["min-score"]
219
            (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
220
            " mininum score to aim for"
221

  
222
oIMem :: OptType
223
oIMem = Option "" ["memory"]
224
        (ReqArg (\ m opts -> opts { optIMem = read m }) "MEMORY")
225
        "memory size for instances"
226

  
227
oIDisk :: OptType
228
oIDisk = Option "" ["disk"]
229
         (ReqArg (\ d opts -> opts { optIDsk = read d }) "DISK")
230
         "disk size for instances"
231

  
232
oIVcpus :: OptType
233
oIVcpus = Option "" ["vcpus"]
234
          (ReqArg (\ p opts -> opts { optIVCPUs = read p }) "NUM")
235
          "number of virtual cpus for instances"
236

  
237
oINodes :: OptType
238
oINodes = Option "" ["req-nodes"]
239
          (ReqArg (\ n opts -> opts { optINodes = read n }) "NODES")
240
          "number of nodes for the new instances (1=plain, 2=mirrored)"
241

  
242
oMaxCpu :: OptType
243
oMaxCpu = Option "" ["max-cpu"]
244
          (ReqArg (\ n opts -> opts { optMcpu = read n }) "RATIO")
245
          "maximum virtual-to-physical cpu ratio for nodes"
246

  
247
oMinDisk :: OptType
248
oMinDisk = Option "" ["min-disk"]
249
           (ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
250
           "minimum free disk space for nodes (between 0 and 1)"
251

  
252
oShowVer :: OptType
253
oShowVer = Option "V" ["version"]
254
           (NoArg (\ opts -> opts { optShowVer = True}))
255
           "show the version of the program"
256

  
257
oShowHelp :: OptType
258
oShowHelp = Option "h" ["help"]
259
            (NoArg (\ opts -> opts { optShowHelp = True}))
260
            "show help"
87 261

  
88 262
-- | Usage info
89
usageHelp :: (CLIOptions a) => String -> [OptDescr (a -> a)] -> String
263
usageHelp :: String -> [OptType] -> String
90 264
usageHelp progname =
91 265
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
92 266
               progname Version.version progname)
93 267

  
94 268
-- | Command line parser, using the 'options' structure.
95
parseOpts :: (CLIOptions b) =>
96
             [String]            -- ^ The command line arguments
97
          -> String              -- ^ The program name
98
          -> [OptDescr (b -> b)] -- ^ The supported command line options
99
          -> b                   -- ^ The default options record
100
          -> IO (b, [String])    -- ^ The resulting options a leftover
101
                                 -- arguments
102
parseOpts argv progname options defaultOptions =
269
parseOpts :: [String]               -- ^ The command line arguments
270
          -> String                 -- ^ The program name
271
          -> [OptType]              -- ^ The supported command line options
272
          -> IO (Options, [String]) -- ^ The resulting options and leftover
273
                                    -- arguments
274
parseOpts argv progname options =
103 275
    case getOpt Permute options argv of
104 276
      (o, n, []) ->
105 277
          do
106 278
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
107
            when (showHelp po) $ do
279
            when (optShowHelp po) $ do
108 280
              putStr $ usageHelp progname options
109 281
              exitWith ExitSuccess
110
            when (showVersion po) $ do
282
            when (optShowVer po) $ do
111 283
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
112 284
                     progname Version.version
113 285
                     compilerName (Data.Version.showVersion compilerVersion)
......
141 313
           \}\n\n"
142 314

  
143 315
-- | External tool data loader from a variety of sources.
144
loadExternalData :: (EToolOptions a) =>
145
                    a
316
loadExternalData :: Options
146 317
                 -> IO (Node.List, Instance.List, String)
147 318
loadExternalData opts = do
148 319
  (env_node, env_inst) <- parseEnv ()
149
  let nodef = if nodeSet opts then nodeFile opts
320
  let nodef = if optNodeSet opts then optNodeFile opts
150 321
              else env_node
151
      instf = if instSet opts then instFile opts
322
      instf = if optInstSet opts then optInstFile opts
152 323
              else env_inst
153
      mhost = masterName opts
154
      lsock = luxiSocket opts
324
      mhost = optMaster opts
325
      lsock = optLuxi opts
155 326
      setRapi = mhost /= ""
156 327
      setLuxi = isJust lsock
157
      setFiles = nodeSet opts || instSet opts
328
      setFiles = optNodeSet opts || optInstSet opts
158 329
      allSet = filter id [setRapi, setLuxi, setFiles]
159 330
  when (length allSet > 1) $
160 331
       do
......
178 349
      )
179 350
  let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il
180 351

  
181
  unless (null fix_msgs || silent opts) $ do
352
  unless (null fix_msgs || optVerbose opts == 0) $ do
182 353
         hPutStrLn stderr "Warning: cluster has inconsistent data:"
183 354
         hPutStrLn stderr . unlines . map (printf "  - %s") $ fix_msgs
184 355

  

Also available in: Unified diff