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