import Control.Concurrent (threadDelay)
import Control.Exception (bracket)
import Data.List
-import Data.Function
import Data.Maybe (isJust, fromJust)
import Monad
-import System
+import System (exitWith, ExitCode(..))
import System.IO
import qualified System
import Text.Printf (printf, hPrintf)
-import Text.JSON (showJSON)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Cluster as Cluster
import Ganeti.HTools.Types
import qualified Ganeti.Luxi as L
-import qualified Ganeti.OpCodes as OpCodes
import Ganeti.Jobs
-- | Options list and functions
, oPrintInsts
, oPrintCommands
, oOneline
- , oNodeFile
- , oInstFile
+ , oDataFile
+ , oEvacMode
, oRapiMaster
, oLuxiSocket
, oExecJobs
, oDiskMoves
, oDynuFile
, oExTags
+ , oExInst
, oShowVer
, oShowHelp
]
-> [MoveJob] -- ^ Current command list
-> Bool -- ^ Whether to be silent
-> Score -- ^ Score at which to stop
+ -> Bool -- ^ Enable evacuation mode
-> IO (Cluster.Table, [MoveJob]) -- ^ The resulting table
-- and commands
iterateDepth ini_tbl max_rounds disk_moves nmlen imlen
- cmd_strs oneline min_score =
+ cmd_strs oneline min_score evac_mode =
let Cluster.Table ini_nl ini_il _ _ = ini_tbl
- m_fin_tbl = Cluster.tryBalance ini_tbl max_rounds disk_moves min_score
+ allowed_next = Cluster.doNextBalance ini_tbl max_rounds min_score
+ m_fin_tbl = if allowed_next
+ then Cluster.tryBalance ini_tbl disk_moves evac_mode
+ else Nothing
in
case m_fin_tbl of
Just fin_tbl ->
hFlush stdout
iterateDepth fin_tbl max_rounds disk_moves
nmlen imlen upd_cmd_strs oneline min_score
+ evac_mode
Nothing -> return (ini_tbl, cmd_strs)
-- | Formats the solution for the oneline display
printf "%.8f %d %.8f %8.3f" ini_cv plc_len fin_cv
(if fin_cv == 0 then 1 else ini_cv / fin_cv)
--- | Submits a list of jobs and waits for all to finish execution
-execJobs :: L.Client -> [[OpCodes.OpCode]] -> IO (Result [String])
-execJobs client = L.submitManyJobs client . showJSON
-
-- | Polls a set of jobs at a fixed interval until all are finished
-- one way or another
waitForJobs :: L.Client -> [String] -> IO (Result [JobStatus])
sts <- L.queryJobsStatus client jids
case sts of
Bad x -> return $ Bad x
- Ok s -> if any (<= JobRunning) s
+ Ok s -> if any (<= JOB_STATUS_RUNNING) s
then do
-- TODO: replace hardcoded value with a better thing
threadDelay (1000000 * 15)
-- | Check that a set of job statuses is all success
checkJobsStatus :: [JobStatus] -> Bool
-checkJobsStatus = all (== JobSuccess)
+checkJobsStatus = all (== JOB_STATUS_SUCCESS)
-- | Execute an entire jobset
-execJobSet :: String -> String -> Node.List
+execJobSet :: String -> Node.List
-> Instance.List -> [JobSet] -> IO ()
-execJobSet _ _ _ _ [] = return ()
-execJobSet master csf nl il (js:jss) = do
+execJobSet _ _ _ [] = return ()
+execJobSet master nl il (js:jss) = do
-- map from jobset (htools list of positions) to [[opcodes]]
let jobs = map (\(_, idx, move, _) ->
- Cluster.iMoveToJob csf nl il idx move) js
+ Cluster.iMoveToJob nl il idx move) js
let descr = map (\(_, idx, _, _) -> Container.nameOf il idx) js
putStrLn $ "Executing jobset for instances " ++ commaJoin descr
jrs <- bracket (L.getClient master) L.closeClient
(\client -> do
- jids <- execJobs client jobs
+ jids <- L.submitManyJobs client jobs
case jids of
Bad x -> return $ Bad x
Ok x -> do
hPutStrLn stderr $ "Cannot compute job status, aborting: " ++ show x
return ()
Ok x -> if checkJobsStatus x
- then execJobSet master csf nl il jss
+ then execJobSet master nl il jss
else do
hPutStrLn stderr $ "Not all jobs completed successfully: " ++
show x
verbose = optVerbose opts
shownodes = optShowNodes opts
- (fixed_nl, il, csf) <- loadExternalData opts
+ (fixed_nl, il, ctags) <- loadExternalData opts
let offline_names = optOffline opts
all_nodes = Container.elems fixed_nl
- all_names = map Node.name all_nodes
- offline_wrong = filter (flip notElem all_names) offline_names
+ all_names = concatMap allNames all_nodes
+ offline_wrong = filter (`notElem` all_names) offline_names
offline_indices = map Node.idx $
- filter (\n -> elem (Node.name n) offline_names)
+ filter (\n ->
+ Node.name n `elem` offline_names ||
+ Node.alias n `elem` offline_names)
all_nodes
m_cpu = optMcpu opts
m_dsk = optMdsk opts
+ csf = commonSuffix fixed_nl il
when (length offline_wrong > 0) $ do
hPrintf stderr "Wrong node name(s) set as offline: %s\n"
- (commaJoin offline_wrong)
+ (commaJoin offline_wrong) :: IO ()
exitWith $ ExitFailure 1
- let nm = Container.map (\n -> if elem (Node.idx n) offline_indices
+ let nm = Container.map (\n -> if Node.idx n `elem` offline_indices
then Node.setOffline n True
else n) fixed_nl
nl = Container.map (flip Node.setMdsk m_dsk . flip Node.setMcpu m_cpu)
nm
+ when (not oneline && verbose > 1) $
+ putStrLn $ "Loaded cluster tags: " ++ intercalate "," ctags
+
when (Container.size il == 0) $ do
(if oneline then putStrLn $ formatOneline 0 0 0
else printf "Cluster is empty, exiting.\n")
printf "Initial score: %.8f\n" ini_cv)
unless oneline $ putStrLn "Trying to minimize the CV..."
- let imlen = Container.maxNameLen il
- nmlen = Container.maxNameLen nl
+ let imlen = maximum . map (length . Instance.alias) $ Container.elems il
+ nmlen = maximum . map (length . Node.alias) $ Container.elems nl
(fin_tbl, cmd_strs) <- iterateDepth ini_tbl (optMaxLength opts)
(optDiskMoves opts)
- nmlen imlen [] oneline min_cv
+ nmlen imlen [] oneline min_cv (optEvacMode opts)
let (Cluster.Table fin_nl fin_il fin_cv fin_plc) = fin_tbl
ord_plc = reverse fin_plc
sol_msg = if null fin_plc
Nothing -> do
hPutStrLn stderr "Execution of commands possible only on LUXI"
exitWith $ ExitFailure 1
- Just master -> execJobSet master csf fin_nl il cmd_jobs)
+ Just master -> execJobSet master fin_nl il cmd_jobs)
when (optShowInsts opts) $ do
putStrLn ""
when (verbose > 3) $
do
printf "Original: mem=%d disk=%d\n"
- (Cluster.csFmem ini_cs) (Cluster.csFdsk ini_cs)
+ (Cluster.csFmem ini_cs) (Cluster.csFdsk ini_cs) :: IO ()
printf "Final: mem=%d disk=%d\n"
(Cluster.csFmem fin_cs) (Cluster.csFdsk fin_cs)
when oneline $