On Fri, Apr 17, 2015 at 06:46:32PM +0200, 'Klaus Aehlig' via ganeti-devel wrote:
Move all data structures related to allocation solutions to a separate
module. In this way, we can extend these in a clean way, e.g., to support
later filtering.

Signed-off-by: Klaus Aehlig <[email protected]>
---
Makefile.am                                     |   1 +
src/Ganeti/HTools/Backend/IAlloc.hs             |  13 +-
src/Ganeti/HTools/Cluster.hs                    | 142 +-----------------
src/Ganeti/HTools/Cluster/AllocationSolution.hs | 192 ++++++++++++++++++++++++
src/Ganeti/HTools/Dedicated.hs                  |  37 ++---
src/Ganeti/HTools/Node.hs                       |  10 --
test/hs/Test/Ganeti/HTools/Cluster.hs           |   9 +-
7 files changed, 232 insertions(+), 172 deletions(-)
create mode 100644 src/Ganeti/HTools/Cluster/AllocationSolution.hs

diff --git a/Makefile.am b/Makefile.am
index 762e355..ed4e1b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -905,6 +905,7 @@ HS_LIB_SRCS = \
        src/Ganeti/HTools/Backend/Text.hs \
        src/Ganeti/HTools/CLI.hs \
        src/Ganeti/HTools/Cluster.hs \
+       src/Ganeti/HTools/Cluster/AllocationSolution.hs \
        src/Ganeti/HTools/Cluster/Evacuate.hs \
        src/Ganeti/HTools/Cluster/Metrics.hs \
        src/Ganeti/HTools/Cluster/Moves.hs \
diff --git a/src/Ganeti/HTools/Backend/IAlloc.hs 
b/src/Ganeti/HTools/Backend/IAlloc.hs
index 3451282..ffd277d 100644
--- a/src/Ganeti/HTools/Backend/IAlloc.hs
+++ b/src/Ganeti/HTools/Backend/IAlloc.hs
@@ -52,6 +52,7 @@ import Text.JSON (JSObject, JSValue(JSArray),

import Ganeti.BasicTypes
import qualified Ganeti.HTools.Cluster as Cluster
+import qualified Ganeti.HTools.Cluster.AllocationSolution as AllocSol
import qualified Ganeti.HTools.Cluster.Evacuate as Evacuate
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Group as Group
@@ -287,16 +288,16 @@ formatResponse success info result =
  in encodeStrict $ makeObj [e_success, e_info, e_result]

-- | Flatten the log of a solution into a string.
-describeSolution :: Cluster.GenericAllocSolution a -> String
-describeSolution = intercalate ", " . Cluster.asLog
+describeSolution :: AllocSol.GenericAllocSolution a -> String
+describeSolution = intercalate ", " . AllocSol.asLog

-- | Convert allocation/relocation results into the result format.
formatAllocate :: Instance.List
-               -> Cluster.GenericAllocSolution a
+               -> AllocSol.GenericAllocSolution a
               -> Result IAllocResult
formatAllocate il as = do
  let info = describeSolution as
-  case Cluster.asSolution as of
+  case AllocSol.asSolution as of
    Nothing -> fail info
    Just (nl, inst, nodes, _) ->
      do
@@ -309,9 +310,9 @@ formatMultiAlloc :: ( Node.List, Instance.List
                 -> Result IAllocResult
formatMultiAlloc (fin_nl, fin_il, ars) =
  let rars = reverse ars
-      (allocated, failed) = partition (isJust . Cluster.asSolution . snd) rars
+      (allocated, failed) = partition (isJust . AllocSol.asSolution . snd) rars
      aars = map (\(_, ar) ->
-                     let (_, inst, nodes, _) = fromJust $ Cluster.asSolution ar
+                     let (_, inst, nodes, _) = fromJust $ AllocSol.asSolution 
ar
                         iname = Instance.name inst
                         nnames = map Node.name nodes
                     in (iname, nnames)) allocated
diff --git a/src/Ganeti/HTools/Cluster.hs b/src/Ganeti/HTools/Cluster.hs
index 8913c37..b7191fe 100644
--- a/src/Ganeti/HTools/Cluster.hs
+++ b/src/Ganeti/HTools/Cluster.hs
@@ -39,13 +39,6 @@ module Ganeti.HTools.Cluster
  (
    -- * Types
    AllocDetails(..)
-  , GenericAllocSolution(..)
-  , AllocSolution
-  , emptyAllocSolution
-  , concatAllocs
-  , sumAllocs
-  , updateIl
-  , extractNl
  , Table(..)
  , CStats(..)
  , AllocNodes
@@ -68,8 +61,6 @@ module Ganeti.HTools.Cluster
  -- * Display functions
  , printNodes
  , printInsts
-  , genericAnnotateSolution
-  , solutionDescription
  -- * Balacing functions
  , doNextBalance
  , tryBalance
@@ -82,7 +73,6 @@ module Ganeti.HTools.Cluster
  , filterMGResults
  , sortMGResults
  , tryChangeGroup
-  , collapseFailures
  , allocList
  -- * Allocation functions
  , iterateAlloc
@@ -104,6 +94,10 @@ import Text.Printf (printf)
import Ganeti.BasicTypes
import Ganeti.HTools.AlgorithmParams (AlgorithmOptions(..), defaultOptions)
import qualified Ganeti.HTools.Container as Container
+import Ganeti.HTools.Cluster.AllocationSolution
+    ( AllocElement, GenericAllocSolution(..) , AllocSolution, 
emptyAllocSolution
+    , sumAllocs, concatAllocs, extractNl, updateIl
+    , annotateSolution, solutionDescription, collapseFailures )
import Ganeti.HTools.Cluster.Evacuate ( EvacSolution(..), emptyEvacSolution
                                      , updateEvacSolution, reverseEvacSolution
                                      , nodeEvacInstance)
@@ -131,23 +125,13 @@ import Ganeti.Types (EvacMode(..))
data AllocDetails = AllocDetails Int (Maybe String)
                    deriving (Show)

--- | Allocation\/relocation solution.
-data GenericAllocSolution a = AllocSolution
-  { asFailures :: [FailMode]              -- ^ Failure counts
-  , asAllocs   :: Int                     -- ^ Good allocation count
-  , asSolution :: Maybe (Node.GenericAllocElement a) -- ^ The actual allocation
-                                          -- result
-  , asLog      :: [String]                -- ^ Informational messages
-  }
-
-type AllocSolution = GenericAllocSolution Score
-
-- | Allocation results, as used in 'iterateAlloc' and 'tieredAlloc'.
type AllocResult = (FailStats, Node.List, Instance.List,
                    [Instance.Instance], [CStats])

-- | Type alias for easier handling.
-type GenericAllocSolutionList a = [(Instance.Instance, GenericAllocSolution a)]
+type GenericAllocSolutionList a =
+  [(Instance.Instance, GenericAllocSolution a)]
type AllocSolutionList = GenericAllocSolutionList Score

-- | A type denoting the valid allocation mode/pairs.
@@ -159,11 +143,6 @@ type AllocSolutionList = GenericAllocSolutionList Score
-- secondary nodes in the sub-list.
type AllocNodes = Either [Ndx] [(Ndx, [Ndx])]

--- | The empty solution we start with when computing allocations.
-emptyAllocSolution :: GenericAllocSolution a
-emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0
-                                   , asSolution = Nothing, asLog = [] }
-
-- | The complete state for the balancing solution.
data Table = Table Node.List Instance.List Score [Placement]
             deriving (Show)
@@ -336,7 +315,7 @@ compareTables a@(Table _ _ a_cv _) b@(Table _ _ b_cv _ ) =
-- | Tries to allocate an instance on one given node.
allocateOnSingle :: AlgorithmOptions
                 -> Node.List -> Instance.Instance -> Ndx
-                 -> OpResult Node.AllocElement
+                 -> OpResult AllocElement
allocateOnSingle opts nl inst new_pdx =
  let p = Container.find new_pdx nl
      new_inst = Instance.setBoth inst new_pdx Node.noSecondary
@@ -352,7 +331,7 @@ allocateOnSingle opts nl inst new_pdx =
allocateOnPair :: AlgorithmOptions
               -> [Statistics]
               -> Node.List -> Instance.Instance -> Ndx -> Ndx
-               -> OpResult Node.AllocElement
+               -> OpResult AllocElement
allocateOnPair opts stats nl inst new_pdx new_sdx =
  let tgt_p = Container.find new_pdx nl
      tgt_s = Container.find new_sdx nl
@@ -529,86 +508,6 @@ tryBalance opts ini_tbl =

-- * Allocation functions

--- | Build failure stats out of a list of failures.
-collapseFailures :: [FailMode] -> FailStats
-collapseFailures flst =
-    map (\k -> (k, foldl' (\a e -> if e == k then a + 1 else a) 0 flst))
-            [minBound..maxBound]
-
--- | Compares two Maybe AllocElement and chooses the best score.
-bestAllocElement :: Ord a
-                 => Maybe (Node.GenericAllocElement a)
-                 -> Maybe (Node.GenericAllocElement a)
-                 -> Maybe (Node.GenericAllocElement a)
-bestAllocElement a Nothing = a
-bestAllocElement Nothing b = b
-bestAllocElement a@(Just (_, _, _, ascore)) b@(Just (_, _, _, bscore)) =
-  if ascore < bscore then a else b
-
--- | Update current Allocation solution and failure stats with new
--- elements.
-concatAllocs :: Ord a
-             => GenericAllocSolution a
-             -> OpResult (Node.GenericAllocElement a)
-             -> GenericAllocSolution a
-concatAllocs as (Bad reason) = as { asFailures = reason : asFailures as }
-
-concatAllocs as (Ok ns) =
-  let -- Choose the old or new solution, based on the cluster score
-    cntok = asAllocs as
-    osols = asSolution as
-    nsols = bestAllocElement osols (Just ns)
-    nsuc = cntok + 1
-    -- Note: we force evaluation of nsols here in order to keep the
-    -- memory profile low - we know that we will need nsols for sure
-    -- in the next cycle, so we force evaluation of nsols, since the
-    -- foldl' in the caller will only evaluate the tuple, but not the
-    -- elements of the tuple
-  in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols }
-
--- | Sums two 'AllocSolution' structures.
-sumAllocs :: Ord a
-          => GenericAllocSolution a
-          -> GenericAllocSolution a
-          -> GenericAllocSolution a
-sumAllocs (AllocSolution aFails aAllocs aSols aLog)
-          (AllocSolution bFails bAllocs bSols bLog) =
-  -- note: we add b first, since usually it will be smaller; when
-  -- fold'ing, a will grow and grow whereas b is the per-group
-  -- result, hence smaller
-  let nFails  = bFails ++ aFails
-      nAllocs = aAllocs + bAllocs
-      nSols   = bestAllocElement aSols bSols
-      nLog    = bLog ++ aLog
-  in AllocSolution nFails nAllocs nSols nLog
-
--- | Given a solution, generates a reasonable description for it.
-genericDescribeSolution :: (a -> String) -> GenericAllocSolution a -> String
-genericDescribeSolution formatMetrics as =
-  let fcnt = asFailures as
-      sols = asSolution as
-      freasons =
-        intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) .
-        filter ((> 0) . snd) . collapseFailures $ fcnt
-  in case sols of
-     Nothing -> "No valid allocation solutions, failure reasons: " ++
-                (if null fcnt then "unknown reasons" else freasons)
-     Just (_, _, nodes, cv) ->
-         printf ("score: %s, successes %d, failures %d (%s)" ++
-                 " for node(s) %s") (formatMetrics cv) (asAllocs as)
-               (length fcnt) freasons
-               (intercalate "/" . map Node.name $ nodes)
-
--- | Annotates a solution with the appropriate string.
-genericAnnotateSolution :: (a -> String)
-                        ->GenericAllocSolution a -> GenericAllocSolution a
-genericAnnotateSolution formatMetrics as =
-  as { asLog = genericDescribeSolution formatMetrics as : asLog as }
-
--- | Annotate a solution based on the standard metrics
-annotateSolution :: AllocSolution -> AllocSolution
-annotateSolution = genericAnnotateSolution (printf "%.8f")
-
-- | Generate the valid node allocation singles or pairs for a new instance.
genAllocNodes :: Group.List        -- ^ Group list
              -> Node.List         -- ^ The node map
@@ -658,16 +557,6 @@ tryAlloc opts nl _ inst (Left all_nodes) =
                    ) emptyAllocSolution all_nodes
  in return $ annotateSolution sols

--- | Given a group/result, describe it as a nice (list of) messages.
-solutionDescription :: (Group.Group, Result (GenericAllocSolution a))
-                    -> [String]
-solutionDescription (grp, result) =
-  case result of
-    Ok solution -> map (printf "Group %s (%s): %s" gname pol) (asLog solution)
-    Bad message -> [printf "Group %s: error %s" gname message]
-  where gname = Group.name grp
-        pol = allocPolicyToRaw (Group.allocPolicy grp)
-
-- | From a list of possibly bad and possibly empty solutions, filter
-- only the groups with a valid result. Note that the result will be
-- reversed compared to the original list.
@@ -797,21 +686,6 @@ tryGroupAlloc opts mggl mgnl ngil gn inst cnt = do
  (solution, msgs) <- findAllocation opts mggl mgnl ngil gdx inst cnt
  return $ solution { asLog = msgs }

--- | Calculate the new instance list after allocation solution.
-updateIl :: Instance.List           -- ^ The original instance list
-         -> Maybe (Node.GenericAllocElement a) -- ^ The result of
-                                               -- the allocation attempt
-         -> Instance.List           -- ^ The updated instance list
-updateIl il Nothing = il
-updateIl il (Just (_, xi, _, _)) = Container.add (Container.size il) xi il
-
--- | Extract the the new node list from the allocation solution.
-extractNl :: Node.List               -- ^ The original node list
-          -> Maybe (Node.GenericAllocElement a) -- ^ The result of the
-                                                -- allocation attempt
-          -> Node.List               -- ^ The new node list
-extractNl nl Nothing = nl
-extractNl _ (Just (xnl, _, _, _)) = xnl

-- | Try to allocate a list of instances on a multi-group cluster.
allocList :: AlgorithmOptions
diff --git a/src/Ganeti/HTools/Cluster/AllocationSolution.hs 
b/src/Ganeti/HTools/Cluster/AllocationSolution.hs
new file mode 100644
index 0000000..267d4cf
--- /dev/null
+++ b/src/Ganeti/HTools/Cluster/AllocationSolution.hs
@@ -0,0 +1,192 @@
+{-| Implementation of handling of Allocation Solutions
+
+-}
+
+{-
+
+Copyright (C) 2009, 2010, 2011, 2012, 2013 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.Cluster.AllocationSolution
+  ( GenericAllocElement
+  , AllocElement
+  , GenericAllocSolution(..)
+  , AllocSolution
+  , emptyAllocSolution
+  , sumAllocs
+  , concatAllocs
+  , updateIl
+  , extractNl
+  , collapseFailures
+  , genericAnnotateSolution
+  , annotateSolution
+  , solutionDescription
+  ) where
+
+import Data.List (intercalate, foldl')
+import Text.Printf (printf)
+
+import Ganeti.BasicTypes (GenericResult(..), Result)
+import qualified Ganeti.HTools.Container as Container
+import qualified Ganeti.HTools.Group as Group
+import qualified Ganeti.HTools.Instance as Instance
+import qualified Ganeti.HTools.Node as Node
+import qualified Ganeti.HTools.Types as T
+
+-- | A simple name for an allocation element (here just for logistic
+-- reasons), generic in the type of the metric.
+type GenericAllocElement a = (Node.List, Instance.Instance, [Node.Node], a)
+
+-- | A simple name for an allocation element (here just for logistic
+-- reasons).
+type AllocElement = GenericAllocElement T.Score
+
+-- | Allocation\/relocation solution.
+data GenericAllocSolution a = AllocSolution
+  { asFailures :: [T.FailMode]            -- ^ Failure counts
+  , asAllocs   :: Int                     -- ^ Good allocation count
+  , asSolution :: Maybe (GenericAllocElement a) -- ^ The actual allocation
+                                          -- result
+  , asLog      :: [String]                -- ^ Informational messages
+  }
+type AllocSolution = GenericAllocSolution T.Score
+
+-- | The empty solution we start with when computing allocations.
+emptyAllocSolution :: GenericAllocSolution a
+emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0
+                                   , asSolution = Nothing, asLog = [] }
+
+
+-- | Calculate the new instance list after allocation solution.
+updateIl :: Instance.List           -- ^ The original instance list
+         -> Maybe (GenericAllocElement a) -- ^ The result of
+                                          -- the allocation attempt
+         -> Instance.List           -- ^ The updated instance list
+updateIl il Nothing = il
+updateIl il (Just (_, xi, _, _)) = Container.add (Container.size il) xi il
+
+-- | Extract the the new node list from the allocation solution.
+extractNl :: Node.List               -- ^ The original node list
+          -> Maybe (GenericAllocElement a) -- ^ The result of the
+                                           -- allocation attempt
+          -> Node.List               -- ^ The new node list
+extractNl nl Nothing = nl
+extractNl _ (Just (xnl, _, _, _)) = xnl
+
+-- | Compares two Maybe AllocElement and chooses the best score.
+bestAllocElement :: Ord a
+                 => Maybe (GenericAllocElement a)
+                 -> Maybe (GenericAllocElement a)
+                 -> Maybe (GenericAllocElement a)
+bestAllocElement a Nothing = a
+bestAllocElement Nothing b = b
+bestAllocElement a@(Just (_, _, _, ascore)) b@(Just (_, _, _, bscore)) =
+  if ascore < bscore then a else b
+
+-- | Update current Allocation solution and failure stats with new
+-- elements.
+concatAllocs :: Ord a
+             => GenericAllocSolution a
+             -> T.OpResult (GenericAllocElement a)
+             -> GenericAllocSolution a
+concatAllocs as (Bad reason) = as { asFailures = reason : asFailures as }
+
+concatAllocs as (Ok ns) =
+  let -- Choose the old or new solution, based on the cluster score
+    cntok = asAllocs as
+    osols = asSolution as
+    nsols = bestAllocElement osols (Just ns)
+    nsuc = cntok + 1
+    -- Note: we force evaluation of nsols here in order to keep the
+    -- memory profile low - we know that we will need nsols for sure
+    -- in the next cycle, so we force evaluation of nsols, since the
+    -- foldl' in the caller will only evaluate the tuple, but not the
+    -- elements of the tuple
+  in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols }
+
+-- | Sums two 'AllocSolution' structures.
+sumAllocs :: Ord a
+          => GenericAllocSolution a
+          -> GenericAllocSolution a
+          -> GenericAllocSolution a
+sumAllocs (AllocSolution aFails aAllocs aSols aLog)
+          (AllocSolution bFails bAllocs bSols bLog) =
+  -- note: we add b first, since usually it will be smaller; when
+  -- fold'ing, a will grow and grow whereas b is the per-group
+  -- result, hence smaller
+  let nFails  = bFails ++ aFails
+      nAllocs = aAllocs + bAllocs
+      nSols   = bestAllocElement aSols bSols
+      nLog    = bLog ++ aLog
+  in AllocSolution nFails nAllocs nSols nLog
+
+-- | Build failure stats out of a list of failures.
+collapseFailures :: [T.FailMode] -> T.FailStats
+collapseFailures flst =
+    map (\k -> (k, foldl' (\a e -> if e == k then a + 1 else a) 0 flst))
+            [minBound..maxBound]
+
+
+-- | Given a solution, generates a reasonable description for it.
+genericDescribeSolution :: (a -> String) -> GenericAllocSolution a -> String
+genericDescribeSolution formatMetrics as =
+  let fcnt = asFailures as
+      sols = asSolution as
+      freasons =
+        intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) .
+        filter ((> 0) . snd) . collapseFailures $ fcnt
+  in case sols of
+     Nothing -> "No valid allocation solutions, failure reasons: " ++
+                (if null fcnt then "unknown reasons" else freasons)
+     Just (_, _, nodes, cv) ->
+         printf ("score: %s, successes %d, failures %d (%s)" ++
+                 " for node(s) %s") (formatMetrics cv) (asAllocs as)
+               (length fcnt) freasons
+               (intercalate "/" . map Node.name $ nodes)
+
+-- | Annotates a solution with the appropriate string.
+genericAnnotateSolution :: (a -> String)
+                        ->GenericAllocSolution a -> GenericAllocSolution a
+genericAnnotateSolution formatMetrics as =
+  as { asLog = genericDescribeSolution formatMetrics as : asLog as }
+
+-- | Annotate a solution based on the standard metrics
+annotateSolution :: AllocSolution -> AllocSolution
+annotateSolution = genericAnnotateSolution (printf "%.8f")
+
+
+-- | Given a group/result, describe it as a nice (list of) messages.
+solutionDescription :: (Group.Group, Result (GenericAllocSolution a))
+                    -> [String]
+solutionDescription (grp, result) =
+  case result of
+    Ok solution -> map (printf "Group %s (%s): %s" gname pol) (asLog solution)
+    Bad message -> [printf "Group %s: error %s" gname message]
+  where gname = Group.name grp
+        pol = T.allocPolicyToRaw (Group.allocPolicy grp)
+
diff --git a/src/Ganeti/HTools/Dedicated.hs b/src/Ganeti/HTools/Dedicated.hs
index 8059766..d31641d 100644
--- a/src/Ganeti/HTools/Dedicated.hs
+++ b/src/Ganeti/HTools/Dedicated.hs
@@ -57,6 +57,7 @@ import Ganeti.BasicTypes (iterateOk, Result, failError)
import qualified Ganeti.HTools.AlgorithmParams as Alg
import qualified Ganeti.HTools.Backend.IAlloc as IAlloc
import qualified Ganeti.HTools.Cluster as Cluster
+import qualified Ganeti.HTools.Cluster.AllocationSolution as AllocSol
import qualified Ganeti.HTools.Cluster.Utils as ClusterUtils
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Group as Group
@@ -132,7 +133,7 @@ lostAllocationsMetric opts insts inst node = do
-- | Allocate an instance on a given node.
allocateOnSingle :: Alg.AlgorithmOptions
                 -> Node.List -> Instance.Instance -> T.Ndx
-                 -> T.OpResult (Node.GenericAllocElement Metric)
+                 -> T.OpResult (AllocSol.GenericAllocElement Metric)
allocateOnSingle opts nl inst new_pdx = do
  let primary = Container.find new_pdx nl
      policy = Node.iPolicy primary
@@ -150,7 +151,7 @@ allocateOnPair :: Alg.AlgorithmOptions
               -> Instance.Instance
               -> T.Ndx
               -> T.Ndx
-               -> T.OpResult (Node.GenericAllocElement Metric)
+               -> T.OpResult (AllocSol.GenericAllocElement Metric)
allocateOnPair opts nl inst pdx sdx = do
  let primary = Container.find pdx nl
      secondary = Container.find sdx nl
@@ -174,7 +175,7 @@ findAllocation :: Alg.AlgorithmOptions
               -> T.Gdx
               -> Instance.Instance
               -> Int
-               -> Result (Cluster.GenericAllocSolution Metric, [String])
+               -> Result (AllocSol.GenericAllocSolution Metric, [String])
findAllocation opts mggl mgnl gdx inst count = do
  let nl = Container.filter ((== gdx) . Node.group) mgnl
      group = Container.find gdx mggl
@@ -185,22 +186,22 @@ findAllocation opts mggl mgnl gdx inst count = do
  solution <- case allocNodes of
    (Right []) -> fail "Not enough online nodes"
    (Right pairs) ->
-      let sols = foldl Cluster.sumAllocs Cluster.emptyAllocSolution
+      let sols = foldl AllocSol.sumAllocs AllocSol.emptyAllocSolution
                   $ map (\(p, ss) -> foldl
                           (\cstate ->
-                             Cluster.concatAllocs cstate
+                             AllocSol.concatAllocs cstate
                             . allocateOnPair opts nl inst p)
-                           Cluster.emptyAllocSolution ss)
+                           AllocSol.emptyAllocSolution ss)
                     pairs
-       in return $ Cluster.genericAnnotateSolution show sols
+       in return $ AllocSol.genericAnnotateSolution show sols
    (Left []) -> fail "No online nodes"
    (Left nodes) ->
      let sols = foldl (\cstate ->
-                          Cluster.concatAllocs cstate
+                          AllocSol.concatAllocs cstate
                          . allocateOnSingle opts nl inst)
-                       Cluster.emptyAllocSolution nodes
-      in return $ Cluster.genericAnnotateSolution show sols
-  return (solution, Cluster.solutionDescription (group, return solution))
+                       AllocSol.emptyAllocSolution nodes
+      in return $ AllocSol.genericAnnotateSolution show sols
+  return (solution, AllocSol.solutionDescription (group, return solution))

-- | Find an allocation in a suitable group.
findMGAllocation :: Alg.AlgorithmOptions
@@ -209,19 +210,19 @@ findMGAllocation :: Alg.AlgorithmOptions
                 -> Instance.List
                 -> Instance.Instance
                 -> Int
-                 -> Result (Cluster.GenericAllocSolution Metric)
+                 -> Result (AllocSol.GenericAllocSolution Metric)
findMGAllocation opts gl nl il inst count = do
  let groups_by_idx = ClusterUtils.splitCluster nl il
      genSol (gdx, (nl', _)) =
        liftM fst $ findAllocation opts gl nl' gdx inst count
      sols = map (flip Container.find gl . fst &&& genSol) groups_by_idx
      goodSols = Cluster.sortMGResults $ Cluster.filterMGResults sols
-      all_msgs = concatMap Cluster.solutionDescription sols
+      all_msgs = concatMap AllocSol.solutionDescription sols
  case goodSols of
    [] -> fail $ intercalate ", " all_msgs
    (final_group, final_sol):_ ->
      let sel_msg = "Selected group: " ++ Group.name final_group
-      in return $ final_sol { Cluster.asLog = sel_msg : all_msgs }
+      in return $ final_sol { AllocSol.asLog = sel_msg : all_msgs }

-- | Handle allocation requests in the dedicated scenario.
runDedicatedAllocation :: Alg.AlgorithmOptions
@@ -234,7 +235,7 @@ runDedicatedAllocation opts request =
          Loader.Allocate inst (Cluster.AllocDetails count (Just gn)) -> do
            gdx <- Group.idx <$> Container.findByName gl gn
            (solution, msgs) <- findAllocation opts gl nl gdx inst count
-            IAlloc.formatAllocate il $ solution { Cluster.asLog = msgs }
+            IAlloc.formatAllocate il $ solution { AllocSol.asLog = msgs }
          Loader.Allocate inst (Cluster.AllocDetails count Nothing) ->
            findMGAllocation opts gl nl il inst count
              >>= IAlloc.formatAllocate il
@@ -248,9 +249,9 @@ runDedicatedAllocation opts request =
                               liftM fst
                                 $ findAllocation opts gl nl gdx inst count)
                          maybeGroup
-                  let sol = Cluster.asSolution ares
-                      nl'' = Cluster.extractNl nl' sol
-                      il'' = Cluster.updateIl il' sol
+                  let sol = AllocSol.asSolution ares
+                      nl'' = AllocSol.extractNl nl' sol
+                      il'' = AllocSol.updateIl il' sol
                  return (nl'', il'', (inst, ares):res))
               (nl, il, []) insts
          _ -> fail "Dedicated Allocation only for proper allocation requests"
diff --git a/src/Ganeti/HTools/Node.hs b/src/Ganeti/HTools/Node.hs
index 55712d1..795054b 100644
--- a/src/Ganeti/HTools/Node.hs
+++ b/src/Ganeti/HTools/Node.hs
@@ -91,8 +91,6 @@ module Ganeti.HTools.Node
  , list
  -- * Misc stuff
  , AssocList
-  , GenericAllocElement
-  , AllocElement
  , noSecondary
  , computeGroups
  , mkNodeGraph
@@ -245,14 +243,6 @@ type AssocList = [(T.Ndx, Node)]
-- | A simple name for a node map.
type List = Container.Container Node

--- | A simple name for an allocation element (here just for logistic
--- reasons), generic in the type of the metric.
-type GenericAllocElement a = (List, Instance.Instance, [Node], a)
-
--- | A simple name for an allocation element (here just for logistic
--- reasons).
-type AllocElement = GenericAllocElement T.Score
-
-- | Constant node index for a non-moveable instance.
noSecondary :: T.Ndx
noSecondary = -1
diff --git a/test/hs/Test/Ganeti/HTools/Cluster.hs 
b/test/hs/Test/Ganeti/HTools/Cluster.hs
index 2d54c27..61cfd2d 100644
--- a/test/hs/Test/Ganeti/HTools/Cluster.hs
+++ b/test/hs/Test/Ganeti/HTools/Cluster.hs
@@ -54,6 +54,7 @@ import Ganeti.BasicTypes
import qualified Ganeti.HTools.AlgorithmParams as Alg
import qualified Ganeti.HTools.Backend.IAlloc as IAlloc
import qualified Ganeti.HTools.Cluster as Cluster
+import qualified Ganeti.HTools.Cluster.AllocationSolution as AllocSol
import qualified Ganeti.HTools.Cluster.Evacuate as Evacuate
import qualified Ganeti.HTools.Cluster.Metrics as Metrics
import qualified Ganeti.HTools.Cluster.Utils as ClusterUtils
@@ -161,7 +162,7 @@ prop_Alloc_sane inst =
     Cluster.tryAlloc opts nl il inst' of
       Bad msg -> failTest msg
       Ok as ->
-         case Cluster.asSolution as of
+         case AllocSol.asSolution as of
           Nothing -> failTest "Failed to allocate, empty solution"
           Just (xnl, xi, _, cv) ->
             let il' = Container.add (Instance.idx xi) xi il
@@ -218,7 +219,7 @@ genClusterAlloc count node inst =
     Cluster.tryAlloc opts nl Container.empty inst of
       Bad msg -> Bad $ "Can't allocate: " ++ msg
       Ok as ->
-         case Cluster.asSolution as of
+         case AllocSol.asSolution as of
           Nothing -> Bad "Empty solution?"
           Just (xnl, xi, _, _) ->
             let xil = Container.add (Instance.idx xi) xi Container.empty
@@ -373,9 +374,9 @@ canAllocOn nl reqnodes inst =
       Cluster.tryAlloc Alg.defaultOptions nl Container.empty inst of
       Bad msg -> Just $ "Can't allocate: " ++ msg
       Ok as ->
-         case Cluster.asSolution as of
+         case AllocSol.asSolution as of
           Nothing -> Just $ "No allocation solution; failures: " ++
-                      show (Cluster.collapseFailures $ Cluster.asFailures as)
+                      show (AllocSol.collapseFailures $ AllocSol.asFailures as)
           Just _ -> Nothing

-- | Checks that allocation obeys minimum and maximum instance
--
2.2.0.rc0.207.ga3a616c


LGTM

Reply via email to