60230f0deb473bb86cbb58cd266607ce707fc32d
[ganeti-local] / src / 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 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 Container
30 import qualified Instance
31 import qualified PeerMap
32
33 import 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 -- ^ primary node to instance
43                                            -- mapping
44                  , failN1:: Bool -- ^ whether the node has failed n1
45                  , maxRes :: Int -- ^ maximum memory needed for
46                                  -- failover by primaries of this node
47                  , p_mem :: Double
48                  , p_dsk :: Double
49                  , p_rem :: Double
50   } deriving (Show)
51
52 {- | Create a new node.
53
54 The index and the peers maps are empty, and will be need to be update
55 later via the 'setIdx' and 'buildPeers' functions.
56
57 -}
58 create :: String -> String -> String -> String -> Node
59 create mem_t_init mem_f_init dsk_t_init dsk_f_init =
60     let mem_t = read mem_t_init
61         mem_f = read mem_f_init
62         dsk_t = read dsk_t_init
63         dsk_f = read dsk_f_init
64     in
65       Node
66       {
67        t_mem = read mem_t_init,
68        f_mem = read mem_f_init,
69        t_dsk = read dsk_t_init,
70        f_dsk = read dsk_f_init,
71        plist = [],
72        slist = [],
73        failN1 = True,
74        idx = -1,
75        peers = PeerMap.empty,
76        maxRes = 0,
77        p_mem = (fromIntegral mem_f) / (fromIntegral mem_t),
78        p_dsk = (fromIntegral dsk_f) / (fromIntegral dsk_t),
79        p_rem = 0
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 -- | Given the rmem, free memory and disk, computes the failn1 status.
88 computeFailN1 :: Int -> Int -> Int -> Bool
89 computeFailN1 new_rmem new_mem new_dsk =
90     new_mem <= new_rmem || new_dsk <= 0
91
92 -- | Given the new free memory and disk, fail if any of them is below zero.
93 failHealth :: Int -> Int -> Bool
94 failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
95
96 -- | Computes the maximum reserved memory for peers from a peer map.
97 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
98 computeMaxRes new_peers = PeerMap.maxElem new_peers
99
100 -- | Builds the peer map for a given node.
101 buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
102 buildPeers t il num_nodes =
103     let mdata = map
104                 (\i_idx -> let inst = Container.find i_idx il
105                            in (Instance.pnode inst, Instance.mem inst))
106                 (slist t)
107         pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
108         new_rmem = computeMaxRes pmap
109         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
110         new_prem = (fromIntegral new_rmem) / (t_mem t)
111     in t {peers=pmap, failN1 = new_failN1, maxRes = new_rmem, p_rem = new_prem}
112
113 -- | Removes a primary instance.
114 removePri :: Node -> Instance.Instance -> Node
115 removePri t inst =
116     let iname = Instance.idx inst
117         new_plist = delete iname (plist t)
118         new_mem = f_mem t + Instance.mem inst
119         new_dsk = f_dsk t + Instance.dsk inst
120         new_mp = (fromIntegral new_mem) / (t_mem t)
121         new_dp = (fromIntegral new_dsk) / (t_dsk t)
122         new_failn1 = computeFailN1 (maxRes t) new_mem new_dsk
123     in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
124           failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
125
126 -- | Removes a secondary instance.
127 removeSec :: Node -> Instance.Instance -> Node
128 removeSec t inst =
129     let iname = Instance.idx inst
130         pnode = Instance.pnode inst
131         new_slist = delete iname (slist t)
132         new_dsk = f_dsk t + Instance.dsk inst
133         old_peers = peers t
134         old_peem = PeerMap.find pnode old_peers
135         new_peem =  old_peem - (Instance.mem inst)
136         new_peers = PeerMap.add pnode new_peem old_peers
137         old_rmem = maxRes t
138         new_rmem = if old_peem < old_rmem then
139                        old_rmem
140                    else
141                        computeMaxRes new_peers
142         new_prem = (fromIntegral new_rmem) / (t_mem t)
143         new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
144         new_dp = (fromIntegral new_dsk) / (t_dsk t)
145     in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
146           failN1 = new_failn1, maxRes = new_rmem, p_dsk = new_dp,
147           p_rem = new_prem}
148
149 -- | Adds a primary instance.
150 addPri :: Node -> Instance.Instance -> Maybe Node
151 addPri t inst =
152     let iname = Instance.idx inst
153         new_mem = f_mem t - Instance.mem inst
154         new_dsk = f_dsk t - Instance.dsk inst
155         new_failn1 = computeFailN1 (maxRes t) new_mem new_dsk in
156       if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
157         Nothing
158       else
159         let new_plist = iname:(plist t)
160             new_mp = (fromIntegral new_mem) / (t_mem t)
161             new_dp = (fromIntegral new_dsk) / (t_dsk t)
162         in
163         Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
164                 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
165
166 -- | Adds a secondary instance.
167 addSec :: Node -> Instance.Instance -> Int -> Maybe Node
168 addSec t inst pdx =
169     let iname = Instance.idx inst
170         old_peers = peers t
171         old_mem = f_mem t
172         new_dsk = f_dsk t - Instance.dsk inst
173         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
174         new_peers = PeerMap.add pdx new_peem old_peers
175         new_rmem = max (maxRes t) new_peem
176         new_prem = (fromIntegral new_rmem) / (t_mem t)
177         new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
178     if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
179         Nothing
180     else
181         let new_slist = iname:(slist t)
182             new_dp = (fromIntegral new_dsk) / (t_dsk t)
183         in
184         Just t {slist = new_slist, f_dsk = new_dsk,
185                 peers = new_peers, failN1 = new_failn1,
186                 maxRes = new_rmem, p_dsk = new_dp,
187                 p_rem = new_prem}
188
189 -- | Add a primary instance to a node without other updates
190 setPri :: Node -> Int -> Node
191 setPri t idx = t { plist = idx:(plist t) }
192
193 -- | Add a secondary instance to a node without other updates
194 setSec :: Node -> Int -> Node
195 setSec t idx = t { slist = idx:(slist t) }
196
197 -- | Simple converter to string.
198 str :: Node -> String
199 str t =
200     printf ("Node %d (mem=%5d MiB, disk=%5.2f GiB)\n  Primaries:" ++
201             " %s\nSecondaries: %s")
202       (idx t) (f_mem t) ((f_dsk t) `div` 1024)
203       (commaJoin (map show (plist t)))
204       (commaJoin (map show (slist t)))
205
206 -- | String converter for the node list functionality.
207 list :: Int -> String -> Node -> String
208 list mname n t =
209     let pl = plist t
210         sl = slist t
211         mp = p_mem t
212         dp = p_dsk t
213         fn = failN1 t
214     in
215       printf " %c %-*s %5d %5d %5d %3d %3d %.5f %.5f"
216                  (if fn then '*' else ' ')
217                  mname n (f_mem t) (maxRes t) ((f_dsk t) `div` 1024)
218                  (length pl) (length sl)
219                  mp dp