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