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