diff --git a/Ganeti/HTools/Cluster.hs b/Ganeti/HTools/Cluster.hs
index 8ba4edc4008525daff3588fbf0bc9e1438827a33..09b281459b77e7fe8d9f6ccd7e951fa90b76a708 100644
--- a/Ganeti/HTools/Cluster.hs
+++ b/Ganeti/HTools/Cluster.hs
@@ -29,7 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 module Ganeti.HTools.Cluster
     (
      -- * Types
-      AllocSolution
+      AllocSolution(..)
     , Table(..)
     , CStats(..)
     , AllocStats
@@ -81,7 +81,19 @@ import qualified Ganeti.OpCodes as OpCodes
 -- * Types
 
 -- | Allocation\/relocation solution.
-type AllocSolution = ([FailMode], Int, [Node.AllocElement])
+data AllocSolution = AllocSolution
+  { asFailures  :: [FailMode]          -- ^ Failure counts
+  , asAllocs    :: Int                 -- ^ Good allocation count
+  , asSolutions :: [Node.AllocElement] -- ^ The actual result, length
+                                       -- of the list depends on the
+                                       -- allocation/relocation mode
+
+  }
+
+-- | The empty solution we start with when computing allocations
+emptySolution :: AllocSolution
+emptySolution = AllocSolution { asFailures = [], asAllocs = 0
+                              , asSolutions = [] }
 
 -- | The complete state for the balancing solution
 data Table = Table Node.List Instance.List Score [Placement]
@@ -533,10 +545,12 @@ collapseFailures flst =
 -- | Update current Allocation solution and failure stats with new
 -- elements
 concatAllocs :: AllocSolution -> OpResult Node.AllocElement -> AllocSolution
-concatAllocs (flst, cntok, sols) (OpFail reason) = (reason:flst, cntok, sols)
+concatAllocs as (OpFail reason) = as { asFailures = reason : asFailures as }
 
-concatAllocs (flst, cntok, osols) (OpGood ns@(_, _, _, nscore)) =
+concatAllocs as (OpGood ns@(_, _, _, nscore)) =
     let -- Choose the old or new solution, based on the cluster score
+        cntok = asAllocs as
+        osols = asSolutions as
         nsols = case osols of
                   [] -> [ns]
                   (_, _, _, oscore):[] ->
@@ -553,7 +567,7 @@ concatAllocs (flst, cntok, osols) (OpGood ns@(_, _, _, nscore)) =
     -- 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` (flst, nsuc, nsols)
+    in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolutions = nsols }
 
 -- | Try to allocate an instance on the cluster.
 tryAlloc :: (Monad m) =>
@@ -568,14 +582,14 @@ tryAlloc nl _ inst 2 =
         ok_pairs = filter (\(x, y) -> Node.idx x /= Node.idx y) all_pairs
         sols = foldl' (\cstate (p, s) ->
                            concatAllocs cstate $ allocateOnPair nl inst p s
-                      ) ([], 0, []) ok_pairs
+                      ) emptySolution ok_pairs
     in return sols
 
 tryAlloc nl _ inst 1 =
     let all_nodes = getOnline nl
         sols = foldl' (\cstate ->
                            concatAllocs cstate . allocateOnSingle nl inst
-                      ) ([], 0, []) all_nodes
+                      ) emptySolution all_nodes
     in return sols
 
 tryAlloc _ _ _ reqn = fail $ "Unsupported number of allocation \
@@ -603,7 +617,7 @@ tryReloc nl il xid 1 ex_idx =
                                   return (mnl, i, [Container.find x mnl],
                                           compCV mnl)
                             in concatAllocs cstate em
-                       ) ([], 0, []) valid_idxes
+                       ) emptySolution valid_idxes
     in return sols1
 
 tryReloc _ _ _ reqn _  = fail $ "Unsupported number of relocation \
@@ -620,15 +634,23 @@ tryEvac nl il ex_ndx =
     let ex_nodes = map (`Container.find` nl) ex_ndx
         all_insts = nub . concatMap Node.sList $ ex_nodes
     in do
-      (_, sol) <- foldM (\(nl', (_, _, rsols)) idx -> do
-                           -- FIXME: hardcoded one node here
-                           (fm, cs, aes) <- tryReloc nl' il idx 1 ex_ndx
-                           case aes of
-                             csol@(nl'', _, _, _):_ ->
-                                 return (nl'', (fm, cs, csol:rsols))
-                             _ -> fail $ "Can't evacuate instance " ++
-                                  Instance.name (Container.find idx il)
-                        ) (nl, ([], 0, [])) all_insts
+      (_, sol) <- foldM (\(nl', old_as) idx -> do
+                            -- FIXME: hardcoded one node here
+                            -- (fm, cs, aes)
+                            new_as <- tryReloc nl' il idx 1 ex_ndx
+                            case asSolutions new_as of
+                              csol@(nl'', _, _, _):_ ->
+                                -- an individual relocation succeeded,
+                                -- we kind of compose the data from
+                                -- the two solutions
+                                return (nl'',
+                                        new_as { asSolutions =
+                                                    csol:asSolutions old_as })
+                              -- this relocation failed, so we fail
+                              -- the entire evac
+                              _ -> fail $ "Can't evacuate instance " ++
+                                   Instance.name (Container.find idx il)
+                        ) (nl, emptySolution) all_insts
       return sol
 
 -- | Recursively place instances on the cluster until we're out of space
@@ -646,7 +668,7 @@ iterateAlloc nl il newinst nreq ixes =
           newi2 = Instance.setIdx (Instance.setName newinst newname) newidx
       in case tryAlloc nl il newi2 nreq of
            Bad s -> Bad s
-           Ok (errs, _, sols3) ->
+           Ok (AllocSolution { asFailures = errs, asSolutions = sols3 }) ->
                case sols3 of
                  [] -> Ok (collapseFailures errs, nl, il, ixes)
                  (xnl, xi, _, _):[] ->
diff --git a/Ganeti/HTools/QC.hs b/Ganeti/HTools/QC.hs
index 9f471cedbdbcfb400af08f5c226e49464a760d6a..ebcc93970d55762220a4aaeda778feba033211ef 100644
--- a/Ganeti/HTools/QC.hs
+++ b/Ganeti/HTools/QC.hs
@@ -673,8 +673,8 @@ prop_ClusterAlloc_sane node inst =
         inst' = setInstanceSmallerThanNode node inst
     in case Cluster.tryAlloc nl il inst' rqnodes of
          Types.Bad _ -> False
-         Types.Ok (_, _, sols3) ->
-             case sols3 of
+         Types.Ok as ->
+             case Cluster.asSolutions as of
                [] -> False
                (xnl, xi, _, cv):[] ->
                    let il' = Container.add (Instance.idx xi) xi il
@@ -713,8 +713,8 @@ prop_ClusterAllocEvac node inst =
         inst' = setInstanceSmallerThanNode node inst
     in case Cluster.tryAlloc nl il inst' rqnodes of
          Types.Bad _ -> False
-         Types.Ok (_, _, sols3) ->
-             case sols3 of
+         Types.Ok as ->
+             case Cluster.asSolutions as of
                [] -> False
                (xnl, xi, _, _):[] ->
                    let sdx = Instance.sNode xi
diff --git a/hail.hs b/hail.hs
index 90902e679e2e1108db1a0922b90fa7f329aec971..e007504ed1ca1415792a20e4858ac2720af705bd 100644
--- a/hail.hs
+++ b/hail.hs
@@ -49,21 +49,24 @@ options = [oPrintNodes, oShowVer, oShowHelp]
 processResults :: (Monad m) =>
                   RqType -> Cluster.AllocSolution
                -> m (String, Cluster.AllocSolution)
-processResults _ (_, _, []) = fail "No valid allocation solutions"
-processResults (Evacuate _) as@(fstats, successes, sols) =
-    let (_, _, _, best) = head sols
+processResults _ (Cluster.AllocSolution { Cluster.asSolutions = [] }) =
+  fail "No valid allocation solutions"
+processResults (Evacuate _) as =
+    let fstats = Cluster.asFailures as
+        successes = Cluster.asAllocs as
+        (_, _, _, best) = head (Cluster.asSolutions as)
         tfails = length fstats
         info = printf "for last allocation, successes %d, failures %d,\
                       \ best score: %.8f" successes tfails best::String
     in return (info, as)
 
-processResults _ as@(fstats, successes, sols) =
-    case sols of
+processResults _ as =
+    case Cluster.asSolutions as of
       (_, _, w, best):[] ->
-          let tfails = length fstats
+          let tfails = length (Cluster.asFailures as)
               info = printf "successes %d, failures %d,\
                             \ best score: %.8f for node(s) %s"
-                            successes tfails
+                            (Cluster.asAllocs as) tfails
                             best (intercalate "/" . map Node.name $ w)::String
           in return (info, as)
       _ -> fail "Internal error: multiple allocation solutions"
@@ -107,8 +110,8 @@ main = do
   let sols = processRequest request >>= processResults rq
   let (ok, info, rn) =
           case sols of
-            Ok (ginfo, (_, _, sn)) -> (True, "Request successful: " ++ ginfo,
-                                       sn)
+            Ok (ginfo, as) -> (True, "Request successful: " ++ ginfo,
+                               Cluster.asSolutions as)
             Bad s -> (False, "Request failed: " ++ s, [])
       resp = formatResponse ok info rq rn
   putStrLn resp