Revision 129734d3

b/htools/Ganeti/HTools/Cluster.hs
75 75

  
76 76
import qualified Data.IntSet as IntSet
77 77
import Data.List
78
import Data.Maybe (fromJust)
78
import Data.Maybe (fromJust, isNothing)
79 79
import Data.Ord (comparing)
80 80
import Text.Printf (printf)
81 81
import Control.Monad
......
93 93

  
94 94
-- | Allocation\/relocation solution.
95 95
data AllocSolution = AllocSolution
96
  { asFailures  :: [FailMode]          -- ^ Failure counts
97
  , asAllocs    :: Int                 -- ^ Good allocation count
98
  , asSolutions :: [Node.AllocElement] -- ^ The actual result, length
99
                                       -- of the list depends on the
100
                                       -- allocation/relocation mode
101
  , asLog       :: [String]            -- ^ A list of informational messages
96
  { asFailures :: [FailMode]              -- ^ Failure counts
97
  , asAllocs   :: Int                     -- ^ Good allocation count
98
  , asSolution :: Maybe Node.AllocElement -- ^ The actual allocation result
99
  , asLog      :: [String]                -- ^ Informational messages
102 100
  }
103 101

  
104 102
-- | Node evacuation/group change iallocator result type. This result
......
125 123
-- | The empty solution we start with when computing allocations.
126 124
emptyAllocSolution :: AllocSolution
127 125
emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0
128
                                   , asSolutions = [], asLog = [] }
126
                                   , asSolution = Nothing, asLog = [] }
129 127

  
130 128
-- | The empty evac solution.
131 129
emptyEvacSolution :: EvacSolution
......
610 608
concatAllocs as (OpGood ns@(_, _, _, nscore)) =
611 609
    let -- Choose the old or new solution, based on the cluster score
612 610
        cntok = asAllocs as
613
        osols = asSolutions as
611
        osols = asSolution as
614 612
        nsols = case osols of
615
                  [] -> [ns]
616
                  (_, _, _, oscore):[] ->
613
                  Nothing -> Just ns
614
                  Just (_, _, _, oscore) ->
617 615
                      if oscore < nscore
618 616
                      then osols
619
                      else [ns]
620
                  -- FIXME: here we simply concat to lists with more
621
                  -- than one element; we should instead abort, since
622
                  -- this is not a valid usage of this function
623
                  xs -> ns:xs
617
                      else Just ns
624 618
        nsuc = cntok + 1
625 619
    -- Note: we force evaluation of nsols here in order to keep the
626 620
    -- memory profile low - we know that we will need nsols for sure
627 621
    -- in the next cycle, so we force evaluation of nsols, since the
628 622
    -- foldl' in the caller will only evaluate the tuple, but not the
629 623
    -- elements of the tuple
630
    in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolutions = nsols }
624
    in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols }
631 625

  
632 626
-- | Given a solution, generates a reasonable description for it.
633 627
describeSolution :: AllocSolution -> String
634 628
describeSolution as =
635 629
  let fcnt = asFailures as
636
      sols = asSolutions as
630
      sols = asSolution as
637 631
      freasons =
638 632
        intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) .
639 633
        filter ((> 0) . snd) . collapseFailures $ fcnt
640
  in if null sols
641
     then "No valid allocation solutions, failure reasons: " ++
642
          (if null fcnt
643
           then "unknown reasons"
644
           else freasons)
645
     else let (_, _, nodes, cv) = head sols
646
          in printf ("score: %.8f, successes %d, failures %d (%s)" ++
647
                     " for node(s) %s") cv (asAllocs as) (length fcnt) freasons
648
             (intercalate "/" . map Node.name $ nodes)
634
  in case sols of
635
     Nothing -> "No valid allocation solutions, failure reasons: " ++
636
                (if null fcnt then "unknown reasons" else freasons)
637
     Just (_, _, nodes, cv) ->
638
         printf ("score: %.8f, successes %d, failures %d (%s)" ++
639
                 " for node(s) %s") cv (asAllocs as) (length fcnt) freasons
640
               (intercalate "/" . map Node.name $ nodes)
649 641

  
650 642
-- | Annotates a solution with the appropriate string.
651 643
annotateSolution :: AllocSolution -> AllocSolution
......
725 717
          fn accu (gdx, rasol) =
726 718
              case rasol of
727 719
                Bad _ -> accu
728
                Ok sol | null (asSolutions sol) -> accu
720
                Ok sol | isNothing (asSolution sol) -> accu
729 721
                       | unallocable gdx -> accu
730 722
                       | otherwise -> (gdx, sol):accu
731 723

  
......
736 728
sortMGResults gl sols =
737 729
    let extractScore (_, _, _, x) = x
738 730
        solScore (gdx, sol) = (Group.allocPolicy (Container.find gdx gl),
739
                               (extractScore . head . asSolutions) sol)
731
                               (extractScore . fromJust . asSolution) sol)
740 732
    in sortBy (comparing solScore) sols
741 733

  
742 734
-- | Finds the best group for an instance on a multi-group cluster.
......
1150 1142
          newlimit = fmap (flip (-) 1) limit
1151 1143
      in case tryAlloc nl il newi2 allocnodes of
1152 1144
           Bad s -> Bad s
1153
           Ok (AllocSolution { asFailures = errs, asSolutions = sols3 }) ->
1145
           Ok (AllocSolution { asFailures = errs, asSolution = sols3 }) ->
1154 1146
               let newsol = Ok (collapseFailures errs, nl, il, ixes, cstats) in
1155 1147
               case sols3 of
1156
                 [] -> newsol
1157
                 (xnl, xi, _, _):[] ->
1148
                 Nothing -> newsol
1149
                 Just (xnl, xi, _, _) ->
1158 1150
                     if limit == Just 0
1159 1151
                     then newsol
1160 1152
                     else iterateAlloc xnl (Container.add newidx xi il)
1161 1153
                          newlimit newinst allocnodes (xi:ixes)
1162 1154
                          (totalResources xnl:cstats)
1163
                 _ -> Bad "Internal error: multiple solutions for single\
1164
                          \ allocation"
1165 1155

  
1166 1156
-- | The core of the tiered allocation mode.
1167 1157
tieredAlloc :: Node.List
b/htools/Ganeti/HTools/IAlloc.hs
220 220
formatAllocate :: Instance.List -> Cluster.AllocSolution -> Result IAllocResult
221 221
formatAllocate il as = do
222 222
  let info = describeSolution as
223
  case Cluster.asSolutions as of
224
    [] -> fail info
225
    (nl, inst, nodes, _):[] ->
223
  case Cluster.asSolution as of
224
    Nothing -> fail info
225
    Just (nl, inst, nodes, _) ->
226 226
        do
227 227
          let il' = Container.add (Instance.idx inst) inst il
228 228
          return (info, showJSON $ map Node.name nodes, nl, il')
229
    _ -> fail "Internal error: multiple allocation solutions"
230 229

  
231 230
-- | Convert a node-evacuation/change group result.
232 231
formatNodeEvac :: Group.List
b/htools/Ganeti/HTools/QC.hs
860 860
       Cluster.tryAlloc nl il inst' of
861 861
         Types.Bad _ -> False
862 862
         Types.Ok as ->
863
             case Cluster.asSolutions as of
864
               [] -> False
865
               (xnl, xi, _, cv):[] ->
863
             case Cluster.asSolution as of
864
               Nothing -> False
865
               Just (xnl, xi, _, cv) ->
866 866
                   let il' = Container.add (Instance.idx xi) xi il
867 867
                       tbl = Cluster.Table xnl il' cv []
868 868
                   in not (canBalance tbl True True False)
869
               _ -> False
870 869

  
871 870
-- | Checks that on a 2-5 node cluster, we can allocate a random
872 871
-- instance spec via tiered allocation (whatever the original instance
......
903 902
       Cluster.tryAlloc nl il inst' of
904 903
         Types.Bad _ -> False
905 904
         Types.Ok as ->
906
             case Cluster.asSolutions as of
907
               [] -> False
908
               (xnl, xi, _, _):[] ->
905
             case Cluster.asSolution as of
906
               Nothing -> False
907
               Just (xnl, xi, _, _) ->
909 908
                   let sdx = Instance.sNode xi
910 909
                       il' = Container.add (Instance.idx xi) xi il
911 910
                   in case IAlloc.processRelocate defGroupList xnl il'
912 911
                          (Instance.idx xi) 1 [sdx] of
913 912
                        Types.Ok _ -> True
914 913
                        _ -> False
915
               _ -> False
916 914

  
917 915
-- | Check that allocating multiple instances on a cluster, then
918 916
-- adding an empty node, results in a valid rebalance.

Also available in: Unified diff