diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index ffaefad6d..0022dbd73 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -26,14 +26,16 @@ from .nested_serializers import * class VRFSerializer(TaggedObjectSerializer, CustomFieldModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail') tenant = NestedTenantSerializer(required=False, allow_null=True) + import_targets = NestedRouteTargetSerializer(required=False, allow_null=True, many=True) + export_targets = NestedRouteTargetSerializer(required=False, allow_null=True, many=True) ipaddress_count = serializers.IntegerField(read_only=True) prefix_count = serializers.IntegerField(read_only=True) class Meta: model = VRF fields = [ - 'id', 'url', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags', 'display_name', - 'custom_fields', 'created', 'last_updated', 'ipaddress_count', 'prefix_count', + 'id', 'url', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', + 'tags', 'display_name', 'custom_fields', 'created', 'last_updated', 'ipaddress_count', 'prefix_count', ] diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 69277a8ea..449ef3245 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -30,7 +30,9 @@ class IPAMRootView(APIRootView): # class VRFViewSet(CustomFieldModelViewSet): - queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate( + queryset = VRF.objects.prefetch_related('tenant').prefetch_related( + 'import_targets', 'export_targets', 'tags' + ).annotate( ipaddress_count=get_subquery(IPAddress, 'vrf'), prefix_count=get_subquery(Prefix, 'vrf') ).order_by(*VRF._meta.ordering) diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index 6059b4330..0cbbd3f78 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -35,6 +35,28 @@ class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Create method='search', label='Search', ) + import_target_id = django_filters.ModelMultipleChoiceFilter( + field_name='import_targets', + queryset=RouteTarget.objects.all(), + label='Import target', + ) + import_target = django_filters.ModelMultipleChoiceFilter( + field_name='import_targets__name', + queryset=RouteTarget.objects.all(), + to_field_name='name', + label='Import target (name)', + ) + export_target_id = django_filters.ModelMultipleChoiceFilter( + field_name='export_targets', + queryset=RouteTarget.objects.all(), + label='Export target', + ) + export_target = django_filters.ModelMultipleChoiceFilter( + field_name='export_targets__name', + queryset=RouteTarget.objects.all(), + to_field_name='name', + label='Export target (name)', + ) tag = TagFilter() def search(self, queryset, name, value): @@ -56,6 +78,28 @@ class RouteTargetFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet method='search', label='Search', ) + importing_vrf_id = django_filters.ModelMultipleChoiceFilter( + field_name='importing_vrfs', + queryset=VRF.objects.all(), + label='Importing VRF', + ) + importing_vrf = django_filters.ModelMultipleChoiceFilter( + field_name='importing_vrfs__rd', + queryset=VRF.objects.all(), + to_field_name='rd', + label='Import VRF (RD)', + ) + exporting_vrf_id = django_filters.ModelMultipleChoiceFilter( + field_name='exporting_vrfs', + queryset=VRF.objects.all(), + label='Exporting VRF', + ) + exporting_vrf = django_filters.ModelMultipleChoiceFilter( + field_name='exporting_vrfs__rd', + queryset=VRF.objects.all(), + to_field_name='rd', + label='Export VRF (RD)', + ) tag = TagFilter() def search(self, queryset, name, value): diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index ed6071756..714279859 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -31,6 +31,14 @@ IPADDRESS_MASK_LENGTH_CHOICES = add_blank_choice([ # class VRFForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): + import_targets = DynamicModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + required=False + ) + export_targets = DynamicModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + required=False + ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False @@ -39,7 +47,8 @@ class VRFForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): class Meta: model = VRF fields = [ - 'name', 'rd', 'enforce_unique', 'description', 'tenant_group', 'tenant', 'tags', + 'name', 'rd', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tenant_group', 'tenant', + 'tags', ] labels = { 'rd': "RD", @@ -89,11 +98,21 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): model = VRF - field_order = ['q', 'tenant_group', 'tenant'] + field_order = ['q', 'import_target', 'export_target', 'tenant_group', 'tenant'] q = forms.CharField( required=False, label='Search' ) + import_target = DynamicModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + to_field_name='name', + required=False + ) + export_target = DynamicModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + to_field_name='name', + required=False + ) tag = TagFilterField(model) @@ -149,11 +168,21 @@ class RouteTargetBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulk class RouteTargetFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): model = RouteTarget - field_order = ['q', 'name', 'tenant_group', 'tenant'] + field_order = ['q', 'name', 'tenant_group', 'tenant', 'importing_vrfs', 'exporting_vrfs'] q = forms.CharField( required=False, label='Search' ) + importing_vrf_id = DynamicModelMultipleChoiceField( + queryset=VRF.objects.all(), + required=False, + label='Imported by VRF' + ) + exporting_vrf_id = DynamicModelMultipleChoiceField( + queryset=VRF.objects.all(), + required=False, + label='Exported by VRF' + ) tag = TagFilterField(model) diff --git a/netbox/ipam/migrations/0041_routetarget.py b/netbox/ipam/migrations/0041_routetarget.py index d2e800be2..9cc37b742 100644 --- a/netbox/ipam/migrations/0041_routetarget.py +++ b/netbox/ipam/migrations/0041_routetarget.py @@ -31,4 +31,14 @@ class Migration(migrations.Migration): 'ordering': ['name'], }, ), + migrations.AddField( + model_name='vrf', + name='export_targets', + field=models.ManyToManyField(blank=True, related_name='exporting_vrfs', to='ipam.RouteTarget'), + ), + migrations.AddField( + model_name='vrf', + name='import_targets', + field=models.ManyToManyField(blank=True, related_name='importing_vrfs', to='ipam.RouteTarget'), + ), ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index f743fe5b0..f7e4d9cf4 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -71,6 +71,16 @@ class VRF(ChangeLoggedModel, CustomFieldModel): max_length=200, blank=True ) + import_targets = models.ManyToManyField( + to='ipam.RouteTarget', + related_name='importing_vrfs', + blank=True + ) + export_targets = models.ManyToManyField( + to='ipam.RouteTarget', + related_name='exporting_vrfs', + blank=True + ) tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 240bfedd3..bc3e4b69b 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -39,9 +39,20 @@ class VRFView(ObjectView): vrf = get_object_or_404(self.queryset, pk=pk) prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=vrf).count() + import_targets_table = tables.RouteTargetTable( + vrf.import_targets.prefetch_related('tenant'), + orderable=False + ) + export_targets_table = tables.RouteTargetTable( + vrf.export_targets.prefetch_related('tenant'), + orderable=False + ) + return render(request, 'ipam/vrf.html', { 'vrf': vrf, 'prefix_count': prefix_count, + 'import_targets_table': import_targets_table, + 'export_targets_table': export_targets_table, }) @@ -91,8 +102,19 @@ class RouteTargetView(ObjectView): def get(self, request, pk): routetarget = get_object_or_404(self.queryset, pk=pk) + importing_vrfs_table = tables.VRFTable( + routetarget.importing_vrfs.prefetch_related('tenant'), + orderable=False + ) + exporting_vrfs_table = tables.VRFTable( + routetarget.exporting_vrfs.prefetch_related('tenant'), + orderable=False + ) + return render(request, 'ipam/routetarget.html', { 'routetarget': routetarget, + 'importing_vrfs_table': importing_vrfs_table, + 'exporting_vrfs_table': exporting_vrfs_table, }) diff --git a/netbox/templates/ipam/routetarget.html b/netbox/templates/ipam/routetarget.html index d891f9241..2271a8b39 100644 --- a/netbox/templates/ipam/routetarget.html +++ b/netbox/templates/ipam/routetarget.html @@ -83,10 +83,12 @@ {% include 'extras/inc/tags_panel.html' with tags=routetarget.tags.all url='ipam:routetarget_list' %} + {% include 'inc/custom_fields_panel.html' with obj=routetarget %} {% plugin_left_page routetarget %}