module Ganeti.HTools.Loader
( mergeData
, checkData
, assignIndices
, lookupNode
, lookupInstance
, lookupGroup
, commonSuffix
, RqType(..)
, Request(..)
, ClusterData(..)
, emptyCluster
) where
import Data.List
import qualified Data.Map as M
import Text.Printf (printf)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Group as Group
import qualified Ganeti.HTools.Cluster as Cluster
import Ganeti.BasicTypes
import Ganeti.HTools.Types
import Ganeti.HTools.Utils
exTagsPrefix :: String
exTagsPrefix = "htools:iextags:"
data RqType
= Allocate Instance.Instance Int
| Relocate Idx Int [Ndx]
| NodeEvacuate [Idx] EvacMode
| ChangeGroup [Gdx] [Idx]
deriving (Show, Read)
data Request = Request RqType ClusterData
deriving (Show, Read)
data ClusterData = ClusterData
{ cdGroups :: Group.List
, cdNodes :: Node.List
, cdInstances :: Instance.List
, cdTags :: [String]
, cdIPolicy :: IPolicy
} deriving (Show, Read, Eq)
emptyCluster :: ClusterData
emptyCluster = ClusterData Container.empty Container.empty Container.empty []
defIPolicy
lookupNode :: (Monad m) => NameAssoc -> String -> String -> m Ndx
lookupNode ktn inst node =
case M.lookup node ktn of
Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
Just idx -> return idx
lookupInstance :: (Monad m) => NameAssoc -> String -> m Idx
lookupInstance kti inst =
case M.lookup inst kti of
Nothing -> fail $ "Unknown instance '" ++ inst ++ "'"
Just idx -> return idx
lookupGroup :: (Monad m) => NameAssoc -> String -> String -> m Gdx
lookupGroup ktg nname gname =
case M.lookup gname ktg of
Nothing -> fail $ "Unknown group '" ++ gname ++ "' for node " ++ nname
Just idx -> return idx
assignIndices :: (Element a) =>
[(String, a)]
-> (NameAssoc, Container.Container a)
assignIndices nodes =
let (na, idx_node) =
unzip . map (\ (idx, (k, v)) -> ((k, idx), (idx, setIdx v idx)))
. zip [0..] $ nodes
in (M.fromList na, Container.fromList idx_node)
fixNodes :: Node.List
-> Instance.Instance
-> Node.List
fixNodes accu inst =
let pdx = Instance.pNode inst
sdx = Instance.sNode inst
pold = Container.find pdx accu
pnew = Node.setPri pold inst
ac2 = Container.add pdx pnew accu
in if sdx /= Node.noSecondary
then let sold = Container.find sdx accu
snew = Node.setSec sold inst
in Container.add sdx snew ac2
else ac2
setNodePolicy :: Group.List -> Node.Node -> Node.Node
setNodePolicy gl node =
let grp = Container.find (Node.group node) gl
gpol = Group.iPolicy grp
in Node.setPolicy gpol node
filterExTags :: [String] -> Instance.Instance -> Instance.Instance
filterExTags tl inst =
let old_tags = Instance.tags inst
new_tags = filter (\tag -> any (`isPrefixOf` tag) tl) old_tags
in inst { Instance.tags = new_tags }
updateMovable :: [String]
-> [String]
-> Instance.Instance
-> Instance.Instance
updateMovable selinsts exinsts inst =
if Instance.name inst `elem` exinsts ||
not (null selinsts || Instance.name inst `elem` selinsts)
then Instance.setMovable inst False
else inst
disableSplitMoves :: Node.List -> Instance.Instance -> Instance.Instance
disableSplitMoves nl inst =
if not . isOk . Cluster.instanceGroup nl $ inst
then Instance.setMovable inst False
else inst
longestDomain :: [String] -> String
longestDomain [] = ""
longestDomain (x:xs) =
foldr (\ suffix accu -> if all (isSuffixOf suffix) xs
then suffix
else accu)
"" $ filter (isPrefixOf ".") (tails x)
extractExTags :: [String] -> [String]
extractExTags =
map (drop (length exTagsPrefix)) .
filter (isPrefixOf exTagsPrefix)
commonSuffix :: Node.List -> Instance.List -> String
commonSuffix nl il =
let node_names = map Node.name $ Container.elems nl
inst_names = map Instance.name $ Container.elems il
in longestDomain (node_names ++ inst_names)
mergeData :: [(String, DynUtil)]
-> [String]
-> [String]
-> [String]
-> ClusterData
-> Result ClusterData
mergeData um extags selinsts exinsts cdata@(ClusterData gl nl il2 tags _) =
let il = Container.elems il2
il3 = foldl' (\im (name, n_util) ->
case Container.findByName im name of
Nothing -> im
Just inst ->
let new_i = inst { Instance.util = n_util }
in Container.add (Instance.idx inst) new_i im
) il2 um
allextags = extags ++ extractExTags tags
inst_names = map Instance.name il
selinst_lkp = map (lookupName inst_names) selinsts
exinst_lkp = map (lookupName inst_names) exinsts
lkp_unknown = filter (not . goodLookupResult) (selinst_lkp ++ exinst_lkp)
selinst_names = map lrContent selinst_lkp
exinst_names = map lrContent exinst_lkp
node_names = map Node.name (Container.elems nl)
common_suffix = longestDomain (node_names ++ inst_names)
il4 = Container.map (computeAlias common_suffix .
filterExTags allextags .
updateMovable selinst_names exinst_names) il3
nl2 = foldl' fixNodes nl (Container.elems il4)
nl3 = Container.map (setNodePolicy gl .
computeAlias common_suffix .
(`Node.buildPeers` il4)) nl2
il5 = Container.map (disableSplitMoves nl3) il4
in if' (null lkp_unknown)
(Ok cdata { cdNodes = nl3, cdInstances = il5 })
(Bad $ "Unknown instance(s): " ++ show(map lrContent lkp_unknown))
checkData :: Node.List -> Instance.List
-> ([String], Node.List)
checkData nl il =
Container.mapAccum
(\ msgs node ->
let nname = Node.name node
nilst = map (`Container.find` il) (Node.pList node)
dilst = filter Instance.instanceDown nilst
adj_mem = sum . map Instance.mem $ dilst
delta_mem = truncate (Node.tMem node)
Node.nMem node
Node.fMem node
nodeImem node il
+ adj_mem
delta_dsk = truncate (Node.tDsk node)
Node.fDsk node
nodeIdsk node il
newn = Node.setFmem (Node.setXmem node delta_mem)
(Node.fMem node adj_mem)
umsg1 =
if delta_mem > 512 || delta_dsk > 1024
then printf "node %s is missing %d MB ram \
\and %d GB disk"
nname delta_mem (delta_dsk `div` 1024):msgs
else msgs
in (umsg1, newn)
) [] nl
nodeImem :: Node.Node -> Instance.List -> Int
nodeImem node il =
let rfind = flip Container.find il
il' = map rfind $ Node.pList node
oil' = filter Instance.notOffline il'
in sum . map Instance.mem $ oil'
nodeIdsk :: Node.Node -> Instance.List -> Int
nodeIdsk node il =
let rfind = flip Container.find il
in sum . map (Instance.dsk . rfind)
$ Node.pList node ++ Node.sList node