Compute the p_mem / p_dsk statically
[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_disk, p_mem, p_dsk, slist, plist)
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 :: Int -- ^ total memory (Mib)
36                  , f_mem :: Int -- ^ free memory (MiB)
37                  , t_disk :: Int -- ^ total disk space (MiB)
38                  , f_disk :: 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   } 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 disk_t_init disk_f_init =
59     let mem_t = read mem_t_init
60         mem_f = read mem_f_init
61         disk_t = read disk_t_init
62         disk_f = read disk_f_init
63     in
64       Node
65       {
66        t_mem = read mem_t_init,
67        f_mem = read mem_f_init,
68        t_disk = read disk_t_init,
69        f_disk = read disk_f_init,
70        plist = [],
71        slist = [],
72        failN1 = True,
73        idx = -1,
74        peers = PeerMap.empty,
75        maxRes = 0,
76        p_mem = (fromIntegral mem_f) / (fromIntegral mem_t),
77        p_dsk = (fromIntegral disk_f) / (fromIntegral disk_t)
78       }
79
80 -- | Changes the index.
81 -- This is used only during the building of the data structures.
82 setIdx :: Node -> Int -> Node
83 setIdx t i = t {idx = i}
84
85 -- | Given the rmem, free memory and disk, computes the failn1 status.
86 computeFailN1 :: Int -> Int -> Int -> Bool
87 computeFailN1 new_rmem new_mem new_disk =
88     new_mem <= new_rmem || new_disk <= 0
89
90 -- | Computes the maximum reserved memory for peers from a peer map.
91 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
92 computeMaxRes new_peers = PeerMap.maxElem new_peers
93
94 -- | Builds the peer map for a given node.
95 buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
96 buildPeers t il num_nodes =
97     let mdata = map
98                 (\i_idx -> let inst = Container.find i_idx il
99                            in (Instance.pnode inst, Instance.mem inst))
100                 (slist t)
101         pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
102         new_rmem = computeMaxRes pmap
103         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_disk t)
104     in t {peers=pmap, failN1 = new_failN1, maxRes = new_rmem}
105
106 -- | Removes a primary instance.
107 removePri :: Node -> Instance.Instance -> Node
108 removePri t inst =
109     let iname = Instance.idx inst
110         new_plist = delete iname (plist t)
111         new_mem = f_mem t + Instance.mem inst
112         new_disk = f_disk t + Instance.disk inst
113         new_mp = (fromIntegral new_mem) / (fromIntegral $ t_mem t)
114         new_dp = (fromIntegral new_disk) / (fromIntegral $ t_disk t)
115         new_failn1 = computeFailN1 (maxRes t) new_mem new_disk
116     in t {plist = new_plist, f_mem = new_mem, f_disk = new_disk,
117           failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
118
119 -- | Removes a secondary instance.
120 removeSec :: Node -> Instance.Instance -> Node
121 removeSec t inst =
122     let iname = Instance.idx inst
123         pnode = Instance.pnode inst
124         new_slist = delete iname (slist t)
125         new_disk = f_disk t + Instance.disk inst
126         old_peers = peers t
127         old_peem = PeerMap.find pnode old_peers
128         new_peem =  old_peem - (Instance.mem inst)
129         new_peers = PeerMap.add pnode new_peem old_peers
130         old_rmem = maxRes t
131         new_rmem = if old_peem < old_rmem then
132                        old_rmem
133                    else
134                        computeMaxRes new_peers
135         new_failn1 = computeFailN1 new_rmem (f_mem t) new_disk
136         new_dp = (fromIntegral new_disk) / (fromIntegral $ t_disk t)
137     in t {slist = new_slist, f_disk = new_disk, peers = new_peers,
138           failN1 = new_failn1, maxRes = new_rmem, p_dsk = new_dp}
139
140 -- | Adds a primary instance.
141 addPri :: Node -> Instance.Instance -> Maybe Node
142 addPri t inst =
143     let iname = Instance.idx inst
144         new_mem = f_mem t - Instance.mem inst
145         new_disk = f_disk t - Instance.disk inst
146         new_failn1 = computeFailN1 (maxRes t) new_mem new_disk in
147       if new_failn1 then
148         Nothing
149       else
150         let new_plist = iname:(plist t)
151             new_mp = (fromIntegral new_mem) / (fromIntegral $ t_mem t)
152             new_dp = (fromIntegral new_disk) / (fromIntegral $ t_disk t)
153         in
154         Just t {plist = new_plist, f_mem = new_mem, f_disk = new_disk,
155                 failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
156
157 -- | Adds a secondary instance.
158 addSec :: Node -> Instance.Instance -> Int -> Maybe Node
159 addSec t inst pdx =
160     let iname = Instance.idx inst
161         old_peers = peers t
162         new_disk = f_disk t - Instance.disk inst
163         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
164         new_peers = PeerMap.add pdx new_peem old_peers
165         new_rmem = max (maxRes t) new_peem
166         new_failn1 = computeFailN1 new_rmem (f_mem t) new_disk in
167     if new_failn1 then
168         Nothing
169     else
170         let new_slist = iname:(slist t)
171             new_dp = (fromIntegral new_disk) / (fromIntegral $ t_disk t)
172         in
173         Just t {slist = new_slist, f_disk = new_disk,
174                 peers = new_peers, failN1 = new_failn1,
175                 maxRes = new_rmem, p_dsk = new_dp}
176
177 -- | Add a primary instance to a node without other updates
178 setPri :: Node -> Int -> Node
179 setPri t idx = t { plist = idx:(plist t) }
180
181 -- | Add a secondary instance to a node without other updates
182 setSec :: Node -> Int -> Node
183 setSec t idx = t { slist = idx:(slist t) }
184
185 -- | Simple converter to string.
186 str :: Node -> String
187 str t =
188     printf ("Node %d (mem=%5d MiB, disk=%5.2f GiB)\n  Primaries:" ++
189             " %s\nSecondaries: %s")
190       (idx t) (f_mem t) ((f_disk t) `div` 1024)
191       (commaJoin (map show (plist t)))
192       (commaJoin (map show (slist t)))
193
194 -- | String converter for the node list functionality.
195 list :: String -> Node -> String
196 list n t =
197     let pl = plist t
198         sl = slist t
199         mp = p_mem t
200         dp = p_dsk t
201     in
202       printf "  %s(%d)\t%5d\t%5d\t%3d\t%3d\t%s\t%s\t%.5f\t%.5f"
203                  n (idx t) (f_mem t) ((f_disk t) `div` 1024)
204                  (length pl) (length sl)
205                  (commaJoin (map show pl))
206                  (commaJoin (map show sl))
207                  mp dp