Fixes #8192: Add "add prefix" button to aggregate child prefixes view

This commit is contained in:
jeremystretch 2021-12-30 12:00:37 -05:00
parent 881305af1f
commit 1ef8379a38
4 changed files with 34 additions and 21 deletions

View File

@ -2,6 +2,10 @@
## v3.1.4 (FUTURE) ## v3.1.4 (FUTURE)
### Enhancements
* [#8192](https://github.com/netbox-community/netbox/issues/8192) - Add "add prefix" button to aggregate child prefixes view
### Bug Fixes ### Bug Fixes
* [#8191](https://github.com/netbox-community/netbox/issues/8191) - Fix return URL when adding IP addresses to VM interfaces * [#8191](https://github.com/netbox-community/netbox/issues/8191) - Fix return URL when adding IP addresses to VM interfaces

View File

@ -32,6 +32,28 @@ __all__ = (
) )
class GetAvailablePrefixesMixin:
def get_available_prefixes(self):
"""
Return all available Prefixes within this aggregate as an IPSet.
"""
prefix = netaddr.IPSet(self.prefix)
child_prefixes = netaddr.IPSet([child.prefix for child in self.get_child_prefixes()])
available_prefixes = prefix - child_prefixes
return available_prefixes
def get_first_available_prefix(self):
"""
Return the first available child prefix within the prefix (or None).
"""
available_prefixes = self.get_available_prefixes()
if not available_prefixes:
return None
return available_prefixes.iter_cidrs()[0]
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
class RIR(OrganizationalModel): class RIR(OrganizationalModel):
""" """
@ -110,7 +132,7 @@ class ASN(PrimaryModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
class Aggregate(PrimaryModel): class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):
""" """
An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR. the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR.
@ -245,7 +267,7 @@ class Role(OrganizationalModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
class Prefix(PrimaryModel): class Prefix(GetAvailablePrefixesMixin, PrimaryModel):
""" """
A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and
VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be
@ -458,16 +480,6 @@ class Prefix(PrimaryModel):
else: else:
return IPAddress.objects.filter(address__net_host_contained=str(self.prefix), vrf=self.vrf) return IPAddress.objects.filter(address__net_host_contained=str(self.prefix), vrf=self.vrf)
def get_available_prefixes(self):
"""
Return all available Prefixes within this prefix as an IPSet.
"""
prefix = netaddr.IPSet(self.prefix)
child_prefixes = netaddr.IPSet([child.prefix for child in self.get_child_prefixes()])
available_prefixes = prefix - child_prefixes
return available_prefixes
def get_available_ips(self): def get_available_ips(self):
""" """
Return all available IPs within this prefix as an IPSet. Return all available IPs within this prefix as an IPSet.
@ -494,15 +506,6 @@ class Prefix(PrimaryModel):
return available_ips return available_ips
def get_first_available_prefix(self):
"""
Return the first available child prefix within the prefix (or None).
"""
available_prefixes = self.get_available_prefixes()
if not available_prefixes:
return None
return available_prefixes.iter_cidrs()[0]
def get_first_available_ip(self): def get_first_available_ip(self):
""" """
Return the first available IP within the prefix (or None). Return the first available IP within the prefix (or None).

View File

@ -299,6 +299,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
return { return {
'bulk_querystring': f'within={instance.prefix}', 'bulk_querystring': f'within={instance.prefix}',
'active_tab': 'prefixes', 'active_tab': 'prefixes',
'first_available_prefix': instance.get_first_available_prefix(),
'show_available': bool(request.GET.get('show_available', 'true') == 'true'), 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
} }

View File

@ -3,6 +3,11 @@
{% block extra_controls %} {% block extra_controls %}
{% include 'ipam/inc/toggle_available.html' %} {% include 'ipam/inc/toggle_available.html' %}
{% if perms.ipam.add_prefix and first_available_prefix %}
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}" class="btn btn-sm btn-primary">
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Prefix
</a>
{% endif %}
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}