Fixes to pass pep8 (make lint)
[ganeti-local] / lib / network.py
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 bitarray import bitarray
29
30 from ganeti import errors
31
32
33 class AddressPool(object):
34   """Address pool class, wrapping an objects.Network object
35
36   This class provides methods to manipulate address pools, backed by
37   L{objects.Network} objects.
38
39   """
40   def __init__(self, network):
41     """Initialize a new IPv4 address pool from an objects.Network object
42
43     @type network: L{objects.Network}
44     @param network: the network object from which the pool will be generated
45
46     """
47     self.network = None
48     self.gateway = None
49     self.network6 = None
50     self.gateway6 = None
51
52     self.net = network
53
54     self.network = ipaddr.IPNetwork(self.net.network)
55     if self.net.gateway:
56       self.gateway = ipaddr.IPAddress(self.net.gateway)
57
58     if self.net.network6:
59       self.network6 = ipaddr.IPv6Network(self.net.network6)
60     if self.net.gateway6:
61       self.gateway6 = ipaddr.IPv6Address(self.net.gateway6)
62
63     if self.net.reservations:
64       self.reservations = bitarray(self.net.reservations)
65     else:
66       self.reservations = bitarray(self.network.numhosts)
67       # pylint: disable=E1103
68       self.reservations.setall(False)
69
70     if self.net.ext_reservations:
71       self.ext_reservations = bitarray(self.net.ext_reservations)
72     else:
73       self.ext_reservations = bitarray(self.network.numhosts)
74       # pylint: disable=E1103
75       self.ext_reservations.setall(False)
76
77     assert len(self.reservations) == self.network.numhosts
78     assert len(self.ext_reservations) == self.network.numhosts
79
80   def Contains(self, address):
81     if address is None:
82       return False
83     addr = ipaddr.IPAddress(address)
84
85     return addr in self.network
86
87   def _GetAddrIndex(self, address):
88     addr = ipaddr.IPAddress(address)
89
90     if not addr in self.network:
91       raise errors.AddressPoolError("%s does not contain %s" %
92                                     (self.network, addr))
93
94     return int(addr) - int(self.network.network)
95
96   def Update(self):
97     """Write address pools back to the network object"""
98     # pylint: disable=E1103
99     self.net.ext_reservations = self.ext_reservations.to01()
100     self.net.reservations = self.reservations.to01()
101
102   def _Mark(self, address, value=True, external=False):
103     idx = self._GetAddrIndex(address)
104     if external:
105       self.ext_reservations[idx] = value
106     else:
107       self.reservations[idx] = value
108     self.Update()
109
110   def _GetSize(self):
111     return 2 ** (32 - self.network.prefixlen)
112
113   @property
114   def all_reservations(self):
115     """Return a combined map of internal + external reservations."""
116     return (self.reservations | self.ext_reservations)
117
118   def Validate(self):
119     assert self.net.family == 4
120     assert len(self.reservations) == self._GetSize()
121     assert len(self.ext_reservations) == self._GetSize()
122     all_res = self.reservations & self.ext_reservations
123     assert not all_res.any()
124
125     if self.gateway is not None:
126       assert self.net.family == self.gateway.version
127       assert self.gateway in self.network
128
129     if self.network6 and self.gateway6:
130       assert self.gateway6 in self.network6
131
132     return True
133
134   def IsFull(self):
135     """Check whether the network is full"""
136     return self.all_reservations.all()
137
138   def GetReservedCount(self):
139     """Get the count of reserved addresses"""
140     return self.all_reservations.count(True)
141
142   def GetFreeCount(self):
143     """Get the count of unused addresses"""
144     return self.all_reservations.count(False)
145
146   def GetMap(self):
147     """Return a textual representation of the network's occupation status."""
148     return self.all_reservations.to01().replace("1", "X").replace("0", ".")
149
150   def IsReserved(self, address):
151     """Checks if the given IP is reserved"""
152     idx = self._GetAddrIndex(address)
153     return self.all_reservations[idx]
154
155   def Reserve(self, address, external=False):
156     """Mark an address as used."""
157     if self.IsReserved(address):
158       raise errors.AddressPoolError("%s is already reserved" % address)
159     self._Mark(address, external=external)
160
161   def Release(self, address, external=False):
162     """Release a given address reservation."""
163     self._Mark(address, value=False, external=external)
164
165   def GetFreeAddress(self):
166     """Returns the first available address."""
167     if self.IsFull():
168       raise errors.AddressPoolError("%s is full" % self.network)
169
170     idx = self.all_reservations.index(False)
171     address = str(self.network[idx])
172     self.Reserve(address)
173     return address
174
175   def GenerateFree(self):
176     """A generator for free addresses."""
177     def _iter_free():
178       for idx in self.all_reservations.search("0", 64):
179         yield str(self.network[idx])
180     # pylint: disable=E1101
181     return _iter_free().next
182
183   def GetExternalReservations(self):
184     """Returns a list of all externally reserved addresses"""
185     idxs = self.ext_reservations.search("1")
186     return [str(self.network[idx]) for idx in idxs]
187
188   @classmethod
189   def InitializeNetwork(cls, net):
190     """Initialize an L{objects.Network} object
191
192     Reserve the network, broadcast and gateway IPs
193
194     """
195     obj = cls(net)
196     obj.Update()
197     for ip in [obj.network[0], obj.network[-1]]:
198       obj.Reserve(ip, external=True)
199     if obj.net.gateway is not None:
200       obj.Reserve(obj.net.gateway, external=True)
201     obj.Validate()
202     return obj