diff --git a/Ganeti/HTools/Cluster.hs b/Ganeti/HTools/Cluster.hs
index efe9d34e47aae9566386b9d30f016018e8760fd9..4eb07c362b02c5ab50194e63817c7f1ed4d0fa01 100644
--- a/Ganeti/HTools/Cluster.hs
+++ b/Ganeti/HTools/Cluster.hs
@@ -57,7 +57,6 @@ module Ganeti.HTools.Cluster
     ) where
 
 import Data.List
-import Data.Maybe (isNothing, fromJust)
 import Text.Printf (printf)
 import Data.Function
 import Control.Monad
@@ -77,7 +76,7 @@ type Score = Double
 type Placement = (Idx, Ndx, Ndx, Score)
 
 -- | Allocation\/relocation solution.
-type AllocSolution = [(Maybe Node.List, Instance.Instance, [Node.Node])]
+type AllocSolution = [(OpResult Node.List, Instance.Instance, [Node.Node])]
 
 -- | An instance move definition
 data IMove = Failover                -- ^ Failover the instance (f)
@@ -203,7 +202,7 @@ compareTables a@(Table _ _ a_cv _) b@(Table _ _ b_cv _ ) =
 
 -- | Applies an instance move to a given node list and instance.
 applyMove :: Node.List -> Instance.Instance
-          -> IMove -> (Maybe Node.List, Instance.Instance, Ndx, Ndx)
+          -> IMove -> (OpResult Node.List, Instance.Instance, Ndx, Ndx)
 -- Failover (f)
 applyMove nl inst Failover =
     let old_pdx = Instance.pnode inst
@@ -284,7 +283,7 @@ applyMove nl inst (FailoverAndReplace new_sdx) =
 
 -- | Tries to allocate an instance on one given node.
 allocateOnSingle :: Node.List -> Instance.Instance -> Node.Node
-                 -> (Maybe Node.List, Instance.Instance)
+                 -> (OpResult Node.List, Instance.Instance)
 allocateOnSingle nl inst p =
     let new_pdx = Node.idx p
         new_nl = Node.addPri p inst >>= \new_p ->
@@ -293,7 +292,7 @@ allocateOnSingle nl inst p =
 
 -- | Tries to allocate an instance on a given pair of nodes.
 allocateOnPair :: Node.List -> Instance.Instance -> Node.Node -> Node.Node
-               -> (Maybe Node.List, Instance.Instance)
+               -> (OpResult Node.List, Instance.Instance)
 allocateOnPair nl inst tgt_p tgt_s =
     let new_pdx = Node.idx tgt_p
         new_sdx = Node.idx tgt_s
@@ -315,16 +314,16 @@ checkSingleStep ini_tbl target cur_tbl move =
         Table ini_nl ini_il _ ini_plc = ini_tbl
         (tmp_nl, new_inst, pri_idx, sec_idx) = applyMove ini_nl target move
     in
-      if isNothing tmp_nl then cur_tbl
-      else
-          let tgt_idx = Instance.idx target
-              upd_nl = fromJust tmp_nl
-              upd_cvar = compCV upd_nl
-              upd_il = Container.add tgt_idx new_inst ini_il
-              upd_plc = (tgt_idx, pri_idx, sec_idx, upd_cvar):ini_plc
-              upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
-          in
-            compareTables cur_tbl upd_tbl
+      case tmp_nl of
+        OpFail _ -> cur_tbl
+        OpGood upd_nl ->
+            let tgt_idx = Instance.idx target
+                upd_cvar = compCV upd_nl
+                upd_il = Container.add tgt_idx new_inst ini_il
+                upd_plc = (tgt_idx, pri_idx, sec_idx, upd_cvar):ini_plc
+                upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
+            in
+              compareTables cur_tbl upd_tbl
 
 -- | Given the status of the current secondary as a valid new node
 -- and the current candidate target node,
diff --git a/Ganeti/HTools/Node.hs b/Ganeti/HTools/Node.hs
index 34210ccd19c711af8cec3bcf2f8e43ba79931e74..db5e402e2c38bf79879549614740346ee4b01a7a 100644
--- a/Ganeti/HTools/Node.hs
+++ b/Ganeti/HTools/Node.hs
@@ -294,7 +294,7 @@ removeSec t inst =
           p_rem = new_prem}
 
 -- | Adds a primary instance.
-addPri :: Node -> Instance.Instance -> Maybe Node
+addPri :: Node -> Instance.Instance -> T.OpResult Node
 addPri t inst =
     let iname = Instance.idx inst
         new_mem = f_mem t - Instance.mem inst
@@ -307,17 +307,17 @@ addPri t inst =
       if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) ||
          (failLimits t new_dp new_pcpu)
       then
-        Nothing
+        T.OpFail T.FailN1
       else
         let new_plist = iname:(plist t)
             new_mp = (fromIntegral new_mem) / (t_mem t)
         in
-        Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
-                failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
-                u_cpu = new_ucpu, p_cpu = new_pcpu}
+        T.OpGood t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
+                    failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
+                    u_cpu = new_ucpu, p_cpu = new_pcpu}
 
 -- | Adds a secondary instance.
-addSec :: Node -> Instance.Instance -> T.Ndx -> Maybe Node
+addSec :: Node -> Instance.Instance -> T.Ndx -> T.OpResult Node
 addSec t inst pdx =
     let iname = Instance.idx inst
         old_peers = peers t
@@ -332,14 +332,14 @@ addSec t inst pdx =
     in if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) ||
           (failLimits t new_dp noLimit)
        then
-           Nothing
+           T.OpFail T.FailN1
        else
            let new_slist = iname:(slist t)
            in
-             Just t {slist = new_slist, f_dsk = new_dsk,
-                     peers = new_peers, failN1 = new_failn1,
-                     r_mem = new_rmem, p_dsk = new_dp,
-                     p_rem = new_prem}
+             T.OpGood t {slist = new_slist, f_dsk = new_dsk,
+                         peers = new_peers, failN1 = new_failn1,
+                         r_mem = new_rmem, p_dsk = new_dp,
+                         p_rem = new_prem}
 
 -- * Stats functions
 
diff --git a/Ganeti/HTools/Types.hs b/Ganeti/HTools/Types.hs
index 662cef2c0b5cd164bfbeb10c683919e1d8ad1dac..3a494f7f7f154cca9b92cb25b12d3dfa5271a96c 100644
--- a/Ganeti/HTools/Types.hs
+++ b/Ganeti/HTools/Types.hs
@@ -29,6 +29,8 @@ module Ganeti.HTools.Types
     , NameAssoc
     , Result(..)
     , Element(..)
+    , FailMode(..)
+    , OpResult(..)
     ) where
 
 -- | The instance index type.
@@ -58,6 +60,21 @@ instance Monad Result where
     return = Ok
     fail = Bad
 
+-- | Reason for an operation's falure
+data FailMode = FailMem  -- ^ Failed due to not enough RAM
+              | FailDisk -- ^ Failed due to not enough disk
+              | FailCPU  -- ^ Failed due to not enough CPU capacity
+              | FailN1   -- ^ Failed due to not passing N1 checks
+
+-- | Either-like data-type customized for our failure modes
+data OpResult a = OpFail FailMode -- ^ Failed operation
+                | OpGood a        -- ^ Success operation
+
+instance Monad OpResult where
+    (OpGood x) >>= fn = fn x
+    (OpFail y) >>= _ = OpFail y
+    return = OpGood
+
 -- | A generic class for items that have updateable names and indices.
 class Element a where
     -- | Returns the name of the element
diff --git a/hail.hs b/hail.hs
index ae8e112766f28bbcbfd33ac56e73ee6dc4fff0c0..3503b5bf03477bad1e122476a6ce41c1af7c44e7 100644
--- a/hail.hs
+++ b/hail.hs
@@ -27,7 +27,6 @@ module Main (main) where
 
 import Data.List
 import Data.Function
-import Data.Maybe (isJust, fromJust)
 import Monad
 import System
 import System.IO
@@ -43,7 +42,6 @@ import qualified Ganeti.HTools.CLI as CLI
 import Ganeti.HTools.IAlloc
 import Ganeti.HTools.Types
 import Ganeti.HTools.Loader (RqType(..), Request(..))
-import Ganeti.HTools.Utils
 
 -- | Command line options structure.
 data Options = Options
@@ -74,15 +72,21 @@ options =
     ]
 
 
-filterFails :: (Monad m) => [(Maybe Node.List, Instance.Instance, [Node.Node])]
+filterFails :: (Monad m) => [(OpResult Node.List,
+                              Instance.Instance, [Node.Node])]
             -> m [(Node.List, [Node.Node])]
 filterFails sols =
     if null sols then fail "No nodes onto which to allocate at all"
-    else let sols' = filter (isJust . fst3) sols
-         in if null sols' then
-                fail "No valid allocation solutions"
-            else
-                return $ map (\(x, _, y) -> (fromJust x, y)) sols'
+    else let sols' = concat . map (\ (onl, _, nn) ->
+                                       case onl of
+                                         OpFail _ -> []
+                                         OpGood gnl -> [(gnl, nn)]
+                                  ) $ sols
+         in
+           if null sols' then
+               fail "No valid allocation solutions"
+           else
+               return sols'
 
 processResults :: (Monad m) => [(Node.List, [Node.Node])]
                -> m (String, [Node.Node])
@@ -98,9 +102,8 @@ processResults sols =
     in return (info, w)
 
 -- | Process a request and return new node lists
-processRequest ::
-                  Request
-               -> Result [(Maybe Node.List, Instance.Instance, [Node.Node])]
+processRequest :: Request
+               -> Result [(OpResult Node.List, Instance.Instance, [Node.Node])]
 processRequest request =
   let Request rqtype nl il _ = request
   in case rqtype of
diff --git a/hspace.hs b/hspace.hs
index 4a5c2f18db26d7a446c88f66303fc24b32529a81..ec7df9c6827b15955c5f6ad2e7acf1f8ed09d225 100644
--- a/hspace.hs
+++ b/hspace.hs
@@ -27,7 +27,6 @@ module Main (main) where
 
 import Data.List
 import Data.Function
-import Data.Maybe (isJust, fromJust, isNothing)
 import Monad
 import System
 import System.IO
@@ -43,6 +42,7 @@ import qualified Ganeti.HTools.Instance as Instance
 import qualified Ganeti.HTools.CLI as CLI
 
 import Ganeti.HTools.Utils
+import Ganeti.HTools.Types
 
 -- | Command line options structure.
 data Options = Options
@@ -148,21 +148,25 @@ options =
     ]
 
 filterFails :: Cluster.AllocSolution
-            -> Maybe [(Node.List, Instance.Instance, [Node.Node])]
+            -> OpResult [(Node.List, Instance.Instance, [Node.Node])]
 filterFails sols =
-    if null sols then Nothing -- No nodes onto which to allocate at all
-    else let sols' = filter (isJust . fst3) sols
-         in if null sols' then
-                Nothing -- No valid allocation solutions
-            else
-                return $ map (\(x, y, z) -> (fromJust x, y, z)) sols'
-
-processResults :: (Monad m) => [(Node.List, Instance.Instance, [Node.Node])]
-               -> m (Node.List, Instance.Instance, [Node.Node])
+    let sols' = concat . map (\ (onl, i, nn) ->
+                                  case onl of
+                                    OpFail _ -> []
+                                    OpGood gnl -> [(gnl, i, nn)]
+                             ) $ sols
+    in
+      if null sols' then
+          OpFail FailN1
+      else
+          return sols'
+
+processResults :: [(Node.List, Instance.Instance, [Node.Node])]
+               -> (Node.List, Instance.Instance, [Node.Node])
 processResults sols =
     let sols' = map (\e@(nl', _, _) -> (Cluster.compCV  nl', e)) sols
         sols'' = sortBy (compare `on` fst) sols'
-    in return $ snd $ head sols''
+    in snd $ head sols''
 
 iterateDepth :: Node.List
              -> Instance.List
@@ -176,16 +180,18 @@ iterateDepth nl il newinst nreq ixes =
           newidx = (length $ Container.elems il) + depth
           newi2 = Instance.setIdx (Instance.setName newinst newname) newidx
           sols = (Cluster.tryAlloc nl il newi2 nreq)::
-                 Maybe Cluster.AllocSolution
+                 OpResult Cluster.AllocSolution
           orig = (nl, ixes)
-      in
-        if isNothing sols then orig
-        else let sols' = fromJust sols
-                 sols'' = filterFails sols'
-             in if isNothing sols'' then orig
-                else let (xnl, xi, _) = fromJust $ processResults $
-                                        fromJust sols''
-                     in iterateDepth xnl il newinst nreq (xi:ixes)
+      in case sols of
+           OpFail _ -> orig
+           OpGood sols' ->
+               let
+                   sols'' = filterFails sols'
+               in case sols'' of
+                    OpFail _ -> orig
+                    OpGood sols''' ->
+                        let (xnl, xi, _) = processResults sols'''
+                        in iterateDepth xnl il newinst nreq (xi:ixes)
 
 printStats :: String -> Cluster.CStats -> IO ()
 printStats kind cs = do