1 {-| Loading data from external sources
3 This module holds the common code for loading the cluster state from external sources.
7 module Ganeti.HTools.Loader
17 import Data.Maybe (fromJust)
18 import Text.Printf (printf)
20 import qualified Ganeti.HTools.Container as Container
21 import qualified Ganeti.HTools.Instance as Instance
22 import qualified Ganeti.HTools.Node as Node
24 import Ganeti.HTools.Types
26 -- | Lookups a node into an assoc list
27 lookupNode :: (Monad m) => [(String, Ndx)] -> String -> String -> m Ndx
28 lookupNode ktn inst node =
29 case lookup node ktn of
30 Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
31 Just idx -> return idx
33 -- | Lookups an instance into an assoc list
34 lookupInstance :: (Monad m) => [(String, Idx)] -> String -> m Idx
35 lookupInstance kti inst =
36 case lookup inst kti of
37 Nothing -> fail $ "Unknown instance '" ++ inst ++ "'"
38 Just idx -> return idx
40 -- | Given a list of elements (and their names), assign indices to them
41 assignIndices :: (Element a) =>
43 -> (NameAssoc, [(Int, a)])
45 unzip . map (\ (idx, (k, v)) -> ((k, idx), (idx, setIdx v idx)))
48 -- | For each instance, add its index to its primary and secondary nodes
49 fixNodes :: [(Ndx, Node.Node)]
50 -> [(Idx, Instance.Instance)]
53 foldl' (\accu (idx, inst) ->
55 assocEqual = (\ (i, _) (j, _) -> i == j)
56 pdx = Instance.pnode inst
57 sdx = Instance.snode inst
58 pold = fromJust $ lookup pdx accu
59 pnew = Node.setPri pold idx
60 ac1 = deleteBy assocEqual (pdx, pold) accu
63 if sdx /= Node.noSecondary then
65 sold = fromJust $ lookup sdx accu
66 snew = Node.setSec sold idx
67 ac3 = deleteBy assocEqual (sdx, sold) ac2
74 -- | Compute the longest common suffix of a list of strings that
75 -- | starts with a dot
76 longestDomain :: [String] -> String
78 longestDomain (x:xs) =
79 foldr (\ suffix accu -> if all (isSuffixOf suffix) xs
82 "" $ filter (isPrefixOf ".") (tails x)
84 -- | Remove tail suffix from a string
85 stripSuffix :: Int -> String -> String
86 stripSuffix sflen name = take ((length name) - sflen) name
88 {-| Initializer function that loads the data from a node and list file
89 and massages it into the correct format. -}
90 mergeData :: (Node.AssocList,
91 Instance.AssocList) -- ^ Data from either Text.loadData
93 -> Result (Node.List, Instance.List, String)
94 mergeData (nl, il) = do
97 il3 = Container.fromAssocList il
98 nl3 = Container.fromAssocList
99 (map (\ (k, v) -> (k, Node.buildPeers v il3 (length nl2))) nl2)
100 node_names = map Node.name $ Container.elems nl3
101 inst_names = map Instance.name $ Container.elems il3
102 common_suffix = longestDomain (node_names ++ inst_names)
103 csl = length common_suffix
104 snl = Container.map (\n -> setName n (stripSuffix csl $ nameOf n)) nl3
105 sil = Container.map (\i -> setName i (stripSuffix csl $ nameOf i)) il3
106 return (snl, sil, common_suffix)
108 -- | Check cluster data for consistency
109 checkData :: Node.List -> Instance.List
110 -> ([String], Node.List)
114 let nname = Node.name node
115 nilst = map (flip Container.find $ il) (Node.plist node)
116 dilst = filter (not . Instance.running) nilst
117 adj_mem = sum . map Instance.mem $ dilst
118 delta_mem = (truncate $ Node.t_mem node)
123 delta_dsk = (truncate $ Node.t_dsk node)
126 newn = Node.setFmem (Node.setXmem node delta_mem)
127 (Node.f_mem node - adj_mem)
128 umsg1 = if delta_mem > 512 || delta_dsk > 1024
129 then [printf "node %s is missing %d MB ram \
131 nname delta_mem (delta_dsk `div` 1024)]
133 in (msgs ++ umsg1, newn)
136 -- | Compute the amount of memory used by primary instances on a node.
137 nodeImem :: Node.Node -> Instance.List -> Int
139 let rfind = flip Container.find $ il
140 in sum . map Instance.mem .
141 map rfind $ Node.plist node
143 -- | Compute the amount of disk used by instances on a node (either primary
145 nodeIdsk :: Node.Node -> Instance.List -> Int
147 let rfind = flip Container.find $ il
148 in sum . map Instance.dsk .
149 map rfind $ (Node.plist node) ++ (Node.slist node)