From aa8d2e71e8876343a4a48e72b2b65ecbbb5257db Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Fri, 16 Oct 2009 14:17:01 +0200 Subject: [PATCH] Add loading and processing of utilisation data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds loading and processing the utilisation data during instance moves. While the data is not yet used, it is correctly modified by instance changes between nodes. hbal has the new β-Uβ command line argument for this. The format of the file is simply instance name and the four stats, space-separated. --- Ganeti/HTools/ExtLoader.hs | 27 ++++++++++++++++++++++++++- Ganeti/HTools/IAlloc.hs | 2 +- Ganeti/HTools/Loader.hs | 23 +++++++++++++++-------- Ganeti/HTools/Node.hs | 38 +++++++++++++++++++++++--------------- hbal.hs | 1 + hscan.hs | 2 +- 6 files changed, 67 insertions(+), 26 deletions(-) diff --git a/Ganeti/HTools/ExtLoader.hs b/Ganeti/HTools/ExtLoader.hs index c244e1358..cc11b1c8b 100644 --- a/Ganeti/HTools/ExtLoader.hs +++ b/Ganeti/HTools/ExtLoader.hs @@ -52,6 +52,7 @@ import qualified Ganeti.HTools.Node as Node import Ganeti.HTools.Types import Ganeti.HTools.CLI +import Ganeti.HTools.Utils (sepSplit, tryRead) -- | Parse the environment and return the node\/instance names. -- @@ -66,6 +67,20 @@ parseEnv () = do wrapIO :: IO (Result a) -> IO (Result a) wrapIO = flip catch (return . Bad . show) +parseUtilisation :: String -> Result (String, DynUtil) +parseUtilisation line = + let columns = sepSplit ' ' line + in case columns of + [name, cpu, mem, dsk, net] -> do + rcpu <- tryRead name cpu + rmem <- tryRead name mem + rdsk <- tryRead name dsk + rnet <- tryRead name net + let du = DynUtil { cpuWeight = rcpu, memWeight = rmem + , dskWeight = rdsk, netWeight = rnet } + return (name, du) + _ -> Bad $ "Cannot parse line " ++ line + -- | External tool data loader from a variety of sources. loadExternalData :: Options -> IO (Node.List, Instance.List, String) @@ -89,6 +104,16 @@ loadExternalData opts = do " files options should be given.") exitWith $ ExitFailure 1 + util_contents <- (case optDynuFile opts of + Just path -> readFile path + Nothing -> return "") + let util_data = mapM parseUtilisation $ lines util_contents + util_data' <- (case util_data of + Ok x -> return x + Bad y -> do + hPutStrLn stderr ("Error: can't parse utilisation" ++ + " data: " ++ show y) + exitWith $ ExitFailure 1) input_data <- case () of _ | setRapi -> @@ -101,7 +126,7 @@ loadExternalData opts = do | setSim -> Simu.loadData $ fromJust simdata | otherwise -> wrapIO $ Text.loadData nodef instf - let ldresult = input_data >>= Loader.mergeData + let ldresult = input_data >>= Loader.mergeData util_data' (loaded_nl, il, csf) <- (case ldresult of Ok x -> return x diff --git a/Ganeti/HTools/IAlloc.hs b/Ganeti/HTools/IAlloc.hs index 5063b9ab8..af177f74b 100644 --- a/Ganeti/HTools/IAlloc.hs +++ b/Ganeti/HTools/IAlloc.hs @@ -110,7 +110,7 @@ parseData body = do let idata = fromJSObject ilist iobj <- mapM (\(x,y) -> asJSObject y >>= parseInstance ktn x) idata let (kti, il) = assignIndices iobj - (map_n, map_i, csf) <- mergeData (nl, il) + (map_n, map_i, csf) <- mergeData [] (nl, il) req_nodes <- fromObj "required_nodes" request optype <- fromObj "type" request rqtype <- diff --git a/Ganeti/HTools/Loader.hs b/Ganeti/HTools/Loader.hs index 8756f1a41..87f11f092 100644 --- a/Ganeti/HTools/Loader.hs +++ b/Ganeti/HTools/Loader.hs @@ -37,6 +37,7 @@ module Ganeti.HTools.Loader , Request(..) ) where +import Control.Monad (foldM) import Data.Function (on) import Data.List import Data.Maybe (fromJust) @@ -96,9 +97,9 @@ assocEqual = (==) `on` fst -- | For each instance, add its index to its primary and secondary nodes. fixNodes :: [(Ndx, Node.Node)] - -> (Idx, Instance.Instance) + -> Instance.Instance -> [(Ndx, Node.Node)] -fixNodes accu (_, inst) = +fixNodes accu inst = let pdx = Instance.pNode inst sdx = Instance.sNode inst @@ -130,15 +131,21 @@ stripSuffix sflen name = take (length name - sflen) name -- | Initializer function that loads the data from a node and instance -- list and massages it into the correct format. -mergeData :: (Node.AssocList, +mergeData :: [(String, DynUtil)] -- ^ Instance utilisation data + -> (Node.AssocList, Instance.AssocList) -- ^ Data from either Text.loadData -- or Rapi.loadData -> Result (Node.List, Instance.List, String) -mergeData (nl, il) = do - let - nl2 = foldl' fixNodes nl il - il3 = Container.fromAssocList il - nl3 = Container.fromAssocList +mergeData um (nl, il) = do + let il2 = Container.fromAssocList il + il3 <- foldM (\im (name, n_util) -> do + idx <- Container.findByName im name + let inst = Container.find idx im + new_i = inst { Instance.util = n_util } + return $ Container.add idx new_i im + ) il2 um + let nl2 = foldl' fixNodes nl (Container.elems il3) + let nl3 = Container.fromAssocList (map (\ (k, v) -> (k, Node.buildPeers v il3)) nl2) node_names = map Node.name $ Container.elems nl3 inst_names = map Instance.name $ Container.elems il3 diff --git a/Ganeti/HTools/Node.hs b/Ganeti/HTools/Node.hs index bc3210824..49d7b86c6 100644 --- a/Ganeti/HTools/Node.hs +++ b/Ganeti/HTools/Node.hs @@ -26,13 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -} module Ganeti.HTools.Node - ( Node(failN1, name, idx, - tMem, nMem, fMem, rMem, xMem, - tDsk, fDsk, - tCpu, uCpu, - pMem, pDsk, pRem, pCpu, - mDsk, mCpu, loDsk, hiCpu, - pList, sList, offline) + ( Node(..) , List -- * Constructor , create @@ -217,16 +211,22 @@ buildPeers t il = in t {peers=pmap, failN1 = new_failN1, rMem = new_rmem, pRem = new_prem} -- | Assigns an instance to a node as primary and update the used VCPU --- count. +-- count and utilisation data. setPri :: Node -> Instance.Instance -> Node -setPri t inst = t { pList = (Instance.idx inst):pList t +setPri t inst = t { pList = Instance.idx inst:pList t , uCpu = new_count - , pCpu = fromIntegral new_count / tCpu t } + , pCpu = fromIntegral new_count / tCpu t + , utilLoad = utilLoad t `T.addUtil` Instance.util inst + } where new_count = uCpu t + Instance.vcpus inst -- | 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 } +setSec t inst = t { sList = Instance.idx inst:sList t + , utilLoad = old_load { T.dskWeight = T.dskWeight old_load + + T.dskWeight (Instance.util inst) } + } + where old_load = utilLoad t -- * Update functions @@ -250,9 +250,10 @@ removePri t inst = new_failn1 = new_mem <= rMem t new_ucpu = 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} + uCpu = new_ucpu, pCpu = new_rcpu, utilLoad = new_load} -- | Removes a secondary instance. removeSec :: Node -> Instance.Instance -> Node @@ -273,9 +274,12 @@ removeSec t inst = new_prem = fromIntegral new_rmem / tMem t new_failn1 = fMem t <= new_rmem new_dp = fromIntegral 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} + pRem = new_prem, utilLoad = new_load} -- | Adds a primary instance. addPri :: Node -> Instance.Instance -> T.OpResult Node @@ -288,6 +292,7 @@ addPri t inst = new_pcpu = fromIntegral new_ucpu / tCpu t new_dp = fromIntegral new_dsk / tDsk t l_cpu = mCpu t + new_load = utilLoad t `T.addUtil` Instance.util inst in if new_mem <= 0 then T.OpFail T.FailMem else if new_dsk <= 0 || mDsk t > new_dp then T.OpFail T.FailDisk else if new_failn1 && not (failN1 t) then T.OpFail T.FailMem @@ -297,7 +302,7 @@ addPri t inst = new_mp = fromIntegral new_mem / tMem t r = t { pList = new_plist, fMem = new_mem, fDsk = new_dsk, failN1 = new_failn1, pMem = new_mp, pDsk = new_dp, - uCpu = new_ucpu, pCpu = new_pcpu } + uCpu = new_ucpu, pCpu = new_pcpu, utilLoad = new_load } in T.OpGood r -- | Adds a secondary instance. @@ -313,13 +318,16 @@ addSec t inst pdx = new_prem = fromIntegral new_rmem / tMem t new_failn1 = old_mem <= new_rmem new_dp = fromIntegral new_dsk / tDsk t + old_load = utilLoad t + new_load = old_load { T.dskWeight = T.dskWeight old_load + + T.dskWeight (Instance.util inst) } in if new_dsk <= 0 || mDsk t > new_dp then T.OpFail T.FailDisk else if new_failn1 && not (failN1 t) then T.OpFail T.FailMem else let new_slist = iname:sList t r = t { sList = new_slist, fDsk = new_dsk, peers = new_peers, failN1 = new_failn1, rMem = new_rmem, pDsk = new_dp, - pRem = new_prem } + pRem = new_prem, utilLoad = new_load } in T.OpGood r -- * Stats functions diff --git a/hbal.hs b/hbal.hs index f3d2e584a..aa5cfde75 100644 --- a/hbal.hs +++ b/hbal.hs @@ -72,6 +72,7 @@ options = , oMaxCpu , oMinDisk , oDiskMoves + , oDynuFile , oShowVer , oShowHelp ] diff --git a/hscan.hs b/hscan.hs index 21d27aaa0..d4b86417b 100644 --- a/hscan.hs +++ b/hscan.hs @@ -135,7 +135,7 @@ main = do printf "%-*s " nlen name hFlush stdout input_data <- Rapi.loadData name - let ldresult = input_data >>= Loader.mergeData + let ldresult = input_data >>= Loader.mergeData [] (case ldresult of Bad err -> printf "\nError: failed to load data. \ \Details:\n%s\n" err -- GitLab