Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / Node.hs @ 47a8bade

History | View | Annotate | Download (8.1 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, t_mem, f_mem, t_dsk, f_dsk, p_mem, p_dsk, p_rem,
10
           plist, slist, 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 :: Double -> Int -> Double -> Int -> Node
63
create mem_t_init mem_f_init dsk_t_init dsk_f_init =
64
    Node
65
    {
66
      t_mem = mem_t_init,
67
      f_mem = mem_f_init,
68
      t_dsk = dsk_t_init,
69
      f_dsk = dsk_f_init,
70
      plist = [],
71
      slist = [],
72
      failN1 = True,
73
      idx = -1,
74
      peers = PeerMap.empty,
75
      r_mem = 0,
76
      p_mem = (fromIntegral mem_f_init) / mem_t_init,
77
      p_dsk = (fromIntegral dsk_f_init) / dsk_t_init,
78
      p_rem = 0,
79
      offline = False
80
    }
81

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

    
87
-- | Sets the offline attribute
88
setOffline :: Node -> Bool -> Node
89
setOffline t val = t { offline = val }
90

    
91
-- | Given the rmem, free memory and disk, computes the failn1 status.
92
computeFailN1 :: Int -> Int -> Int -> Bool
93
computeFailN1 new_rmem new_mem new_dsk =
94
    new_mem <= new_rmem || new_dsk <= 0
95

    
96
-- | Given the new free memory and disk, fail if any of them is below zero.
97
failHealth :: Int -> Int -> Bool
98
failHealth new_mem new_dsk = new_mem <= 0 || new_dsk <= 0
99

    
100
-- | Computes the maximum reserved memory for peers from a peer map.
101
computeMaxRes :: PeerMap.PeerMap -> PeerMap.Elem
102
computeMaxRes new_peers = PeerMap.maxElem new_peers
103

    
104
-- | Builds the peer map for a given node.
105
buildPeers :: Node -> Container.Container Instance.Instance -> Int -> Node
106
buildPeers t il num_nodes =
107
    let mdata = map
108
                (\i_idx -> let inst = Container.find i_idx il
109
                           in (Instance.pnode inst, Instance.mem inst))
110
                (slist t)
111
        pmap = PeerMap.accumArray (+) 0 (0, num_nodes - 1) mdata
112
        new_rmem = computeMaxRes pmap
113
        new_failN1 = computeFailN1 new_rmem (f_mem t) (f_dsk t)
114
        new_prem = (fromIntegral new_rmem) / (t_mem t)
115
    in t {peers=pmap, failN1 = new_failN1, r_mem = new_rmem, p_rem = new_prem}
116

    
117
-- | Removes a primary instance.
118
removePri :: Node -> Instance.Instance -> Node
119
removePri t inst =
120
    let iname = Instance.idx inst
121
        new_plist = delete iname (plist t)
122
        new_mem = f_mem t + Instance.mem inst
123
        new_dsk = f_dsk t + Instance.dsk inst
124
        new_mp = (fromIntegral new_mem) / (t_mem t)
125
        new_dp = (fromIntegral new_dsk) / (t_dsk t)
126
        new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk
127
    in t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
128
          failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
129

    
130
-- | Removes a secondary instance.
131
removeSec :: Node -> Instance.Instance -> Node
132
removeSec t inst =
133
    let iname = Instance.idx inst
134
        pnode = Instance.pnode inst
135
        new_slist = delete iname (slist t)
136
        new_dsk = f_dsk t + Instance.dsk inst
137
        old_peers = peers t
138
        old_peem = PeerMap.find pnode old_peers
139
        new_peem =  old_peem - (Instance.mem inst)
140
        new_peers = PeerMap.add pnode new_peem old_peers
141
        old_rmem = r_mem t
142
        new_rmem = if old_peem < old_rmem then
143
                       old_rmem
144
                   else
145
                       computeMaxRes new_peers
146
        new_prem = (fromIntegral new_rmem) / (t_mem t)
147
        new_failn1 = computeFailN1 new_rmem (f_mem t) new_dsk
148
        new_dp = (fromIntegral new_dsk) / (t_dsk t)
149
    in t {slist = new_slist, f_dsk = new_dsk, peers = new_peers,
150
          failN1 = new_failn1, r_mem = new_rmem, p_dsk = new_dp,
151
          p_rem = new_prem}
152

    
153
-- | Adds a primary instance.
154
addPri :: Node -> Instance.Instance -> Maybe Node
155
addPri t inst =
156
    let iname = Instance.idx inst
157
        new_mem = f_mem t - Instance.mem inst
158
        new_dsk = f_dsk t - Instance.dsk inst
159
        new_failn1 = computeFailN1 (r_mem t) new_mem new_dsk in
160
      if (failHealth new_mem new_dsk) || (new_failn1 && not (failN1 t)) then
161
        Nothing
162
      else
163
        let new_plist = iname:(plist t)
164
            new_mp = (fromIntegral new_mem) / (t_mem t)
165
            new_dp = (fromIntegral new_dsk) / (t_dsk t)
166
        in
167
        Just t {plist = new_plist, f_mem = new_mem, f_dsk = new_dsk,
168
                failN1 = new_failn1, p_mem = new_mp, p_dsk = new_dp}
169

    
170
-- | Adds a secondary instance.
171
addSec :: Node -> Instance.Instance -> Int -> Maybe Node
172
addSec t inst pdx =
173
    let iname = Instance.idx inst
174
        old_peers = peers t
175
        old_mem = f_mem t
176
        new_dsk = f_dsk t - Instance.dsk inst
177
        new_peem = PeerMap.find pdx old_peers + Instance.mem inst
178
        new_peers = PeerMap.add pdx new_peem old_peers
179
        new_rmem = max (r_mem t) new_peem
180
        new_prem = (fromIntegral new_rmem) / (t_mem t)
181
        new_failn1 = computeFailN1 new_rmem old_mem new_dsk in
182
    if (failHealth old_mem new_dsk) || (new_failn1 && not (failN1 t)) then
183
        Nothing
184
    else
185
        let new_slist = iname:(slist t)
186
            new_dp = (fromIntegral new_dsk) / (t_dsk t)
187
        in
188
        Just t {slist = new_slist, f_dsk = new_dsk,
189
                peers = new_peers, failN1 = new_failn1,
190
                r_mem = new_rmem, p_dsk = new_dp,
191
                p_rem = new_prem}
192

    
193
-- | Add a primary instance to a node without other updates
194
setPri :: Node -> Int -> Node
195
setPri t idx = t { plist = idx:(plist t) }
196

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

    
201
-- | Simple converter to string.
202
str :: Node -> String
203
str t =
204
    printf ("Node %d (mem=%5d MiB, disk=%5.2f GiB)\n  Primaries:" ++
205
            " %s\nSecondaries: %s")
206
      (idx t) (f_mem t) ((f_dsk t) `div` 1024)
207
      (commaJoin (map show (plist t)))
208
      (commaJoin (map show (slist t)))
209

    
210
-- | String converter for the node list functionality.
211
list :: Int -> String -> Node -> String
212
list mname n t =
213
    let pl = plist t
214
        sl = slist t
215
        mp = p_mem t
216
        dp = p_dsk t
217
        off = offline t
218
        fn = failN1 t
219
    in
220
      printf " %c %-*s %5.0f %5d %5d %5.0f %5d %3d %3d %.5f %.5f"
221
                 (if off then '-' else if fn then '*' else ' ')
222
                 mname n (t_mem t) (f_mem t) (r_mem t)
223
                 ((t_dsk t) / 1024) ((f_dsk t) `div` 1024)
224
                 (length pl) (length sl)
225
                 mp dp