Statistics
| Branch: | Tag: | Revision:

root / lib / network.py @ 8c5acc2e

History | View | Annotate | Download (5.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2011 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 ganeti import errors
29

    
30
from base64 import b64encode
31
from base64 import b64decode
32

    
33
from bitarray import bitarray
34

    
35

    
36
class AddressPool(object):
37
  """Address pool class, wrapping an objects.Network object
38

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

42
  """
43
  def __init__(self, network):
44
    """Initialize a new IPv4 address pool from an objects.Network object
45

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

49
    """
50
    self.net = network
51
    self.network = ipaddr.IPNetwork(self.net.network)
52
    if self.net.gateway:
53
      self.gateway = ipaddr.IPAddress(self.net.gateway)
54
    else:
55
      self.gateway = None
56

    
57
    if self.net.reservations:
58
      self.reservations = bitarray()
59
      self.reservations.fromstring(b64decode(self.net.reservations))
60
    else:
61
      self.reservations = bitarray(self.network.numhosts)
62
      self.reservations.setall(False)
63

    
64
    if self.net.ext_reservations:
65
      self.ext_reservations = bitarray()
66
      self.ext_reservations.fromstring(b64decode(self.net.ext_reservations))
67
    else:
68
      self.ext_reservations = bitarray(self.network.numhosts)
69
      self.ext_reservations.setall(False)
70

    
71
    assert len(self.reservations) == self.network.numhosts
72
    assert len(self.ext_reservations) == self.network.numhosts
73

    
74
  def _GetAddrIndex(self, address):
75
    addr = ipaddr.IPAddress(address)
76

    
77
    if not addr in self.network:
78
      raise errors.AddressPoolError("%s does not contain %s" %
79
                                    (self.network, addr))
80

    
81
    return int(addr) - int(self.network.network)
82

    
83
  def _Update(self):
84
    """Write address pools back to the network object"""
85
    self.net.ext_reservations = b64encode(self.ext_reservations.tostring())
86
    self.net.reservations = b64encode(self.reservations.tostring())
87

    
88
  def _Mark(self, address, value=True, external=False):
89
    idx = self._GetAddrIndex(address)
90
    if external:
91
      self.ext_reservations[idx] = value
92
    else:
93
      self.reservations[idx] = value
94
    self._Update()
95

    
96
  def _GetSize(self):
97
    return 2**(32 - self.network.prefixlen)
98

    
99
  @property
100
  def all_reservations(self):
101
    """Return a combined map of internal + external reservations."""
102
    return (self.reservations | self.ext_reservations)
103

    
104
  def Validate(self):
105
    assert self.net.family == 4
106
    assert len(self.reservations) == self._GetSize()
107
    assert len(self.ext_reservations) == self._GetSize()
108
    assert not (self.reservations & self.ext_reservations).any()
109

    
110
    if self.gateway is not None:
111
      assert self.net.family == self.gateway.version
112
      assert self.gateway in self.network
113

    
114
    return True
115

    
116
  def IsFull(self):
117
    """Check whether the network is full"""
118
    return self.all_reservations.all()
119

    
120
  def GetReservedCount(self):
121
    """Get the count of reserved addresses"""
122
    return self.all_reservations.count(True)
123

    
124
  def GetFreeCount(self):
125
    """Get the count of unused addresses"""
126
    return self.all_reservations.count(False)
127

    
128
  def GetMap(self):
129
    """Return a textual representation of the network's occupation status."""
130
    return self.all_reservations.to01().replace("1", "X").replace("0", ".")
131

    
132
  def IsReserved(self, address):
133
    """Checks if the given IP is reserved"""
134
    idx = self._GetAddrIndex(address)
135
    return self.all_reservations[idx]
136

    
137
  def Reserve(self, address, external=False):
138
    """Mark an address as used."""
139
    if self.IsReserved(address):
140
      raise errors.AddressPoolError("%s is already reserved" % address)
141
    self._Mark(address, external=external)
142

    
143
  def Release(self, address, external=False):
144
    """Release a given address reservation."""
145
    self._Mark(address, value=False, external=external)
146

    
147
  def GetFreeAddress(self):
148
    """Returns the first available address."""
149
    if self.IsFull():
150
      raise errors.AddressPoolError("%s is full" % self.network)
151

    
152
    idx = self.all_reservations.index(False)
153
    address = str(self.network[idx])
154
    self.Reserve(address)
155
    return address
156

    
157
  def GenerateFree(self):
158
    """A generator for free addresses."""
159
    def _iter_free():
160
      for idx in self.all_reservations.search("0", 64):
161
        yield str(self.network[idx])
162

    
163
    return _iter_free().next
164

    
165
  def GetExternalReservations(self):
166
    """Returns a list of all externally reserved addresses"""
167
    idxs = self.ext_reservations.search("1")
168
    return [str(self.network[idx]) for idx in idxs]
169

    
170
  @classmethod
171
  def InitializeNetwork(cls, net):
172
    """Initialize an L{objects.Network} object
173

174
    Reserve the network, broadcast and gateway IPs
175

176
    """
177
    obj = cls(net)
178
    obj._Update()
179
    for ip in [obj.network[0], obj.network[-1]]:
180
      obj.Reserve(ip, external=True)
181
    if obj.net.gateway is not None:
182
      obj.Reserve(obj.net.gateway, external=True)
183
    obj.Validate()
184
    return obj