mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-26 18:38:38 -06:00
Replace add_available_ipaddresses() with annotate_ip_space()
This commit is contained in:
parent
d82040bdc4
commit
ecb56dda14
@ -383,14 +383,15 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
else:
|
||||
return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf)
|
||||
|
||||
def get_child_ranges(self):
|
||||
def get_child_ranges(self, **kwargs):
|
||||
"""
|
||||
Return all IPRanges within this Prefix and VRF.
|
||||
"""
|
||||
return IPRange.objects.filter(
|
||||
vrf=self.vrf,
|
||||
start_address__net_host_contained=str(self.prefix),
|
||||
end_address__net_host_contained=str(self.prefix)
|
||||
end_address__net_host_contained=str(self.prefix),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def get_child_ips(self):
|
||||
|
@ -26,12 +26,14 @@ PREFIX_LINK_WITH_DEPTH = """
|
||||
""" + PREFIX_LINK
|
||||
|
||||
IPADDRESS_LINK = """
|
||||
{% if record.pk %}
|
||||
{% if record.address %}
|
||||
<a href="{{ record.get_absolute_url }}" id="ipaddress_{{ record.pk }}">{{ record.address }}</a>
|
||||
{% elif record.start_address %}
|
||||
<a href="{{ record.get_absolute_url }}" id="range_{{ record.pk }}">{{ record }}</a>
|
||||
{% elif perms.ipam.add_ipaddress %}
|
||||
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}&return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-sm btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available</a>
|
||||
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.first_ip }}{% if object.vrf %}&vrf={{ object.vrf.pk }}{% endif %}{% if object.tenant %}&tenant={{ object.tenant.pk }}{% endif %}&return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-sm btn-success">{% if record.size <= 65536 %}{{ record.size }}{% else %}Many{% endif %} IP{{ record.size|pluralize }} available</a>
|
||||
{% else %}
|
||||
{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
|
||||
{% if record.size <= 65536 %}{{ record.size }}{% else %}Many{% endif %} IP{{ record.size|pluralize }} available
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
|
@ -1,17 +1,25 @@
|
||||
from dataclasses import dataclass
|
||||
import netaddr
|
||||
|
||||
from .constants import *
|
||||
from .models import Prefix, VLAN
|
||||
|
||||
__all__ = (
|
||||
'add_available_ipaddresses',
|
||||
'AvailableIPSpace',
|
||||
'add_available_vlans',
|
||||
'add_requested_prefixes',
|
||||
'annotate_ip_space',
|
||||
'get_next_available_prefix',
|
||||
'rebuild_prefixes',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AvailableIPSpace:
|
||||
size: int
|
||||
first_ip: str
|
||||
|
||||
|
||||
def add_requested_prefixes(parent, prefix_list, show_available=True, show_assigned=True):
|
||||
"""
|
||||
Return a list of requested prefixes using show_available, show_assigned filters. If available prefixes are
|
||||
@ -42,50 +50,74 @@ def add_requested_prefixes(parent, prefix_list, show_available=True, show_assign
|
||||
return child_prefixes
|
||||
|
||||
|
||||
def add_available_ipaddresses(prefix, ipaddress_list, is_pool=False):
|
||||
"""
|
||||
Annotate ranges of available IP addresses within a given prefix. If is_pool is True, the first and last IP will be
|
||||
considered usable (regardless of mask length).
|
||||
"""
|
||||
def annotate_ip_space(prefix):
|
||||
# Compile child objects
|
||||
records = []
|
||||
records.extend([
|
||||
(iprange.start_address.ip, iprange) for iprange in prefix.get_child_ranges(mark_populated=True)
|
||||
])
|
||||
records.extend([
|
||||
(ip.address.ip, ip) for ip in prefix.get_child_ips()
|
||||
])
|
||||
records = sorted(records, key=lambda x: x[0])
|
||||
|
||||
# Determine the first & last valid IP addresses in the prefix
|
||||
if prefix.family == 4 and prefix.mask_length < 31 and not prefix.is_pool:
|
||||
# Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31
|
||||
first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first + 1)
|
||||
last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last - 1)
|
||||
else:
|
||||
first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first)
|
||||
last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last)
|
||||
|
||||
if not records:
|
||||
return [
|
||||
AvailableIPSpace(
|
||||
size=int(last_ip_in_prefix - first_ip_in_prefix + 1),
|
||||
first_ip=f'{first_ip_in_prefix}/{prefix.mask_length}'
|
||||
)
|
||||
]
|
||||
|
||||
output = []
|
||||
prev_ip = None
|
||||
|
||||
# Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31.
|
||||
if prefix.version == 4 and prefix.prefixlen < 31 and not is_pool:
|
||||
first_ip_in_prefix = netaddr.IPAddress(prefix.first + 1)
|
||||
last_ip_in_prefix = netaddr.IPAddress(prefix.last - 1)
|
||||
else:
|
||||
first_ip_in_prefix = netaddr.IPAddress(prefix.first)
|
||||
last_ip_in_prefix = netaddr.IPAddress(prefix.last)
|
||||
|
||||
if not ipaddress_list:
|
||||
return [(
|
||||
int(last_ip_in_prefix - first_ip_in_prefix + 1),
|
||||
'{}/{}'.format(first_ip_in_prefix, prefix.prefixlen)
|
||||
)]
|
||||
|
||||
# Account for any available IPs before the first real IP
|
||||
if ipaddress_list[0].address.ip > first_ip_in_prefix:
|
||||
skipped_count = int(ipaddress_list[0].address.ip - first_ip_in_prefix)
|
||||
first_skipped = '{}/{}'.format(first_ip_in_prefix, prefix.prefixlen)
|
||||
output.append((skipped_count, first_skipped))
|
||||
if records[0][0] > first_ip_in_prefix:
|
||||
output.append(AvailableIPSpace(
|
||||
size=int(records[0][0] - first_ip_in_prefix),
|
||||
first_ip=f'{first_ip_in_prefix}/{prefix.mask_length}'
|
||||
))
|
||||
|
||||
# Iterate through existing IPs and annotate free ranges
|
||||
for ip in ipaddress_list:
|
||||
for record in records:
|
||||
if prev_ip:
|
||||
diff = int(ip.address.ip - prev_ip.address.ip)
|
||||
if diff > 1:
|
||||
first_skipped = '{}/{}'.format(prev_ip.address.ip + 1, prefix.prefixlen)
|
||||
output.append((diff - 1, first_skipped))
|
||||
output.append(ip)
|
||||
prev_ip = ip
|
||||
# We've already listed a range which covers this IP
|
||||
if record[0] < prev_ip:
|
||||
continue
|
||||
|
||||
# Annotate available space
|
||||
if (diff := int(record[0] - prev_ip)) > 1:
|
||||
first_skipped = f'{prev_ip + 1}/{prefix.mask_length}'
|
||||
output.append(AvailableIPSpace(
|
||||
size=diff - 1,
|
||||
first_ip=first_skipped
|
||||
))
|
||||
|
||||
output.append(record[1])
|
||||
|
||||
# Update the previous IP address
|
||||
if hasattr(record[1], 'end_address'):
|
||||
prev_ip = record[1].end_address.ip
|
||||
else:
|
||||
prev_ip = record[0]
|
||||
print(f'prev_ip: {prev_ip}')
|
||||
|
||||
# Include any remaining available IPs
|
||||
if prev_ip.address.ip < last_ip_in_prefix:
|
||||
skipped_count = int(last_ip_in_prefix - prev_ip.address.ip)
|
||||
first_skipped = '{}/{}'.format(prev_ip.address.ip + 1, prefix.prefixlen)
|
||||
output.append((skipped_count, first_skipped))
|
||||
if prev_ip < last_ip_in_prefix:
|
||||
output.append(AvailableIPSpace(
|
||||
size=int(last_ip_in_prefix - prev_ip),
|
||||
first_ip=f'{prev_ip + 1}/{prefix.mask_length}'
|
||||
))
|
||||
|
||||
return output
|
||||
|
||||
|
@ -21,7 +21,7 @@ from . import filtersets, forms, tables
|
||||
from .choices import PrefixStatusChoices
|
||||
from .constants import *
|
||||
from .models import *
|
||||
from .utils import add_requested_prefixes, add_available_ipaddresses, add_available_vlans
|
||||
from .utils import add_requested_prefixes, add_available_vlans, annotate_ip_space
|
||||
|
||||
|
||||
#
|
||||
@ -635,7 +635,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
|
||||
|
||||
def prep_table_data(self, request, queryset, parent):
|
||||
if not request.GET.get('q') and not get_table_ordering(request, self.table):
|
||||
return add_available_ipaddresses(parent.prefix, queryset, parent.is_pool)
|
||||
return annotate_ip_space(parent)
|
||||
return queryset
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
|
Loading…
Reference in New Issue
Block a user