Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / CLI.hs @ 78694255

History | View | Annotate | Download (5.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
    ( CLIOptions(..)
32
    , EToolOptions(..)
33
    , parseOpts
34
    , parseEnv
35
    , shTemplate
36
    , loadExternalData
37
    ) where
38

    
39
import System.Console.GetOpt
40
import System.Posix.Env
41
import System.IO
42
import System.Info
43
import System
44
import Monad
45
import Text.Printf (printf)
46
import qualified Data.Version
47

    
48
import qualified Ganeti.HTools.Version as Version(version)
49
import qualified Ganeti.HTools.Rapi as Rapi
50
import qualified Ganeti.HTools.Text as Text
51
import qualified Ganeti.HTools.Loader as Loader
52
import qualified Ganeti.HTools.Instance as Instance
53
import qualified Ganeti.HTools.Node as Node
54

    
55
import Ganeti.HTools.Types
56

    
57
-- | Class for types which support show help and show version.
58
class CLIOptions a where
59
    -- | Denotes whether the show help option has been passed.
60
    showHelp    :: a -> Bool
61
    -- | Denotes whether the show version option has been passed.
62
    showVersion :: a -> Bool
63

    
64
-- | Class for types which support the -i\/-n\/-m options.
65
class EToolOptions a where
66
    -- | Returns the node file name.
67
    nodeFile   :: a -> FilePath
68
    -- | Tells whether the node file has been passed as an option.
69
    nodeSet    :: a -> Bool
70
    -- | Returns the instance file name.
71
    instFile   :: a -> FilePath
72
    -- | Tells whether the instance file has been passed as an option.
73
    instSet    :: a -> Bool
74
    -- | Rapi target, if one has been passed.
75
    masterName :: a -> String
76
    -- | Whether to be less verbose.
77
    silent     :: a -> Bool
78

    
79
-- | Usage info
80
usageHelp :: (CLIOptions a) => String -> [OptDescr (a -> a)] -> String
81
usageHelp progname options =
82
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
83
               progname Version.version progname) options
84

    
85
-- | Command line parser, using the 'options' structure.
86
parseOpts :: (CLIOptions b) =>
87
             [String]            -- ^ The command line arguments
88
          -> String              -- ^ The program name
89
          -> [OptDescr (b -> b)] -- ^ The supported command line options
90
          -> b                   -- ^ The default options record
91
          -> IO (b, [String])    -- ^ The resulting options a leftover
92
                                 -- arguments
93
parseOpts argv progname options defaultOptions =
94
    case getOpt Permute options argv of
95
      (o, n, []) ->
96
          do
97
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
98
            when (showHelp po) $ do
99
              putStr $ usageHelp progname options
100
              exitWith ExitSuccess
101
            when (showVersion po) $ do
102
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
103
                     progname Version.version
104
                     compilerName (Data.Version.showVersion compilerVersion)
105
                     os arch
106
              exitWith ExitSuccess
107
            return resu
108
      (_, _, errs) ->
109
          ioError (userError (concat errs ++ usageHelp progname options))
110

    
111
-- | Parse the environment and return the node\/instance names.
112
--
113
-- This also hardcodes here the default node\/instance file names.
114
parseEnv :: () -> IO (String, String)
115
parseEnv () = do
116
  a <- getEnvDefault "HTOOLS_NODES" "nodes"
117
  b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
118
  return (a, b)
119

    
120
-- | A shell script template for autogenerated scripts.
121
shTemplate :: String
122
shTemplate =
123
    printf "#!/bin/sh\n\n\
124
           \# Auto-generated script for executing cluster rebalancing\n\n\
125
           \# To stop, touch the file /tmp/stop-htools\n\n\
126
           \set -e\n\n\
127
           \check() {\n\
128
           \  if [ -f /tmp/stop-htools ]; then\n\
129
           \    echo 'Stop requested, exiting'\n\
130
           \    exit 0\n\
131
           \  fi\n\
132
           \}\n\n"
133

    
134
-- | External tool data loader from a variety of sources.
135
loadExternalData :: (EToolOptions a) =>
136
                    a
137
                 -> IO (Node.List, Instance.List, String)
138
loadExternalData opts = do
139
  (env_node, env_inst) <- parseEnv ()
140
  let nodef = if nodeSet opts then nodeFile opts
141
              else env_node
142
      instf = if instSet opts then instFile opts
143
              else env_inst
144
  input_data <-
145
      case masterName opts of
146
        "" -> Text.loadData nodef instf
147
        host -> Rapi.loadData host
148

    
149
  let ldresult = input_data >>= Loader.mergeData
150
  (loaded_nl, il, csf) <-
151
      (case ldresult of
152
         Ok x -> return x
153
         Bad s -> do
154
           printf "Error: failed to load data. Details:\n%s\n" s
155
           exitWith $ ExitFailure 1
156
      )
157
  let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il
158

    
159
  unless (null fix_msgs || silent opts) $ do
160
         putStrLn "Warning: cluster has inconsistent data:"
161
         putStrLn . unlines . map (\s -> printf "  - %s" s) $ fix_msgs
162

    
163
  return (fixed_nl, il, csf)