Statistics
| Branch: | Tag: | Revision:

root / lib / network.py @ ea54a0b9

History | View | Annotate | Download (6.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""IP address pool management functions.
23

24
"""
25

    
26
import ipaddr
27

    
28
from bitarray import bitarray
29
from base64 import b64encode
30
from base64 import b64decode
31

    
32
from ganeti import errors
33

    
34

    
35
class AddressPool(object):
36
  """Address pool class, wrapping an C{objects.Network} object.
37

38
  This class provides methods to manipulate address pools, backed by
39
  L{objects.Network} objects.
40

41
  """
42
  FREE = bitarray("0")
43
  RESERVED = bitarray("1")
44

    
45
  def __init__(self, network):
46
    """Initialize a new IPv4 address pool from an L{objects.Network} object.
47

48
    @type network: L{objects.Network}
49
    @param network: the network object from which the pool will be generated
50

51
    """
52
    self.network = None
53
    self.gateway = None
54
    self.network6 = None
55
    self.gateway6 = None
56

    
57
    self.net = network
58

    
59
    self.network = ipaddr.IPNetwork(self.net.network)
60
    if self.net.gateway:
61
      self.gateway = ipaddr.IPAddress(self.net.gateway)
62

    
63
    if self.net.network6:
64
      self.network6 = ipaddr.IPv6Network(self.net.network6)
65
    if self.net.gateway6:
66
      self.gateway6 = ipaddr.IPv6Address(self.net.gateway6)
67

    
68
    if self.net.reservations:
69
      self.reservations = bitarray()
70
      self.reservations.frombytes(b64decode(self.net.reservations))
71
    else:
72
      self.reservations = bitarray(self.network.numhosts)
73
      # pylint: disable=E1103
74
      self.reservations.setall(False)
75

    
76
    if self.net.ext_reservations:
77
      self.ext_reservations = bitarray()
78
      self.ext_reservations.frombytes(b64decode(self.net.ext_reservations))
79
    else:
80
      self.ext_reservations = bitarray(self.network.numhosts)
81
      # pylint: disable=E1103
82
      self.ext_reservations.setall(False)
83

    
84
    assert len(self.reservations) == self.network.numhosts
85
    assert len(self.ext_reservations) == self.network.numhosts
86

    
87
  def Contains(self, address):
88
    if address is None:
89
      return False
90
    addr = ipaddr.IPAddress(address)
91

    
92
    return addr in self.network
93

    
94
  def _GetAddrIndex(self, address):
95
    addr = ipaddr.IPAddress(address)
96

    
97
    if not addr in self.network:
98
      raise errors.AddressPoolError("%s does not contain %s" %
99
                                    (self.network, addr))
100

    
101
    return int(addr) - int(self.network.network)
102

    
103
  def Update(self):
104
    """Write address pools back to the network object.
105

106
    """
107
    # pylint: disable=E1103
108
    self.net.ext_reservations = b64encode(self.ext_reservations.tobytes())
109
    self.net.reservations = b64encode(self.reservations.tobytes())
110

    
111
  def _Mark(self, address, value=True, external=False):
112
    idx = self._GetAddrIndex(address)
113
    if external:
114
      self.ext_reservations[idx] = value
115
    else:
116
      self.reservations[idx] = value
117
    self.Update()
118

    
119
  def _GetSize(self):
120
    return 2 ** (32 - self.network.prefixlen)
121

    
122
  @property
123
  def all_reservations(self):
124
    """Return a combined map of internal and external reservations.
125

126
    """
127
    return (self.reservations | self.ext_reservations)
128

    
129
  def Validate(self):
130
    assert self.net.family == 4
131
    assert len(self.reservations) == self._GetSize()
132
    assert len(self.ext_reservations) == self._GetSize()
133
    all_res = self.reservations & self.ext_reservations
134
    assert not all_res.any()
135

    
136
    if self.gateway is not None:
137
      assert self.net.family == self.gateway.version
138
      assert self.gateway in self.network
139

    
140
    if self.network6 and self.gateway6:
141
      assert self.gateway6 in self.network6
142

    
143
    return True
144

    
145
  def IsFull(self):
146
    """Check whether the network is full.
147

148
    """
149
    return self.all_reservations.all()
150

    
151
  def GetReservedCount(self):
152
    """Get the count of reserved addresses.
153

154
    """
155
    return self.all_reservations.count(True)
156

    
157
  def GetFreeCount(self):
158
    """Get the count of unused addresses.
159

160
    """
161
    return self.all_reservations.count(False)
162

    
163
  def GetMap(self):
164
    """Return a textual representation of the network's occupation status.
165

166
    """
167
    return self.all_reservations.to01().replace("1", "X").replace("0", ".")
168

    
169
  def IsReserved(self, address):
170
    """Checks if the given IP is reserved.
171

172
    """
173
    idx = self._GetAddrIndex(address)
174
    return self.all_reservations[idx]
175

    
176
  def Reserve(self, address, external=False):
177
    """Mark an address as used.
178

179
    """
180
    if self.IsReserved(address):
181
      raise errors.AddressPoolError("%s is already reserved" % address)
182
    self._Mark(address, external=external)
183

    
184
  def Release(self, address, external=False):
185
    """Release a given address reservation.
186

187
    """
188
    self._Mark(address, value=False, external=external)
189

    
190
  def GetFreeAddress(self):
191
    """Returns the first available address.
192

193
    """
194
    if self.IsFull():
195
      raise errors.AddressPoolError("%s is full" % self.network)
196

    
197
    idx = self.all_reservations.index(False)
198
    address = str(self.network[idx])
199
    self.Reserve(address)
200
    return address
201

    
202
  def GenerateFree(self):
203
    """Returns the first free address of the network.
204

205
    @raise errors.AddressPoolError: Pool is full
206

207
    """
208
    idx = self.all_reservations.search(self.FREE, 1)
209
    if idx:
210
      return str(self.network[idx[0]])
211
    else:
212
      raise errors.AddressPoolError("%s is full" % self.network)
213

    
214
  def GetExternalReservations(self):
215
    """Returns a list of all externally reserved addresses.
216

217
    """
218
    # pylint: disable=E1103
219
    idxs = self.ext_reservations.search(self.RESERVED)
220
    return [str(self.network[idx]) for idx in idxs]
221

    
222
  @classmethod
223
  def InitializeNetwork(cls, net):
224
    """Initialize an L{objects.Network} object.
225

226
    Reserve the network, broadcast and gateway IP addresses.
227

228
    """
229
    obj = cls(net)
230
    obj.Update()
231
    for ip in [obj.network[0], obj.network[-1]]:
232
      obj.Reserve(ip, external=True)
233
    if obj.net.gateway is not None:
234
      obj.Reserve(obj.net.gateway, external=True)
235
    obj.Validate()
236
    return obj