( Options(..)
, OptType
, parseOpts
- , parseEnv
, shTemplate
- , loadExternalData
- , defaultLuxiSocket
-- * The options
, oPrintNodes
, oPrintCommands
, oOutputDir
, oNodeFile
, oInstFile
+ , oNodeSim
, oRapiMaster
, oLuxiSocket
, oMaxSolLength
, oINodes
, oMaxCpu
, oMinDisk
+ , oDiskMoves
, oShowVer
, oShowHelp
) where
-import Data.Maybe (isJust, fromJust, fromMaybe)
+import Data.Maybe (fromMaybe)
import qualified Data.Version
import Monad
import System.Console.GetOpt
-import System.Posix.Env
import System.IO
import System.Info
import System
-import Text.Printf (printf, hPrintf)
+import Text.Printf (printf)
import qualified Ganeti.HTools.Version as Version(version)
-import qualified Ganeti.HTools.Luxi as Luxi
-import qualified Ganeti.HTools.Rapi as Rapi
-import qualified Ganeti.HTools.Text as Text
-import qualified Ganeti.HTools.Loader as Loader
-import qualified Ganeti.HTools.Instance as Instance
-import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Cluster as Cluster
-import Ganeti.HTools.Types
-
-- | The default value for the luxi socket
defaultLuxiSocket :: FilePath
defaultLuxiSocket = "/var/run/ganeti/socket/ganeti-master"
, optNodeSet :: Bool -- ^ The nodes have been set by options
, optInstFile :: FilePath -- ^ Path to the instances file
, optInstSet :: Bool -- ^ The insts have been set by options
+ , optNodeSim :: Maybe String -- ^ Cluster simulation mode
, optMaxLength :: Int -- ^ Stop after this many steps
, optMaster :: String -- ^ Collect data from RAPI
, optLuxi :: Maybe FilePath -- ^ Collect data from Luxi
, optMinScore :: Cluster.Score -- ^ The minimum score we aim for
, optMcpu :: Double -- ^ Max cpu ratio for nodes
, optMdsk :: Double -- ^ Max disk usage ratio for nodes
+ , optDiskMoves :: Bool -- ^ Allow disk moves
, optVerbose :: Int -- ^ Verbosity level
, optShowVer :: Bool -- ^ Just show the program version
, optShowHelp :: Bool -- ^ Just show the help
, optNodeSet = False
, optInstFile = "instances"
, optInstSet = False
+ , optNodeSim = Nothing
, optMaxLength = -1
, optMaster = ""
, optLuxi = Nothing
, optMinScore = 1e-9
, optMcpu = -1
, optMdsk = -1
+ , optDiskMoves = True
, optVerbose = 1
, optShowVer = False
, optShowHelp = False
(ReqArg (\ f o -> o { optInstFile = f, optInstSet = True }) "FILE")
"the instance list FILE"
+oNodeSim :: OptType
+oNodeSim = Option "" ["simulate"]
+ (ReqArg (\ f o -> o { optNodeSim = Just f }) "SPEC")
+ "simulate an empty cluster, given as 'num_nodes,disk,memory,cpus'"
+
oRapiMaster :: OptType
oRapiMaster = Option "m" ["master"]
(ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
(ReqArg (\ n opts -> opts { optMdsk = read n }) "RATIO")
"minimum free disk space for nodes (between 0 and 1)"
+oDiskMoves :: OptType
+oDiskMoves = Option "" ["no-disk-moves"]
+ (NoArg (\ opts -> opts { optDiskMoves = False}))
+ "disallow disk moves from the list of allowed instance changes,\
+ \ thus allowing only the 'cheap' failover/migrate operations"
+
oShowVer :: OptType
oShowVer = Option "V" ["version"]
(NoArg (\ opts -> opts { optShowVer = True}))
os arch
exitWith ExitSuccess
return resu
- (_, _, errs) ->
- ioError (userError (concat errs ++ usageHelp progname options))
-
--- | Parse the environment and return the node\/instance names.
---
--- This also hardcodes here the default node\/instance file names.
-parseEnv :: () -> IO (String, String)
-parseEnv () = do
- a <- getEnvDefault "HTOOLS_NODES" "nodes"
- b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
- return (a, b)
+ (_, _, errs) -> do
+ hPutStrLn stderr $ "Command line error: " ++ concat errs
+ hPutStrLn stderr $ usageHelp progname options
+ exitWith $ ExitFailure 2
-- | A shell script template for autogenerated scripts.
shTemplate :: String
\ exit 0\n\
\ fi\n\
\}\n\n"
-
--- | External tool data loader from a variety of sources.
-loadExternalData :: Options
- -> IO (Node.List, Instance.List, String)
-loadExternalData opts = do
- (env_node, env_inst) <- parseEnv ()
- let nodef = if optNodeSet opts then optNodeFile opts
- else env_node
- instf = if optInstSet opts then optInstFile opts
- else env_inst
- mhost = optMaster opts
- lsock = optLuxi opts
- setRapi = mhost /= ""
- setLuxi = isJust lsock
- setFiles = optNodeSet opts || optInstSet opts
- allSet = filter id [setRapi, setLuxi, setFiles]
- when (length allSet > 1) $
- do
- hPutStrLn stderr "Error: Only one of the rapi, luxi, and data\
- \ files options should be given."
- exitWith $ ExitFailure 1
-
- input_data <-
- case () of
- _ | mhost /= "" -> Rapi.loadData mhost
- | isJust lsock -> Luxi.loadData $ fromJust lsock
- | otherwise -> Text.loadData nodef instf
-
- let ldresult = input_data >>= Loader.mergeData
- (loaded_nl, il, csf) <-
- (case ldresult of
- Ok x -> return x
- Bad s -> do
- hPrintf stderr "Error: failed to load data. Details:\n%s\n" s
- exitWith $ ExitFailure 1
- )
- let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il
-
- unless (null fix_msgs || optVerbose opts == 0) $ do
- hPutStrLn stderr "Warning: cluster has inconsistent data:"
- hPutStrLn stderr . unlines . map (printf " - %s") $ fix_msgs
-
- return (fixed_nl, il, csf)