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