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