Node.hs 9.17 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
3
4
5
6
{-| Module describing a node.

    All updates are functional (copy-based) and return a new node with
    updated value.
-}

7
module Ganeti.HTools.Node
8
    ( Node(failN1, name, idx, t_mem, n_mem, f_mem, t_dsk, f_dsk,
9
           p_mem, p_dsk, p_rem,
10
           plist, slist, offline)
11
    , List
Iustin Pop's avatar
Iustin Pop committed
12
13
14
15
16
    -- * Constructor
    , create
    -- ** Finalization after data loading
    , buildPeers
    , setIdx
17
    , setName
18
    , setOffline
Iustin Pop's avatar
Iustin Pop committed
19
    , setXmem
20
    , setFmem
Iustin Pop's avatar
Iustin Pop committed
21
22
23
24
25
    -- * Instance (re)location
    , removePri
    , removeSec
    , addPri
    , addSec
Iustin Pop's avatar
Iustin Pop committed
26
27
    , setPri
    , setSec
Iustin Pop's avatar
Iustin Pop committed
28
29
    -- * Formatting
    , list
Iustin Pop's avatar
Iustin Pop committed
30
31
32
    -- * Misc stuff
    , AssocList
    , noSecondary
Iustin Pop's avatar
Iustin Pop committed
33
34
35
36
37
    ) where

import Data.List
import Text.Printf (printf)

38
39
40
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.PeerMap as PeerMap
Iustin Pop's avatar
Iustin Pop committed
41

42
43
import qualified Ganeti.HTools.Types as T

Iustin Pop's avatar
Iustin Pop committed
44
45
data Node = Node { name  :: String -- ^ the node name
                 , t_mem :: Double -- ^ total memory (MiB)
46
                 , n_mem :: Int    -- ^ node memory (MiB)
47
                 , f_mem :: Int    -- ^ free memory (MiB)
Iustin Pop's avatar
Iustin Pop committed
48
                 , x_mem :: Int    -- ^ unaccounted memory (MiB)
49
50
51
52
                 , t_dsk :: Double -- ^ total disk space (MiB)
                 , f_dsk :: Int    -- ^ free disk space (MiB)
                 , plist :: [Int]  -- ^ list of primary instance indices
                 , slist :: [Int]  -- ^ list of secondary instance indices
Iustin Pop's avatar
Iustin Pop committed
53
                 , idx :: Int      -- ^ internal index for book-keeping
Iustin Pop's avatar
Iustin Pop committed
54
55
56
57
58
59
60
                 , peers :: PeerMap.PeerMap -- ^ pnode to instance mapping
                 , failN1:: Bool   -- ^ whether the node has failed n1
                 , r_mem :: Int    -- ^ maximum memory needed for
                                   -- failover by primaries of this node
                 , p_mem :: Double -- ^ percent of free memory
                 , p_dsk :: Double -- ^ percent of free disk
                 , p_rem :: Double -- ^ percent of reserved memory
61
62
63
                 , offline :: Bool -- ^ whether the node should not be used
                                   -- for allocations and skipped from
                                   -- score computations
Iustin Pop's avatar
Iustin Pop committed
64
65
  } deriving (Show)

66
67
68
69
70
71
instance T.Element Node where
    nameOf = name
    idxOf = idx
    setName = setName
    setIdx = setIdx

Iustin Pop's avatar
Iustin Pop committed
72
73
74
-- | A simple name for the int, node association list
type AssocList = [(Int, Node)]

75
76
77
-- | A simple name for a node map
type List = Container.Container Node

Iustin Pop's avatar
Iustin Pop committed
78
79
80
81
-- | Constant node index for a non-moveable instance
noSecondary :: Int
noSecondary = -1

Iustin Pop's avatar
Iustin Pop committed
82
83
84
85
86
87
{- | Create a new node.

The index and the peers maps are empty, and will be need to be update
later via the 'setIdx' and 'buildPeers' functions.

-}
Iustin Pop's avatar
Iustin Pop committed
88
89
90
create :: String -> Double -> Int -> Int -> Double -> Int -> Bool -> Node
create name_init mem_t_init mem_n_init mem_f_init
       dsk_t_init dsk_f_init offline_init =
91
92
    Node
    {
Iustin Pop's avatar
Iustin Pop committed
93
      name  = name_init,
94
      t_mem = mem_t_init,
95
      n_mem = mem_n_init,
96
97
98
99
100
101
102
103
104
105
106
107
      f_mem = mem_f_init,
      t_dsk = dsk_t_init,
      f_dsk = dsk_f_init,
      plist = [],
      slist = [],
      failN1 = True,
      idx = -1,
      peers = PeerMap.empty,
      r_mem = 0,
      p_mem = (fromIntegral mem_f_init) / mem_t_init,
      p_dsk = (fromIntegral dsk_f_init) / dsk_t_init,
      p_rem = 0,
Iustin Pop's avatar
Iustin Pop committed
108
      offline = offline_init,
Iustin Pop's avatar
Iustin Pop committed
109
      x_mem = 0
110
    }
Iustin Pop's avatar
Iustin Pop committed
111
112
113
114
115
116

-- | Changes the index.
-- This is used only during the building of the data structures.
setIdx :: Node -> Int -> Node
setIdx t i = t {idx = i}

117
118
119
120
-- | Changes the name
-- This is used only during the building of the data structures.
setName t s = t {name = s}

121
122
123
124
-- | Sets the offline attribute
setOffline :: Node -> Bool -> Node
setOffline t val = t { offline = val }

Iustin Pop's avatar
Iustin Pop committed
125
126
127
128
-- | Sets the unnaccounted memory
setXmem :: Node -> Int -> Node
setXmem t val = t { x_mem = val }

129
130
-- | Sets the free memory
setFmem :: Node -> Int -> Node
131
132
133
134
135
setFmem t new_mem =
    let new_n1 = computeFailN1 (r_mem t) new_mem (f_dsk t)
        new_mp = (fromIntegral new_mem) / (t_mem t)
    in
      t { f_mem = new_mem, failN1 = new_n1, p_mem = new_mp }
136

Iustin Pop's avatar
Iustin Pop committed
137
138
-- | Given the rmem, free memory and disk, computes the failn1 status.
computeFailN1 :: Int -> Int -> Int -> Bool
Iustin Pop's avatar
Iustin Pop committed
139
140
computeFailN1 new_rmem new_mem new_dsk =
    new_mem <= new_rmem || new_dsk <= 0
Iustin Pop's avatar
Iustin Pop committed
141

Iustin Pop's avatar
Iustin Pop committed
142
143
144
145
-- | Given the new free memory and disk, fail if any of them is below zero.
failHealth :: Int -> Int -> Bool
failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0

Iustin Pop's avatar
Iustin Pop committed
146
147
148
149
150
151
152
153
154
155
156
157
158
-- | Computes the maximum reserved memory for peers from a peer map.
computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
computeMaxRes new_peers = PeerMap.maxElem new_peers

-- | Builds the peer map for a given node.
buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
buildPeers t il num_nodes =
    let mdata = map
                (\i_idx -> let inst = Container.find i_idx il
                           in (Instance.pnode inst, Instance.mem inst))
                (slist t)
        pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
        new_rmem = computeMaxRes pmap
Iustin Pop's avatar
Iustin Pop committed
159
        new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
160
        new_prem = (fromIntegral new_rmem) / (t_mem t)
Iustin Pop's avatar
Iustin Pop committed
161
    in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
Iustin Pop's avatar
Iustin Pop committed
162
163
164
165
166
167
168

-- | Removes a primary instance.
removePri :: Node -> Instance.Instance -> Node
removePri t inst =
    let iname = Instance.idx inst
        new_plist = delete iname (plist t)
        new_mem = f_mem t + Instance.mem inst
Iustin Pop's avatar
Iustin Pop committed
169
        new_dsk = f_dsk t + Instance.dsk inst
170
171
        new_mp = (fromIntegral new_mem) / (t_mem t)
        new_dp = (fromIntegral new_dsk) / (t_dsk t)
Iustin Pop's avatar
Iustin Pop committed
172
        new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
Iustin Pop's avatar
Iustin Pop committed
173
    in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
174
          failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
Iustin Pop's avatar
Iustin Pop committed
175
176
177
178
179
180
181

-- | Removes a secondary instance.
removeSec :: Node -> Instance.Instance -> Node
removeSec t inst =
    let iname = Instance.idx inst
        pnode = Instance.pnode inst
        new_slist = delete iname (slist t)
Iustin Pop's avatar
Iustin Pop committed
182
        new_dsk = f_dsk t + Instance.dsk inst
Iustin Pop's avatar
Iustin Pop committed
183
184
185
186
        old_peers = peers t
        old_peem = PeerMap.find pnode old_peers
        new_peem =  old_peem - (Instance.mem inst)
        new_peers = PeerMap.add pnode new_peem old_peers
Iustin Pop's avatar
Iustin Pop committed
187
        old_rmem = r_mem t
Iustin Pop's avatar
Iustin Pop committed
188
189
190
191
        new_rmem = if old_peem < old_rmem then
                       old_rmem
                   else
                       computeMaxRes new_peers
192
        new_prem = (fromIntegral new_rmem) / (t_mem t)
Iustin Pop's avatar
Iustin Pop committed
193
        new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
194
        new_dp = (fromIntegral new_dsk) / (t_dsk t)
Iustin Pop's avatar
Iustin Pop committed
195
    in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
Iustin Pop's avatar
Iustin Pop committed
196
          failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
197
          p_rem = new_prem}
Iustin Pop's avatar
Iustin Pop committed
198
199
200
201
202
203

-- | Adds a primary instance.
addPri :: Node -> Instance.Instance -> Maybe Node
addPri t inst =
    let iname = Instance.idx inst
        new_mem = f_mem t - Instance.mem inst
Iustin Pop's avatar
Iustin Pop committed
204
        new_dsk = f_dsk t - Instance.dsk inst
Iustin Pop's avatar
Iustin Pop committed
205
        new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk in
Iustin Pop's avatar
Iustin Pop committed
206
      if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
Iustin Pop's avatar
Iustin Pop committed
207
208
        Nothing
      else
209
        let new_plist = iname:(plist t)
210
211
            new_mp = (fromIntegral new_mem) / (t_mem t)
            new_dp = (fromIntegral new_dsk) / (t_dsk t)
212
        in
Iustin Pop's avatar
Iustin Pop committed
213
        Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
214
                failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
Iustin Pop's avatar
Iustin Pop committed
215
216
217
218
219
220

-- | Adds a secondary instance.
addSec :: Node -> Instance.Instance -> Int -> Maybe Node
addSec t inst pdx =
    let iname = Instance.idx inst
        old_peers = peers t
Iustin Pop's avatar
Iustin Pop committed
221
        old_mem = f_mem t
Iustin Pop's avatar
Iustin Pop committed
222
        new_dsk = f_dsk t - Instance.dsk inst
Iustin Pop's avatar
Iustin Pop committed
223
224
        new_peem = PeerMap.find pdx old_peers + Instance.mem inst
        new_peers = PeerMap.add pdx new_peem old_peers
Iustin Pop's avatar
Iustin Pop committed
225
        new_rmem = max (r_mem t) new_peem
226
        new_prem = (fromIntegral new_rmem) / (t_mem t)
Iustin Pop's avatar
Iustin Pop committed
227
228
        new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
    if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
Iustin Pop's avatar
Iustin Pop committed
229
230
        Nothing
    else
231
        let new_slist = iname:(slist t)
232
            new_dp = (fromIntegral new_dsk) / (t_dsk t)
233
        in
Iustin Pop's avatar
Iustin Pop committed
234
        Just t {slist = new_slist, f_dsk = new_dsk,
Iustin Pop's avatar
Iustin Pop committed
235
                peers = new_peers, failN1 = new_failn1,
Iustin Pop's avatar
Iustin Pop committed
236
                r_mem = new_rmem, p_dsk = new_dp,
237
                p_rem = new_prem}
Iustin Pop's avatar
Iustin Pop committed
238

Iustin Pop's avatar
Iustin Pop committed
239
240
241
242
243
244
245
246
-- | Add a primary instance to a node without other updates
setPri :: Node -> Int -> Node
setPri t idx = t { plist = idx:(plist t) }

-- | Add a secondary instance to a node without other updates
setSec :: Node -> Int -> Node
setSec t idx = t { slist = idx:(slist t) }

Iustin Pop's avatar
Iustin Pop committed
247
-- | String converter for the node list functionality.
248
249
list :: Int -> Node -> String
list mname t =
Iustin Pop's avatar
Iustin Pop committed
250
251
    let pl = plist t
        sl = slist t
252
253
        mp = p_mem t
        dp = p_dsk t
254
        off = offline t
255
        fn = failN1 t
Iustin Pop's avatar
Iustin Pop committed
256
257
258
259
260
        tmem = t_mem t
        nmem = n_mem t
        xmem = x_mem t
        fmem = f_mem t
        imem = (truncate tmem) - nmem - xmem - fmem
Iustin Pop's avatar
Iustin Pop committed
261
    in
Iustin Pop's avatar
Iustin Pop committed
262
      printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
263
                 (if off then '-' else if fn then '*' else ' ')
264
                 mname (name t) tmem nmem imem xmem fmem (r_mem t)
265
                 ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
Iustin Pop's avatar
Iustin Pop committed
266
267
                 (length pl) (length sl)
                 mp dp