Add support for 'offline' 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 -> Bool -> Node
68 create mem_t_init mem_n_init mem_f_init dsk_t_init dsk_f_init
69        offline_init =
70     Node
71     {
72       t_mem = mem_t_init,
73       n_mem = mem_n_init,
74       f_mem = mem_f_init,
75       t_dsk = dsk_t_init,
76       f_dsk = dsk_f_init,
77       plist = [],
78       slist = [],
79       failN1 = True,
80       idx = -1,
81       peers = PeerMap.empty,
82       r_mem = 0,
83       p_mem = (fromIntegral mem_f_init) / mem_t_init,
84       p_dsk = (fromIntegral dsk_f_init) / dsk_t_init,
85       p_rem = 0,
86       offline = offline_init,
87       x_mem = 0
88     }
89
90 -- | Changes the index.
91 -- This is used only during the building of the data structures.
92 setIdx :: Node -> Int -> Node
93 setIdx t i = t {idx = i}
94
95 -- | Sets the offline attribute
96 setOffline :: Node -> Bool -> Node
97 setOffline t val = t { offline = val }
98
99 -- | Sets the unnaccounted memory
100 setXmem :: Node -> Int -> Node
101 setXmem t val = t { x_mem = val }
102
103 -- | Sets the free memory
104 setFmem :: Node -> Int -> Node
105 setFmem t new_mem =
106     let new_n1 = computeFailN1 (r_mem t) new_mem (f_dsk t)
107         new_mp = (fromIntegral new_mem) / (t_mem t)
108     in
109       t { f_mem = new_mem, failN1 = new_n1, p_mem = new_mp }
110
111 -- | Given the rmem, free memory and disk, computes the failn1 status.
112 computeFailN1 :: Int -> Int -> Int -> Bool
113 computeFailN1 new_rmem new_mem new_dsk =
114     new_mem <= new_rmem || new_dsk <= 0
115
116 -- | Given the new free memory and disk, fail if any of them is below zero.
117 failHealth :: Int -> Int -> Bool
118 failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
119
120 -- | Computes the maximum reserved memory for peers from a peer map.
121 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
122 computeMaxRes new_peers = PeerMap.maxElem new_peers
123
124 -- | Builds the peer map for a given node.
125 buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
126 buildPeers t il num_nodes =
127     let mdata = map
128                 (\i_idx -> let inst = Container.find i_idx il
129                            in (Instance.pnode inst, Instance.mem inst))
130                 (slist t)
131         pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
132         new_rmem = computeMaxRes pmap
133         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
134         new_prem = (fromIntegral new_rmem) / (t_mem t)
135     in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
136
137 -- | Removes a primary instance.
138 removePri :: Node -> Instance.Instance -> Node
139 removePri t inst =
140     let iname = Instance.idx inst
141         new_plist = delete iname (plist t)
142         new_mem = f_mem t + Instance.mem inst
143         new_dsk = f_dsk t + Instance.dsk inst
144         new_mp = (fromIntegral new_mem) / (t_mem t)
145         new_dp = (fromIntegral new_dsk) / (t_dsk t)
146         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
147     in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
148           failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
149
150 -- | Removes a secondary instance.
151 removeSec :: Node -> Instance.Instance -> Node
152 removeSec t inst =
153     let iname = Instance.idx inst
154         pnode = Instance.pnode inst
155         new_slist = delete iname (slist t)
156         new_dsk = f_dsk t + Instance.dsk inst
157         old_peers = peers t
158         old_peem = PeerMap.find pnode old_peers
159         new_peem =  old_peem - (Instance.mem inst)
160         new_peers = PeerMap.add pnode new_peem old_peers
161         old_rmem = r_mem t
162         new_rmem = if old_peem < old_rmem then
163                        old_rmem
164                    else
165                        computeMaxRes new_peers
166         new_prem = (fromIntegral new_rmem) / (t_mem t)
167         new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
168         new_dp = (fromIntegral new_dsk) / (t_dsk t)
169     in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
170           failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
171           p_rem = new_prem}
172
173 -- | Adds a primary instance.
174 addPri :: Node -> Instance.Instance -> Maybe Node
175 addPri t inst =
176     let iname = Instance.idx inst
177         new_mem = f_mem t - Instance.mem inst
178         new_dsk = f_dsk t - Instance.dsk inst
179         new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk in
180       if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
181         Nothing
182       else
183         let new_plist = iname:(plist t)
184             new_mp = (fromIntegral new_mem) / (t_mem t)
185             new_dp = (fromIntegral new_dsk) / (t_dsk t)
186         in
187         Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
188                 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
189
190 -- | Adds a secondary instance.
191 addSec :: Node -> Instance.Instance -> Int -> Maybe Node
192 addSec t inst pdx =
193     let iname = Instance.idx inst
194         old_peers = peers t
195         old_mem = f_mem t
196         new_dsk = f_dsk t - Instance.dsk inst
197         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
198         new_peers = PeerMap.add pdx new_peem old_peers
199         new_rmem = max (r_mem t) new_peem
200         new_prem = (fromIntegral new_rmem) / (t_mem t)
201         new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
202     if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
203         Nothing
204     else
205         let new_slist = iname:(slist t)
206             new_dp = (fromIntegral new_dsk) / (t_dsk t)
207         in
208         Just t {slist = new_slist, f_dsk = new_dsk,
209                 peers = new_peers, failN1 = new_failn1,
210                 r_mem = new_rmem, p_dsk = new_dp,
211                 p_rem = new_prem}
212
213 -- | Add a primary instance to a node without other updates
214 setPri :: Node -> Int -> Node
215 setPri t idx = t { plist = idx:(plist t) }
216
217 -- | Add a secondary instance to a node without other updates
218 setSec :: Node -> Int -> Node
219 setSec t idx = t { slist = idx:(slist t) }
220
221 -- | String converter for the node list functionality.
222 list :: Int -> String -> Node -> String
223 list mname n t =
224     let pl = plist t
225         sl = slist t
226         mp = p_mem t
227         dp = p_dsk t
228         off = offline t
229         fn = failN1 t
230         tmem = t_mem t
231         nmem = n_mem t
232         xmem = x_mem t
233         fmem = f_mem t
234         imem = (truncate tmem) - nmem - xmem - fmem
235     in
236       printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
237                  (if off then '-' else if fn then '*' else ' ')
238                  mname n tmem nmem imem xmem fmem (r_mem t)
239                  ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
240                  (length pl) (length sl)
241                  mp dp