Fix interaction between down instances and nodes
[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 val = t { f_mem = val }
105
106 -- | Given the rmem, free memory and disk, computes the failn1 status.
107 computeFailN1 :: Int -> Int -> Int -> Bool
108 computeFailN1 new_rmem new_mem new_dsk =
109     new_mem <= new_rmem || new_dsk <= 0
110
111 -- | Given the new free memory and disk, fail if any of them is below zero.
112 failHealth :: Int -> Int -> Bool
113 failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
114
115 -- | Computes the maximum reserved memory for peers from a peer map.
116 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
117 computeMaxRes new_peers = PeerMap.maxElem new_peers
118
119 -- | Builds the peer map for a given node.
120 buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
121 buildPeers t il num_nodes =
122     let mdata = map
123                 (\i_idx -> let inst = Container.find i_idx il
124                            in (Instance.pnode inst, Instance.mem inst))
125                 (slist t)
126         pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
127         new_rmem = computeMaxRes pmap
128         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
129         new_prem = (fromIntegral new_rmem) / (t_mem t)
130     in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
131
132 -- | Removes a primary instance.
133 removePri :: Node -> Instance.Instance -> Node
134 removePri t inst =
135     let iname = Instance.idx inst
136         new_plist = delete iname (plist t)
137         new_mem = f_mem t + Instance.mem inst
138         new_dsk = f_dsk t + Instance.dsk inst
139         new_mp = (fromIntegral new_mem) / (t_mem t)
140         new_dp = (fromIntegral new_dsk) / (t_dsk t)
141         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
142     in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
143           failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
144
145 -- | Removes a secondary instance.
146 removeSec :: Node -> Instance.Instance -> Node
147 removeSec t inst =
148     let iname = Instance.idx inst
149         pnode = Instance.pnode inst
150         new_slist = delete iname (slist t)
151         new_dsk = f_dsk t + Instance.dsk inst
152         old_peers = peers t
153         old_peem = PeerMap.find pnode old_peers
154         new_peem =  old_peem - (Instance.mem inst)
155         new_peers = PeerMap.add pnode new_peem old_peers
156         old_rmem = r_mem t
157         new_rmem = if old_peem < old_rmem then
158                        old_rmem
159                    else
160                        computeMaxRes new_peers
161         new_prem = (fromIntegral new_rmem) / (t_mem t)
162         new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
163         new_dp = (fromIntegral new_dsk) / (t_dsk t)
164     in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
165           failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
166           p_rem = new_prem}
167
168 -- | Adds a primary instance.
169 addPri :: Node -> Instance.Instance -> Maybe Node
170 addPri t inst =
171     let iname = Instance.idx inst
172         new_mem = f_mem t - Instance.mem inst
173         new_dsk = f_dsk t - Instance.dsk inst
174         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk in
175       if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
176         Nothing
177       else
178         let new_plist = iname:(plist t)
179             new_mp = (fromIntegral new_mem) / (t_mem t)
180             new_dp = (fromIntegral new_dsk) / (t_dsk t)
181         in
182         Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
183                 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
184
185 -- | Adds a secondary instance.
186 addSec :: Node -> Instance.Instance -> Int -> Maybe Node
187 addSec t inst pdx =
188     let iname = Instance.idx inst
189         old_peers = peers t
190         old_mem = f_mem t
191         new_dsk = f_dsk t - Instance.dsk inst
192         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
193         new_peers = PeerMap.add pdx new_peem old_peers
194         new_rmem = max (r_mem t) new_peem
195         new_prem = (fromIntegral new_rmem) / (t_mem t)
196         new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
197     if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
198         Nothing
199     else
200         let new_slist = iname:(slist t)
201             new_dp = (fromIntegral new_dsk) / (t_dsk t)
202         in
203         Just t {slist = new_slist, f_dsk = new_dsk,
204                 peers = new_peers, failN1 = new_failn1,
205                 r_mem = new_rmem, p_dsk = new_dp,
206                 p_rem = new_prem}
207
208 -- | Add a primary instance to a node without other updates
209 setPri :: Node -> Int -> Node
210 setPri t idx = t { plist = idx:(plist t) }
211
212 -- | Add a secondary instance to a node without other updates
213 setSec :: Node -> Int -> Node
214 setSec t idx = t { slist = idx:(slist t) }
215
216 -- | String converter for the node list functionality.
217 list :: Int -> String -> Node -> String
218 list mname n t =
219     let pl = plist t
220         sl = slist t
221         mp = p_mem t
222         dp = p_dsk t
223         off = offline t
224         fn = failN1 t
225         tmem = t_mem t
226         nmem = n_mem t
227         xmem = x_mem t
228         fmem = f_mem t
229         imem = (truncate tmem) - nmem - xmem - fmem
230     in
231       printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
232                  (if off then '-' else if fn then '*' else ' ')
233                  mname n tmem nmem imem xmem fmem (r_mem t)
234                  ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
235                  (length pl) (length sl)
236                  mp dp