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
173 """Check whether the network is full.
174
175 """
176 return self.all_reservations.all()
177
179 """Get the count of reserved addresses.
180
181 """
182 return self.all_reservations.count(True)
183
185 """Get the count of unused addresses.
186
187 """
188 return self.all_reservations.count(False)
189
191 """Return a textual representation of the network's occupation status.
192
193 """
194 return self.all_reservations.to01().replace("1", "X").replace("0", ".")
195
197 """Checks if the given IP is reserved.
198
199 """
200 idx = self._GetAddrIndex(address)
201 if external:
202 return self.ext_reservations[idx]
203 else:
204 return self.reservations[idx]
205
206 - def Reserve(self, address, external=False):
207 """Mark an address as used.
208
209 """
210 if self.IsReserved(address, external):
211 if external:
212 msg = "IP %s is already externally reserved" % address
213 else:
214 msg = "IP %s is already used by an instance" % address
215 raise errors.AddressPoolError(msg)
216
217 self._Mark(address, external=external)
218
219 - def Release(self, address, external=False):
220 """Release a given address reservation.
221
222 """
223 if not self.IsReserved(address, external):
224 if external:
225 msg = "IP %s is not externally reserved" % address
226 else:
227 msg = "IP %s is not used by an instance" % address
228 raise errors.AddressPoolError(msg)
229
230 self._Mark(address, value=False, external=external)
231
243
245 """Returns the first free address of the network.
246
247 @raise errors.AddressPoolError: Pool is full
248
249 """
250 idx = self.all_reservations.search(self.FREE, 1)
251 if idx:
252 return str(self.network[idx[0]])
253 else:
254 raise errors.AddressPoolError("%s is full" % self.network)
255
257 """Returns a list of all externally reserved addresses.
258
259 """
260
261 idxs = self.ext_reservations.search(self.RESERVED)
262 return [str(self.network[idx]) for idx in idxs]
263
264 @classmethod
266 """Initialize an L{objects.Network} object.
267
268 Reserve the network, broadcast and gateway IP addresses.
269
270 """
271 obj = cls(net)
272 obj.Update()
273 for ip in [obj.network[0], obj.network[-1]]:
274 obj.Reserve(ip, external=True)
275 if obj.net.gateway is not None:
276 obj.Reserve(obj.net.gateway, external=True)
277 obj.Validate()
278 return obj
279