Skip to content

Commit

Permalink
Restore supernet calculation optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
TuxPowered42 committed Dec 15, 2023
1 parent dc08c29 commit 375ead3
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 91 deletions.
15 changes: 10 additions & 5 deletions serveradmin/serverdb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def is_supernet_consistent(ip_address, server):
supernet_servertypes = [x.target_servertype for x in supernet_attributes]

for supernet_servertype in supernet_servertypes:
supernet_1 = server.get_supernet(supernet_servertype) # The result is guaranteed to be consistent
supernet_1, _, _ = server.get_supernet(supernet_servertype) # The result is guaranteed to be consistent

if supernet_1 is None:
continue
Expand All @@ -109,7 +109,6 @@ def is_supernet_consistent(ip_address, server):
server__servertype_id=supernet_servertype,
).server
except server_attribute_cls.DoesNotExist:
print(f'Zupa: {supernet_1} DoesNotExist')
# There are some provider networks which are valid only for one address family.
# TODO: Enforce validation once the "local" network is gone.
continue
Expand Down Expand Up @@ -515,17 +514,21 @@ def get_supernet(self, servertype):
This function will check all IP addresses of a server which have the "supernet" feature enabled.
If data is inconsistent, an exception is raised.
No matching network for just some of addresses does not mean inconsitency.
No matching network for just some of the addresses does not mean inconsistency.
"""

supernet_1 = None
supernet_ip_address = None
supernet_attribute = None

# TODO: Remove "intern_ip" support.
# TODO: Remove "intern_ip" support. Just remove this block of code below.
supernet_1 = Server.objects.get(
servertype=servertype,
# It should probably match on ip_addr_type too, but we will remove this soon anyway.
intern_ip__net_contains_or_equals=self.intern_ip,
)
supernet_ip_address = self.intern_ip
supernet_attribute = None # Magic value for intern_ip

for server_attribute_cls in (ServerInet4Attribute, ServerInet6Attribute):
ip_addresses = server_attribute_cls.objects.filter(
Expand All @@ -551,11 +554,13 @@ def get_supernet(self, servertype):
# Always trust the 1st found network
if supernet_1 is None:
supernet_1 = supernet_2
supernet_ip_address = ip_address.value
supernet_attribute = ip_addresses.attribute
# Verify that all found networks match the 1st found one.
elif supernet_1 != supernet_2:
raise ValidationError(f'Can\'t determine {servertype} for {self.hostname}!')

return supernet_1
return (supernet_1, supernet_ip_address, supernet_attribute)

def clean(self):
super(Server, self).clean()
Expand Down
113 changes: 27 additions & 86 deletions serveradmin/serverdb/query_materializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@

from ipaddress import IPv4Address, IPv6Address

from django.core.exceptions import ValidationError

from adminapi.dataset import DatasetObject
from serveradmin.serverdb.models import (
Servertype,
Attribute,
ServertypeAttribute,
Server,
ServerAttribute,
ServerRelationAttribute, ServerInet4Attribute, ServerInet6Attribute,
ServerRelationAttribute
)


Expand Down Expand Up @@ -119,6 +117,7 @@ def _add_attributes(self, servers_by_type):
for key, attributes in self._attributes_by_type.items():
if key == 'supernet':
# Add supernets only after all other attributes.
# Some might be needed for supernet calculation.
continue
elif key == 'domain':
for attribute in attributes:
Expand Down Expand Up @@ -192,93 +191,35 @@ def _add_supernet_attribute(self, attribute, servers):
This function takes advantage of networks in the same servertype not
overlapping with each other.
"""
supernets = {}
for source in sorted(servers, key=lambda s: _sort_key(s.intern_ip)):
# TODO: We have a working get_supernet() now, why not use it instead?
# TODO: We can't depend on servers being sorted anymore.
# TODO: Remove "intern_ip" support.
# There never was nor is "primary_ip6" support.
supernets['intern_ip'] = {
'source': source.intern_ip,
'target': None,
}

for net_attribute in self._server_attributes[source]:
# Find server attributes which can be used for calculating supernets.
# This allows for having "primary" ip addresses used for supernet calculation
# and "additional" ip address which won't be used here.
# FIXME: We never check if there's more than 1 attribute per address family.
if net_attribute.supernet == False:
continue
net_attribute_value = self._server_attributes.get(source).get(net_attribute)
if net_attribute_value is None:
continue
source_address = self._server_attributes[source][net_attribute]
supernets[net_attribute] = {
'source': source_address,
'target': None,
}
# Sort servers by IP addresses so that when iterating over them
# subsequent servers might belong to the same supernet.
servers_sorted = sorted(servers, key=lambda s:
(_sort_key(s.intern_ip),) + # TODO: Remove "intern_ip" support
tuple(x for x in self._server_attributes[s] if x.supernet)
)

for supernet_k, supernet_v in supernets.items():
# Check the previous target
if supernet_v['target'] is not None:
network = supernet_v['source'].network
if network.version != supernet_v['source'].version:
supernet_v['target'] = None
elif network.broadcast_address < supernet_v['source'].ip:
supernet_v['target'] = None
elif supernet_v['source'] not in network:
continue
# Check for a new target
if supernet_v['target'] is None and supernet_v['source']:
if supernet_k == 'intern_ip':
try:
supernet_v['target'] = source.get_supernet(attribute.target_servertype)
except Server.DoesNotExist:
continue
else:
if supernet_v['source'].version == 4:
server_attribute_cls = ServerInet4Attribute
else:
server_attribute_cls = ServerInet6Attribute

try:
network = server_attribute_cls.objects.get(
value__net_contains_or_equals=supernet_v['source'],
server__servertype__ip_addr_type='network',
server__servertype=attribute.target_servertype,
)
supernet_v['target'] = Server.objects.get(server_id=network.server_id)
except (
Server.DoesNotExist,
ServerInet4Attribute.DoesNotExist,
ServerInet6Attribute.DoesNotExist,
):
continue

# Verify that the networks are consistent. Refuse to return data if they are not.
supernet_temp_k = None
supernet_temp_v = None
for supernet_k, supernet_v in supernets.items():
# Ignore if target supernet is not found by given method.
# We might not have all data in yet, and we will be fine trusting "intern_ip".
if supernet_v['target'] is None:
continue
supernet = None
cached_address = None
cached_attribute = None

for server in servers_sorted:
# Check if the current server belongs to a supernet found
# for the previous server.
if supernet is not None:
# TODO: Remove "intern_ip" support.
if cached_attribute is None:
server_address = server.intern_ip
else:
server_address = self._server_attributes[server][cached_attribute].value
if server_address.ip not in cached_address.network:
supernet = None # It will be re-assigned in the next step.

# Trust the 1st found supernet.
if supernet_temp_v == None:
supernet_temp_k = supernet_k
supernet_temp_v = supernet_v
continue
# Check for a new target
if supernet is None:
supernet, cached_address, cached_attribute = server.get_supernet(attribute.target_servertype)

# Verify the 2nd and later supernets against the 1st one.
if supernet_v['target'] != supernet_temp_v['target']:
raise ValidationError(
f'Inconsistent {attribute} for {source}: '
f'{supernet_temp_v["target"]} from {supernet_temp_k} vs '
f'{supernet_v["target"]} from {supernet_k}!'
)
self._server_attributes[source][attribute] = supernet_temp_v['target']
self._server_attributes[server][attribute] = supernet

def _add_related_attribute(
self, attribute, servertype_attribute, servers_by_type
Expand Down

0 comments on commit 375ead3

Please sign in to comment.