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
32 import Ganeti.HTools.Types
34 -- | Class for types which support show help and show version
35 class CLIOptions a where
37 showVersion :: a -> Bool
39 -- | Class for types which support the -i/-n/-m options
40 class EToolOptions a where
41 nodeFile :: a -> FilePath
43 instFile :: a -> FilePath
45 masterName :: a -> String
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
56 parseOpts argv progname options defaultOptions =
57 case getOpt Permute options argv of
60 let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
61 when (showHelp po) $ do
62 putStr $ usageInfo header options
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)
72 ioError (userError (concat errs ++ usageInfo header options))
73 where header = printf "%s %s\nUsage: %s [OPTION...]"
74 progname Version.version progname
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)
80 a <- getEnvDefault "HTOOLS_NODES" "nodes"
81 b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
84 -- | A shell script template for autogenerated scripts
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\
92 \ if [ -f /tmp/stop-htools ]; then\n\
93 \ echo 'Stop requested, exiting'\n\
98 -- | External tool data loader from a variety of sources
99 loadExternalData :: (EToolOptions 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
106 instf = if instSet opts then instFile opts
109 case masterName opts of
110 "" -> Text.loadData nodef instf
111 host -> Rapi.loadData host
113 let ldresult = input_data >>= Loader.mergeData
114 (loaded_nl, il, csf, ktn, kti) <-
118 printf "Error: failed to load data. Details:\n%s\n" s
119 exitWith $ ExitFailure 1
121 let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il ktn kti
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
127 return (fixed_nl, il, csf, ktn, kti)