commit c0501c694f35537f5a096f5a34c082c71ee17777
Author: Iustin Pop <iustin@google.com>
Date:   Wed Aug 26 10:04:38 2009 +0200

    Implement support for 'cheap' moves only
    
    This patch adds support for cheap (failover/migrate) operations only in
    the balancing algorithm and in the hbal command line options.
    
    This allows a very quick balancing (compared to allowing replace-disks)
    which can be useful as a scheduled operation.

diff --git a/Ganeti/HTools/CLI.hs b/Ganeti/HTools/CLI.hs
index 0309687..175aa9b 100644
--- a/Ganeti/HTools/CLI.hs
+++ b/Ganeti/HTools/CLI.hs
@@ -57,6 +57,7 @@ module Ganeti.HTools.CLI
     , oINodes
     , oMaxCpu
     , oMinDisk
+    , oDiskMoves
     , oShowVer
     , oShowHelp
     ) where
@@ -111,6 +112,7 @@ data Options = Options
     , 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
@@ -140,6 +142,7 @@ defaultOptions  = Options
  , optMinScore  = 1e-9
  , optMcpu      = -1
  , optMdsk      = -1
+ , optDiskMoves = True
  , optVerbose   = 1
  , optShowVer   = False
  , optShowHelp  = False
@@ -259,6 +262,12 @@ oMinDisk = Option "" ["min-disk"]
            (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}))
diff --git a/Ganeti/HTools/Cluster.hs b/Ganeti/HTools/Cluster.hs
index 06e1ee1..f582c7f 100644
--- a/Ganeti/HTools/Cluster.hs
+++ b/Ganeti/HTools/Cluster.hs
@@ -381,10 +381,12 @@ checkSingleStep ini_tbl target cur_tbl move =
             in
               compareTables cur_tbl upd_tbl
 
--- | Given the status of the current secondary as a valid new node
--- and the current candidate target node,
--- generate the possible moves for a instance.
-possibleMoves :: Bool -> Ndx -> [IMove]
+-- | Given the status of the current secondary as a valid new node and
+-- the current candidate target node, generate the possible moves for
+-- a instance.
+possibleMoves :: Bool      -- ^ Whether the secondary node is a valid new node
+              -> Ndx       -- ^ Target node candidate
+              -> [IMove]   -- ^ List of valid result moves
 possibleMoves True tdx =
     [ReplaceSecondary tdx,
      ReplaceAndFailover tdx,
@@ -396,11 +398,12 @@ possibleMoves False tdx =
      ReplaceAndFailover tdx]
 
 -- | Compute the best move for a given instance.
-checkInstanceMove :: [Ndx]             -- Allowed target node indices
-                  -> Table             -- Original table
-                  -> Instance.Instance -- Instance to move
-                  -> Table             -- Best new table for this instance
-checkInstanceMove nodes_idx ini_tbl target =
+checkInstanceMove :: [Ndx]             -- ^ Allowed target node indices
+                  -> Bool              -- ^ Whether disk moves are allowed
+                  -> Table             -- ^ Original table
+                  -> Instance.Instance -- ^ Instance to move
+                  -> Table             -- ^ Best new table for this instance
+checkInstanceMove nodes_idx disk_moves ini_tbl target =
     let
         opdx = Instance.pnode target
         osdx = Instance.snode target
@@ -409,17 +412,20 @@ checkInstanceMove nodes_idx ini_tbl target =
         aft_failover = if use_secondary -- if allowed to failover
                        then checkSingleStep ini_tbl target ini_tbl Failover
                        else ini_tbl
-        all_moves = concatMap (possibleMoves use_secondary) nodes
+        all_moves = if disk_moves
+                    then concatMap (possibleMoves use_secondary) nodes
+                    else []
     in
       -- iterate over the possible nodes for this instance
       foldl' (checkSingleStep ini_tbl target) aft_failover all_moves
 
 -- | Compute the best next move.
 checkMove :: [Ndx]               -- ^ Allowed target node indices
+          -> Bool                -- ^ Whether disk moves are allowed
           -> Table               -- ^ The current solution
           -> [Instance.Instance] -- ^ List of instances still to move
           -> Table               -- ^ The new solution
-checkMove nodes_idx ini_tbl victims =
+checkMove nodes_idx disk_moves ini_tbl victims =
     let Table _ _ _ ini_plc = ini_tbl
         -- iterate over all instances, computing the best move
         best_tbl =
@@ -427,7 +433,7 @@ checkMove nodes_idx ini_tbl victims =
             (\ step_tbl elem ->
                  if Instance.snode elem == Node.noSecondary then step_tbl
                     else compareTables step_tbl $
-                         checkInstanceMove nodes_idx ini_tbl elem)
+                         checkInstanceMove nodes_idx disk_moves ini_tbl elem)
             ini_tbl victims
         Table _ _ _ best_plc = best_tbl
     in
diff --git a/hbal.1 b/hbal.1
index 08e80b5..1f8149c 100644
--- a/hbal.1
+++ b/hbal.1
@@ -27,6 +27,7 @@ Algorithm options:
 .BI "[ -l " limit " ]"
 .BI "[ -e " score " ]"
 .BI "[ -O " name... " ]"
+.B "[ --no-disk-moves ]"
 
 .TP
 Reporting options:
@@ -303,6 +304,13 @@ empirically).
 .RE
 
 .TP
+.BI "--no-disk-moves"
+This parameter prevent hbal from using disk move (i.e. "gnt-instance
+replace-disks") operations. This will result in a much quicker
+balancing, but of course the improvements are limited. It is up to the
+user to decide when to use one or another.
+
+.TP
 .BI "-n" nodefile ", --nodes=" nodefile
 The name of the file holding node information (if not collecting via
 RAPI), instead of the default \fInodes\fR file (but see below how to
diff --git a/hbal.hs b/hbal.hs
index 353bf55..f29f73b 100644
--- a/hbal.hs
+++ b/hbal.hs
@@ -59,6 +59,7 @@ options =
     , oMinScore
     , oMaxCpu
     , oMinDisk
+    , oDiskMoves
     , oShowVer
     , oShowHelp
     ]
@@ -69,6 +70,7 @@ we find a valid solution or we exceed the maximum depth.
 -}
 iterateDepth :: Cluster.Table    -- ^ The starting table
              -> Int              -- ^ Remaining length
+             -> Bool             -- ^ Allow disk moves
              -> Int              -- ^ Max node name len
              -> Int              -- ^ Max instance name len
              -> [[String]]       -- ^ Current command list
@@ -76,13 +78,13 @@ iterateDepth :: Cluster.Table    -- ^ The starting table
              -> Cluster.Score    -- ^ Score at which to stop
              -> IO (Cluster.Table, [[String]]) -- ^ The resulting table and
                                                -- commands
-iterateDepth ini_tbl max_rounds nmlen imlen
+iterateDepth ini_tbl max_rounds disk_moves nmlen imlen
              cmd_strs oneline min_score =
     let Cluster.Table ini_nl ini_il ini_cv ini_plc = ini_tbl
         all_inst = Container.elems ini_il
         node_idx = map Node.idx . filter (not . Node.offline) $
                    Container.elems ini_nl
-        fin_tbl = Cluster.checkMove node_idx ini_tbl all_inst
+        fin_tbl = Cluster.checkMove node_idx disk_moves ini_tbl all_inst
         (Cluster.Table _ _ fin_cv fin_plc) = fin_tbl
         ini_plc_len = length ini_plc
         fin_plc_len = length fin_plc
@@ -98,7 +100,7 @@ iterateDepth ini_tbl max_rounds nmlen imlen
           hFlush stdout
         (if fin_cv < ini_cv then -- this round made success, try deeper
              if allowed_next && fin_cv > min_score
-             then iterateDepth fin_tbl max_rounds
+             then iterateDepth fin_tbl max_rounds disk_moves
                   nmlen imlen upd_cmd_strs oneline min_score
              -- don't go deeper, but return the better solution
              else return (fin_tbl, upd_cmd_strs)
@@ -196,6 +198,7 @@ main = do
       nmlen = Container.maxNameLen nl
 
   (fin_tbl, cmd_strs) <- iterateDepth ini_tbl (optMaxLength opts)
+                         (optDiskMoves opts)
                          nmlen imlen [] oneline min_cv
   let (Cluster.Table fin_nl _ fin_cv fin_plc) = fin_tbl
       ord_plc = reverse fin_plc
