Revision f2280553

b/Ganeti/HTools/Cluster.hs
57 57
    ) where
58 58

  
59 59
import Data.List
60
import Data.Maybe (isNothing, fromJust)
61 60
import Text.Printf (printf)
62 61
import Data.Function
63 62
import Control.Monad
......
77 76
type Placement = (Idx, Ndx, Ndx, Score)
78 77

  
79 78
-- | Allocation\/relocation solution.
80
type AllocSolution = [(Maybe Node.List, Instance.Instance, [Node.Node])]
79
type AllocSolution = [(OpResult Node.List, Instance.Instance, [Node.Node])]
81 80

  
82 81
-- | An instance move definition
83 82
data IMove = Failover                -- ^ Failover the instance (f)
......
203 202

  
204 203
-- | Applies an instance move to a given node list and instance.
205 204
applyMove :: Node.List -> Instance.Instance
206
          -> IMove -> (Maybe Node.List, Instance.Instance, Ndx, Ndx)
205
          -> IMove -> (OpResult Node.List, Instance.Instance, Ndx, Ndx)
207 206
-- Failover (f)
208 207
applyMove nl inst Failover =
209 208
    let old_pdx = Instance.pnode inst
......
284 283

  
285 284
-- | Tries to allocate an instance on one given node.
286 285
allocateOnSingle :: Node.List -> Instance.Instance -> Node.Node
287
                 -> (Maybe Node.List, Instance.Instance)
286
                 -> (OpResult Node.List, Instance.Instance)
288 287
allocateOnSingle nl inst p =
289 288
    let new_pdx = Node.idx p
290 289
        new_nl = Node.addPri p inst >>= \new_p ->
......
293 292

  
294 293
-- | Tries to allocate an instance on a given pair of nodes.
295 294
allocateOnPair :: Node.List -> Instance.Instance -> Node.Node -> Node.Node
296
               -> (Maybe Node.List, Instance.Instance)
295
               -> (OpResult Node.List, Instance.Instance)
297 296
allocateOnPair nl inst tgt_p tgt_s =
298 297
    let new_pdx = Node.idx tgt_p
299 298
        new_sdx = Node.idx tgt_s
......
315 314
        Table ini_nl ini_il _ ini_plc = ini_tbl
316 315
        (tmp_nl, new_inst, pri_idx, sec_idx) = applyMove ini_nl target move
317 316
    in
318
      if isNothing tmp_nl then cur_tbl
319
      else
320
          let tgt_idx = Instance.idx target
321
              upd_nl = fromJust tmp_nl
322
              upd_cvar = compCV upd_nl
323
              upd_il = Container.add tgt_idx new_inst ini_il
324
              upd_plc = (tgt_idx, pri_idx, sec_idx, upd_cvar):ini_plc
325
              upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
326
          in
327
            compareTables cur_tbl upd_tbl
317
      case tmp_nl of
318
        OpFail _ -> cur_tbl
319
        OpGood upd_nl ->
320
            let tgt_idx = Instance.idx target
321
                upd_cvar = compCV upd_nl
322
                upd_il = Container.add tgt_idx new_inst ini_il
323
                upd_plc = (tgt_idx, pri_idx, sec_idx, upd_cvar):ini_plc
324
                upd_tbl = Table upd_nl upd_il upd_cvar upd_plc
325
            in
326
              compareTables cur_tbl upd_tbl
328 327

  
329 328
-- | Given the status of the current secondary as a valid new node
330 329
-- and the current candidate target node,
b/Ganeti/HTools/Node.hs
294 294
          p_rem = new_prem}
295 295

  
296 296
-- | Adds a primary instance.
297
addPri :: Node -> Instance.Instance -> Maybe Node
297
addPri :: Node -> Instance.Instance -> T.OpResult Node
298 298
addPri t inst =
299 299
    let iname = Instance.idx inst
300 300
        new_mem = f_mem t - Instance.mem inst
......
307 307
      if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) ||
308 308
         (failLimits t new_dp new_pcpu)
309 309
      then
310
        Nothing
310
        T.OpFail T.FailN1
311 311
      else
312 312
        let new_plist = iname:(plist t)
313 313
            new_mp = (fromIntegral new_mem) / (t_mem t)
314 314
        in
315
        Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
316
                failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
317
                u_cpu = new_ucpu, p_cpu = new_pcpu}
315
        T.OpGood t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
316
                    failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
317
                    u_cpu = new_ucpu, p_cpu = new_pcpu}
318 318

  
319 319
-- | Adds a secondary instance.
320
addSec :: Node -> Instance.Instance -> T.Ndx -> Maybe Node
320
addSec :: Node -> Instance.Instance -> T.Ndx -> T.OpResult Node
321 321
addSec t inst pdx =
322 322
    let iname = Instance.idx inst
323 323
        old_peers = peers t
......
332 332
    in if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) ||
333 333
          (failLimits t new_dp noLimit)
334 334
       then
335
           Nothing
335
           T.OpFail T.FailN1
336 336
       else
337 337
           let new_slist = iname:(slist t)
338 338
           in
339
             Just t {slist = new_slist, f_dsk = new_dsk,
340
                     peers = new_peers, failN1 = new_failn1,
341
                     r_mem = new_rmem, p_dsk = new_dp,
342
                     p_rem = new_prem}
339
             T.OpGood t {slist = new_slist, f_dsk = new_dsk,
340
                         peers = new_peers, failN1 = new_failn1,
341
                         r_mem = new_rmem, p_dsk = new_dp,
342
                         p_rem = new_prem}
343 343

  
344 344
-- * Stats functions
345 345

  
b/Ganeti/HTools/Types.hs
29 29
    , NameAssoc
30 30
    , Result(..)
31 31
    , Element(..)
32
    , FailMode(..)
33
    , OpResult(..)
32 34
    ) where
33 35

  
34 36
-- | The instance index type.
......
58 60
    return = Ok
59 61
    fail = Bad
60 62

  
63
-- | Reason for an operation's falure
64
data FailMode = FailMem  -- ^ Failed due to not enough RAM
65
              | FailDisk -- ^ Failed due to not enough disk
66
              | FailCPU  -- ^ Failed due to not enough CPU capacity
67
              | FailN1   -- ^ Failed due to not passing N1 checks
68

  
69
-- | Either-like data-type customized for our failure modes
70
data OpResult a = OpFail FailMode -- ^ Failed operation
71
                | OpGood a        -- ^ Success operation
72

  
73
instance Monad OpResult where
74
    (OpGood x) >>= fn = fn x
75
    (OpFail y) >>= _ = OpFail y
76
    return = OpGood
77

  
61 78
-- | A generic class for items that have updateable names and indices.
62 79
class Element a where
63 80
    -- | Returns the name of the element
b/hail.hs
27 27

  
28 28
import Data.List
29 29
import Data.Function
30
import Data.Maybe (isJust, fromJust)
31 30
import Monad
32 31
import System
33 32
import System.IO
......
43 42
import Ganeti.HTools.IAlloc
44 43
import Ganeti.HTools.Types
45 44
import Ganeti.HTools.Loader (RqType(..), Request(..))
46
import Ganeti.HTools.Utils
47 45

  
48 46
-- | Command line options structure.
49 47
data Options = Options
......
74 72
    ]
75 73

  
76 74

  
77
filterFails :: (Monad m) => [(Maybe Node.List, Instance.Instance, [Node.Node])]
75
filterFails :: (Monad m) => [(OpResult Node.List,
76
                              Instance.Instance, [Node.Node])]
78 77
            -> m [(Node.List, [Node.Node])]
79 78
filterFails sols =
80 79
    if null sols then fail "No nodes onto which to allocate at all"
81
    else let sols' = filter (isJust . fst3) sols
82
         in if null sols' then
83
                fail "No valid allocation solutions"
84
            else
85
                return $ map (\(x, _, y) -> (fromJust x, y)) sols'
80
    else let sols' = concat . map (\ (onl, _, nn) ->
81
                                       case onl of
82
                                         OpFail _ -> []
83
                                         OpGood gnl -> [(gnl, nn)]
84
                                  ) $ sols
85
         in
86
           if null sols' then
87
               fail "No valid allocation solutions"
88
           else
89
               return sols'
86 90

  
87 91
processResults :: (Monad m) => [(Node.List, [Node.Node])]
88 92
               -> m (String, [Node.Node])
......
98 102
    in return (info, w)
99 103

  
100 104
-- | Process a request and return new node lists
101
processRequest ::
102
                  Request
103
               -> Result [(Maybe Node.List, Instance.Instance, [Node.Node])]
105
processRequest :: Request
106
               -> Result [(OpResult Node.List, Instance.Instance, [Node.Node])]
104 107
processRequest request =
105 108
  let Request rqtype nl il _ = request
106 109
  in case rqtype of
b/hspace.hs
27 27

  
28 28
import Data.List
29 29
import Data.Function
30
import Data.Maybe (isJust, fromJust, isNothing)
31 30
import Monad
32 31
import System
33 32
import System.IO
......
43 42
import qualified Ganeti.HTools.CLI as CLI
44 43

  
45 44
import Ganeti.HTools.Utils
45
import Ganeti.HTools.Types
46 46

  
47 47
-- | Command line options structure.
48 48
data Options = Options
......
148 148
    ]
149 149

  
150 150
filterFails :: Cluster.AllocSolution
151
            -> Maybe [(Node.List, Instance.Instance, [Node.Node])]
151
            -> OpResult [(Node.List, Instance.Instance, [Node.Node])]
152 152
filterFails sols =
153
    if null sols then Nothing -- No nodes onto which to allocate at all
154
    else let sols' = filter (isJust . fst3) sols
155
         in if null sols' then
156
                Nothing -- No valid allocation solutions
157
            else
158
                return $ map (\(x, y, z) -> (fromJust x, y, z)) sols'
159

  
160
processResults :: (Monad m) => [(Node.List, Instance.Instance, [Node.Node])]
161
               -> m (Node.List, Instance.Instance, [Node.Node])
153
    let sols' = concat . map (\ (onl, i, nn) ->
154
                                  case onl of
155
                                    OpFail _ -> []
156
                                    OpGood gnl -> [(gnl, i, nn)]
157
                             ) $ sols
158
    in
159
      if null sols' then
160
          OpFail FailN1
161
      else
162
          return sols'
163

  
164
processResults :: [(Node.List, Instance.Instance, [Node.Node])]
165
               -> (Node.List, Instance.Instance, [Node.Node])
162 166
processResults sols =
163 167
    let sols' = map (\e@(nl', _, _) -> (Cluster.compCV  nl', e)) sols
164 168
        sols'' = sortBy (compare `on` fst) sols'
165
    in return $ snd $ head sols''
169
    in snd $ head sols''
166 170

  
167 171
iterateDepth :: Node.List
168 172
             -> Instance.List
......
176 180
          newidx = (length $ Container.elems il) + depth
177 181
          newi2 = Instance.setIdx (Instance.setName newinst newname) newidx
178 182
          sols = (Cluster.tryAlloc nl il newi2 nreq)::
179
                 Maybe Cluster.AllocSolution
183
                 OpResult Cluster.AllocSolution
180 184
          orig = (nl, ixes)
181
      in
182
        if isNothing sols then orig
183
        else let sols' = fromJust sols
184
                 sols'' = filterFails sols'
185
             in if isNothing sols'' then orig
186
                else let (xnl, xi, _) = fromJust $ processResults $
187
                                        fromJust sols''
188
                     in iterateDepth xnl il newinst nreq (xi:ixes)
185
      in case sols of
186
           OpFail _ -> orig
187
           OpGood sols' ->
188
               let
189
                   sols'' = filterFails sols'
190
               in case sols'' of
191
                    OpFail _ -> orig
192
                    OpGood sols''' ->
193
                        let (xnl, xi, _) = processResults sols'''
194
                        in iterateDepth xnl il newinst nreq (xi:ixes)
189 195

  
190 196
printStats :: String -> Cluster.CStats -> IO ()
191 197
printStats kind cs = do

Also available in: Unified diff