1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
34 """Derives the number of hosts in an IPv4 network from the size.
35
36 """
37 return 2 ** (32 - network_size)
38
39
40 IPV4_NETWORK_MIN_SIZE = 30
41
42
43 IPV4_NETWORK_MAX_SIZE = 16
44 IPV4_NETWORK_MIN_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MIN_SIZE)
45 IPV4_NETWORK_MAX_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MAX_SIZE)
49 """Address pool class, wrapping an C{objects.Network} object.
50
51 This class provides methods to manipulate address pools, backed by
52 L{objects.Network} objects.
53
54 """
55 FREE = bitarray("0")
56 RESERVED = bitarray("1")
57
59 """Initialize a new IPv4 address pool from an L{objects.Network} object.
60
61 @type network: L{objects.Network}
62 @param network: the network object from which the pool will be generated
63
64 """
65 self.network = None
66 self.gateway = None
67 self.network6 = None
68 self.gateway6 = None
69
70 self.net = network
71
72 self.network = ipaddr.IPNetwork(self.net.network)
73 if self.network.numhosts > IPV4_NETWORK_MAX_NUM_HOSTS:
74 raise errors.AddressPoolError("A big network with %s host(s) is currently"
75 " not supported. please specify at most a"
76 " /%s network" %
77 (str(self.network.numhosts),
78 IPV4_NETWORK_MAX_SIZE))
79
80 if self.network.numhosts < IPV4_NETWORK_MIN_NUM_HOSTS:
81 raise errors.AddressPoolError("A network with only %s host(s) is too"
82 " small, please specify at least a /%s"
83 " network" %
84 (str(self.network.numhosts),
85 IPV4_NETWORK_MIN_SIZE))
86 if self.net.gateway:
87 self.gateway = ipaddr.IPAddress(self.net.gateway)
88
89 if self.net.network6:
90 self.network6 = ipaddr.IPv6Network(self.net.network6)
91 if self.net.gateway6:
92 self.gateway6 = ipaddr.IPv6Address(self.net.gateway6)
93
94 if self.net.reservations:
95 self.reservations = bitarray(self.net.reservations)
96 else:
97 self.reservations = bitarray(self.network.numhosts)
98
99 self.reservations.setall(False)
100
101 if self.net.ext_reservations:
102 self.ext_reservations = bitarray(self.net.ext_reservations)
103 else:
104 self.ext_reservations = bitarray(self.network.numhosts)
105
106 self.ext_reservations.setall(False)
107
108 assert len(self.reservations) == self.network.numhosts
109 assert len(self.ext_reservations) == self.network.numhosts
110
112 if address is None:
113 return False
114 addr = ipaddr.IPAddress(address)
115
116 return addr in self.network
117
126
128 """Write address pools back to the network object.
129
130 """
131
132 self.net.ext_reservations = self.ext_reservations.to01()
133 self.net.reservations = self.reservations.to01()
134
135 - def _Mark(self, address, value=True, external=False):
136 idx = self._GetAddrIndex(address)
137 if external:
138 self.ext_reservations[idx] = value
139 else:
140 self.reservations[idx] = value
141 self.Update()
142
144 return 2 ** (32 - self.network.prefixlen)
145
146 @property
148 """Return a combined map of internal and external reservations.
149
150 """
151 return (self.reservations | self.ext_reservations)
152
154 assert len(self.reservations) == self._GetSize()
155 assert len(self.ext_reservations) == self._GetSize()
156 all_res = self.reservations & self.ext_reservations
157 assert not all_res.any()
158
159 if self.gateway is not None:
160 assert self.gateway in self.network
161
162 if self.network6 and self.gateway6:
163 assert self.gateway6 in self.network6 or self.gateway6.is_link_local
164
165 return True
166
168 """Check whether the network is full.
169
170 """
171 return self.all_reservations.all()
172
174 """Get the count of reserved addresses.
175
176 """
177 return self.all_reservations.count(True)
178
180 """Get the count of unused addresses.
181
182 """
183 return self.all_reservations.count(False)
184
186 """Return a textual representation of the network's occupation status.
187
188 """
189 return self.all_reservations.to01().replace("1", "X").replace("0", ".")
190
197
198 - def Reserve(self, address, external=False):
199 """Mark an address as used.
200
201 """
202 if self.IsReserved(address):
203 raise errors.AddressPoolError("%s is already reserved" % address)
204 self._Mark(address, external=external)
205
206 - def Release(self, address, external=False):
207 """Release a given address reservation.
208
209 """
210 self._Mark(address, value=False, external=external)
211
223
225 """Returns the first free address of the network.
226
227 @raise errors.AddressPoolError: Pool is full
228
229 """
230 idx = self.all_reservations.search(self.FREE, 1)
231 if idx:
232 return str(self.network[idx[0]])
233 else:
234 raise errors.AddressPoolError("%s is full" % self.network)
235
237 """Returns a list of all externally reserved addresses.
238
239 """
240
241 idxs = self.ext_reservations.search(self.RESERVED)
242 return [str(self.network[idx]) for idx in idxs]
243
244 @classmethod
246 """Initialize an L{objects.Network} object.
247
248 Reserve the network, broadcast and gateway IP addresses.
249
250 """
251 obj = cls(net)
252 obj.Update()
253 for ip in [obj.network[0], obj.network[-1]]:
254 obj.Reserve(ip, external=True)
255 if obj.net.gateway is not None:
256 obj.Reserve(obj.net.gateway, external=True)
257 obj.Validate()
258 return obj
259