Revision 30ce253e

b/man/hroller.rst
36 36
**[ -v... | -q ]**
37 37
**[ -S *file* ]**
38 38
**[ --one-step-only ]**
39
**[ --print-moves ]**
39 40

  
40 41
DESCRIPTION
41 42
-----------
......
88 89
\--one-step-only
89 90
  Restrict to the first reboot group. Output the group one node per line.
90 91

  
92
\--print-moves
93
  After each group list for each affected non-redundant instance a node
94
  where it can be evacuated to.
95

  
91 96

  
92 97
BUGS
93 98
----
......
140 145
    node8.example.com,node6.example.com,node2.example.com
141 146
    node7.example.com,node4.example.com
142 147

  
148
Rolling reboots with non-redundant instances
149
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
150

  
151
By default, hroller plans capacity to move the non-redundant instances
152
out of the nodes to be rebooted. If requested, apropriate locations for
153
the non-redundant instances can be shown. The assumption is that instances
154
are moved back to their original node after each reboot; these back moves
155
are not part of the output.
156
::
157

  
158
    $ hroller --print-moves -L
159
    'Node Reboot Groups'
160
    node-01-002,node-01-003
161
      inst-20 node-01-001
162
      inst-21 node-01-000
163
      inst-30 node-01-005
164
      inst-31 node-01-004
165
    node-01-004,node-01-005
166
      inst-40 node-01-001
167
      inst-41 node-01-000
168
      inst-50 node-01-003
169
      inst-51 node-01-002
170
    node-01-001,node-01-000
171
      inst-00 node-01-002
172
      inst-01 node-01-003
173
      inst-10 node-01-005
174
      inst-11 node-01-004
175

  
176

  
177

  
143 178
.. vim: set textwidth=72 :
144 179
.. Local Variables:
145 180
.. mode: rst
b/src/Ganeti/HTools/CLI.hs
77 77
  , oOutputDir
78 78
  , oPrintCommands
79 79
  , oPrintInsts
80
  , oPrintMoves
80 81
  , oPrintNodes
81 82
  , oQuiet
82 83
  , oRapiMaster
......
146 147
  , optOfflineMaintenance :: Bool    -- ^ Pretend all instances are offline
147 148
  , optOneStepOnly :: Bool           -- ^ Only do the first step
148 149
  , optOutPath     :: FilePath       -- ^ Path to the output directory
150
  , optPrintMoves  :: Bool           -- ^ Whether to show the instance moves
149 151
  , optSaveCluster :: Maybe FilePath -- ^ Save cluster state to this file
150 152
  , optShowCmds    :: Maybe FilePath -- ^ Whether to show the command list
151 153
  , optShowHelp    :: Bool           -- ^ Just show the help
......
199 201
  , optOfflineMaintenance = False
200 202
  , optOneStepOnly = False
201 203
  , optOutPath     = "."
204
  , optPrintMoves  = False
202 205
  , optSaveCluster = Nothing
203 206
  , optShowCmds    = Nothing
204 207
  , optShowHelp    = False
......
535 538
   "print the final instance map",
536 539
   OptComplNone)
537 540

  
541
oPrintMoves :: OptType
542
oPrintMoves =
543
  (Option "" ["print-moves"]
544
   (NoArg (\ opts -> Ok opts { optPrintMoves = True }))
545
   "print the moves of the instances",
546
   OptComplNone)
547

  
538 548
oPrintNodes :: OptType
539 549
oPrintNodes =
540 550
  (Option "p" ["print-nodes"]
b/src/Ganeti/HTools/Program/Hroller.hs
30 30
  ) where
31 31

  
32 32
import Control.Applicative
33
import Control.Arrow
33 34
import Control.Monad
34 35
import Data.Function
35 36
import Data.List
36 37
import Data.Ord
38
import Text.Printf
37 39

  
38 40
import qualified Data.IntMap as IntMap
39 41

  
......
68 70
    , oNodeTags
69 71
    , oSaveCluster
70 72
    , oGroup
73
    , oPrintMoves
71 74
    , oSkipNonRedundant
72 75
    , oIgnoreNonRedundant
73 76
    , oForce
......
127 130

  
128 131
-- | Parition a list of nodes into chunks according cluster capacity.
129 132
partitionNonRedundant :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
130
                         -> Result [[Ndx]]
133
                         -> Result [([Ndx], (Node.List, Instance.List))]
131 134
partitionNonRedundant [] _ _ = return []
132 135
partitionNonRedundant ndxs targets conf = do
133
  (grp, _) <- clearNodes ndxs targets conf
136
  (grp, conf') <- clearNodes ndxs targets conf
134 137
  guard . not . null $ grp
135 138
  let remaining = ndxs \\ grp
136 139
  part <- partitionNonRedundant remaining targets conf
137
  return $ grp : part
140
  return $ (grp, conf') : part
138 141

  
139 142
-- | Gather statistics for the coloring algorithms.
140 143
-- Returns a string with a summary on how each algorithm has performed,
......
177 180
noNonRedundant conf = null . nonRedundant conf . Node.idx
178 181

  
179 182
-- | Put the master node last.
180
-- Reorder a list of lists of nodes such that the master node (if present)
181
-- is the last node of the last group.
182
masterLast :: [[Node.Node]] -> [[Node.Node]]
183
-- Reorder a list groups of nodes (with additional information) such that the
184
-- master node (if present) is the last node of the last group.
185
masterLast :: [([Node.Node], a)] -> [([Node.Node], a)]
183 186
masterLast rebootgroups =
184
  map (uncurry (++)) . uncurry (++) . partition (null . snd) $
185
  map (partition (not . Node.isMaster)) rebootgroups
187
  map (first $ uncurry (++)) . uncurry (++) . partition (null . snd . fst) $
188
  map (first $ partition (not . Node.isMaster)) rebootgroups
189

  
190
-- | From two configurations compute the list of moved instances.
191
getMoves :: (Node.List, Instance.List) -> (Node.List, Instance.List)
192
            -> [(Instance.Instance, Node.Node)]
193
getMoves (_, il) (nl', il') = do
194
  ix <- Container.keys il
195
  let inst = Container.find ix il
196
      inst' = Container.find ix il'
197
  guard $ Instance.pNode inst /= Instance.pNode inst'
198
  return (inst', Container.find (Instance.pNode inst') nl')
186 199

  
187 200
-- | Main function.
188 201
main :: Options -> [String] -> IO ()
......
245 258
      splitted = mapM (\ grp -> partitionNonRedundant grp allNdx (nlf,ilf))
246 259
                 smallestColoring
247 260
  rebootGroups <- if optIgnoreNonRedundant opts
248
                     then return smallestColoring
261
                     then return $ zip smallestColoring (repeat (nlf, ilf))
249 262
                     else case splitted of
250 263
                            Ok splitgroups -> return $ concat splitgroups
251 264
                            Bad _ -> exitErr "Not enough capacity to move\ 
252 265
                                             \ non-redundant instances"
253 266
  let idToNode = (`Container.find` nodes)
254 267
      nodesRebootGroups =
255
        map (map idToNode . filter (`IntMap.member` nodes)) rebootGroups
268
        map (first $ map idToNode . filter (`IntMap.member` nodes)) rebootGroups
256 269
      outputRebootGroups = masterLast .
257
                           sortBy (flip compare `on` length) $
270
                           sortBy (flip compare `on` length . fst) $
258 271
                           nodesRebootGroups
259
      outputRebootNames = map (map Node.name) outputRebootGroups
272
      confToMoveNames = map (Instance.name *** Node.name) . getMoves (nlf, ilf)
273
      namesAndMoves = map (map Node.name *** confToMoveNames) outputRebootGroups
260 274

  
261 275
  when (verbose > 1) . putStrLn $ getStats colorings
262 276

  
277
  let showGroup = if optOneStepOnly opts
278
                    then mapM_ putStrLn
279
                    else putStrLn . commaJoin
280
      showMoves = if optPrintMoves opts
281
                    then mapM_ $ putStrLn . uncurry (printf "  %s %s")
282
                    else const $ return ()
283
      showBoth = liftM2 (>>) (showGroup . fst) (showMoves . snd)
284

  
285

  
263 286
  if optOneStepOnly opts
264 287
     then do
265 288
       unless (optNoHeaders opts) $
266 289
              putStrLn "'First Reboot Group'"
267
       case outputRebootNames of
290
       case namesAndMoves of
268 291
         [] -> return ()
269
         y : _ -> mapM_ putStrLn y
292
         y : _ -> showBoth y
270 293
     else do
271 294
       unless (optNoHeaders opts) $
272 295
              putStrLn "'Node Reboot Groups'"
273
       mapM_ (putStrLn . commaJoin) outputRebootNames
296
       mapM_ showBoth namesAndMoves

Also available in: Unified diff