Make IAlloc.loadData return maps
[ganeti-local] / Ganeti / HTools / CLI.hs
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, NameList, NameList)
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, ktn, kti) <-
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 ktn kti
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, ktn, kti)