diff --git a/docs/features/tenants.md b/docs/features/tenants.md index bf8b8cb..b628fe7 100644 --- a/docs/features/tenants.md +++ b/docs/features/tenants.md @@ -103,6 +103,7 @@ The *ACIBridgeDomain* model has the following fields: *Required fields*: - **Name**: represent the Bridge Domain name in the ACI +- **ACI Tenant**: a reference to the ACITenant model. - **ACI VRF**: a reference to the ACIVRF model. *Optional fields*: diff --git a/netbox_aci_plugin/api/serializers.py b/netbox_aci_plugin/api/serializers.py index 4089fb3..66e175f 100644 --- a/netbox_aci_plugin/api/serializers.py +++ b/netbox_aci_plugin/api/serializers.py @@ -144,6 +144,7 @@ class ACIBridgeDomainSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField( view_name="plugins-api:netbox_aci_plugin-api:acibridgedomain-detail" ) + aci_tenant = ACITenantSerializer(nested=True, required=True) aci_vrf = ACIVRFSerializer(nested=True, required=True) nb_tenant = TenantSerializer(nested=True, required=False, allow_null=True) @@ -156,6 +157,7 @@ class Meta: "name", "name_alias", "description", + "aci_tenant", "aci_vrf", "nb_tenant", "advertise_host_routes_enabled", @@ -191,6 +193,7 @@ class Meta: "name", "name_alias", "description", + "aci_tenant", "aci_vrf", "nb_tenant", ) diff --git a/netbox_aci_plugin/api/views.py b/netbox_aci_plugin/api/views.py index 2588255..b3ecf47 100644 --- a/netbox_aci_plugin/api/views.py +++ b/netbox_aci_plugin/api/views.py @@ -71,6 +71,7 @@ class ACIBridgeDomainListViewSet(NetBoxModelViewSet): """API view for listing ACI Bridge Domain instances.""" queryset = ACIBridgeDomain.objects.prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", diff --git a/netbox_aci_plugin/filtersets/tenant_networks.py b/netbox_aci_plugin/filtersets/tenant_networks.py index 73cfe1b..790af0f 100644 --- a/netbox_aci_plugin/filtersets/tenant_networks.py +++ b/netbox_aci_plugin/filtersets/tenant_networks.py @@ -67,6 +67,11 @@ class ACIVRFFilterSet(NetBoxModelFilterSet): choices=VRFPCEnforcementPreferenceChoices, null_value=None, ) + present_in_aci_tenant_or_common = django_filters.ModelChoiceFilter( + queryset=ACITenant.objects.all(), + method="filter_present_in_aci_tenant_or_common", + label=_("ACI Tenant (ID)"), + ) class Meta: model = ACIVRF @@ -107,18 +112,26 @@ def search(self, queryset, name, value): ) return queryset.filter(queryset_filter) + def filter_present_in_aci_tenant_or_common( + self, queryset, name, aci_tenant + ): + """Return a QuerySet filtered by given ACI Tenant or 'common'.""" + if aci_tenant is None: + return queryset.none + return queryset.filter( + Q(aci_tenant=aci_tenant) | Q(aci_tenant__name="common") + ) + class ACIBridgeDomainFilterSet(NetBoxModelFilterSet): """Filter set for ACI Bridge Domain model.""" aci_tenant = django_filters.ModelMultipleChoiceFilter( - field_name="aci_vrf__aci_tenant", queryset=ACITenant.objects.all(), to_field_name="name", label=_("ACI Tenant (name)"), ) aci_tenant_id = django_filters.ModelMultipleChoiceFilter( - field_name="aci_vrf__aci_tenant", queryset=ACITenant.objects.all(), to_field_name="id", label=_("ACI Tenant (ID)"), @@ -151,6 +164,11 @@ class ACIBridgeDomainFilterSet(NetBoxModelFilterSet): choices=BDMultiDestinationFloodingChoices, null_value=None, ) + present_in_aci_tenant_or_common = django_filters.ModelChoiceFilter( + queryset=ACITenant.objects.all(), + method="filter_present_in_aci_tenant_or_common", + label=_("ACI Tenant (ID)"), + ) unknown_ipv4_multicast = django_filters.MultipleChoiceFilter( choices=BDUnknownMulticastChoices, null_value=None, @@ -175,6 +193,7 @@ class Meta: "name", "name_alias", "description", + "aci_tenant", "aci_vrf", "nb_tenant", "advertise_host_routes_enabled", @@ -218,18 +237,28 @@ def search(self, queryset, name, value): ) return queryset.filter(queryset_filter) + def filter_present_in_aci_tenant_or_common( + self, queryset, name, aci_tenant + ): + """Return a QuerySet filtered by given ACI Tenant or 'common'.""" + if aci_tenant is None: + return queryset.none + return queryset.filter( + Q(aci_tenant=aci_tenant) | Q(aci_tenant__name="common") + ) + class ACIBridgeDomainSubnetFilterSet(NetBoxModelFilterSet): """Filter set for ACI Bridge Domain Subnet model.""" aci_tenant = django_filters.ModelMultipleChoiceFilter( - field_name="aci_bridge_domain__aci_vrf__aci_tenant", + field_name="aci_bridge_domain__aci_tenant", queryset=ACITenant.objects.all(), to_field_name="name", label=_("ACI Tenant (name)"), ) aci_tenant_id = django_filters.ModelMultipleChoiceFilter( - field_name="aci_bridge_domain__aci_vrf__aci_tenant", + field_name="aci_bridge_domain__aci_tenant", queryset=ACITenant.objects.all(), to_field_name="id", label=_("ACI Tenant (ID)"), diff --git a/netbox_aci_plugin/forms/tenant_app_profiles.py b/netbox_aci_plugin/forms/tenant_app_profiles.py index cba2e09..5e746aa 100644 --- a/netbox_aci_plugin/forms/tenant_app_profiles.py +++ b/netbox_aci_plugin/forms/tenant_app_profiles.py @@ -237,7 +237,7 @@ class ACIEndpointGroupForm(NetBoxModelForm): ) aci_vrf = DynamicModelChoiceField( queryset=ACIVRF.objects.all(), - query_params={"aci_tenant_id": "$aci_tenant"}, + query_params={"present_in_aci_tenant_or_common": "$aci_tenant"}, initial_params={"aci_bridge_domains": "$aci_bridge_domain"}, required=False, label=_("ACI VRF"), @@ -245,7 +245,7 @@ class ACIEndpointGroupForm(NetBoxModelForm): aci_bridge_domain = DynamicModelChoiceField( queryset=ACIBridgeDomain.objects.all(), query_params={ - "aci_tenant_id": "$aci_tenant", + "present_in_aci_tenant_or_common": "$aci_tenant", "aci_vrf_id": "$aci_vrf", }, label=_("ACI Bridge Domain"), @@ -367,6 +367,29 @@ class Meta: "tags", ) + def clean(self): + """Cleaning and validation of ACI Endpoint Group Form.""" + + super().clean() + + aci_app_profile = self.cleaned_data.get("aci_app_profile") + aci_bridge_domain = self.cleaned_data.get("aci_bridge_domain") + + if ( + not aci_app_profile.aci_tenant.id + == aci_bridge_domain.aci_tenant.id + and not aci_bridge_domain.aci_tenant.name == "common" + ): + raise forms.ValidationError( + { + "aci_bridge_domain": _( + "A Bridge Domain can only be assigned from the same" + " ACI Tenant as the Endpoint Group or ACI Tenant" + " 'common'." + ) + } + ) + class ACIEndpointGroupBulkEditForm(NetBoxModelBulkEditForm): """NetBox bulk edit form for ACI Endpoint Group model.""" @@ -636,13 +659,6 @@ class ACIEndpointGroupImportForm(NetBoxModelImportForm): label=_("ACI Application Profile"), help_text=_("Assigned ACI Application Profile"), ) - aci_vrf = CSVModelChoiceField( - queryset=ACIVRF.objects.all(), - to_field_name="name", - required=True, - label=_("ACI VRF"), - help_text=_("Parent ACI VRF of ACI Bridge Domain"), - ) aci_bridge_domain = CSVModelChoiceField( queryset=ACIBridgeDomain.objects.all(), to_field_name="name", @@ -650,6 +666,11 @@ class ACIEndpointGroupImportForm(NetBoxModelImportForm): label=_("ACI Bridge Domain"), help_text=_("Assigned ACI Bridge Domain"), ) + is_aci_bd_in_common = forms.BooleanField( + label=_("Is ACI Bridge Domain in 'common'"), + required=False, + help_text=_("Assigned ACI Bridge Domain is in ACI Tenant 'common'"), + ) nb_tenant = CSVModelChoiceField( queryset=Tenant.objects.all(), to_field_name="name", @@ -674,10 +695,10 @@ class Meta: "name_alias", "aci_tenant", "aci_app_profile", - "aci_vrf", "aci_bridge_domain", "description", "nb_tenant", + "is_aci_bd_in_common", "admin_shutdown", "custom_qos_policy_name", "flood_in_encap_enabled", @@ -705,15 +726,16 @@ def __init__(self, data=None, *args, **kwargs) -> None: ) self.fields["aci_app_profile"].queryset = aci_appprofile_queryset - # Limit ACIBridgeDomain queryset by parent ACIVRF and ACITenant - if data.get("aci_tenant") and data.get("aci_vrf"): - # Limit ACIVRF queryset by parent ACITenant - self.fields["aci_vrf"].queryset = ACIVRF.objects.filter( - aci_tenant__name=data["aci_tenant"] + # Limit ACIBridgeDomain queryset by "common" ACITenant + if data.get("is_aci_bd_in_common") == "true": + aci_bd_queryset = ACIBridgeDomain.objects.filter( + aci_tenant__name="common" ) - # Limit ACIBridgeDomain queryset by parent ACIVRF + self.fields["aci_bridge_domain"].queryset = aci_bd_queryset + # Limit ACIBridgeDomain queryset by ACITenant + elif data.get("aci_tenant") and data.get("aci_bridge_domain"): + # Limit ACIBridgeDomain queryset by parent ACITenant aci_bd_queryset = ACIBridgeDomain.objects.filter( - aci_vrf__aci_tenant__name=data["aci_tenant"], - aci_vrf__name=data["aci_vrf"], + aci_tenant__name=data["aci_tenant"] ) self.fields["aci_bridge_domain"].queryset = aci_bd_queryset diff --git a/netbox_aci_plugin/forms/tenant_networks.py b/netbox_aci_plugin/forms/tenant_networks.py index 43a2bc7..68d3542 100644 --- a/netbox_aci_plugin/forms/tenant_networks.py +++ b/netbox_aci_plugin/forms/tenant_networks.py @@ -521,13 +521,11 @@ class ACIBridgeDomainForm(NetBoxModelForm): aci_tenant = DynamicModelChoiceField( queryset=ACITenant.objects.all(), - initial_params={"aci_vrfs": "$aci_vrf"}, - required=False, label=_("ACI Tenant"), ) aci_vrf = DynamicModelChoiceField( queryset=ACIVRF.objects.all(), - query_params={"aci_tenant_id": "$aci_tenant"}, + query_params={"present_in_aci_tenant_or_common": "$aci_tenant"}, label=_("ACI VRF"), ) nb_tenant_group = DynamicModelChoiceField( @@ -710,6 +708,7 @@ class Meta: "name", "name_alias", "description", + "aci_tenant", "aci_vrf", "nb_tenant", "advertise_host_routes_enabled", @@ -736,6 +735,27 @@ class Meta: "tags", ) + def clean(self): + """Cleaning and validation of ACI Bridge Domain Form.""" + + super().clean() + + aci_tenant = self.cleaned_data.get("aci_tenant") + aci_vrf = self.cleaned_data.get("aci_vrf") + + if ( + not aci_tenant.id == aci_vrf.aci_tenant.id + and not aci_vrf.aci_tenant.name == "common" + ): + raise forms.ValidationError( + { + "aci_vrf": _( + "A VRF can only be assigned from the same ACI Tenant" + " as the Bridge Domain or ACI Tenant 'common'." + ) + } + ) + class ACIBridgeDomainBulkEditForm(NetBoxModelBulkEditForm): """NetBox bulk edit form for ACI Bridge Domain model.""" @@ -750,6 +770,11 @@ class ACIBridgeDomainBulkEditForm(NetBoxModelBulkEditForm): required=False, label=_("Description"), ) + aci_tenant = DynamicModelChoiceField( + queryset=ACITenant.objects.all(), + required=False, + label=_("ACI Tenant"), + ) aci_vrf = DynamicModelChoiceField( queryset=ACIVRF.objects.all(), required=False, @@ -878,6 +903,7 @@ class ACIBridgeDomainBulkEditForm(NetBoxModelBulkEditForm): FieldSet( "name", "name_alias", + "aci_tenant", "aci_vrf", "description", "tags", @@ -1124,7 +1150,7 @@ class ACIBridgeDomainImportForm(NetBoxModelImportForm): to_field_name="name", required=True, label=_("ACI Tenant"), - help_text=_("Parent ACI Tenant of ACI VRF"), + help_text=_("Assigned ACI Tenant"), ) aci_vrf = CSVModelChoiceField( queryset=ACIVRF.objects.all(), @@ -1133,6 +1159,11 @@ class ACIBridgeDomainImportForm(NetBoxModelImportForm): label=_("ACI VRF"), help_text=_("Assigned ACI VRF"), ) + is_aci_vrf_in_common = forms.BooleanField( + label=_("Is ACI VRF in 'common'"), + required=False, + help_text=_("Assigned ACI VRF is in ACI Tenant 'common'"), + ) nb_tenant = CSVModelChoiceField( queryset=Tenant.objects.all(), to_field_name="name", @@ -1177,6 +1208,7 @@ class Meta: "aci_vrf", "description", "nb_tenant", + "is_aci_vrf_in_common", "advertise_host_routes_enabled", "arp_flooding_enabled", "clear_remote_mac_enabled", @@ -1209,8 +1241,13 @@ def __init__(self, data=None, *args, **kwargs) -> None: if not data: return + # Limit ACIVRF queryset by "common" ACITenant + if data.get("is_aci_vrf_in_common") == "true": + self.fields["aci_vrf"].queryset = ACIVRF.objects.filter( + aci_tenant__name="common" + ) # Limit ACIVRF queryset by parent ACITenant - if data.get("aci_tenant"): + elif data.get("aci_tenant"): self.fields["aci_vrf"].queryset = ACIVRF.objects.filter( aci_tenant__name=data["aci_tenant"] ) @@ -1765,9 +1802,9 @@ def __init__(self, data=None, *args, **kwargs) -> None: self.fields["aci_vrf"].queryset = ACIVRF.objects.filter( aci_tenant__name=data["aci_tenant"] ) - # Limit ACIBridgeDomain queryset by parent ACIVRF + # Limit ACIBridgeDomain queryset by parent ACIVRF and ACITenant aci_bd_queryset = ACIBridgeDomain.objects.filter( - aci_vrf__aci_tenant__name=data["aci_tenant"], + aci_tenant__name=data["aci_tenant"], aci_vrf__name=data["aci_vrf"], ) self.fields["aci_bridge_domain"].queryset = aci_bd_queryset diff --git a/netbox_aci_plugin/graphql/types.py b/netbox_aci_plugin/graphql/types.py index 63ee5d8..514cdb4 100644 --- a/netbox_aci_plugin/graphql/types.py +++ b/netbox_aci_plugin/graphql/types.py @@ -59,6 +59,7 @@ class ACIVRFType(NetBoxObjectType): class ACIBridgeDomainType(NetBoxObjectType): """GraphQL type definition for ACIBridgeDomain model.""" + aci_tenant: ACITenantType aci_vrf: ACIVRFType nb_tenant: Optional[TenantType] dhcp_labels: Optional[List[str]] diff --git a/netbox_aci_plugin/models/tenant_networks.py b/netbox_aci_plugin/models/tenant_networks.py index 6a945aa..9499aa0 100644 --- a/netbox_aci_plugin/models/tenant_networks.py +++ b/netbox_aci_plugin/models/tenant_networks.py @@ -176,7 +176,10 @@ class Meta: def __str__(self) -> str: """Return string representation of the instance.""" - return self.name + if self.aci_tenant.name == "common": + return f"{self.name} ({self.aci_tenant.name})" + else: + return self.name def get_absolute_url(self) -> str: """Return the absolute URL of the instance.""" @@ -224,6 +227,12 @@ class ACIBridgeDomain(NetBoxModel): ACIPolicyDescriptionValidator, ], ) + aci_tenant = models.ForeignKey( + to=ACITenant, + on_delete=models.PROTECT, + related_name="aci_bridge_domains", + verbose_name=_("ACI Tenant"), + ) aci_vrf = models.ForeignKey( to=ACIVRF, on_delete=models.PROTECT, @@ -425,6 +434,7 @@ class ACIBridgeDomain(NetBoxModel): clone_fields: tuple = ( "description", + "aci_tenant", "aci_vrf", "nb_tenant", "advertise_host_routes_enabled", @@ -448,26 +458,27 @@ class ACIBridgeDomain(NetBoxModel): "unknown_unicast", "virtual_mac_address", ) - prerequisite_models: tuple = ("netbox_aci_plugin.ACIVRF",) + prerequisite_models: tuple = ( + "netbox_aci_plugin.ACITenant", + "netbox_aci_plugin.ACIVRF", + ) class Meta: constraints: list[models.UniqueConstraint] = [ models.UniqueConstraint( - fields=("aci_vrf", "name"), - name="unique_aci_bridge_domain_name_per_aci_vrf", + fields=("aci_tenant", "name"), + name="unique_aci_bridge_domain_name_per_aci_tenant", ), ] - ordering: tuple = ("aci_vrf", "name") + ordering: tuple = ("aci_tenant", "aci_vrf", "name") verbose_name: str = _("ACI Bridge Domain") def __str__(self) -> str: """Return string representation of the instance.""" - return self.name - - @property - def aci_tenant(self) -> ACITenant: - """Return the ACITenant instance of related ACIVRF.""" - return self.aci_vrf.aci_tenant + if self.aci_tenant.name == "common": + return f"{self.name} ({self.aci_tenant.name})" + else: + return self.name def get_absolute_url(self) -> str: """Return the absolute URL of the instance.""" @@ -672,8 +683,8 @@ def __str__(self) -> str: @property def aci_tenant(self) -> ACITenant: - """Return the ACITenant instance of related ACIBridgeDomain's ACIVRF.""" - return self.aci_bridge_domain.aci_vrf.aci_tenant + """Return the ACITenant instance of related ACIBridgeDomain.""" + return self.aci_bridge_domain.aci_tenant @property def aci_vrf(self) -> ACIVRF: diff --git a/netbox_aci_plugin/tests/test_api.py b/netbox_aci_plugin/tests/test_api.py index 8b14e94..d7a0859 100644 --- a/netbox_aci_plugin/tests/test_api.py +++ b/netbox_aci_plugin/tests/test_api.py @@ -290,6 +290,7 @@ class ACIBridgeDomainAPIViewTestCase(APIViewTestCases.APIViewTestCase): model = ACIBridgeDomain view_namespace: str = f"plugins-api:{app_name}" brief_fields: list[str] = [ + "aci_tenant", "aci_vrf", "description", "display", @@ -324,6 +325,7 @@ def setUpTestData(cls) -> None: name_alias="Testing", description="First ACI Test", comments="# ACI Test 1", + aci_tenant=aci_tenant1, aci_vrf=aci_vrf1, nb_tenant=nb_tenant1, advertise_host_routes_enabled=False, @@ -355,6 +357,7 @@ def setUpTestData(cls) -> None: name_alias="Testing", description="Second ACI Test", comments="# ACI Test 2", + aci_tenant=aci_tenant2, aci_vrf=aci_vrf2, nb_tenant=nb_tenant1, ), @@ -363,6 +366,7 @@ def setUpTestData(cls) -> None: name_alias="Testing", description="Third ACI Test", comments="# ACI Test 3", + aci_tenant=aci_tenant1, aci_vrf=aci_vrf1, nb_tenant=nb_tenant2, advertise_host_routes_enabled=True, @@ -396,6 +400,7 @@ def setUpTestData(cls) -> None: "name_alias": "Testing", "description": "Forth ACI Test", "comments": "# ACI Test 4", + "aci_tenant": aci_tenant2.id, "aci_vrf": aci_vrf2.id, "nb_tenant": nb_tenant1.id, "advertise_host_routes_enabled": False, @@ -424,6 +429,7 @@ def setUpTestData(cls) -> None: "name_alias": "Testing", "description": "Fifth ACI Test", "comments": "# ACI Test 5", + "aci_tenant": aci_tenant1.id, "aci_vrf": aci_vrf1.id, "nb_tenant": nb_tenant2.id, "advertise_host_routes_enabled": True, @@ -493,10 +499,16 @@ def setUpTestData(cls) -> None: nb_vrf=nb_vrf2, ) aci_bd1 = ACIBridgeDomain.objects.create( - name="ACI-BD-API-1", aci_vrf=aci_vrf1, nb_tenant=nb_tenant1 + name="ACI-BD-API-1", + aci_tenant=aci_tenant1, + aci_vrf=aci_vrf1, + nb_tenant=nb_tenant1, ) aci_bd2 = ACIBridgeDomain.objects.create( - name="ACI-BD-API-2", aci_vrf=aci_vrf2, nb_tenant=nb_tenant2 + name="ACI-BD-API-2", + aci_tenant=aci_tenant2, + aci_vrf=aci_vrf2, + nb_tenant=nb_tenant2, ) gw_ip1 = IPAddress.objects.create(address="10.0.0.1/24", vrf=nb_vrf1) gw_ip2 = IPAddress.objects.create(address="10.0.1.1/24", vrf=nb_vrf1) @@ -646,10 +658,16 @@ def setUpTestData(cls) -> None: nb_vrf=nb_vrf2, ) aci_bd1 = ACIBridgeDomain.objects.create( - name="ACI-BD-API-1", aci_vrf=aci_vrf1, nb_tenant=nb_tenant1 + name="ACI-BD-API-1", + aci_tenant=aci_tenant1, + aci_vrf=aci_vrf1, + nb_tenant=nb_tenant1, ) aci_bd2 = ACIBridgeDomain.objects.create( - name="ACI-BD-API-2", aci_vrf=aci_vrf2, nb_tenant=nb_tenant2 + name="ACI-BD-API-2", + aci_tenant=aci_tenant2, + aci_vrf=aci_vrf2, + nb_tenant=nb_tenant2, ) aci_epgs: tuple = ( diff --git a/netbox_aci_plugin/tests/test_models.py b/netbox_aci_plugin/tests/test_models.py index d07d8f9..1228cd0 100644 --- a/netbox_aci_plugin/tests/test_models.py +++ b/netbox_aci_plugin/tests/test_models.py @@ -295,6 +295,7 @@ def setUp(self) -> None: name_alias=acibd_name_alias, description=acibd_description, comments=acibd_comments, + aci_tenant=aci_tenant, aci_vrf=aci_vrf, nb_tenant=nb_tenant, advertise_host_routes_enabled=acibd_advertise_host_routes_enabled, @@ -380,12 +381,13 @@ def test_invalid_aci_bridge_domain_description(self) -> None: ) self.assertRaises(ValidationError, bd.full_clean) - def test_constraint_unique_aci_bridge_domain_name_per_aci_vrf( + def test_constraint_unique_aci_bridge_domain_name_per_aci_tenant( self, ) -> None: - """Test unique constraint of ACI Bridge Domain name per ACI VRF.""" + """Test unique constraint of ACI Bridge Domain name per ACI Tenant.""" + tenant = ACITenant.objects.get(name="ACITestTenant1") vrf = ACIVRF.objects.get(name="VRFTest1") - bd = ACIBridgeDomain(name="BDTest1", aci_vrf=vrf) + bd = ACIBridgeDomain(name="BDTest1", aci_tenant=tenant, aci_vrf=vrf) self.assertRaises(IntegrityError, bd.save) @@ -419,7 +421,7 @@ def setUp(self) -> None: name=acivrf_name, aci_tenant=aci_tenant ) aci_bridge_domain = ACIBridgeDomain.objects.create( - name=acibd_name, aci_vrf=aci_vrf + name=acibd_name, aci_tenant=aci_tenant, aci_vrf=aci_vrf ) aci_bd_gateway = IPAddress.objects.create( address=acisnet_gateway_ip_address @@ -563,7 +565,7 @@ def setUp(self) -> None: name=acivrf_name, aci_tenant=aci_tenant ) aci_bd = ACIBridgeDomain.objects.create( - name=acibd_name, aci_vrf=aci_vrf + name=acibd_name, aci_tenant=aci_tenant, aci_vrf=aci_vrf ) nb_tenant = Tenant.objects.create(name="NetBox Tenant") diff --git a/netbox_aci_plugin/views/tenant_networks.py b/netbox_aci_plugin/views/tenant_networks.py index 821ad56..e265f7a 100644 --- a/netbox_aci_plugin/views/tenant_networks.py +++ b/netbox_aci_plugin/views/tenant_networks.py @@ -83,6 +83,7 @@ def get_children(self, request, parent): return ACIBridgeDomain.objects.restrict( request.user, "view" ).prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", @@ -223,6 +224,7 @@ class ACIBridgeDomainView(generic.ObjectView): """Detail view for displaying a single object of ACI Bridge Domain.""" queryset = ACIBridgeDomain.objects.prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", @@ -244,6 +246,7 @@ class ACIBridgeDomainListView(generic.ObjectListView): """List view for listing all objects of ACI Bridge Domain.""" queryset = ACIBridgeDomain.objects.prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", @@ -258,6 +261,7 @@ class ACIBridgeDomainEditView(generic.ObjectEditView): """Edit view for editing an object of ACI Bridge Domain.""" queryset = ACIBridgeDomain.objects.prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", @@ -270,6 +274,7 @@ class ACIBridgeDomainDeleteView(generic.ObjectDeleteView): """Delete view for deleting an object of ACI Bridge Domain.""" queryset = ACIBridgeDomain.objects.prefetch_related( + "aci_tenant", "aci_vrf", "nb_tenant", "tags", diff --git a/netbox_aci_plugin/views/tenants.py b/netbox_aci_plugin/views/tenants.py index b1db0f8..d3731dc 100644 --- a/netbox_aci_plugin/views/tenants.py +++ b/netbox_aci_plugin/views/tenants.py @@ -15,7 +15,6 @@ ACITenantImportForm, ) from ..models.tenant_app_profiles import ACIEndpointGroup -from ..models.tenant_networks import ACIBridgeDomain from ..models.tenants import ACITenant from ..tables.tenants import ACITenantTable from .tenant_app_profiles import ( @@ -54,12 +53,6 @@ def get_extra_context(self, request, instance) -> dict: # Get related models of directly referenced models related_sub_models: list[tuple] = [ - ( - ACIBridgeDomain.objects.restrict(request.user, "view").filter( - aci_vrf__aci_tenant=instance - ), - "aci_tenant_id", - ), ( ACIEndpointGroup.objects.restrict(request.user, "view").filter( aci_app_profile__aci_tenant=instance @@ -161,14 +154,6 @@ class ACITenantBridgeDomainView(ACIBridgeDomainChildrenView): """Children view of ACI Bridge Domain of ACI Tenant.""" queryset = ACITenant.objects.all() - tab = ViewTab( - label=_("Bridge Domains"), - badge=lambda obj: ACITenantBridgeDomainView.child_model.objects.filter( - aci_vrf__aci_tenant=obj.pk - ).count(), - permission="netbox_aci_plugin.view_acibridgedomain", - weight=1000, - ) template_name = "netbox_aci_plugin/acitenant_bridgedomains.html" def get_children(self, request, parent): @@ -176,7 +161,7 @@ def get_children(self, request, parent): return ( super() .get_children(request, parent) - .filter(aci_vrf__aci_tenant=parent.pk) + .filter(aci_tenant_id=parent.pk) ) def get_table(self, *args, **kwargs):