Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Common.hs @ f5af3409

History | View | Annotate | Download (6.4 kB)

1
{-| Base common functionality.
2

    
3
This module holds common functionality shared across Ganeti daemons,
4
HTools and any other programs.
5

    
6
-}
7

    
8
{-
9

    
10
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
11

    
12
This program is free software; you can redistribute it and/or modify
13
it under the terms of the GNU General Public License as published by
14
the Free Software Foundation; either version 2 of the License, or
15
(at your option) any later version.
16

    
17
This program is distributed in the hope that it will be useful, but
18
WITHOUT ANY WARRANTY; without even the implied warranty of
19
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
General Public License for more details.
21

    
22
You should have received a copy of the GNU General Public License
23
along with this program; if not, write to the Free Software
24
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
02110-1301, USA.
26

    
27
-}
28

    
29
module Ganeti.Common
30
  ( GenericOptType
31
  , StandardOptions(..)
32
  , OptCompletion(..)
33
  , optComplYesNo
34
  , oShowHelp
35
  , oShowVer
36
  , usageHelp
37
  , versionInfo
38
  , reqWithConversion
39
  , parseYesNo
40
  , parseOpts
41
  , parseOptsInner
42
  ) where
43

    
44
import Control.Monad (foldM)
45
import qualified Data.Version
46
import System.Console.GetOpt
47
import System.Exit
48
import System.Info
49
import System.IO
50
import Text.Printf (printf)
51

    
52
import Ganeti.BasicTypes
53
import qualified Ganeti.Version as Version (version)
54

    
55
-- | Parameter type.
56
data OptCompletion = OptComplNone             -- ^ No parameter to this option
57
                   | OptComplFile             -- ^ An existing file
58
                   | OptComplDir              -- ^ An existing directory
59
                   | OptComplHost             -- ^ Host name
60
                   | OptComplInetAddr         -- ^ One ipv4\/ipv6 address
61
                   | OptComplOneNode          -- ^ One node
62
                   | OptComplManyNodes        -- ^ Many nodes, comma-sep
63
                   | OptComplOneInstance      -- ^ One instance
64
                   | OptComplManyInstances    -- ^ Many instances, comma-sep
65
                   | OptComplOneOs            -- ^ One OS name
66
                   | OptComplOneIallocator    -- ^ One iallocator
67
                   | OptComplInstAddNodes     -- ^ Either one or two nodes
68
                   | OptComplOneGroup         -- ^ One group
69
                   | OptComplNumeric          -- ^ Float values
70
                   | OptComplString           -- ^ Arbitrary string
71
                   | OptComplChoices [String] -- ^ List of string choices
72
                   deriving (Show, Read, Eq)
73

    
74
-- | Yes\/no choices completion.
75
optComplYesNo :: OptCompletion
76
optComplYesNo = OptComplChoices ["yes", "no"]
77

    
78
-- | Abrreviation for the option type.
79
type GenericOptType a = OptDescr (a -> Result a)
80

    
81
-- | Type class for options which support help and version.
82
class StandardOptions a where
83
  helpRequested :: a -> Bool
84
  verRequested  :: a -> Bool
85
  requestHelp   :: a -> a
86
  requestVer    :: a -> a
87

    
88
-- | Options to request help output.
89
oShowHelp :: (StandardOptions a) => GenericOptType a
90
oShowHelp = Option "h" ["help"] (NoArg (Ok . requestHelp))
91
            "show help"
92

    
93
oShowVer :: (StandardOptions a) => GenericOptType a
94
oShowVer = Option "V" ["version"] (NoArg (Ok . requestVer))
95
           "show the version of the program"
96

    
97
-- | Usage info.
98
usageHelp :: String -> [GenericOptType a] -> String
99
usageHelp progname =
100
  usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
101
             progname Version.version progname)
102

    
103
-- | Show the program version info.
104
versionInfo :: String -> String
105
versionInfo progname =
106
  printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
107
         progname Version.version compilerName
108
         (Data.Version.showVersion compilerVersion)
109
         os arch
110

    
111
-- | Helper for parsing a yes\/no command line flag.
112
parseYesNo :: Bool         -- ^ Default value (when we get a @Nothing@)
113
           -> Maybe String -- ^ Parameter value
114
           -> Result Bool  -- ^ Resulting boolean value
115
parseYesNo v Nothing      = return v
116
parseYesNo _ (Just "yes") = return True
117
parseYesNo _ (Just "no")  = return False
118
parseYesNo _ (Just s)     = fail ("Invalid choice '" ++ s ++
119
                                  "', pass one of 'yes' or 'no'")
120

    
121
-- | Helper function for required arguments which need to be converted
122
-- as opposed to stored just as string.
123
reqWithConversion :: (String -> Result a)
124
                  -> (a -> b -> Result b)
125
                  -> String
126
                  -> ArgDescr (b -> Result b)
127
reqWithConversion conversion_fn updater_fn =
128
  ReqArg (\string_opt opts -> do
129
            parsed_value <- conversion_fn string_opt
130
            updater_fn parsed_value opts)
131

    
132
-- | Command line parser, using a generic 'Options' structure.
133
parseOpts :: (StandardOptions a) =>
134
             a                      -- ^ The default options
135
          -> [String]               -- ^ The command line arguments
136
          -> String                 -- ^ The program name
137
          -> [GenericOptType a]     -- ^ The supported command line options
138
          -> IO (a, [String])       -- ^ The resulting options and
139
                                    -- leftover arguments
140
parseOpts defaults argv progname options =
141
  case parseOptsInner defaults argv progname options of
142
    Left (code, msg) -> do
143
      hPutStr (if code == ExitSuccess then stdout else stderr) msg
144
      exitWith code
145
    Right result ->
146
      return result
147

    
148
-- | Inner parse options. The arguments are similar to 'parseOpts',
149
-- but it returns either a 'Left' composed of exit code and message,
150
-- or a 'Right' for the success case.
151
parseOptsInner :: (StandardOptions a) =>
152
                  a
153
               -> [String]
154
               -> String
155
               -> [GenericOptType a]
156
               -> Either (ExitCode, String) (a, [String])
157
parseOptsInner defaults argv progname options  =
158
  case getOpt Permute options argv of
159
    (opts, args, []) ->
160
      case foldM (flip id) defaults opts of
161
           Bad msg -> Left (ExitFailure 1,
162
                            "Error while parsing command line arguments:\n"
163
                            ++ msg ++ "\n")
164
           Ok parsed ->
165
             select (Right (parsed, args))
166
                 [ (helpRequested parsed,
167
                    Left (ExitSuccess, usageHelp progname options))
168
                 , (verRequested parsed,
169
                    Left (ExitSuccess, versionInfo progname))
170
                 ]
171
    (_, _, errs) ->
172
      Left (ExitFailure 2, "Command line error: "  ++ concat errs ++ "\n" ++
173
            usageHelp progname options)