2a8ee17e1abf87375d9ad8471b67e8928af2b3b2
[ganeti-local] / Ganeti / HTools / Node.hs
1 {-| Module describing a node.
2
3     All updates are functional (copy-based) and return a new node with
4     updated value.
5 -}
6
7 {-
8
9 Copyright (C) 2009 Google Inc.
10
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.
15
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.
20
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
24 02110-1301, USA.
25
26 -}
27
28 module Ganeti.HTools.Node
29     ( Node(failN1, name, idx, t_mem, n_mem, f_mem, r_mem, t_dsk, f_dsk,
30            p_mem, p_dsk, p_rem,
31            plist, slist, offline)
32     , List
33     -- * Constructor
34     , create
35     -- ** Finalization after data loading
36     , buildPeers
37     , setIdx
38     , setName
39     , setOffline
40     , setXmem
41     , setFmem
42     , setPri
43     , setSec
44     -- * Instance (re)location
45     , removePri
46     , removeSec
47     , addPri
48     , addSec
49     -- * Formatting
50     , list
51     -- * Misc stuff
52     , AssocList
53     , noSecondary
54     ) where
55
56 import Data.List
57 import Text.Printf (printf)
58
59 import qualified Ganeti.HTools.Container as Container
60 import qualified Ganeti.HTools.Instance as Instance
61 import qualified Ganeti.HTools.PeerMap as PeerMap
62
63 import qualified Ganeti.HTools.Types as T
64
65 -- * Type declarations
66
67 -- | The node type.
68 data Node = Node { name  :: String -- ^ The node name
69                  , t_mem :: Double -- ^ Total memory (MiB)
70                  , n_mem :: Int    -- ^ Node memory (MiB)
71                  , f_mem :: Int    -- ^ Free memory (MiB)
72                  , x_mem :: Int    -- ^ Unaccounted memory (MiB)
73                  , t_dsk :: Double -- ^ Total disk space (MiB)
74                  , f_dsk :: Int    -- ^ Free disk space (MiB)
75                  , plist :: [T.Idx]-- ^ List of primary instance indices
76                  , slist :: [T.Idx]-- ^ List of secondary instance indices
77                  , idx :: T.Ndx    -- ^ Internal index for book-keeping
78                  , peers :: PeerMap.PeerMap -- ^ Pnode to instance mapping
79                  , failN1:: Bool   -- ^ Whether the node has failed n1
80                  , r_mem :: Int    -- ^ Maximum memory needed for
81                                    -- failover by primaries of this node
82                  , p_mem :: Double -- ^ Percent of free memory
83                  , p_dsk :: Double -- ^ Percent of free disk
84                  , p_rem :: Double -- ^ Percent of reserved memory
85                  , offline :: Bool -- ^ Whether the node should not be used
86                                    -- for allocations and skipped from
87                                    -- score computations
88   } deriving (Show)
89
90 instance T.Element Node where
91     nameOf = name
92     idxOf = idx
93     setName = setName
94     setIdx = setIdx
95
96 -- | A simple name for the int, node association list.
97 type AssocList = [(T.Ndx, Node)]
98
99 -- | A simple name for a node map.
100 type List = Container.Container Node
101
102 -- | Constant node index for a non-moveable instance.
103 noSecondary :: T.Ndx
104 noSecondary = -1
105
106 -- * Initialization functions
107
108 -- | Create a new node.
109 --
110 -- The index and the peers maps are empty, and will be need to be
111 -- update later via the 'setIdx' and 'buildPeers' functions.
112 create :: String -> Double -> Int -> Int -> Double -> Int -> Bool -> Node
113 create name_init mem_t_init mem_n_init mem_f_init
114        dsk_t_init dsk_f_init offline_init =
115     Node
116     {
117       name  = name_init,
118       t_mem = mem_t_init,
119       n_mem = mem_n_init,
120       f_mem = mem_f_init,
121       t_dsk = dsk_t_init,
122       f_dsk = dsk_f_init,
123       plist = [],
124       slist = [],
125       failN1 = True,
126       idx = -1,
127       peers = PeerMap.empty,
128       r_mem = 0,
129       p_mem = (fromIntegral mem_f_init) / mem_t_init,
130       p_dsk = (fromIntegral dsk_f_init) / dsk_t_init,
131       p_rem = 0,
132       offline = offline_init,
133       x_mem = 0
134     }
135
136 -- | Changes the index.
137 --
138 -- This is used only during the building of the data structures.
139 setIdx :: Node -> T.Ndx -> Node
140 setIdx t i = t {idx = i}
141
142 -- | Changes the name.
143 --
144 -- This is used only during the building of the data structures.
145 setName :: Node -> String -> Node
146 setName t s = t {name = s}
147
148 -- | Sets the offline attribute.
149 setOffline :: Node -> Bool -> Node
150 setOffline t val = t { offline = val }
151
152 -- | Sets the unnaccounted memory.
153 setXmem :: Node -> Int -> Node
154 setXmem t val = t { x_mem = val }
155
156 -- | Computes the maximum reserved memory for peers from a peer map.
157 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
158 computeMaxRes new_peers = PeerMap.maxElem new_peers
159
160 -- | Builds the peer map for a given node.
161 buildPeers :: Node -> Instance.List -> Node
162 buildPeers t il =
163     let mdata = map
164                 (\i_idx -> let inst = Container.find i_idx il
165                            in (Instance.pnode inst, Instance.mem inst))
166                 (slist t)
167         pmap = PeerMap.accumArray (+) mdata
168         new_rmem = computeMaxRes pmap
169         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
170         new_prem = (fromIntegral new_rmem) / (t_mem t)
171     in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
172
173 -- | Assigns an instance to a node as primary without other updates.
174 setPri :: Node -> T.Idx -> Node
175 setPri t idx = t { plist = idx:(plist t) }
176
177 -- | Assigns an instance to a node as secondary without other updates.
178 setSec :: Node -> T.Idx -> Node
179 setSec t idx = t { slist = idx:(slist t) }
180
181 -- * Update functions
182
183 -- | Sets the free memory.
184 setFmem :: Node -> Int -> Node
185 setFmem t new_mem =
186     let new_n1 = computeFailN1 (r_mem t) new_mem (f_dsk t)
187         new_mp = (fromIntegral new_mem) / (t_mem t)
188     in
189       t { f_mem = new_mem, failN1 = new_n1, p_mem = new_mp }
190
191 -- | Given the rmem, free memory and disk, computes the failn1 status.
192 computeFailN1 :: Int -> Int -> Int -> Bool
193 computeFailN1 new_rmem new_mem new_dsk =
194     new_mem <= new_rmem || new_dsk <= 0
195
196 -- | Given the new free memory and disk, fail if any of them is below zero.
197 failHealth :: Int -> Int -> Bool
198 failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
199
200 -- | Removes a primary instance.
201 removePri :: Node -> Instance.Instance -> Node
202 removePri t inst =
203     let iname = Instance.idx inst
204         new_plist = delete iname (plist t)
205         new_mem = f_mem t + Instance.mem inst
206         new_dsk = f_dsk t + Instance.dsk inst
207         new_mp = (fromIntegral new_mem) / (t_mem t)
208         new_dp = (fromIntegral new_dsk) / (t_dsk t)
209         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
210     in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
211           failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
212
213 -- | Removes a secondary instance.
214 removeSec :: Node -> Instance.Instance -> Node
215 removeSec t inst =
216     let iname = Instance.idx inst
217         pnode = Instance.pnode inst
218         new_slist = delete iname (slist t)
219         new_dsk = f_dsk t + Instance.dsk inst
220         old_peers = peers t
221         old_peem = PeerMap.find pnode old_peers
222         new_peem =  old_peem - (Instance.mem inst)
223         new_peers = PeerMap.add pnode new_peem old_peers
224         old_rmem = r_mem t
225         new_rmem = if old_peem < old_rmem then
226                        old_rmem
227                    else
228                        computeMaxRes new_peers
229         new_prem = (fromIntegral new_rmem) / (t_mem t)
230         new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
231         new_dp = (fromIntegral new_dsk) / (t_dsk t)
232     in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
233           failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
234           p_rem = new_prem}
235
236 -- | Adds a primary instance.
237 addPri :: Node -> Instance.Instance -> Maybe Node
238 addPri t inst =
239     let iname = Instance.idx inst
240         new_mem = f_mem t - Instance.mem inst
241         new_dsk = f_dsk t - Instance.dsk inst
242         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk in
243       if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
244         Nothing
245       else
246         let new_plist = iname:(plist t)
247             new_mp = (fromIntegral new_mem) / (t_mem t)
248             new_dp = (fromIntegral new_dsk) / (t_dsk t)
249         in
250         Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
251                 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
252
253 -- | Adds a secondary instance.
254 addSec :: Node -> Instance.Instance -> T.Ndx -> Maybe Node
255 addSec t inst pdx =
256     let iname = Instance.idx inst
257         old_peers = peers t
258         old_mem = f_mem t
259         new_dsk = f_dsk t - Instance.dsk inst
260         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
261         new_peers = PeerMap.add pdx new_peem old_peers
262         new_rmem = max (r_mem t) new_peem
263         new_prem = (fromIntegral new_rmem) / (t_mem t)
264         new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
265     if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
266         Nothing
267     else
268         let new_slist = iname:(slist t)
269             new_dp = (fromIntegral new_dsk) / (t_dsk t)
270         in
271         Just t {slist = new_slist, f_dsk = new_dsk,
272                 peers = new_peers, failN1 = new_failn1,
273                 r_mem = new_rmem, p_dsk = new_dp,
274                 p_rem = new_prem}
275
276 -- * Display functions
277
278 -- | String converter for the node list functionality.
279 list :: Int -> Node -> String
280 list mname t =
281     let pl = plist t
282         sl = slist t
283         mp = p_mem t
284         dp = p_dsk t
285         off = offline t
286         fn = failN1 t
287         tmem = t_mem t
288         nmem = n_mem t
289         xmem = x_mem t
290         fmem = f_mem t
291         imem = (truncate tmem) - nmem - xmem - fmem
292     in
293       printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
294                  (if off then '-' else if fn then '*' else ' ')
295                  mname (name t) tmem nmem imem xmem fmem (r_mem t)
296                  ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
297                  (length pl) (length sl)
298                  mp dp