1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """IP address pool management functions.
32
33 """
34
35 import ipaddr
36
37 from bitarray import bitarray
38
39 from ganeti import errors
43 """Derives the number of hosts in an IPv4 network from the size.
44
45 """
46 return 2 ** (32 - network_size)
47
48
49 IPV4_NETWORK_MIN_SIZE = 30
50
51
52 IPV4_NETWORK_MAX_SIZE = 16
53 IPV4_NETWORK_MIN_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MIN_SIZE)
54 IPV4_NETWORK_MAX_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MAX_SIZE)
58 """Address pool class, wrapping an C{objects.Network} object.
59
60 This class provides methods to manipulate address pools, backed by
61 L{objects.Network} objects.
62
63 """
64 FREE = bitarray("0")
65 RESERVED = bitarray("1")
66
68 """Initialize a new IPv4 address pool from an L{objects.Network} object.
69
70 @type network: L{objects.Network}
71 @param network: the network object from which the pool will be generated
72
73 """
74 self.network = None
75 self.gateway = None
76 self.network6 = None
77 self.gateway6 = None
78
79 self.net = network
80
81 self.network = ipaddr.IPNetwork(self.net.network)
82 if self.network.numhosts > IPV4_NETWORK_MAX_NUM_HOSTS:
83 raise errors.AddressPoolError("A big network with %s host(s) is currently"
84 " not supported. please specify at most a"
85 " /%s network" %
86 (str(self.network.numhosts),
87 IPV4_NETWORK_MAX_SIZE))
88
89 if self.network.numhosts < IPV4_NETWORK_MIN_NUM_HOSTS:
90 raise errors.AddressPoolError("A network with only %s host(s) is too"
91 " small, please specify at least a /%s"
92 " network" %
93 (str(self.network.numhosts),
94 IPV4_NETWORK_MIN_SIZE))
95 if self.net.gateway:
96 self.gateway = ipaddr.IPAddress(self.net.gateway)
97
98 if self.net.network6:
99 self.network6 = ipaddr.IPv6Network(self.net.network6)
100 if self.net.gateway6:
101 self.gateway6 = ipaddr.IPv6Address(self.net.gateway6)
102
103 if self.net.reservations:
104 self.reservations = bitarray(self.net.reservations)
105 else:
106 self.reservations = bitarray(self.network.numhosts)
107
108 self.reservations.setall(False)
109
110 if self.net.ext_reservations:
111 self.ext_reservations = bitarray(self.net.ext_reservations)
112 else:
113 self.ext_reservations = bitarray(self.network.numhosts)
114
115 self.ext_reservations.setall(False)
116
117 assert len(self.reservations) == self.network.numhosts
118 assert len(self.ext_reservations) == self.network.numhosts
119
121 if address is None:
122 return False
123 addr = ipaddr.IPAddress(address)
124
125 return addr in self.network
126
135
137 """Write address pools back to the network object.
138
139 """
140
141 self.net.ext_reservations = self.ext_reservations.to01()
142 self.net.reservations = self.reservations.to01()
143
144 - def _Mark(self, address, value=True, external=False):
145 idx = self._GetAddrIndex(address)
146 if external:
147 self.ext_reservations[idx] = value
148 else:
149 self.reservations[idx] = value
150 self.Update()
151
153 return 2 ** (32 - self.network.prefixlen)
154
155 @property
157 """Return a combined map of internal and external reservations.
158
159 """
160 return (self.reservations | self.ext_reservations)
161
163 assert len(self.reservations) == self._GetSize()
164 assert len(self.ext_reservations) == self._GetSize()
165
166 if self.gateway is not None:
167 assert self.gateway in self.network
168
169 if self.network6 and self.gateway6:
170 assert self.gateway6 in self.network6 or self.gateway6.is_link_local
171
172 return True
173
175 """Check whether the network is full.
176
177 """
178 return self.all_reservations.all()
179
181 """Get the count of reserved addresses.
182
183 """
184 return self.all_reservations.count(True)
185
187 """Get the count of unused addresses.
188
189 """
190 return self.all_reservations.count(False)
191
193 """Return a textual representation of the network's occupation status.
194
195 """
196 return self.all_reservations.to01().replace("1", "X").replace("0", ".")
197
199 """Checks if the given IP is reserved.
200
201 """
202 idx = self._GetAddrIndex(address)
203 if external:
204 return self.ext_reservations[idx]
205 else:
206 return self.reservations[idx]
207
208 - def Reserve(self, address, external=False):
209 """Mark an address as used.
210
211 """
212 if self.IsReserved(address, external):
213 if external:
214 msg = "IP %s is already externally reserved" % address
215 else:
216 msg = "IP %s is already used by an instance" % address
217 raise errors.AddressPoolError(msg)
218
219 self._Mark(address, external=external)
220
221 - def Release(self, address, external=False):
222 """Release a given address reservation.
223
224 """
225 if not self.IsReserved(address, external):
226 if external:
227 msg = "IP %s is not externally reserved" % address
228 else:
229 msg = "IP %s is not used by an instance" % address
230 raise errors.AddressPoolError(msg)
231
232 self._Mark(address, value=False, external=external)
233
245
247 """Returns the first free address of the network.
248
249 @raise errors.AddressPoolError: Pool is full
250
251 """
252 idx = self.all_reservations.search(self.FREE, 1)
253 if idx:
254 return str(self.network[idx[0]])
255 else:
256 raise errors.AddressPoolError("%s is full" % self.network)
257
259 """Returns a list of all externally reserved addresses.
260
261 """
262
263 idxs = self.ext_reservations.search(self.RESERVED)
264 return [str(self.network[idx]) for idx in idxs]
265
266 @classmethod
268 """Initialize an L{objects.Network} object.
269
270 Reserve the network, broadcast and gateway IP addresses.
271
272 """
273 obj = cls(net)
274 obj.Update()
275 for ip in [obj.network[0], obj.network[-1]]:
276 obj.Reserve(ip, external=True)
277 if obj.net.gateway is not None:
278 obj.Reserve(obj.net.gateway, external=True)
279 obj.Validate()
280 return obj
281