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