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