On Wed, Apr 15, 2015 at 08:55:06AM +0200, 'Klaus Aehlig' via ganeti-devel wrote:
Redundancy for DRBD nodes is already checked at every node locally.
However, in the presence of shared/external storage, we also have to
consider planning capacity for those instances---globally (within
a node group) as they can be moved to any node. Add a function that
checks whether this is true for a given node in a given cluster.

Signed-off-by: Klaus Aehlig <[email protected]>
---
Makefile.am                   |  1 +
src/Ganeti/HTools/GlobalN1.hs | 85 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+)
create mode 100644 src/Ganeti/HTools/GlobalN1.hs

diff --git a/Makefile.am b/Makefile.am
index f293f40..36f8dee 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -912,6 +912,7 @@ HS_LIB_SRCS = \
        src/Ganeti/HTools/Container.hs \
        src/Ganeti/HTools/Dedicated.hs \
        src/Ganeti/HTools/ExtLoader.hs \
+       src/Ganeti/HTools/GlobalN1.hs \
        src/Ganeti/HTools/Graph.hs \
        src/Ganeti/HTools/Group.hs \
        src/Ganeti/HTools/Instance.hs \
diff --git a/src/Ganeti/HTools/GlobalN1.hs b/src/Ganeti/HTools/GlobalN1.hs
new file mode 100644
index 0000000..d60479c
--- /dev/null
+++ b/src/Ganeti/HTools/GlobalN1.hs
@@ -0,0 +1,85 @@
+{-| Implementation of global N+1 redundancy
+
+-}
+
+{-
+
+Copyright (C) 2015 Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-}
+
+module Ganeti.HTools.GlobalN1
+  ( canEvacuateNode
+  ) where
+
+import Control.Monad (foldM)
+import qualified Data.IntMap as IntMap
+import Data.List (partition)
+
+import Ganeti.BasicTypes (isOk, Result)
+import Ganeti.HTools.AlgorithmParams (AlgorithmOptions(..), defaultOptions)
+import qualified Ganeti.HTools.Cluster.Evacuate as Evacuate
+import Ganeti.HTools.Cluster.Moves (move)
+import qualified Ganeti.HTools.Container as Container
+import qualified Ganeti.HTools.Instance as Instance
+import qualified Ganeti.HTools.Node as Node
+import Ganeti.HTools.Types ( IMove(Failover), Ndx, Gdx, Idx, opToResult)
+import Ganeti.Types ( DiskTemplate(DTDrbd8, DTPlain, DTFile)
+                    , EvacMode(ChangePrimary))
+
+-- | Foldable function describing how a non-DRBD instance
+-- is to be evacuated.
+evac :: Gdx -> [Ndx]
+     -> (Node.List, Instance.List) -> Idx -> Result (Node.List, Instance.List)
+evac gdx ndxs (nl, il) idx = do
+  let opts = defaultOptions { algIgnoreSoftErrors = True, algEvacMode = True }
+      inst = Container.find idx il
+  (nl', il', _) <- Evacuate.nodeEvacInstance opts nl il ChangePrimary inst
+                     gdx ndxs
+  return (nl', il')
+
+-- | Decide if a node can be evacuated, i.e., all DRBD instances
+-- failed over and all shared/external storage instances moved off
+-- to other nodes.
+canEvacuateNode :: (Node.List, Instance.List) -> Node.Node -> Bool
+canEvacuateNode (nl, il) n = isOk $ do
+  let (drbdIdxs, otherIdxs) = partition ((==) DTDrbd8
+                                         . Instance.diskTemplate
+                                         . flip Container.find il)
+                              $ Node.pList n
+      sharedIdxs = filter (not . (`elem` [DTPlain, DTFile])

It could be handy to have the list of external/shared disk templates (that can be moved anywhere) as a constant. I thought Constants.hs might be a good fit, but unfortunately the constants there are just 'FrozenSet String', which is not very useful for Haskell. So probably only after some refactoring of Constants.hs.

+                           . Instance.diskTemplate
+                           . flip Container.find il) otherIdxs
+  -- failover all DRBD instances with primaries on n
+  (nl', il') <- opToResult
+                . foldM move (nl, il) $ map (flip (,) Failover) drbdIdxs
+  -- evacuate other instances
+  let n' = Node.setOffline n True
+      nl'' = Container.add (Node.idx n') n' nl'
+  _ <- foldM (evac (Node.group n') . map Node.idx $ IntMap.elems nl'')
+             (nl'',il') sharedIdxs
+  return ()
--
2.2.0.rc0.207.ga3a616c


LGTM

Reply via email to