Files
Netbox-Better-IPs/netbox_better_ips/views.py
T
MrBlake 24e59ee9c0 feat: automatische Prefix-Zuordnung und IP-Netzübersicht hinzufügen
- fehlende Prefixe beim Speichern von IP-Adressen automatisch erstellen
- VRF, Mandant, Standort und Lokation übernehmen
- bestehende IP-Adressen per Management-Command abgleichen
- filterbare IP-Netzübersicht für Regionen, Standorte und Mandanten ergänzen
- NetBox 4.6.5 unterstützen
2026-07-23 11:06:50 +02:00

90 lines
3.6 KiB
Python

from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.paginator import Paginator
from django.db.models import Q
from django.views.generic import TemplateView
from dcim.models import Location, Region, Site, SiteGroup
from ipam.models import Prefix
import netaddr
from .forms import NetworkOverviewFilterForm
def _descendant_ids(obj):
"""Return an object's ID plus nested-tree descendants across NetBox versions."""
try:
return list(obj.get_descendants(include_self=True).values_list("pk", flat=True))
except (AttributeError, TypeError):
ids = list(obj.get_descendants().values_list("pk", flat=True))
return [obj.pk, *ids]
def _scope_query(model, ids):
content_type = ContentType.objects.get_for_model(model)
return Q(scope_type=content_type, scope_id__in=ids)
def apply_overview_filters(queryset, data):
if q := data.get("q"):
search = Q(description__icontains=q) | Q(comments__icontains=q)
try:
search |= Q(prefix__net_contains_or_equals=str(netaddr.IPNetwork(q)))
except (netaddr.AddrFormatError, ValueError):
pass
queryset = queryset.filter(search)
if tenant := data.get("tenant"):
queryset = queryset.filter(tenant=tenant)
if group := data.get("tenant_group"):
queryset = queryset.filter(tenant__group_id__in=_descendant_ids(group))
if location := data.get("location"):
queryset = queryset.filter(_scope_query(Location, _descendant_ids(location)))
if site := data.get("site"):
location_ids = Location.objects.filter(site=site).values_list("pk", flat=True)
queryset = queryset.filter(
_scope_query(Site, [site.pk]) | _scope_query(Location, location_ids)
)
if site_group := data.get("site_group"):
site_ids = Site.objects.filter(group_id__in=_descendant_ids(site_group)).values_list(
"pk", flat=True
)
location_ids = Location.objects.filter(site_id__in=site_ids).values_list("pk", flat=True)
queryset = queryset.filter(
_scope_query(SiteGroup, _descendant_ids(site_group))
| _scope_query(Site, site_ids)
| _scope_query(Location, location_ids)
)
if region := data.get("organization"):
region_ids = _descendant_ids(region)
site_ids = Site.objects.filter(region_id__in=region_ids).values_list("pk", flat=True)
location_ids = Location.objects.filter(site_id__in=site_ids).values_list("pk", flat=True)
queryset = queryset.filter(
_scope_query(Region, region_ids)
| _scope_query(Site, site_ids)
| _scope_query(Location, location_ids)
)
return queryset
class NetworkOverviewView(PermissionRequiredMixin, TemplateView):
template_name = "netbox_better_ips/network_overview.html"
permission_required = "ipam.view_prefix"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = NetworkOverviewFilterForm(self.request.GET or None)
prefixes = Prefix.objects.restrict(self.request.user, "view").select_related(
"vrf", "tenant", "scope_type"
)
if form.is_valid():
prefixes = apply_overview_filters(prefixes, form.cleaned_data)
prefixes = prefixes.order_by("prefix", "vrf")
paginator = Paginator(prefixes, 50)
context.update(
{
"filter_form": form,
"page_obj": paginator.get_page(self.request.GET.get("page")),
"total_count": prefixes.count(),
}
)
return context