|
8 | 8 | # a good idea to refactor this by using more top level functions instead of |
9 | 9 | # object methods. |
10 | 10 |
|
| 11 | +import itertools |
| 12 | + |
11 | 13 | from ipaddress import IPv4Address, IPv6Address |
| 14 | +from django.db.models import OuterRef, Subquery, AutoField |
12 | 15 |
|
13 | 16 | from adminapi.dataset import DatasetObject |
14 | 17 | from serveradmin.serverdb.models import ( |
|
18 | 21 | Server, |
19 | 22 | ServerAttribute, |
20 | 23 | ServerRelationAttribute, |
| 24 | + ServerInetAttribute, |
21 | 25 | ) |
22 | 26 |
|
23 | 27 |
|
@@ -192,30 +196,63 @@ def _add_domain_attribute(self, attribute, servers): |
192 | 196 | server.hostname.split(".", 1)[-1] |
193 | 197 | ) |
194 | 198 |
|
195 | | - def _add_supernet_attribute(self, attribute, servers): |
196 | | - """Merge-join networks to the servers |
| 199 | + def _add_supernet_attribute(self, attribute: Attribute, servers_in): |
| 200 | + if attribute.inet_address_family: |
| 201 | + self._add_supernet_attribute_af(attribute, servers_in) |
| 202 | + else: |
| 203 | + self._add_supernet_attribute_intern_ip(attribute, servers_in) |
| 204 | + |
| 205 | + def _add_supernet_attribute_af(self, attribute, servers_in): |
| 206 | + supernet_id = ServerInetAttribute.objects.filter( |
| 207 | + value__net_contains=OuterRef("value"), |
| 208 | + server__servertype_id=attribute.target_servertype_id, |
| 209 | + attribute__inet_address_family=attribute.inet_address_family, |
| 210 | + ).values("server__server_id") |
| 211 | + |
| 212 | + attrs = ServerInetAttribute.objects.filter( |
| 213 | + server_id__in=[s.server_id for s in servers_in], |
| 214 | + attribute__inet_address_family=attribute.inet_address_family, |
| 215 | + ).annotate( |
| 216 | + supernet_id=Subquery(supernet_id, output_field=AutoField()), |
| 217 | + ) |
197 | 218 |
|
198 | | - This function takes advantage of networks in the same servertype not |
199 | | - overlapping with each other. |
200 | | - """ |
201 | | - target = None |
202 | | - for source in sorted(servers, key=lambda s: _sort_key(s.intern_ip)): |
203 | | - # Check the previous target |
204 | | - if target is not None: |
205 | | - network = target.intern_ip.network |
206 | | - if network.version != source.intern_ip.version: |
207 | | - target = None |
208 | | - elif network.broadcast_address < source.intern_ip.ip: |
209 | | - target = None |
210 | | - elif source.intern_ip not in network: |
211 | | - continue |
212 | | - # Check for a new target |
213 | | - if target is None and source.intern_ip: |
214 | | - try: |
215 | | - target = source.get_supernet(attribute.target_servertype) |
216 | | - except Server.DoesNotExist: |
217 | | - continue |
218 | | - self._server_attributes[source][attribute] = target |
| 219 | + supernets = Server.objects.filter(server_id__in=attrs.values("supernet_id")) |
| 220 | + supernets_by_id = {s.server_id: s for s in supernets} |
| 221 | + |
| 222 | + for a in attrs: |
| 223 | + if a.supernet_id in supernets_by_id: |
| 224 | + self._server_attributes[a.server][attribute] = supernets_by_id[ |
| 225 | + a.supernet_id |
| 226 | + ] |
| 227 | + |
| 228 | + def _add_supernet_attribute_intern_ip(self, attribute, servers): |
| 229 | + servers_in_1, servers_in_2 = itertools.tee(servers) |
| 230 | + |
| 231 | + supernet_id = Server.objects.filter( |
| 232 | + intern_ip__net_contains=OuterRef("intern_ip"), |
| 233 | + servertype_id=attribute.target_servertype_id, |
| 234 | + ).values("server_id") |
| 235 | + |
| 236 | + servers = Server.objects.filter( |
| 237 | + server_id__in=[s.server_id for s in servers_in_1], |
| 238 | + ).annotate( |
| 239 | + supernet_id=Subquery(supernet_id, output_field=AutoField()), |
| 240 | + ) |
| 241 | + |
| 242 | + supernets = Server.objects.filter(server_id__in=servers.values("supernet_id")) |
| 243 | + supernets_by_id = {s.server_id: s for s in supernets} |
| 244 | + |
| 245 | + supernets_by_server_hostname = { |
| 246 | + s.hostname: supernets_by_id[s.supernet_id] |
| 247 | + for s in servers |
| 248 | + if s.supernet_id in supernets_by_id |
| 249 | + } |
| 250 | + |
| 251 | + for s in servers_in_2: |
| 252 | + if s.hostname in supernets_by_server_hostname: |
| 253 | + self._server_attributes[s][attribute] = supernets_by_server_hostname[ |
| 254 | + s.hostname |
| 255 | + ] |
219 | 256 |
|
220 | 257 | def _add_related_attribute(self, attribute, servertype_attribute, servers_by_type): |
221 | 258 | related_via_attribute = servertype_attribute.related_via_attribute |
|
0 commit comments