Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / Node.hs @ c2c1ef0c

History | View | Annotate | Download (8.2 kB)

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
        fn = failN1 t
223
    in
224
      printf " %c %-*s %5.0f %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
225
                 (if fn then '*' else ' ')
226
                 mname n (t_mem t) (f_mem t) (r_mem t)
227
                 ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
228
                 (length pl) (length sl)
229
                 mp dp