1 {-| Module describing a node.
3 All updates are functional (copy-based) and return a new node with
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.Node
29 ( Node(failN1, name, idx, t_mem, n_mem, f_mem, r_mem,
32 p_mem, p_dsk, p_rem, p_cpu,
34 plist, slist, offline)
38 -- ** Finalization after data loading
50 -- * Instance (re)location
63 import Text.Printf (printf)
65 import qualified Ganeti.HTools.Container as Container
66 import qualified Ganeti.HTools.Instance as Instance
67 import qualified Ganeti.HTools.PeerMap as PeerMap
69 import qualified Ganeti.HTools.Types as T
71 -- * Type declarations
74 data Node = Node { name :: String -- ^ The node name
75 , t_mem :: Double -- ^ Total memory (MiB)
76 , n_mem :: Int -- ^ Node memory (MiB)
77 , f_mem :: Int -- ^ Free memory (MiB)
78 , x_mem :: Int -- ^ Unaccounted memory (MiB)
79 , t_dsk :: Double -- ^ Total disk space (MiB)
80 , f_dsk :: Int -- ^ Free disk space (MiB)
81 , t_cpu :: Double -- ^ Total CPU count
82 , u_cpu :: Int -- ^ Used VCPU count
83 , plist :: [T.Idx]-- ^ List of primary instance indices
84 , slist :: [T.Idx]-- ^ List of secondary instance indices
85 , idx :: T.Ndx -- ^ Internal index for book-keeping
86 , peers :: PeerMap.PeerMap -- ^ Pnode to instance mapping
87 , failN1:: Bool -- ^ Whether the node has failed n1
88 , r_mem :: Int -- ^ Maximum memory needed for
89 -- failover by primaries of this node
90 , p_mem :: Double -- ^ Percent of free memory
91 , p_dsk :: Double -- ^ Percent of free disk
92 , p_rem :: Double -- ^ Percent of reserved memory
93 , p_cpu :: Double -- ^ Ratio of virtual to physical CPUs
94 , m_dsk :: Double -- ^ Minimum free disk ratio
95 , m_cpu :: Double -- ^ Max ratio of virt-to-phys CPUs
96 , offline :: Bool -- ^ Whether the node should not be used
97 -- for allocations and skipped from
101 instance T.Element Node where
107 -- | A simple name for the int, node association list.
108 type AssocList = [(T.Ndx, Node)]
110 -- | A simple name for a node map.
111 type List = Container.Container Node
113 -- | Constant node index for a non-moveable instance.
117 -- * Initialization functions
119 -- | Create a new node.
121 -- The index and the peers maps are empty, and will be need to be
122 -- update later via the 'setIdx' and 'buildPeers' functions.
123 create :: String -> Double -> Int -> Int -> Double
124 -> Int -> Double -> Bool -> Node
125 create name_init mem_t_init mem_n_init mem_f_init
126 dsk_t_init dsk_f_init cpu_t_init offline_init =
141 peers = PeerMap.empty,
143 p_mem = (fromIntegral mem_f_init) / mem_t_init,
144 p_dsk = (fromIntegral dsk_f_init) / dsk_t_init,
147 offline = offline_init,
153 -- | Changes the index.
155 -- This is used only during the building of the data structures.
156 setIdx :: Node -> T.Ndx -> Node
157 setIdx t i = t {idx = i}
159 -- | Changes the name.
161 -- This is used only during the building of the data structures.
162 setName :: Node -> String -> Node
163 setName t s = t {name = s}
165 -- | Sets the offline attribute.
166 setOffline :: Node -> Bool -> Node
167 setOffline t val = t { offline = val }
169 -- | Sets the unnaccounted memory.
170 setXmem :: Node -> Int -> Node
171 setXmem t val = t { x_mem = val }
173 -- | Sets the max disk usage ratio
174 setMdsk :: Node -> Double -> Node
175 setMdsk t val = t { m_dsk = val }
177 -- | Sets the max cpu usage ratio
178 setMcpu :: Node -> Double -> Node
179 setMcpu t val = t { m_cpu = val }
181 -- | Computes the maximum reserved memory for peers from a peer map.
182 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
183 computeMaxRes new_peers = PeerMap.maxElem new_peers
185 -- | Builds the peer map for a given node.
186 buildPeers :: Node -> Instance.List -> Node
189 (\i_idx -> let inst = Container.find i_idx il
190 in (Instance.pnode inst, Instance.mem inst))
192 pmap = PeerMap.accumArray (+) mdata
193 new_rmem = computeMaxRes pmap
194 new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
195 new_prem = (fromIntegral new_rmem) / (t_mem t)
196 in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
198 -- | Assigns an instance to a node as primary without other updates.
199 setPri :: Node -> T.Idx -> Node
200 setPri t idx = t { plist = idx:(plist t) }
202 -- | Assigns an instance to a node as secondary without other updates.
203 setSec :: Node -> T.Idx -> Node
204 setSec t idx = t { slist = idx:(slist t) }
206 -- | Add primary cpus to a node
207 addCpus :: Node -> Int -> Node
209 let new_count = (u_cpu t) + count
210 in t { u_cpu = new_count, p_cpu = (fromIntegral new_count) / (t_cpu t) }
212 -- * Update functions
214 -- | Sets the free memory.
215 setFmem :: Node -> Int -> Node
217 let new_n1 = computeFailN1 (r_mem t) new_mem (f_dsk t)
218 new_mp = (fromIntegral new_mem) / (t_mem t)
220 t { f_mem = new_mem, failN1 = new_n1, p_mem = new_mp }
222 -- | Given the rmem, free memory and disk, computes the failn1 status.
223 computeFailN1 :: Int -> Int -> Int -> Bool
224 computeFailN1 new_rmem new_mem new_dsk =
225 new_mem <= new_rmem || new_dsk <= 0
227 -- | Given the new free memory and disk, fail if any of them is below zero.
228 failHealth :: Int -> Int -> Bool
229 failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
231 -- | Removes a primary instance.
232 removePri :: Node -> Instance.Instance -> Node
234 let iname = Instance.idx inst
235 new_plist = delete iname (plist t)
236 new_mem = f_mem t + Instance.mem inst
237 new_dsk = f_dsk t + Instance.dsk inst
238 new_mp = (fromIntegral new_mem) / (t_mem t)
239 new_dp = (fromIntegral new_dsk) / (t_dsk t)
240 new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
241 new_ucpu = (u_cpu t) - (Instance.vcpus inst)
242 new_rcpu = (fromIntegral new_ucpu) / (t_cpu t)
243 in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
244 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
245 u_cpu = new_ucpu, p_cpu = new_rcpu}
247 -- | Removes a secondary instance.
248 removeSec :: Node -> Instance.Instance -> Node
250 let iname = Instance.idx inst
251 pnode = Instance.pnode inst
252 new_slist = delete iname (slist t)
253 new_dsk = f_dsk t + Instance.dsk inst
255 old_peem = PeerMap.find pnode old_peers
256 new_peem = old_peem - (Instance.mem inst)
257 new_peers = PeerMap.add pnode new_peem old_peers
259 new_rmem = if old_peem < old_rmem then
262 computeMaxRes new_peers
263 new_prem = (fromIntegral new_rmem) / (t_mem t)
264 new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
265 new_dp = (fromIntegral new_dsk) / (t_dsk t)
266 in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
267 failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
270 -- | Adds a primary instance.
271 addPri :: Node -> Instance.Instance -> Maybe Node
273 let iname = Instance.idx inst
274 new_mem = f_mem t - Instance.mem inst
275 new_dsk = f_dsk t - Instance.dsk inst
276 new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
277 new_ucpu = (u_cpu t) + (Instance.vcpus inst)
278 new_pcpu = (fromIntegral new_ucpu) / (t_cpu t)
280 if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
283 let new_plist = iname:(plist t)
284 new_mp = (fromIntegral new_mem) / (t_mem t)
285 new_dp = (fromIntegral new_dsk) / (t_dsk t)
287 Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
288 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
289 u_cpu = new_ucpu, p_cpu = new_pcpu}
291 -- | Adds a secondary instance.
292 addSec :: Node -> Instance.Instance -> T.Ndx -> Maybe Node
294 let iname = Instance.idx inst
297 new_dsk = f_dsk t - Instance.dsk inst
298 new_peem = PeerMap.find pdx old_peers + Instance.mem inst
299 new_peers = PeerMap.add pdx new_peem old_peers
300 new_rmem = max (r_mem t) new_peem
301 new_prem = (fromIntegral new_rmem) / (t_mem t)
302 new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
303 if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
306 let new_slist = iname:(slist t)
307 new_dp = (fromIntegral new_dsk) / (t_dsk t)
309 Just t {slist = new_slist, f_dsk = new_dsk,
310 peers = new_peers, failN1 = new_failn1,
311 r_mem = new_rmem, p_dsk = new_dp,
314 -- * Display functions
316 -- | String converter for the node list functionality.
317 list :: Int -> Node -> String
319 let pl = length $ plist t
320 sl = length $ slist t
330 imem = (truncate tmem) - nmem - xmem - fmem
333 then printf " - %-*s %57s %3d %3d"
334 mname (name t) "" pl sl
336 printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d\
337 \ %4.0f %4d %3d %3d %6.4f %6.4f %5.2f"
338 (if off then '-' else if fn then '*' else ' ')
339 mname (name t) tmem nmem imem xmem fmem (r_mem t)
340 ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)