hspace: Make use of the spindle_use
[ganeti-local] / htools / Ganeti / HTools / Node.hs
index f3a7491..f744e58 100644 (file)
@@ -6,7 +6,7 @@
 
 {-
 
-Copyright (C) 2009, 2010, 2011 Google Inc.
+Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@ module Ganeti.HTools.Node
   , setSec
   , setMdsk
   , setMcpu
+  , setPolicy
   -- * Tag maps
   , addTags
   , delTags
@@ -100,6 +101,7 @@ data Node = Node
   , fDsk     :: Int       -- ^ Free disk space (MiB)
   , tCpu     :: Double    -- ^ Total CPU count
   , uCpu     :: Int       -- ^ Used VCPU count
+  , spindleCount :: Int   -- ^ Node spindles (spindle_count node parameter)
   , pList    :: [T.Idx]   -- ^ List of primary instance indices
   , sList    :: [T.Idx]   -- ^ List of secondary instance indices
   , idx      :: T.Ndx     -- ^ Internal index for book-keeping
@@ -112,11 +114,13 @@ data Node = Node
   , pRem     :: Double    -- ^ Percent of reserved memory
   , pCpu     :: Double    -- ^ Ratio of virtual to physical CPUs
   , mDsk     :: Double    -- ^ Minimum free disk ratio
-  , mCpu     :: Double    -- ^ Max ratio of virt-to-phys CPUs
   , loDsk    :: Int       -- ^ Autocomputed from mDsk low disk
                           -- threshold
   , hiCpu    :: Int       -- ^ Autocomputed from mCpu high cpu
                           -- threshold
+  , hiSpindles :: Double  -- ^ Auto-computed from policy spindle_ratio
+                          -- and the node spindle count
+  , instSpindles :: Double -- ^ Spindles used by instances
   , offline  :: Bool      -- ^ Whether the node should not be used for
                           -- allocations and skipped from score
                           -- computations
@@ -124,6 +128,7 @@ data Node = Node
   , utilLoad :: T.DynUtil -- ^ Sum of instance utilisation
   , pTags    :: TagMap    -- ^ Map of primary instance tags and their count
   , group    :: T.Gdx     -- ^ The node's group (index)
+  , iPolicy  :: T.IPolicy -- ^ The instance policy (of the node's group)
   } deriving (Show, Read, Eq)
 
 instance T.Element Node where
@@ -180,6 +185,18 @@ rejectAddTags t = any (`Map.member` t)
 conflictingPrimaries :: Node -> Int
 conflictingPrimaries (Node { pTags = t }) = Foldable.sum t - Map.size t
 
+-- | Helper function to increment a base value depending on the passed
+-- boolean argument.
+incIf :: (Num a) => Bool -> a -> a -> a
+incIf True  base delta = base + delta
+incIf False base _     = base
+
+-- | Helper function to decrement a base value depending on the passed
+-- boolean argument.
+decIf :: (Num a) => Bool -> a -> a -> a
+decIf True  base delta = base - delta
+decIf False base _     = base
+
 -- * Initialization functions
 
 -- | Create a new node.
@@ -187,9 +204,10 @@ conflictingPrimaries (Node { pTags = t }) = Foldable.sum t - Map.size t
 -- The index and the peers maps are empty, and will be need to be
 -- update later via the 'setIdx' and 'buildPeers' functions.
 create :: String -> Double -> Int -> Int -> Double
-       -> Int -> Double -> Bool -> T.Gdx -> Node
+       -> Int -> Double -> Bool -> Int -> T.Gdx -> Node
 create name_init mem_t_init mem_n_init mem_f_init
-       dsk_t_init dsk_f_init cpu_t_init offline_init group_init =
+       dsk_t_init dsk_f_init cpu_t_init offline_init spindles_init
+       group_init =
   Node { name = name_init
        , alias = name_init
        , tMem = mem_t_init
@@ -198,6 +216,7 @@ create name_init mem_t_init mem_n_init mem_f_init
        , tDsk = dsk_t_init
        , fDsk = dsk_f_init
        , tCpu = cpu_t_init
+       , spindleCount = spindles_init
        , uCpu = 0
        , pList = []
        , sList = []
@@ -206,19 +225,22 @@ create name_init mem_t_init mem_n_init mem_f_init
        , peers = P.empty
        , rMem = 0
        , pMem = fromIntegral mem_f_init / mem_t_init
-       , pDsk = fromIntegral dsk_f_init / dsk_t_init
+       , pDsk = computePDsk dsk_f_init dsk_t_init
        , pRem = 0
        , pCpu = 0
        , offline = offline_init
        , xMem = 0
        , mDsk = T.defReservedDiskRatio
-       , mCpu = T.defVcpuRatio
        , loDsk = mDskToloDsk T.defReservedDiskRatio dsk_t_init
-       , hiCpu = mCpuTohiCpu T.defVcpuRatio cpu_t_init
+       , hiCpu = mCpuTohiCpu (T.iPolicyVcpuRatio T.defIPolicy) cpu_t_init
+       , hiSpindles = computeHiSpindles (T.iPolicySpindleRatio T.defIPolicy)
+                      spindles_init
+       , instSpindles = 0
        , utilPool = T.baseUtil
        , utilLoad = T.zeroUtil
        , pTags = Map.empty
        , group = group_init
+       , iPolicy = T.defIPolicy
        }
 
 -- | Conversion formula from mDsk\/tDsk to loDsk.
@@ -229,6 +251,10 @@ mDskToloDsk mval = floor . (mval *)
 mCpuTohiCpu :: Double -> Double -> Int
 mCpuTohiCpu mval = floor . (mval *)
 
+-- | Conversiojn formula from spindles and spindle ratio to hiSpindles.
+computeHiSpindles :: Double -> Int -> Double
+computeHiSpindles spindle_ratio = (spindle_ratio *) . fromIntegral
+
 -- | Changes the index.
 --
 -- This is used only during the building of the data structures.
@@ -253,9 +279,21 @@ setXmem t val = t { xMem = val }
 setMdsk :: Node -> Double -> Node
 setMdsk t val = t { mDsk = val, loDsk = mDskToloDsk val (tDsk t) }
 
--- | Sets the max cpu usage ratio.
+-- | Sets the max cpu usage ratio. This will update the node's
+-- ipolicy, losing sharing (but it should be a seldomly done operation).
 setMcpu :: Node -> Double -> Node
-setMcpu t val = t { mCpu = val, hiCpu = mCpuTohiCpu val (tCpu t) }
+setMcpu t val =
+  let new_ipol = (iPolicy t) { T.iPolicyVcpuRatio = val }
+  in t { hiCpu = mCpuTohiCpu val (tCpu t), iPolicy = new_ipol }
+
+-- | Sets the policy.
+setPolicy :: T.IPolicy -> Node -> Node
+setPolicy pol node =
+  node { iPolicy = pol
+       , hiCpu = mCpuTohiCpu (T.iPolicyVcpuRatio pol) (tCpu node)
+       , hiSpindles = computeHiSpindles (T.iPolicySpindleRatio pol)
+                      (spindleCount node)
+       }
 
 -- | Computes the maximum reserved memory for peers from a peer map.
 computeMaxRes :: P.PeerMap -> P.Elem
@@ -266,7 +304,7 @@ buildPeers :: Node -> Instance.List -> Node
 buildPeers t il =
   let mdata = map
               (\i_idx -> let inst = Container.find i_idx il
-                             mem = if Instance.autoBalance inst
+                             mem = if Instance.usesSecMem inst
                                      then Instance.mem inst
                                      else 0
                          in (Instance.pNode inst, mem))
@@ -277,6 +315,11 @@ buildPeers t il =
       new_prem = fromIntegral new_rmem / tMem t
   in t {peers=pmap, failN1 = new_failN1, rMem = new_rmem, pRem = new_prem}
 
+-- | Calculate the new spindle usage
+calcSpindleUse :: Node -> Instance.Instance -> Double
+calcSpindleUse n i = incIf (Instance.usesLocalStorage i) (instSpindles n)
+                       (fromIntegral $ Instance.spindleUse i)
+
 -- | Assigns an instance to a node as primary and update the used VCPU
 -- count, utilisation data and tags map.
 setPri :: Node -> Instance.Instance -> Node
@@ -285,17 +328,26 @@ setPri t inst = t { pList = Instance.idx inst:pList t
                   , pCpu = fromIntegral new_count / tCpu t
                   , utilLoad = utilLoad t `T.addUtil` Instance.util inst
                   , pTags = addTags (pTags t) (Instance.tags inst)
+                  , instSpindles = calcSpindleUse t inst
                   }
-  where new_count = uCpu t + Instance.vcpus inst
+  where new_count = Instance.applyIfOnline inst (+ Instance.vcpus inst)
+                    (uCpu t )
 
 -- | Assigns an instance to a node as secondary without other updates.
 setSec :: Node -> Instance.Instance -> Node
 setSec t inst = t { sList = Instance.idx inst:sList t
                   , utilLoad = old_load { T.dskWeight = T.dskWeight old_load +
                                           T.dskWeight (Instance.util inst) }
+                  , instSpindles = calcSpindleUse t inst
                   }
   where old_load = utilLoad t
 
+-- | Computes the new 'pDsk' value, handling nodes without local disk
+-- storage (we consider all their disk used).
+computePDsk :: Int -> Double -> Double
+computePDsk _    0     = 1
+computePDsk used total = fromIntegral used / total
+
 -- * Update functions
 
 -- | Sets the free memory.
@@ -309,36 +361,38 @@ setFmem t new_mem =
 removePri :: Node -> Instance.Instance -> Node
 removePri t inst =
   let iname = Instance.idx inst
+      i_online = Instance.notOffline inst
+      uses_disk = Instance.usesLocalStorage inst
       new_plist = delete iname (pList t)
-      new_mem = Instance.applyIfOnline inst (+ Instance.mem inst) (fMem t)
-      new_dsk = fDsk t + Instance.dsk inst
+      new_mem = incIf i_online (fMem t) (Instance.mem inst)
+      new_dsk = incIf uses_disk (fDsk t) (Instance.dsk inst)
+      new_spindles = decIf uses_disk (instSpindles t) 1
       new_mp = fromIntegral new_mem / tMem t
-      new_dp = fromIntegral new_dsk / tDsk t
+      new_dp = computePDsk new_dsk (tDsk t)
       new_failn1 = new_mem <= rMem t
-      new_ucpu = Instance.applyIfOnline inst
-                 (\x -> x - Instance.vcpus inst) (uCpu t)
+      new_ucpu = decIf i_online (uCpu t) (Instance.vcpus inst)
       new_rcpu = fromIntegral new_ucpu / tCpu t
       new_load = utilLoad t `T.subUtil` Instance.util inst
   in t { pList = new_plist, fMem = new_mem, fDsk = new_dsk
        , failN1 = new_failn1, pMem = new_mp, pDsk = new_dp
        , uCpu = new_ucpu, pCpu = new_rcpu, utilLoad = new_load
-       , pTags = delTags (pTags t) (Instance.tags inst) }
+       , pTags = delTags (pTags t) (Instance.tags inst)
+       , instSpindles = new_spindles
+       }
 
 -- | Removes a secondary instance.
 removeSec :: Node -> Instance.Instance -> Node
 removeSec t inst =
   let iname = Instance.idx inst
+      uses_disk = Instance.usesLocalStorage inst
       cur_dsk = fDsk t
       pnode = Instance.pNode inst
       new_slist = delete iname (sList t)
-      new_dsk = if Instance.usesLocalStorage inst
-                  then cur_dsk + Instance.dsk inst
-                  else cur_dsk
+      new_dsk = incIf uses_disk cur_dsk (Instance.dsk inst)
+      new_spindles = decIf uses_disk (instSpindles t) 1
       old_peers = peers t
       old_peem = P.find pnode old_peers
-      new_peem =  if Instance.autoBalance inst
-                    then old_peem - Instance.mem inst
-                    else old_peem
+      new_peem = decIf (Instance.usesSecMem inst) old_peem (Instance.mem inst)
       new_peers = if new_peem > 0
                     then P.add pnode new_peem old_peers
                     else P.remove pnode old_peers
@@ -348,13 +402,15 @@ removeSec t inst =
                    else computeMaxRes new_peers
       new_prem = fromIntegral new_rmem / tMem t
       new_failn1 = fMem t <= new_rmem
-      new_dp = fromIntegral new_dsk / tDsk t
+      new_dp = computePDsk new_dsk (tDsk t)
       old_load = utilLoad t
       new_load = old_load { T.dskWeight = T.dskWeight old_load -
                                           T.dskWeight (Instance.util inst) }
   in t { sList = new_slist, fDsk = new_dsk, peers = new_peers
        , failN1 = new_failn1, rMem = new_rmem, pDsk = new_dp
-       , pRem = new_prem, utilLoad = new_load }
+       , pRem = new_prem, utilLoad = new_load
+       , instSpindles = new_spindles
+       }
 
 -- | Adds a primary instance (basic version).
 addPri :: Node -> Instance.Instance -> T.OpResult Node
@@ -372,18 +428,17 @@ addPriEx :: Bool               -- ^ Whether to override the N+1 and
                                -- or a failure mode
 addPriEx force t inst =
   let iname = Instance.idx inst
+      i_online = Instance.notOffline inst
       uses_disk = Instance.usesLocalStorage inst
       cur_dsk = fDsk t
-      new_mem = Instance.applyIfOnline inst
-                (\x -> x - Instance.mem inst) (fMem t)
-      new_dsk = if uses_disk
-                  then cur_dsk - Instance.dsk inst
-                  else cur_dsk
+      new_mem = decIf i_online (fMem t) (Instance.mem inst)
+      new_dsk = decIf uses_disk cur_dsk (Instance.dsk inst)
+      new_spindles = incIf uses_disk (instSpindles t) 1
       new_failn1 = new_mem <= rMem t
-      new_ucpu = Instance.applyIfOnline inst (+ Instance.vcpus inst) (uCpu t)
+      new_ucpu = incIf i_online (uCpu t) (Instance.vcpus inst)
       new_pcpu = fromIntegral new_ucpu / tCpu t
-      new_dp = fromIntegral new_dsk / tDsk t
-      l_cpu = mCpu t
+      new_dp = computePDsk new_dsk (tDsk t)
+      l_cpu = T.iPolicyVcpuRatio $ iPolicy t
       new_load = utilLoad t `T.addUtil` Instance.util inst
       inst_tags = Instance.tags inst
       old_tags = pTags t
@@ -392,6 +447,8 @@ addPriEx force t inst =
        _ | new_mem <= 0 -> T.OpFail T.FailMem
          | uses_disk && new_dsk <= 0 -> T.OpFail T.FailDisk
          | uses_disk && mDsk t > new_dp && strict -> T.OpFail T.FailDisk
+         | uses_disk && new_spindles > hiSpindles t
+             && strict -> T.OpFail T.FailDisk
          | new_failn1 && not (failN1 t) && strict -> T.OpFail T.FailMem
          | l_cpu >= 0 && l_cpu < new_pcpu && strict -> T.OpFail T.FailCPU
          | rejectAddTags old_tags inst_tags -> T.OpFail T.FailTags
@@ -402,7 +459,9 @@ addPriEx force t inst =
                      , failN1 = new_failn1, pMem = new_mp, pDsk = new_dp
                      , uCpu = new_ucpu, pCpu = new_pcpu
                      , utilLoad = new_load
-                     , pTags = addTags old_tags inst_tags }
+                     , pTags = addTags old_tags inst_tags
+                     , instSpindles = new_spindles
+                     }
            in T.OpGood r
 
 -- | Adds a secondary instance (basic version).
@@ -416,8 +475,8 @@ addSecEx force t inst pdx =
       old_peers = peers t
       old_mem = fMem t
       new_dsk = fDsk t - Instance.dsk inst
-      secondary_needed_mem = if Instance.autoBalance inst &&
-                             not (Instance.instanceOffline inst)
+      new_spindles = instSpindles t + 1
+      secondary_needed_mem = if Instance.usesSecMem inst
                                then Instance.mem inst
                                else 0
       new_peem = P.find pdx old_peers + secondary_needed_mem
@@ -425,7 +484,7 @@ addSecEx force t inst pdx =
       new_rmem = max (rMem t) new_peem
       new_prem = fromIntegral new_rmem / tMem t
       new_failn1 = old_mem <= new_rmem
-      new_dp = fromIntegral new_dsk / tDsk t
+      new_dp = computePDsk new_dsk (tDsk t)
       old_load = utilLoad t
       new_load = old_load { T.dskWeight = T.dskWeight old_load +
                                           T.dskWeight (Instance.util inst) }
@@ -434,6 +493,7 @@ addSecEx force t inst pdx =
        _ | not (Instance.hasSecondary inst) -> T.OpFail T.FailDisk
          | new_dsk <= 0 -> T.OpFail T.FailDisk
          | mDsk t > new_dp && strict -> T.OpFail T.FailDisk
+         | new_spindles > hiSpindles t && strict -> T.OpFail T.FailDisk
          | secondary_needed_mem >= old_mem && strict -> T.OpFail T.FailMem
          | new_failn1 && not (failN1 t) && strict -> T.OpFail T.FailMem
          | otherwise ->
@@ -441,7 +501,9 @@ addSecEx force t inst pdx =
                r = t { sList = new_slist, fDsk = new_dsk
                      , peers = new_peers, failN1 = new_failn1
                      , rMem = new_rmem, pDsk = new_dp
-                     , pRem = new_prem, utilLoad = new_load }
+                     , pRem = new_prem, utilLoad = new_load
+                     , instSpindles = new_spindles
+                     }
            in T.OpGood r
 
 -- * Stats functions
@@ -521,6 +583,9 @@ showField t field =
     "ptags" -> intercalate "," . map (uncurry (printf "%s=%d")) .
                Map.toList $ pTags t
     "peermap" -> show $ peers t
+    "spindle_count" -> show $ spindleCount t
+    "hi_spindles" -> show $ hiSpindles t
+    "inst_spindles" -> show $ instSpindles t
     _ -> T.unknownField
   where
     T.DynUtil { T.cpuWeight = uC, T.memWeight = uM,
@@ -558,6 +623,9 @@ showHeader field =
     "nload" -> ("lNet", True)
     "ptags" -> ("PrimaryTags", False)
     "peermap" -> ("PeerMap", False)
+    "spindle_count" -> ("NodeSpindles", True)
+    "hi_spindles" -> ("MaxSpindles", True)
+    "inst_spindles" -> ("InstSpindles", True)
     -- TODO: add node fields (group.uuid, group)
     _ -> (T.unknownField, False)