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