Instance pinning is introduced in ganeti locations design document. It adds new instance tag of form htools:desiredlocation:x where x is a location tag of a desired primary node. This implemented by adding second component to the instance locationScore. The metric is extended with the following component:
* Sum of dissatisfied desired locations number among all cluster instances. An instance desired location is dissatisfied when the instance is assigned a desired-location tag x where the node is not tagged with the location tag x. Signed-off-by: Oleg Ponomarev <onponoma...@gmail.com> --- src/Ganeti/HTools/Instance.hs | 3 +++ src/Ganeti/HTools/Loader.hs | 16 ++++++++++++++++ src/Ganeti/HTools/Node.hs | 19 +++++++++++++++++-- src/Ganeti/HTools/Tags/Constants.hs | 7 ++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Ganeti/HTools/Instance.hs b/src/Ganeti/HTools/Instance.hs index 6cf062e..63b3024 100644 --- a/src/Ganeti/HTools/Instance.hs +++ b/src/Ganeti/HTools/Instance.hs @@ -70,6 +70,7 @@ module Ganeti.HTools.Instance ) where import Control.Monad (liftM2) +import qualified Data.Set as Set import Ganeti.BasicTypes import qualified Ganeti.HTools.Types as T @@ -103,6 +104,7 @@ data Instance = Instance , spindleUse :: Int -- ^ The numbers of used spindles , allTags :: [String] -- ^ List of all instance tags , exclTags :: [String] -- ^ List of instance exclusion tags + , dsrdLocTags :: Set.Set String -- ^ Instance desired location tags , locationScore :: Int -- ^ The number of common-failures between -- primary and secondary node of the instance , arPolicy :: T.AutoRepairPolicy -- ^ Instance's auto-repair policy @@ -209,6 +211,7 @@ create name_init mem_init dsk_init disks_init vcpus_init run_init tags_init , spindleUse = su , allTags = tags_init , exclTags = [] + , dsrdLocTags = Set.empty , locationScore = 0 , arPolicy = T.ArNotEnabled , nics = nics_init diff --git a/src/Ganeti/HTools/Loader.hs b/src/Ganeti/HTools/Loader.hs index 35f3e9f..21848c3 100644 --- a/src/Ganeti/HTools/Loader.hs +++ b/src/Ganeti/HTools/Loader.hs @@ -59,6 +59,7 @@ import Control.Monad import Data.List import qualified Data.Map as M import Data.Maybe +import qualified Data.Set as Set import Text.Printf (printf) import System.Time (ClockTime(..)) @@ -208,6 +209,14 @@ updateExclTags tl inst = exclTags = filter (\tag -> any (`isPrefixOf` tag) tl) allTags in inst { Instance.exclTags = exclTags } +-- | Update instance with desired location tags list. +updateDesiredLocationTags :: [String] -> Instance.Instance -> Instance.Instance +updateDesiredLocationTags tl inst = + let allTags = Instance.allTags inst + dsrdLocTags = filter (\tag -> any (`isPrefixOf` tag) tl) allTags + in inst { Instance.dsrdLocTags = Set.fromList dsrdLocTags } + + -- | Update the movable attribute. updateMovable :: [String] -- ^ Selected instances (if not empty) -> [String] -- ^ Excluded instances @@ -281,6 +290,11 @@ longestDomain (x:xs) = extractExTags :: [String] -> [String] extractExTags = filter (not . null) . mapMaybe (chompPrefix TagsC.exTagsPrefix) +-- | Extracts the desired locations from the instance tags. +extractDesiredLocations :: [String] -> [String] +extractDesiredLocations = + filter (not . null) . mapMaybe (chompPrefix TagsC.desiredLocationPrefix) + -- | Extracts the common suffix from node\/instance names. commonSuffix :: Node.List -> Instance.List -> String commonSuffix nl il = @@ -325,6 +339,7 @@ mergeData um extags selinsts exinsts time cdata@(ClusterData gl nl il ctags _) = in Container.add (Instance.idx inst) new_i im ) il2 um allextags = extags ++ extractExTags ctags + dsrdLocTags = extractDesiredLocations ctags inst_names = map Instance.name $ Container.elems il3 selinst_lkp = map (lookupName inst_names) selinsts exinst_lkp = map (lookupName inst_names) exinsts @@ -335,6 +350,7 @@ mergeData um extags selinsts exinsts time cdata@(ClusterData gl nl il ctags _) = common_suffix = longestDomain (node_names ++ inst_names) il4 = Container.map (computeAlias common_suffix . updateExclTags allextags . + updateDesiredLocationTags dsrdLocTags . updateMovable selinst_names exinst_names) il3 nl2 = Container.map (addLocationTags ctags) nl il5 = Container.map (setLocationScore nl2) il4 diff --git a/src/Ganeti/HTools/Node.hs b/src/Ganeti/HTools/Node.hs index fd3b33a..1a2f261 100644 --- a/src/Ganeti/HTools/Node.hs +++ b/src/Ganeti/HTools/Node.hs @@ -208,7 +208,8 @@ data Node = Node , rmigTags :: Set.Set String -- ^ migration tags able to receive , locationTags :: Set.Set String -- ^ common-failure domains the node belongs -- to - , locationScore :: Int + , locationScore :: Int -- ^ Sum of instance location and desired location + -- scores } deriving (Show, Eq) {- A note on how we handle spindles @@ -534,8 +535,19 @@ calcFmemOfflineOrForthcoming node allInstances = . filter (not . Instance.usesMemory) $ nodeInstances +-- | Calculates the desired location score of an instance, given its primary +-- node. +getInstanceDsrdLocScore :: Node -- ^ the primary node of the instance + -> Instance.Instance -- ^ the original instance + -> Int -- ^ the desired location score of the instance +getInstanceDsrdLocScore p t = + desiredLocationScore (Instance.dsrdLocTags t) (locationTags p) + where desiredLocationScore instTags nodeTags = + Set.size instTags - Set.size ( instTags `Set.intersection` nodeTags ) + -- this way we get the number of unsatisfied desired locations + -- | Assigns an instance to a node as primary and update the used VCPU --- count, utilisation data and tags map. +-- count, utilisation data, tags map and desired location score. setPri :: Node -> Instance.Instance -> Node setPri t inst -- Real instance, update real fields and forthcoming fields. @@ -547,6 +559,7 @@ setPri t inst , utilLoad = utilLoad t `T.addUtil` Instance.util inst , instSpindles = calcSpindleUse True t inst , locationScore = locationScore t + Instance.locationScore inst + + getInstanceDsrdLocScore t inst } -- Forthcoming instance, update forthcoming fields only. @@ -744,6 +757,7 @@ removePri t inst = , instSpindles = new_inst_sp, fSpindles = new_free_sp , locationScore = locationScore t - Instance.locationScore inst + - getInstanceDsrdLocScore t inst } -- | Removes a secondary instance. @@ -924,6 +938,7 @@ addPriEx force t inst = , fSpindles = new_free_sp , locationScore = locationScore t + Instance.locationScore inst + + getInstanceDsrdLocScore t inst } -- | Adds a secondary instance (basic version). diff --git a/src/Ganeti/HTools/Tags/Constants.hs b/src/Ganeti/HTools/Tags/Constants.hs index 47bf8db..3b741ac 100644 --- a/src/Ganeti/HTools/Tags/Constants.hs +++ b/src/Ganeti/HTools/Tags/Constants.hs @@ -43,6 +43,7 @@ module Ganeti.HTools.Tags.Constants , migrationPrefix , allowMigrationPrefix , locationPrefix + , desiredLocationPrefix , standbyAuto , autoRepairTagPrefix , autoRepairTagEnabled @@ -70,10 +71,14 @@ migrationPrefix = "htools:migration:" allowMigrationPrefix :: String allowMigrationPrefix = "htools:allowmigration:" --- | The prefix for location tags. +-- | The prefix for node location tags. locationPrefix :: String locationPrefix = "htools:nlocation:" +-- | The prefix for instance desired location tags. +desiredLocationPrefix :: String +desiredLocationPrefix = "htools:desiredlocation:" + -- | The tag to be added to nodes that were shutdown by hsqueeze. standbyAuto :: String standbyAuto = "htools:standby:auto" -- 1.9.1