c1d5bc85eddb28a10a7a96de885d94361b0ad60a
[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, 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     -- * Statistics
21     , normUsed
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   } deriving (Show)
48
49 {- | Create a new node.
50
51 The index and the peers maps are empty, and will be need to be update
52 later via the 'setIdx' and 'buildPeers' functions.
53
54 -}
55 create :: String -> String -> String -> String -> [Int] -> [Int] -> Node
56 create mem_t_init mem_f_init disk_t_init disk_f_init
57        plist_init slist_init = Node
58     {
59       t_mem = read mem_t_init,
60       f_mem = read mem_f_init,
61       t_disk = read disk_t_init,
62       f_disk = read disk_f_init,
63       plist = plist_init,
64       slist = slist_init,
65       failN1 = True,
66       idx = -1,
67       peers = PeerMap.empty,
68       maxRes = 0
69     }
70
71 -- | Changes the index.
72 -- This is used only during the building of the data structures.
73 setIdx :: Node -> Int -> Node
74 setIdx t i = t {idx = i}
75
76 -- | Given the rmem, free memory and disk, computes the failn1 status.
77 computeFailN1 :: Int -> Int -> Int -> Bool
78 computeFailN1 new_rmem new_mem new_disk =
79     new_mem <= new_rmem || new_disk <= 0
80
81
82 -- | Computes the maximum reserved memory for peers from a peer map.
83 computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
84 computeMaxRes new_peers = PeerMap.maxElem new_peers
85
86 -- | Builds the peer map for a given node.
87 buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
88 buildPeers t il num_nodes =
89     let mdata = map
90                 (\i_idx -> let inst = Container.find i_idx il
91                            in (Instance.pnode inst, Instance.mem inst))
92                 (slist t)
93         pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
94         new_rmem = computeMaxRes pmap
95         new_failN1 = computeFailN1 new_rmem (f_mem t) (f_disk t)
96     in t {peers=pmap, failN1 = new_failN1, maxRes = new_rmem}
97
98 -- | Removes a primary instance.
99 removePri :: Node -> Instance.Instance -> Node
100 removePri t inst =
101     let iname = Instance.idx inst
102         new_plist = delete iname (plist t)
103         new_mem = f_mem t + Instance.mem inst
104         new_disk = f_disk t + Instance.disk inst
105         new_failn1 = computeFailN1 (maxRes t) new_mem new_disk
106     in t {plist = new_plist, f_mem = new_mem, f_disk = new_disk,
107           failN1 = new_failn1}
108
109 -- | Removes a secondary instance.
110 removeSec :: Node -> Instance.Instance -> Node
111 removeSec t inst =
112     let iname = Instance.idx inst
113         pnode = Instance.pnode inst
114         new_slist = delete iname (slist t)
115         new_disk = f_disk t + Instance.disk inst
116         old_peers = peers t
117         old_peem = PeerMap.find pnode old_peers
118         new_peem =  old_peem - (Instance.mem inst)
119         new_peers = PeerMap.add pnode new_peem old_peers
120         old_rmem = maxRes t
121         new_rmem = if old_peem < old_rmem then
122                        old_rmem
123                    else
124                        computeMaxRes new_peers
125         new_failn1 = computeFailN1 new_rmem (f_mem t) new_disk
126     in t {slist = new_slist, f_disk = new_disk, peers = new_peers,
127           failN1 = new_failn1, maxRes = new_rmem}
128
129 -- | Adds a primary instance.
130 addPri :: Node -> Instance.Instance -> Maybe Node
131 addPri t inst =
132     let iname = Instance.idx inst
133         new_mem = f_mem t - Instance.mem inst
134         new_disk = f_disk t - Instance.disk inst
135         new_failn1 = computeFailN1 (maxRes t) new_mem new_disk in
136       if new_failn1 then
137         Nothing
138       else
139         let new_plist = iname:(plist t) in
140         Just t {plist = new_plist, f_mem = new_mem, f_disk = new_disk,
141                 failN1 = new_failn1}
142
143 -- | Adds a secondary instance.
144 addSec :: Node -> Instance.Instance -> Int -> Maybe Node
145 addSec t inst pdx =
146     let iname = Instance.idx inst
147         old_peers = peers t
148         new_disk = f_disk t - Instance.disk inst
149         new_peem = PeerMap.find pdx old_peers + Instance.mem inst
150         new_peers = PeerMap.add pdx new_peem old_peers
151         new_rmem = max (maxRes t) new_peem
152         new_failn1 = computeFailN1 new_rmem (f_mem t) new_disk in
153     if new_failn1 then
154         Nothing
155     else
156         let new_slist = iname:(slist t) in
157         Just t {slist = new_slist, f_disk = new_disk,
158                 peers = new_peers, failN1 = new_failn1,
159                 maxRes = new_rmem}
160
161 -- | Simple converter to string.
162 str :: Node -> String
163 str t =
164     printf ("Node %d (mem=%5d MiB, disk=%5.2f GiB)\n  Primaries:" ++
165             " %s\nSecondaries: %s")
166       (idx t) (f_mem t) ((f_disk t) `div` 1024)
167       (commaJoin (map show (plist t)))
168       (commaJoin (map show (slist t)))
169
170 -- | String converter for the node list functionality.
171 list :: String -> Node -> String
172 list n t =
173     let pl = plist t
174         sl = slist t
175         (mp, dp) = normUsed t
176     in
177       printf "  %s(%d)\t%5d\t%5d\t%3d\t%3d\t%s\t%s\t%.5f\t%.5f"
178                  n (idx t) (f_mem t) ((f_disk t) `div` 1024)
179                  (length pl) (length sl)
180                  (commaJoin (map show pl))
181                  (commaJoin (map show sl))
182                  mp dp
183
184 -- | Normalize the usage status
185 -- This converts the used memory and disk values into a normalized integer
186 -- value, currently expresed as per mille of totals
187
188 normUsed :: Node -> (Double, Double)
189 normUsed n =
190     let mp = (fromIntegral $ f_mem n) / (fromIntegral $ t_mem n)
191         dp = (fromIntegral $ f_disk n) / (fromIntegral $ t_disk n)
192     in (mp, dp)