1 {-| Loading data from external sources
3 This module holds the common code for loading the cluster state from external sources.
9 Copyright (C) 2009 Google Inc.
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 module Ganeti.HTools.Loader
39 import Data.Function (on)
41 import Data.Maybe (fromJust)
42 import Text.Printf (printf)
44 import qualified Ganeti.HTools.Container as Container
45 import qualified Ganeti.HTools.Instance as Instance
46 import qualified Ganeti.HTools.Node as Node
48 import Ganeti.HTools.Types
54 This type denotes what request we got from Ganeti and also holds
55 request-specific fields.
59 = Allocate Instance.Instance Int -- ^ A new instance allocation
60 | Relocate Idx Int [Ndx] -- ^ Move an instance to a new
64 -- | A complete request, as received from Ganeti.
65 data Request = Request RqType Node.List Instance.List String
70 -- | Lookups a node into an assoc list.
71 lookupNode :: (Monad m) => [(String, Ndx)] -> String -> String -> m Ndx
72 lookupNode ktn inst node =
73 case lookup node ktn of
74 Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
75 Just idx -> return idx
77 -- | Lookups an instance into an assoc list.
78 lookupInstance :: (Monad m) => [(String, Idx)] -> String -> m Idx
79 lookupInstance kti inst =
80 case lookup inst kti of
81 Nothing -> fail $ "Unknown instance '" ++ inst ++ "'"
82 Just idx -> return idx
84 -- | Given a list of elements (and their names), assign indices to them.
85 assignIndices :: (Element a) =>
87 -> (NameAssoc, [(Int, a)])
89 unzip . map (\ (idx, (k, v)) -> ((k, idx), (idx, setIdx v idx)))
92 -- | Assoc element comparator
93 assocEqual :: (Eq a) => (a, b) -> (a, b) -> Bool
94 assocEqual = (==) `on` fst
96 -- | For each instance, add its index to its primary and secondary nodes.
97 fixNodes :: [(Ndx, Node.Node)]
98 -> [(Idx, Instance.Instance)]
101 foldl' (\accu (idx, inst) ->
103 pdx = Instance.pnode inst
104 sdx = Instance.snode inst
105 pold = fromJust $ lookup pdx accu
106 pnew = Node.setPri pold idx
107 pnew' = Node.addCpus pnew (Instance.vcpus inst)
108 ac1 = deleteBy assocEqual (pdx, pold) accu
109 ac2 = (pdx, pnew'):ac1
111 if sdx /= Node.noSecondary then
113 sold = fromJust $ lookup sdx accu
114 snew = Node.setSec sold idx
115 ac3 = deleteBy assocEqual (sdx, sold) ac2
116 ac4 = (sdx, snew):ac3
122 -- | Compute the longest common suffix of a list of strings that
123 -- | starts with a dot.
124 longestDomain :: [String] -> String
125 longestDomain [] = ""
126 longestDomain (x:xs) =
127 foldr (\ suffix accu -> if all (isSuffixOf suffix) xs
130 "" $ filter (isPrefixOf ".") (tails x)
132 -- | Remove tail suffix from a string.
133 stripSuffix :: Int -> String -> String
134 stripSuffix sflen name = take ((length name) - sflen) name
136 -- | Initializer function that loads the data from a node and instance
137 -- list and massages it into the correct format.
138 mergeData :: (Node.AssocList,
139 Instance.AssocList) -- ^ Data from either Text.loadData
141 -> Result (Node.List, Instance.List, String)
142 mergeData (nl, il) = do
145 il3 = Container.fromAssocList il
146 nl3 = Container.fromAssocList
147 (map (\ (k, v) -> (k, Node.buildPeers v il3)) nl2)
148 node_names = map Node.name $ Container.elems nl3
149 inst_names = map Instance.name $ Container.elems il3
150 common_suffix = longestDomain (node_names ++ inst_names)
151 csl = length common_suffix
152 snl = Container.map (\n -> setName n (stripSuffix csl $ nameOf n)) nl3
153 sil = Container.map (\i -> setName i (stripSuffix csl $ nameOf i)) il3
154 return (snl, sil, common_suffix)
156 -- | Checks the cluster data for consistency.
157 checkData :: Node.List -> Instance.List
158 -> ([String], Node.List)
162 let nname = Node.name node
163 nilst = map (flip Container.find $ il) (Node.plist node)
164 dilst = filter (not . Instance.running) nilst
165 adj_mem = sum . map Instance.mem $ dilst
166 delta_mem = (truncate $ Node.t_mem node)
171 delta_dsk = (truncate $ Node.t_dsk node)
174 newn = Node.setFmem (Node.setXmem node delta_mem)
175 (Node.f_mem node - adj_mem)
176 umsg1 = (if delta_mem > 512 || delta_dsk > 1024
177 then [printf "node %s is missing %d MB ram \
179 nname delta_mem (delta_dsk `div` 1024)]
181 in (msgs ++ umsg1, newn)
184 -- | Compute the amount of memory used by primary instances on a node.
185 nodeImem :: Node.Node -> Instance.List -> Int
187 let rfind = flip Container.find $ il
188 in sum . map Instance.mem .
189 map rfind $ Node.plist node
191 -- | Compute the amount of disk used by instances on a node (either primary
193 nodeIdsk :: Node.Node -> Instance.List -> Int
195 let rfind = flip Container.find $ il
196 in sum . map Instance.dsk .
197 map rfind $ (Node.plist node) ++ (Node.slist node)