1 {-| Implementation of command-line functions.
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.
9 module Ganeti.HTools.CLI
18 import System.Console.GetOpt
19 import System.Posix.Env
24 import Text.Printf (printf)
25 import qualified Data.Version
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 import qualified Ganeti.HTools.Instance as Instance
32 import qualified Ganeti.HTools.Node as Node
34 import Ganeti.HTools.Types
36 -- | Class for types which support show help and show version.
37 class CLIOptions a where
38 -- | Denotes whether the show help option has been passed.
40 -- | Denotes whether the show version option has been passed.
41 showVersion :: a -> Bool
43 -- | Class for types which support the -i\/-n\/-m options.
44 class EToolOptions a where
45 -- | Returns the node file name.
46 nodeFile :: a -> FilePath
47 -- | Tells whether the node file has been passed as an option.
49 -- | Returns the instance file name.
50 instFile :: a -> FilePath
51 -- | Tells whether the instance file has been passed as an option.
53 -- | Rapi target, if one has been passed.
54 masterName :: a -> String
55 -- | Whether to be less verbose.
58 -- | Command line parser, using the 'options' structure.
59 parseOpts :: (CLIOptions b) =>
60 [String] -- ^ The command line arguments
61 -> String -- ^ The program name
62 -> [OptDescr (b -> b)] -- ^ The supported command line options
63 -> b -- ^ The default options record
64 -> IO (b, [String]) -- ^ The resulting options a leftover
66 parseOpts argv progname options defaultOptions =
67 case getOpt Permute options argv of
70 let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
71 when (showHelp po) $ do
72 putStr $ usageInfo header options
74 when (showVersion po) $ do
75 printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
76 progname Version.version
77 compilerName (Data.Version.showVersion compilerVersion)
82 ioError (userError (concat errs ++ usageInfo header options))
83 where header = printf "%s %s\nUsage: %s [OPTION...]"
84 progname Version.version progname
86 -- | Parse the environment and return the node\/instance names.
88 -- This also hardcodes here the default node\/instance file names.
89 parseEnv :: () -> IO (String, String)
91 a <- getEnvDefault "HTOOLS_NODES" "nodes"
92 b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
95 -- | A shell script template for autogenerated scripts.
98 printf "#!/bin/sh\n\n\
99 \# Auto-generated script for executing cluster rebalancing\n\n\
100 \# To stop, touch the file /tmp/stop-htools\n\n\
103 \ if [ -f /tmp/stop-htools ]; then\n\
104 \ echo 'Stop requested, exiting'\n\
109 -- | External tool data loader from a variety of sources.
110 loadExternalData :: (EToolOptions a) =>
112 -> IO (Node.List, Instance.List, String)
113 loadExternalData opts = do
114 (env_node, env_inst) <- parseEnv ()
115 let nodef = if nodeSet opts then nodeFile opts
117 instf = if instSet opts then instFile opts
120 case masterName opts of
121 "" -> Text.loadData nodef instf
122 host -> Rapi.loadData host
124 let ldresult = input_data >>= Loader.mergeData
125 (loaded_nl, il, csf) <-
129 printf "Error: failed to load data. Details:\n%s\n" s
130 exitWith $ ExitFailure 1
132 let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il
134 unless (null fix_msgs || silent opts) $ do
135 putStrLn "Warning: cluster has inconsistent data:"
136 putStrLn . unlines . map (\s -> printf " - %s" s) $ fix_msgs
138 return (fixed_nl, il, csf)