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

Reply via email to