From 3ef7ab4416bb33c3216526b1bb5e2b4cabeb7edd Mon Sep 17 00:00:00 2001 From: Renato Almeida de Oliveira Date: Thu, 6 Mar 2025 12:41:41 -0300 Subject: [PATCH] Fixes: #18579 Add contact filters to services (#18818) * Add Contact filter to Services * Add ContactModelFilterForm to ProviderAccountFilterForm * Add Contact filter support for Aggregate * Add Contact filter support for Prefix * Add Contact filter to IPRange * Add Contact filter to IPAddress * Add Contact filter to L2VPN * Add Contact filter to TunnelGroup * Add Contact filter to Tunnel * Add ContactModelFilterSet to ProviderAccountFilterSet * Fixes classes inheritance order Setup NetBoxModelFilterSetForm as the last inherited class Co-authored-by: Jason Novinger --------- Co-authored-by: Jason Novinger --- netbox/circuits/filtersets.py | 2 +- netbox/circuits/forms/filtersets.py | 3 ++- netbox/ipam/filtersets.py | 13 +++++++------ netbox/ipam/forms/filtersets.py | 17 +++++++++++------ netbox/vpn/filtersets.py | 8 ++++---- netbox/vpn/forms/filtersets.py | 14 ++++++++++---- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index 188b5343e..7775255fc 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -95,7 +95,7 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): ) -class ProviderAccountFilterSet(NetBoxModelFilterSet): +class ProviderAccountFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): provider_id = django_filters.ModelMultipleChoiceFilter( queryset=Provider.objects.all(), label=_('Provider (ID)'), diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index a75684ef5..9b2129989 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -66,11 +66,12 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): tag = TagFilterField(model) -class ProviderAccountFilterForm(NetBoxModelFilterSetForm): +class ProviderAccountFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): model = ProviderAccount fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('provider_id', 'account', name=_('Attributes')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) provider_id = DynamicModelMultipleChoiceField( queryset=Provider.objects.all(), diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 81cbd2ef8..b23322a22 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -12,7 +12,8 @@ from netaddr.core import AddrFormatError from circuits.models import Provider from dcim.models import Device, Interface, Region, Site, SiteGroup from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet -from tenancy.filtersets import TenancyFilterSet +from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet + from utilities.filters import ( ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter, ) @@ -148,7 +149,7 @@ class RIRFilterSet(OrganizationalModelFilterSet): fields = ('id', 'name', 'slug', 'is_private', 'description') -class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): family = django_filters.NumberFilter( field_name='prefix', lookup_expr='family' @@ -276,7 +277,7 @@ class RoleFilterSet(OrganizationalModelFilterSet): fields = ('id', 'name', 'slug', 'description', 'weight') -class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet): +class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, ContactModelFilterSet): family = django_filters.NumberFilter( field_name='prefix', lookup_expr='family' @@ -430,7 +431,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet): ).distinct() -class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet): +class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet, ContactModelFilterSet): family = django_filters.NumberFilter( field_name='start_address', lookup_expr='family' @@ -522,7 +523,7 @@ class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet): return queryset.filter(q) -class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): family = django_filters.NumberFilter( field_name='address', lookup_expr='family' @@ -1136,7 +1137,7 @@ class ServiceTemplateFilterSet(NetBoxModelFilterSet): return queryset.filter(qs_filter) -class ServiceFilterSet(NetBoxModelFilterSet): +class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet): device_id = django_filters.ModelMultipleChoiceFilter( queryset=Device.objects.all(), label=_('Device (ID)'), diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index 3f951512b..a4faa18ed 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -6,7 +6,7 @@ from ipam.choices import * from ipam.constants import * from ipam.models import * from netbox.forms import NetBoxModelFilterSetForm -from tenancy.forms import TenancyFilterForm +from tenancy.forms import ContactModelFilterForm, TenancyFilterForm from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.rendering import FieldSet @@ -94,12 +94,13 @@ class RIRFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class AggregateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class AggregateFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm): model = Aggregate fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('family', 'rir_id', name=_('Attributes')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) family = forms.ChoiceField( required=False, @@ -162,7 +163,7 @@ class RoleFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm, ): model = Prefix fieldsets = ( FieldSet('q', 'filter_id', 'tag'), @@ -174,6 +175,7 @@ class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): FieldSet('vrf_id', 'present_in_vrf_id', name=_('VRF')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) mask_length__lte = forms.IntegerField( widget=forms.HiddenInput() @@ -262,12 +264,13 @@ class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): tag = TagFilterField(model) -class IPRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm): model = IPRange fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('family', 'vrf_id', 'status', 'role_id', 'mark_utilized', name=_('Attributes')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) family = forms.ChoiceField( required=False, @@ -301,7 +304,7 @@ class IPRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): tag = TagFilterField(model) -class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class IPAddressFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm): model = IPAddress fieldsets = ( FieldSet('q', 'filter_id', 'tag'), @@ -312,6 +315,7 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): FieldSet('vrf_id', 'present_in_vrf_id', name=_('VRF')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('device_id', 'virtual_machine_id', name=_('Device/VM')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) selector_fields = ('filter_id', 'q', 'region_id', 'group_id', 'parent', 'status', 'role') parent = forms.CharField( @@ -590,12 +594,13 @@ class ServiceTemplateFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class ServiceFilterForm(ServiceTemplateFilterForm): +class ServiceFilterForm(ContactModelFilterForm, ServiceTemplateFilterForm): model = Service fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('protocol', 'port', name=_('Attributes')), FieldSet('device_id', 'virtual_machine_id', name=_('Assignment')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py index 6403b662f..5b8d1899b 100644 --- a/netbox/vpn/filtersets.py +++ b/netbox/vpn/filtersets.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext as _ from dcim.models import Device, Interface from ipam.models import IPAddress, RouteTarget, VLAN from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet -from tenancy.filtersets import TenancyFilterSet +from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter from virtualization.models import VirtualMachine, VMInterface from .choices import * @@ -25,14 +25,14 @@ __all__ = ( ) -class TunnelGroupFilterSet(OrganizationalModelFilterSet): +class TunnelGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): class Meta: model = TunnelGroup fields = ('id', 'name', 'slug', 'description') -class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): status = django_filters.MultipleChoiceFilter( choices=TunnelStatusChoices ) @@ -293,7 +293,7 @@ class IPSecProfileFilterSet(NetBoxModelFilterSet): ) -class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): type = django_filters.MultipleChoiceFilter( choices=L2VPNTypeChoices, null_value=None diff --git a/netbox/vpn/forms/filtersets.py b/netbox/vpn/forms/filtersets.py index 10dc441e2..619956156 100644 --- a/netbox/vpn/forms/filtersets.py +++ b/netbox/vpn/forms/filtersets.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext as _ from dcim.models import Device, Region, Site from ipam.models import RouteTarget, VLAN from netbox.forms import NetBoxModelFilterSetForm -from tenancy.forms import TenancyFilterForm +from tenancy.forms import ContactModelFilterForm, TenancyFilterForm from utilities.forms.fields import ( ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField, ) @@ -30,18 +30,23 @@ __all__ = ( ) -class TunnelGroupFilterForm(NetBoxModelFilterSetForm): +class TunnelGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): model = TunnelGroup + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), + ) tag = TagFilterField(model) -class TunnelFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class TunnelFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm): model = Tunnel fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('status', 'encapsulation', 'tunnel_id', name=_('Tunnel')), FieldSet('ipsec_profile_id', name=_('Security')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenancy')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) status = forms.MultipleChoiceField( label=_('Status'), @@ -206,12 +211,13 @@ class IPSecProfileFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class L2VPNFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): +class L2VPNFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm): model = L2VPN fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('type', 'import_target_id', 'export_target_id', name=_('Attributes')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), + FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) type = forms.ChoiceField( label=_('Type'),