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