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

Reply via email to