Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / CLI.hs @ 8472a321

History | View | Annotate | Download (4.2 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
module Ganeti.HTools.CLI
10
    ( CLIOptions(..)
11
    , EToolOptions(..)
12
    , parseOpts
13
    , parseEnv
14
    , shTemplate
15
    , loadExternalData
16
    ) where
17

    
18
import System.Console.GetOpt
19
import System.Posix.Env
20
import System.IO
21
import System.Info
22
import System
23
import Monad
24
import Text.Printf (printf)
25
import qualified Data.Version
26

    
27
import qualified Ganeti.HTools.Version as Version(version)
28
import qualified Ganeti.HTools.Rapi as Rapi
29
import qualified Ganeti.HTools.Text as Text
30
import qualified Ganeti.HTools.Loader as Loader
31

    
32
import Ganeti.HTools.Types
33

    
34
-- | Class for types which support show help and show version
35
class CLIOptions a where
36
    showHelp    :: a -> Bool
37
    showVersion :: a -> Bool
38

    
39
-- | Class for types which support the -i/-n/-m options
40
class EToolOptions a where
41
    nodeFile   :: a -> FilePath
42
    nodeSet    :: a -> Bool
43
    instFile   :: a -> FilePath
44
    instSet    :: a -> Bool
45
    masterName :: a -> String
46
    silent     :: a -> Bool
47

    
48
-- | Command line parser, using the 'options' structure.
49
parseOpts :: (CLIOptions b) =>
50
             [String]            -- ^ The command line arguments
51
          -> String              -- ^ The program name
52
          -> [OptDescr (b -> b)] -- ^ The supported command line options
53
          -> b                   -- ^ The default options record
54
          -> IO (b, [String])    -- ^ The resulting options a leftover
55
                                 -- arguments
56
parseOpts argv progname options defaultOptions =
57
    case getOpt Permute options argv of
58
      (o, n, []) ->
59
          do
60
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
61
            when (showHelp po) $ do
62
              putStr $ usageInfo header options
63
              exitWith ExitSuccess
64
            when (showVersion po) $ do
65
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
66
                     progname Version.version
67
                     compilerName (Data.Version.showVersion compilerVersion)
68
                     os arch
69
              exitWith ExitSuccess
70
            return resu
71
      (_, _, errs) ->
72
          ioError (userError (concat errs ++ usageInfo header options))
73
      where header = printf "%s %s\nUsage: %s [OPTION...]"
74
                     progname Version.version progname
75

    
76
-- | Parse the environment and return the node/instance names.
77
-- This also hardcodes here the default node/instance file names.
78
parseEnv :: () -> IO (String, String)
79
parseEnv () = do
80
  a <- getEnvDefault "HTOOLS_NODES" "nodes"
81
  b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
82
  return (a, b)
83

    
84
-- | A shell script template for autogenerated scripts
85
shTemplate :: String
86
shTemplate =
87
    printf "#!/bin/sh\n\n\
88
           \# Auto-generated script for executing cluster rebalancing\n\n\
89
           \# To stop, touch the file /tmp/stop-htools\n\n\
90
           \set -e\n\n\
91
           \check() {\n\
92
           \  if [ -f /tmp/stop-htools ]; then\n\
93
           \    echo 'Stop requested, exiting'\n\
94
           \    exit 0\n\
95
           \  fi\n\
96
           \}\n\n"
97

    
98
-- | External tool data loader from a variety of sources
99
loadExternalData :: (EToolOptions a) =>
100
                    a
101
                 -> IO (NodeList, InstanceList, String)
102
loadExternalData opts = do
103
  (env_node, env_inst) <- parseEnv ()
104
  let nodef = if nodeSet opts then nodeFile opts
105
              else env_node
106
      instf = if instSet opts then instFile opts
107
              else env_inst
108
  input_data <-
109
      case masterName opts of
110
        "" -> Text.loadData nodef instf
111
        host -> Rapi.loadData host
112

    
113
  let ldresult = input_data >>= Loader.mergeData
114
  (loaded_nl, il, csf) <-
115
      (case ldresult of
116
         Ok x -> return x
117
         Bad s -> do
118
           printf "Error: failed to load data. Details:\n%s\n" s
119
           exitWith $ ExitFailure 1
120
      )
121
  let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il
122

    
123
  unless (null fix_msgs || silent opts) $ do
124
         putStrLn "Warning: cluster has inconsistent data:"
125
         putStrLn . unlines . map (\s -> printf "  - %s" s) $ fix_msgs
126

    
127
  return (fixed_nl, il, csf)