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,
30 t_mem, n_mem, f_mem, r_mem, x_mem,
33 p_mem, p_dsk, p_rem, p_cpu,
34 m_dsk, m_cpu, lo_dsk, hi_cpu,
35 plist, slist, offline)
39 -- ** Finalization after data loading
51 -- * Instance (re)location
66 import Text.Printf (printf)
68 import qualified Ganeti.HTools.Container as Container
69 import qualified Ganeti.HTools.Instance as Instance
70 import qualified Ganeti.HTools.PeerMap as PeerMap
72 import qualified Ganeti.HTools.Types as T
74 -- * Type declarations
77 data Node = Node { name :: String -- ^ The node name
78 , t_mem :: Double -- ^ Total memory (MiB)
79 , n_mem :: Int -- ^ Node memory (MiB)
80 , f_mem :: Int -- ^ Free memory (MiB)
81 , x_mem :: Int -- ^ Unaccounted memory (MiB)
82 , t_dsk :: Double -- ^ Total disk space (MiB)
83 , f_dsk :: Int -- ^ Free disk space (MiB)
84 , t_cpu :: Double -- ^ Total CPU count
85 , u_cpu :: Int -- ^ Used VCPU count
86 , plist :: [T.Idx]-- ^ List of primary instance indices
87 , slist :: [T.Idx]-- ^ List of secondary instance indices
88 , idx :: T.Ndx -- ^ Internal index for book-keeping
89 , peers :: PeerMap.PeerMap -- ^ Pnode to instance mapping
90 , failN1:: Bool -- ^ Whether the node has failed n1
91 , r_mem :: Int -- ^ Maximum memory needed for
92 -- failover by primaries of this node
93 , p_mem :: Double -- ^ Percent of free memory
94 , p_dsk :: Double -- ^ Percent of free disk
95 , p_rem :: Double -- ^ Percent of reserved memory
96 , p_cpu :: Double -- ^ Ratio of virtual to physical CPUs
97 , m_dsk :: Double -- ^ Minimum free disk ratio
98 , m_cpu :: Double -- ^ Max ratio of virt-to-phys CPUs
99 , lo_dsk :: Int -- ^ Autocomputed from m_dsk low disk
101 , hi_cpu :: Int -- ^ Autocomputed from m_cpu high cpu
103 , offline :: Bool -- ^ Whether the node should not be used
104 -- for allocations and skipped from
105 -- score computations
108 instance T.Element Node where
114 -- | A simple name for the int, node association list.
115 type AssocList = [(T.Ndx, Node)]
117 -- | A simple name for a node map.
118 type List = Container.Container Node
120 -- | Constant node index for a non-moveable instance.
128 -- | No limit int value
132 -- * Initialization functions
134 -- | Create a new node.
136 -- The index and the peers maps are empty, and will be need to be
137 -- update later via the 'setIdx' and 'buildPeers' functions.
138 create :: String -> Double -> Int -> Int -> Double
139 -> Int -> Double -> Bool -> Node
140 create name_init mem_t_init mem_n_init mem_f_init
141 dsk_t_init dsk_f_init cpu_t_init offline_init =
156 peers = PeerMap.empty,
158 p_mem = fromIntegral mem_f_init / mem_t_init,
159 p_dsk = fromIntegral dsk_f_init / dsk_t_init,
162 offline = offline_init,
170 -- | Changes the index.
172 -- This is used only during the building of the data structures.
173 setIdx :: Node -> T.Ndx -> Node
174 setIdx t i = t {idx = i}
176 -- | Changes the name.
178 -- This is used only during the building of the data structures.
179 setName :: Node -> String -> Node
180 setName t s = t {name = s}
182 -- | Sets the offline attribute.
183 setOffline :: Node -> Bool -> Node
184 setOffline t val = t { offline = val }
186 -- | Sets the unnaccounted memory.
187 setXmem :: Node -> Int -> Node
188 setXmem t val = t { x_mem = val }
190 -- | Sets the max disk usage ratio
191 setMdsk :: Node -> Double -> Node
192 setMdsk t val = t { m_dsk = val,
193 lo_dsk = if val == noLimit
195 else floor (val * t_dsk t) }
197 -- | Sets the max cpu usage ratio
198 setMcpu :: Node -> Double -> Node
199 setMcpu t val = t { m_cpu = val, hi_cpu = floor (val * t_cpu t) }
201 -- | Computes the maximum reserved memory for peers from a peer map.
202 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
203 computeMaxRes = PeerMap.maxElem
205 -- | Builds the peer map for a given node.
206 buildPeers :: Node -> Instance.List -> Node
209 (\i_idx -> let inst = Container.find i_idx il
210 in (Instance.pnode inst, Instance.mem inst))
212 pmap = PeerMap.accumArray (+) mdata
213 new_rmem = computeMaxRes pmap
214 new_failN1 = f_mem t <= new_rmem
215 new_prem = fromIntegral new_rmem / t_mem t
216 in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
218 -- | Assigns an instance to a node as primary without other updates.
219 setPri :: Node -> T.Idx -> Node
220 setPri t idx = t { plist = idx:plist t }
222 -- | Assigns an instance to a node as secondary without other updates.
223 setSec :: Node -> T.Idx -> Node
224 setSec t idx = t { slist = idx:slist t }
226 -- | Add primary cpus to a node
227 addCpus :: Node -> Int -> Node
229 let new_count = u_cpu t + count
230 in t { u_cpu = new_count, p_cpu = fromIntegral new_count / t_cpu t }
232 -- * Update functions
234 -- | Sets the free memory.
235 setFmem :: Node -> Int -> Node
237 let new_n1 = new_mem <= r_mem t
238 new_mp = fromIntegral new_mem / t_mem t
240 t { f_mem = new_mem, failN1 = new_n1, p_mem = new_mp }
242 -- | Removes a primary instance.
243 removePri :: Node -> Instance.Instance -> Node
245 let iname = Instance.idx inst
246 new_plist = delete iname (plist t)
247 new_mem = f_mem t + Instance.mem inst
248 new_dsk = f_dsk t + Instance.dsk inst
249 new_mp = fromIntegral new_mem / t_mem t
250 new_dp = fromIntegral new_dsk / t_dsk t
251 new_failn1 = new_mem <= r_mem t
252 new_ucpu = u_cpu t - Instance.vcpus inst
253 new_rcpu = fromIntegral new_ucpu / t_cpu t
254 in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
255 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
256 u_cpu = new_ucpu, p_cpu = new_rcpu}
258 -- | Removes a secondary instance.
259 removeSec :: Node -> Instance.Instance -> Node
261 let iname = Instance.idx inst
262 pnode = Instance.pnode inst
263 new_slist = delete iname (slist t)
264 new_dsk = f_dsk t + Instance.dsk inst
266 old_peem = PeerMap.find pnode old_peers
267 new_peem = old_peem - Instance.mem inst
268 new_peers = PeerMap.add pnode new_peem old_peers
270 new_rmem = if old_peem < old_rmem then
273 computeMaxRes new_peers
274 new_prem = fromIntegral new_rmem / t_mem t
275 new_failn1 = f_mem t <= new_rmem
276 new_dp = fromIntegral new_dsk / t_dsk t
277 in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
278 failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
281 -- | Adds a primary instance.
282 addPri :: Node -> Instance.Instance -> T.OpResult Node
284 let iname = Instance.idx inst
285 new_mem = f_mem t - Instance.mem inst
286 new_dsk = f_dsk t - Instance.dsk inst
287 new_failn1 = new_mem <= r_mem t
288 new_ucpu = u_cpu t + Instance.vcpus inst
289 new_pcpu = fromIntegral new_ucpu / t_cpu t
290 new_dp = fromIntegral new_dsk / t_dsk t
292 in if new_mem <= 0 then T.OpFail T.FailMem
293 else if new_dsk <= 0 || m_dsk t > new_dp then T.OpFail T.FailDisk
294 else if new_failn1 && not (failN1 t) then T.OpFail T.FailMem
295 else if l_cpu >= 0 && l_cpu < new_pcpu then T.OpFail T.FailCPU
297 let new_plist = iname:plist t
298 new_mp = fromIntegral new_mem / t_mem t
299 r = t { plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
300 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp,
301 u_cpu = new_ucpu, p_cpu = new_pcpu }
304 -- | Adds a secondary instance.
305 addSec :: Node -> Instance.Instance -> T.Ndx -> T.OpResult Node
307 let iname = Instance.idx inst
310 new_dsk = f_dsk t - Instance.dsk inst
311 new_peem = PeerMap.find pdx old_peers + Instance.mem inst
312 new_peers = PeerMap.add pdx new_peem old_peers
313 new_rmem = max (r_mem t) new_peem
314 new_prem = fromIntegral new_rmem / t_mem t
315 new_failn1 = old_mem <= new_rmem
316 new_dp = fromIntegral new_dsk / t_dsk t
317 in if new_dsk <= 0 || m_dsk t > new_dp then T.OpFail T.FailDisk
318 else if new_failn1 && not (failN1 t) then T.OpFail T.FailMem
319 else let new_slist = iname:slist t
320 r = t { slist = new_slist, f_dsk = new_dsk,
321 peers = new_peers, failN1 = new_failn1,
322 r_mem = new_rmem, p_dsk = new_dp,
328 -- | Computes the amount of available disk on a given node
329 availDisk :: Node -> Int
340 -- * Display functions
342 -- | String converter for the node list functionality.
343 list :: Int -> Node -> String
345 let pl = length $ plist t
346 sl = length $ slist t
356 imem = truncate tmem - nmem - xmem - fmem
359 then printf " - %-*s %57s %3d %3d"
360 mname (name t) "" pl sl
362 printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d\
363 \ %4.0f %4d %3d %3d %6.4f %6.4f %5.2f"
364 (if off then '-' else if fn then '*' else ' ')
365 mname (name t) tmem nmem imem xmem fmem (r_mem t)
366 (t_dsk t / 1024) (f_dsk t `div` 1024)