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) |