Implement writing the command list to a script
authorIustin Pop <iustin@google.com>
Sat, 18 Apr 2009 22:17:05 +0000 (00:17 +0200)
committerIustin Pop <iustin@google.com>
Sat, 18 Apr 2009 22:17:05 +0000 (00:17 +0200)
This patch adds support in hbal for writing the command list to a shell
script, with error checking and allowing for early exit.

Ganeti/HTools/CLI.hs
Ganeti/HTools/Cluster.hs
hbal.hs

index ba16766..1a30f58 100644 (file)
@@ -10,6 +10,7 @@ module Ganeti.HTools.CLI
     (
       parseOpts
     , showVersion
+    , shTemplate
     ) where
 
 import System.Console.GetOpt
@@ -54,3 +55,17 @@ showVersion name =
            name Version.version
            compilerName (Data.Version.showVersion compilerVersion)
            os arch
+
+-- | A shell script template for autogenerated scripts
+shTemplate :: String
+shTemplate =
+    printf "#!/bin/sh\n\n\
+           \# Auto-generated script for executing cluster rebalancing\n\n\
+           \# To stop, touch the file /tmp/stop-htools\n\n\
+           \set -e\n\n\
+           \check() {\n\
+           \  if [ -f /tmp/stop-htools ]; then\n\
+           \    echo 'Stop requested, exiting'\n\
+           \    exit 0\n\
+           \  fi\n\
+           \}\n\n"
index c64f9df..4153af6 100644 (file)
@@ -563,27 +563,27 @@ computeMoves i a b c d =
     else
         if c == b then {- Failover and ... -}
             if d == a then {- that's all -}
-                ("f", [printf "migrate %s" i])
+                ("f", [printf "migrate -f %s" i])
             else
                 (printf "f r:%s" d,
-                 [printf "migrate %s" i,
+                 [printf "migrate -f %s" i,
                   printf "replace-disks -n %s %s" d i])
         else
             if d == a then {- ... and keep primary as secondary -}
                 (printf "r:%s f" c,
                  [printf "replace-disks -n %s %s" c i,
-                  printf "migrate %s" i])
+                  printf "migrate -f %s" i])
             else
                 if d == b then {- ... keep same secondary -}
                     (printf "f r:%s f" c,
-                     [printf "migrate %s" i,
+                     [printf "migrate -f %s" i,
                       printf "replace-disks -n %s %s" c i,
-                      printf "migrate %s" i])
+                      printf "migrate -f %s" i])
 
                 else {- Nothing in common -}
                     (printf "r:%s f r:%s" c d,
                      [printf "replace-disks -n %s %s" c i,
-                      printf "migrate %s" i,
+                      printf "migrate -f %s" i,
                       printf "replace-disks -n %s %s" d i])
 
 {-| Converts a placement to string format -}
@@ -616,9 +616,11 @@ printSolutionLine il ktn kti nmlen imlen plc pos =
 
 formatCmds :: [[String]] -> String
 formatCmds cmd_strs =
-    unlines $ map ("  echo " ++) $
+    unlines $
     concat $ map (\(a, b) ->
-        (printf "step %d" (a::Int)):(map ("gnt-instance " ++) b)) $
+        (printf "echo step %d" (a::Int)):
+        (printf "check"):
+        (map ("gnt-instance " ++) b)) $
         zip [1..] cmd_strs
 
 {-| Converts a solution to string format -}
diff --git a/hbal.hs b/hbal.hs
index 65c5e3f..a606077 100644 (file)
--- a/hbal.hs
+++ b/hbal.hs
@@ -6,6 +6,7 @@ module Main (main) where
 
 import Data.List
 import Data.Function
+import Data.Maybe (isJust, fromJust, fromMaybe)
 import Monad
 import System
 import System.IO
@@ -23,24 +24,24 @@ import Ganeti.HTools.Utils
 
 -- | Command line options structure.
 data Options = Options
-    { optShowNodes :: Bool     -- ^ Whether to show node status
-    , optShowCmds  :: Bool     -- ^ Whether to show the command list
-    , optOneline   :: Bool     -- ^ Switch output to a single line
-    , optNodef     :: FilePath -- ^ Path to the nodes file
-    , optInstf     :: FilePath -- ^ Path to the instances file
-    , optMaxLength :: Int      -- ^ Stop after this many steps
-    , optMaster    :: String   -- ^ Collect data from RAPI
-    , optVerbose   :: Int      -- ^ Verbosity level
-    , optOffline   :: [String] -- ^ Names of offline nodes
-    , optShowVer   :: Bool     -- ^ Just show the program version
-    , optShowHelp  :: Bool     -- ^ Just show the help
+    { optShowNodes :: Bool           -- ^ Whether to show node status
+    , optShowCmds  :: Maybe FilePath -- ^ Whether to show the command list
+    , optOneline   :: Bool           -- ^ Switch output to a single line
+    , optNodef     :: FilePath       -- ^ Path to the nodes file
+    , optInstf     :: FilePath       -- ^ Path to the instances file
+    , optMaxLength :: Int            -- ^ Stop after this many steps
+    , optMaster    :: String         -- ^ Collect data from RAPI
+    , optVerbose   :: Int            -- ^ Verbosity level
+    , optOffline   :: [String]       -- ^ Names of offline nodes
+    , optShowVer   :: Bool           -- ^ Just show the program version
+    , optShowHelp  :: Bool           -- ^ Just show the help
     } deriving Show
 
 -- | Default values for the command line options.
 defaultOptions :: Options
 defaultOptions  = Options
  { optShowNodes = False
- , optShowCmds  = False
+ , optShowCmds  = Nothing
  , optOneline   = False
  , optNodef     = "nodes"
  , optInstf     = "instances"
@@ -59,8 +60,11 @@ options =
       (NoArg (\ opts -> opts { optShowNodes = True }))
       "print the final node list"
     , Option ['C']     ["print-commands"]
-      (NoArg (\ opts -> opts { optShowCmds = True }))
-      "print the ganeti command list for reaching the solution"
+      (OptArg ((\ f opts -> opts { optShowCmds = Just f }) . fromMaybe "-")
+                  "FILE")
+      "print the ganeti command list for reaching the solution,\
+      \if an argument is passed then write the commands to a file named\
+      \ as such"
     , Option ['o']     ["oneline"]
       (NoArg (\ opts -> opts { optOneline = True }))
       "print the ganeti command list for reaching the solution"
@@ -226,11 +230,21 @@ main = do
   unless (oneline || verbose == 0) $
          printf "Solution length=%d\n" (length ord_plc)
 
-  when (optShowCmds opts) $
+  let cmd_data = Cluster.formatCmds . reverse $ cmd_strs
+
+  when (isJust $ optShowCmds opts) $
        do
+         let out_path = fromJust $ optShowCmds opts
          putStrLn ""
-         putStrLn "Commands to run to reach the above solution:"
-         putStr . Cluster.formatCmds . reverse $ cmd_strs
+         (if out_path == "-" then
+              printf "Commands to run to reach the above solution:\n%s"
+                     (unlines . map ("  " ++) .
+                      filter (/= "check") .
+                      lines $ cmd_data)
+          else do
+            writeFile out_path (CLI.shTemplate ++ cmd_data)
+            printf "The commands have been written to file '%s'\n" out_path)
+
   when (optShowNodes opts) $
        do
          let (orig_mem, orig_disk) = Cluster.totalResources nl