diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index b5220597c..fefe6c19d 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -105,7 +105,7 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer): class CircuitSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail') - provider = NestedProviderSerializer() + provider_account = NestedProviderAccountSerializer() status = ChoiceField(choices=CircuitStatusChoices, required=False) type = NestedCircuitTypeSerializer() tenant = NestedTenantSerializer(required=False, allow_null=True) @@ -115,7 +115,7 @@ class CircuitSerializer(NetBoxModelSerializer): class Meta: model = Circuit fields = [ - 'id', 'url', 'display', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', + 'id', 'url', 'display', 'cid', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', ] diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py index 7b2b6b706..1c9d2198d 100644 --- a/netbox/circuits/api/views.py +++ b/netbox/circuits/api/views.py @@ -22,7 +22,7 @@ class CircuitsRootView(APIRootView): class ProviderViewSet(NetBoxModelViewSet): queryset = Provider.objects.prefetch_related('asns', 'tags').annotate( - circuit_count=count_related(Circuit, 'provider') + circuit_count=count_related(Circuit, 'provider_account__provider') ) serializer_class = serializers.ProviderSerializer filterset_class = filtersets.ProviderFilterSet @@ -46,7 +46,7 @@ class CircuitTypeViewSet(NetBoxModelViewSet): class CircuitViewSet(NetBoxModelViewSet): queryset = Circuit.objects.prefetch_related( - 'type', 'tenant', 'provider', 'termination_a', 'termination_z' + 'type', 'tenant', 'provider_account', 'provider_account__provider', 'termination_a', 'termination_z' ).prefetch_related('tags') serializer_class = serializers.CircuitSerializer filterset_class = filtersets.CircuitFilterSet diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index 826350975..6668cc647 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -24,37 +24,37 @@ __all__ = ( class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): region_id = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='circuits__terminations__site__region', + field_name='accounts__circuits__terminations__site__region', lookup_expr='in', label=_('Region (ID)'), ) region = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='circuits__terminations__site__region', + field_name='accounts__circuits__terminations__site__region', lookup_expr='in', to_field_name='slug', label=_('Region (slug)'), ) site_group_id = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='circuits__terminations__site__group', + field_name='accounts__circuits__terminations__site__group', lookup_expr='in', label=_('Site group (ID)'), ) site_group = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='circuits__terminations__site__group', + field_name='accounts__circuits__terminations__site__group', lookup_expr='in', to_field_name='slug', label=_('Site group (slug)'), ) site_id = django_filters.ModelMultipleChoiceFilter( - field_name='circuits__terminations__site', + field_name='accounts__circuits__terminations__site', queryset=Site.objects.all(), label=_('Site'), ) site = django_filters.ModelMultipleChoiceFilter( - field_name='circuits__terminations__site__slug', + field_name='accounts__circuits__terminations__site__slug', queryset=Site.objects.all(), to_field_name='slug', label=_('Site (slug)'), @@ -142,15 +142,21 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet): class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): provider_id = django_filters.ModelMultipleChoiceFilter( + field_name='provider_account__provider', queryset=Provider.objects.all(), label=_('Provider (ID)'), ) provider = django_filters.ModelMultipleChoiceFilter( - field_name='provider__slug', + field_name='provider_account__provider__slug', queryset=Provider.objects.all(), to_field_name='slug', label=_('Provider (slug)'), ) + provider_account_id = django_filters.ModelMultipleChoiceFilter( + field_name='provider_account', + queryset=ProviderAccount.objects.all(), + label=_('ProviderAccount (ID)'), + ) provider_network_id = django_filters.ModelMultipleChoiceFilter( field_name='terminations__provider_network', queryset=ProviderNetwork.objects.all(), diff --git a/netbox/circuits/forms/bulk_edit.py b/netbox/circuits/forms/bulk_edit.py index 7de9fb8d1..c46d71a24 100644 --- a/netbox/circuits/forms/bulk_edit.py +++ b/netbox/circuits/forms/bulk_edit.py @@ -118,6 +118,13 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm): queryset=Provider.objects.all(), required=False ) + provider_account = DynamicModelChoiceField( + queryset=ProviderAccount.objects.all(), + required=False, + query_params={ + 'provider': '$provider' + } + ) status = forms.ChoiceField( choices=add_blank_choice(CircuitStatusChoices), required=False, diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py index 9e79b79ba..6fa0255c2 100644 --- a/netbox/circuits/forms/bulk_import.py +++ b/netbox/circuits/forms/bulk_import.py @@ -68,10 +68,10 @@ class CircuitTypeImportForm(NetBoxModelImportForm): class CircuitImportForm(NetBoxModelImportForm): - provider = CSVModelChoiceField( - queryset=Provider.objects.all(), + provider_account = CSVModelChoiceField( + queryset=ProviderAccount.objects.all(), to_field_name='name', - help_text=_('Assigned provider') + help_text=_('Assigned provider account') ) type = CSVModelChoiceField( queryset=CircuitType.objects.all(), @@ -92,7 +92,7 @@ class CircuitImportForm(NetBoxModelImportForm): class Meta: model = Circuit fields = [ - 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', + 'cid', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description', 'comments', 'tags' ] diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index ad9953277..cbeadd4b0 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -118,6 +118,14 @@ class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFi required=False, label=_('Provider') ) + provider_account_id = DynamicModelMultipleChoiceField( + queryset=Provider.objects.all(), + required=False, + query_params={ + 'provider_id': '$provider_id' + }, + label=_('Provider') + ) provider_network_id = DynamicModelMultipleChoiceField( queryset=ProviderNetwork.objects.all(), required=False, diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py index fb776d0cc..26d13186a 100644 --- a/netbox/circuits/forms/model_forms.py +++ b/netbox/circuits/forms/model_forms.py @@ -44,6 +44,9 @@ class ProviderForm(NetBoxModelForm): class ProviderAccountForm(NetBoxModelForm): + provider = DynamicModelChoiceField( + queryset=Provider.objects.all() + ) comments = CommentField() class Meta: @@ -88,7 +91,20 @@ class CircuitTypeForm(NetBoxModelForm): class CircuitForm(TenancyForm, NetBoxModelForm): provider = DynamicModelChoiceField( - queryset=Provider.objects.all() + required=False, + queryset=Provider.objects.all(), + initial_params={ + 'accounts': '$provider_account' + }, + ) + provider_account = DynamicModelChoiceField( + queryset=ProviderAccount.objects.all(), + initial_params={ + 'circuits': '$circuit' + }, + query_params={ + 'provider': '$provider', + } ) type = DynamicModelChoiceField( queryset=CircuitType.objects.all() @@ -96,7 +112,7 @@ class CircuitForm(TenancyForm, NetBoxModelForm): comments = CommentField() fieldsets = ( - ('Circuit', ('provider', 'cid', 'type', 'status', 'description', 'tags')), + ('Circuit', ('provider', 'provider_account', 'cid', 'type', 'status', 'description', 'tags')), ('Service Parameters', ('install_date', 'termination_date', 'commit_rate')), ('Tenancy', ('tenant_group', 'tenant')), ) @@ -104,8 +120,8 @@ class CircuitForm(TenancyForm, NetBoxModelForm): class Meta: model = Circuit fields = [ - 'cid', 'type', 'provider', 'status', 'install_date', 'termination_date', 'commit_rate', 'description', - 'tenant_group', 'tenant', 'comments', 'tags', + 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate', + 'description', 'tenant_group', 'tenant', 'comments', 'tags', ] help_texts = { 'cid': _("Unique circuit ID"), @@ -123,8 +139,18 @@ class CircuitTerminationForm(NetBoxModelForm): provider = DynamicModelChoiceField( queryset=Provider.objects.all(), required=False, + initial_params={ + 'accounts': '$provider_account' + } + ) + provider_account = DynamicModelChoiceField( + queryset=ProviderAccount.objects.all(), + required=False, initial_params={ 'circuits': '$circuit' + }, + query_params={ + 'provider': '$provider', } ) circuit = DynamicModelChoiceField( @@ -174,9 +200,9 @@ class CircuitTerminationForm(NetBoxModelForm): class Meta: model = CircuitTermination fields = [ - 'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network_provider', - 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', - 'description', 'tags', + 'provider', 'provider_account', 'circuit', 'term_side', 'region', 'site_group', 'site', + 'provider_network_provider', 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', + 'xconnect_id', 'pp_info', 'description', 'tags', ] help_texts = { 'port_speed': _("Physical circuit speed"), diff --git a/netbox/circuits/migrations/0042_provideraccount.py b/netbox/circuits/migrations/0042_provideraccount.py index 7f12470fb..206ed8287 100644 --- a/netbox/circuits/migrations/0042_provideraccount.py +++ b/netbox/circuits/migrations/0042_provideraccount.py @@ -34,6 +34,22 @@ def revert_provideraccounts_from_providers(apps, schema_editor): provideraccount.provider.save() +def migrate_circuits_to_provideraccount(apps, schema_editor): + Circuit = apps.get_model('circuits', 'Circuit') + circuits = Circuit.objects.all() + for circuit in circuits: + circuit.provider_account = circuit.provider.accounts.order_by('pk').first() + circuit.save() + + +def migrate_circuits_from_provideraccount(apps, schema_editor): + Circuit = apps.get_model('circuits', 'Circuit') + circuits = Circuit.objects.all().order_by('pk') + for circuit in circuits: + circuit.provider = circuit.provider_account.provider + circuit.save() + + class Migration(migrations.Migration): dependencies = [ @@ -75,4 +91,39 @@ class Migration(migrations.Migration): model_name='provider', name='account', ), + migrations.AddField( + model_name='circuit', + name='provider_account', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount', null=True, blank=True), + preserve_default=False, + ), + migrations.AlterField( + model_name='circuit', + name='provider', + field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, related_name='circuits', to='circuits.provider', null=True, blank=True), + ), + migrations.RunPython( + migrate_circuits_to_provideraccount, migrate_circuits_from_provideraccount + ), + migrations.AlterField( + model_name='circuit', + name='provider_account', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount'), + ), + migrations.RemoveConstraint( + model_name='circuit', + name='circuits_circuit_unique_provider_cid', + ), + migrations.AlterModelOptions( + name='circuit', + options={'ordering': ['provider_account', 'cid']}, + ), + migrations.AddConstraint( + model_name='circuit', + constraint=models.UniqueConstraint(fields=('provider_account', 'cid'), name='circuits_circuit_unique_provider_cid'), + ), + migrations.RemoveField( + model_name='circuit', + name='provider', + ), ] diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 8ef5761fd..3342697df 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -38,8 +38,8 @@ class Circuit(PrimaryModel): max_length=100, verbose_name='Circuit ID' ) - provider = models.ForeignKey( - to='circuits.Provider', + provider_account = models.ForeignKey( + to='circuits.ProviderAccount', on_delete=models.PROTECT, related_name='circuits' ) @@ -102,18 +102,18 @@ class Circuit(PrimaryModel): ) clone_fields = ( - 'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description', + 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description', ) prerequisite_models = ( 'circuits.CircuitType', - 'circuits.Provider', + 'circuits.ProviderAccount', ) class Meta: - ordering = ['provider', 'cid'] + ordering = ['provider_account', 'cid'] constraints = ( models.UniqueConstraint( - fields=('provider', 'cid'), + fields=('provider_account', 'cid'), name='%(app_label)s_%(class)s_unique_provider_cid' ), ) diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index 0398cce9e..6cbe6534b 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -90,6 +90,7 @@ class ProviderAccount(PrimaryModel): def __str__(self): if self.name: return f'{self.account} ({self.name})' + return f'{self.account}' def get_absolute_url(self): return reverse('circuits:provideraccount', args=[self.pk]) diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index b3f62d5fc..94dc73b4a 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -1,4 +1,6 @@ import django_tables2 as tables +from django_tables2.utils import Accessor + from circuits.models import * from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin @@ -48,6 +50,10 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): verbose_name='Circuit ID' ) provider = tables.Column( + accessor=Accessor('provider_account__provider'), + linkify=True + ) + provider_account = tables.Column( linkify=True ) status = columns.ChoiceFieldColumn() @@ -68,7 +74,7 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = Circuit fields = ( - 'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'tenant_group', 'termination_a', 'termination_z', + 'pk', 'id', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'tenant_group', 'termination_a', 'termination_z', 'install_date', 'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated', ) diff --git a/netbox/circuits/tables/providers.py b/netbox/circuits/tables/providers.py index 4c9f34d07..21c541e83 100644 --- a/netbox/circuits/tables/providers.py +++ b/netbox/circuits/tables/providers.py @@ -57,10 +57,10 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable): class ProviderAccountTable(ContactsColumnMixin, NetBoxTable): - name = tables.Column( + account = tables.Column( linkify=True ) - account = tables.Column() + name = tables.Column() provider = tables.Column( linkify=True ) @@ -72,9 +72,9 @@ class ProviderAccountTable(ContactsColumnMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = ProviderAccount fields = ( - 'pk', 'id', 'account', 'name', 'provider', 'comments', 'contacts', 'tags', 'created', 'last_updated', + 'pk', 'id', 'account', 'name', 'provider', 'circuit_count', 'comments', 'contacts', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'account', 'name', 'provider') + default_columns = ('pk', 'account', 'name', 'provider', 'circuit_count') class ProviderNetworkTable(NetBoxTable): diff --git a/netbox/circuits/tests/test_api.py b/netbox/circuits/tests/test_api.py index 9b4c2f22e..722055111 100644 --- a/netbox/circuits/tests/test_api.py +++ b/netbox/circuits/tests/test_api.py @@ -106,6 +106,12 @@ class CircuitTest(APIViewTestCases.APIViewTestCase): ) Provider.objects.bulk_create(providers) + provider_accounts = ( + ProviderAccount(name='Provider Account 1', provider=providers[0], account='1234'), + ProviderAccount(name='Provider Account 2', provider=providers[1], account='2345'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + circuit_types = ( CircuitType(name='Circuit Type 1', slug='circuit-type-1'), CircuitType(name='Circuit Type 2', slug='circuit-type-2'), @@ -113,26 +119,26 @@ class CircuitTest(APIViewTestCases.APIViewTestCase): CircuitType.objects.bulk_create(circuit_types) circuits = ( - Circuit(cid='Circuit 1', provider=providers[0], type=circuit_types[0]), - Circuit(cid='Circuit 2', provider=providers[0], type=circuit_types[0]), - Circuit(cid='Circuit 3', provider=providers[0], type=circuit_types[0]), + Circuit(cid='Circuit 1', provider_account=provider_accounts[0], type=circuit_types[0]), + Circuit(cid='Circuit 2', provider_account=provider_accounts[0], type=circuit_types[0]), + Circuit(cid='Circuit 3', provider_account=provider_accounts[0], type=circuit_types[0]), ) Circuit.objects.bulk_create(circuits) cls.create_data = [ { 'cid': 'Circuit 4', - 'provider': providers[1].pk, + 'provider_account': provider_accounts[1].pk, 'type': circuit_types[1].pk, }, { 'cid': 'Circuit 5', - 'provider': providers[1].pk, + 'provider_account': provider_accounts[1].pk, 'type': circuit_types[1].pk, }, { 'cid': 'Circuit 6', - 'provider': providers[1].pk, + 'provider_account': provider_accounts[1].pk, 'type': circuit_types[1].pk, }, ] @@ -149,6 +155,11 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): provider = Provider.objects.create(name='Provider 1', slug='provider-1') circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') + provider_account = ProviderAccount.objects.create( + name='Provider Account 2', + provider=provider, + account='2345' + ) sites = ( Site(name='Site 1', slug='site-1'), @@ -163,9 +174,9 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): ProviderNetwork.objects.bulk_create(provider_networks) circuits = ( - Circuit(cid='Circuit 1', provider=provider, type=circuit_type), - Circuit(cid='Circuit 2', provider=provider, type=circuit_type), - Circuit(cid='Circuit 3', provider=provider, type=circuit_type), + Circuit(cid='Circuit 1', provider_account=provider_account, type=circuit_type), + Circuit(cid='Circuit 2', provider_account=provider_account, type=circuit_type), + Circuit(cid='Circuit 3', provider_account=provider_account, type=circuit_type), ) Circuit.objects.bulk_create(circuits) diff --git a/netbox/circuits/tests/test_filtersets.py b/netbox/circuits/tests/test_filtersets.py index a932f3f00..ca2511112 100644 --- a/netbox/circuits/tests/test_filtersets.py +++ b/netbox/circuits/tests/test_filtersets.py @@ -192,6 +192,12 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests): ) Provider.objects.bulk_create(providers) + provider_accounts = ( + ProviderAccount(name='Provider Account 1', provider=providers[0], account='1234'), + ProviderAccount(name='Provider Account 2', provider=providers[1], account='2345'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + provider_networks = ( ProviderNetwork(name='Provider Network 1', provider=providers[1]), ProviderNetwork(name='Provider Network 2', provider=providers[1]), @@ -200,12 +206,12 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests): ProviderNetwork.objects.bulk_create(provider_networks) circuits = ( - Circuit(provider=providers[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', termination_date='2021-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar1'), - Circuit(provider=providers[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', termination_date='2021-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar2'), - Circuit(provider=providers[0], tenant=tenants[1], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', termination_date='2021-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED), - Circuit(provider=providers[1], tenant=tenants[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', termination_date='2021-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED), - Circuit(provider=providers[1], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', termination_date='2021-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE), - Circuit(provider=providers[1], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', termination_date='2021-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE), + Circuit(provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', termination_date='2021-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar1'), + Circuit(provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', termination_date='2021-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar2'), + Circuit(provider_account=provider_accounts[0], tenant=tenants[1], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', termination_date='2021-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED), + Circuit(provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', termination_date='2021-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED), + Circuit(provider_account=provider_accounts[1], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', termination_date='2021-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE), + Circuit(provider_account=provider_accounts[1], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', termination_date='2021-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE), ) Circuit.objects.bulk_create(circuits) @@ -242,6 +248,11 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'provider': [provider.slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + def test_provider_account(self): + provider = ProviderAccount.objects.first() + params = {'provider_id': [provider.pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + def test_provider_network(self): provider_networks = ProviderNetwork.objects.all()[:2] params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} @@ -322,6 +333,11 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): ) Provider.objects.bulk_create(providers) + provider_accounts = ( + ProviderAccount(name='Provider Account 1', provider=providers[0], account='1234'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + provider_networks = ( ProviderNetwork(name='Provider Network 1', provider=providers[0]), ProviderNetwork(name='Provider Network 2', provider=providers[0]), @@ -330,13 +346,13 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): ProviderNetwork.objects.bulk_create(provider_networks) circuits = ( - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 1'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 2'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 3'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 4'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 5'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 6'), - Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 7'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 1'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 2'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 3'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 4'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 5'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 6'), + Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 7'), ) Circuit.objects.bulk_create(circuits) diff --git a/netbox/circuits/tests/test_views.py b/netbox/circuits/tests/test_views.py index 191ccd983..12af6f311 100644 --- a/netbox/circuits/tests/test_views.py +++ b/netbox/circuits/tests/test_views.py @@ -122,6 +122,12 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): ) Provider.objects.bulk_create(providers) + provider_accounts = ( + ProviderAccount(name='Provider Account 1', provider=providers[0], account='1234'), + ProviderAccount(name='Provider Account 2', provider=providers[1], account='2345'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + circuittypes = ( CircuitType(name='Circuit Type 1', slug='circuit-type-1'), CircuitType(name='Circuit Type 2', slug='circuit-type-2'), @@ -129,9 +135,9 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): CircuitType.objects.bulk_create(circuittypes) circuits = ( - Circuit(cid='Circuit 1', provider=providers[0], type=circuittypes[0]), - Circuit(cid='Circuit 2', provider=providers[0], type=circuittypes[0]), - Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]), + Circuit(cid='Circuit 1', provider_account=provider_accounts[0], type=circuittypes[0]), + Circuit(cid='Circuit 2', provider_account=provider_accounts[0], type=circuittypes[0]), + Circuit(cid='Circuit 3', provider_account=provider_accounts[0], type=circuittypes[0]), ) Circuit.objects.bulk_create(circuits) @@ -140,7 +146,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.form_data = { 'cid': 'Circuit X', - 'provider': providers[1].pk, + 'provider_account': provider_accounts[1].pk, 'type': circuittypes[1].pk, 'status': CircuitStatusChoices.STATUS_DECOMMISSIONED, 'tenant': None, @@ -153,10 +159,10 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - "cid,provider,type,status", - "Circuit 4,Provider 1,Circuit Type 1,active", - "Circuit 5,Provider 1,Circuit Type 1,active", - "Circuit 6,Provider 1,Circuit Type 1,active", + "cid,provider_account,type,status", + "Circuit 4,Provider Account 1,Circuit Type 1,active", + "Circuit 5,Provider Account 1,Circuit Type 1,active", + "Circuit 6,Provider Account 1,Circuit Type 1,active", ) cls.csv_update_data = ( @@ -167,7 +173,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): ) cls.bulk_edit_data = { - 'provider': providers[1].pk, + 'provider_account': provider_accounts[1].pk, 'type': circuittypes[1].pk, 'status': CircuitStatusChoices.STATUS_DECOMMISSIONED, 'tenant': None, @@ -297,11 +303,12 @@ class CircuitTerminationTestCase( Site.objects.bulk_create(sites) provider = Provider.objects.create(name='Provider 1', slug='provider-1') circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') + account = ProviderAccount.objects.create(name='Provider Account 1', provider=provider, account='1234') circuits = ( - Circuit(cid='Circuit 1', provider=provider, type=circuittype), - Circuit(cid='Circuit 2', provider=provider, type=circuittype), - Circuit(cid='Circuit 3', provider=provider, type=circuittype), + Circuit(cid='Circuit 1', provider_account=account, type=circuittype), + Circuit(cid='Circuit 2', provider_account=account, type=circuittype), + Circuit(cid='Circuit 3', provider_account=account, type=circuittype), ) Circuit.objects.bulk_create(circuits) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 3f2310524..45f55232f 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -18,7 +18,7 @@ from .models import * class ProviderListView(generic.ObjectListView): queryset = Provider.objects.annotate( - count_circuits=count_related(Circuit, 'provider') + count_circuits=count_related(Circuit, 'provider_account__provider') ) filterset = filtersets.ProviderFilterSet filterset_form = forms.ProviderFilterForm @@ -32,7 +32,7 @@ class ProviderView(generic.ObjectView): def get_extra_context(self, request, instance): related_models = ( (ProviderAccount.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'), - (Circuit.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'), + (Circuit.objects.restrict(request.user, 'view').filter(provider_account__provider=instance), 'provider_id'), ) return { @@ -59,7 +59,7 @@ class ProviderBulkImportView(generic.BulkImportView): class ProviderBulkEditView(generic.BulkEditView): queryset = Provider.objects.annotate( - count_circuits=count_related(Circuit, 'provider') + count_circuits=count_related(Circuit, 'provider_account__provider') ) filterset = filtersets.ProviderFilterSet table = tables.ProviderTable @@ -68,7 +68,7 @@ class ProviderBulkEditView(generic.BulkEditView): class ProviderBulkDeleteView(generic.BulkDeleteView): queryset = Provider.objects.annotate( - count_circuits=count_related(Circuit, 'provider') + count_circuits=count_related(Circuit, 'provider_account__provider') ) filterset = filtersets.ProviderFilterSet table = tables.ProviderTable @@ -79,7 +79,9 @@ class ProviderBulkDeleteView(generic.BulkDeleteView): # class ProviderAccountListView(generic.ObjectListView): - queryset = ProviderAccount.objects.all() + queryset = ProviderAccount.objects.annotate( + count_circuits=count_related(Circuit, 'provider_account') + ) filterset = filtersets.ProviderAccountFilterSet filterset_form = forms.ProviderAccountFilterForm table = tables.ProviderAccountTable @@ -89,6 +91,15 @@ class ProviderAccountListView(generic.ObjectListView): class ProviderAccountView(generic.ObjectView): queryset = ProviderAccount.objects.all() + def get_extra_context(self, request, instance): + related_models = ( + (Circuit.objects.restrict(request.user, 'view').filter(provider_account=instance), 'provider_account_id'), + ) + + return { + 'related_models': related_models, + } + @register_model_view(ProviderAccount, 'edit') class ProviderAccountEditView(generic.ObjectEditView): @@ -108,14 +119,18 @@ class ProviderAccountBulkImportView(generic.BulkImportView): class ProviderAccountBulkEditView(generic.BulkEditView): - queryset = ProviderAccount.objects.all() + queryset = ProviderAccount.objects.annotate( + count_circuits=count_related(Circuit, 'provider_account') + ) filterset = filtersets.ProviderAccountFilterSet table = tables.ProviderAccountTable form = forms.ProviderAccountBulkEditForm class ProviderAccountBulkDeleteView(generic.BulkDeleteView): - queryset = ProviderAccount.objects.all() + queryset = ProviderAccount.objects.annotate( + count_circuits=count_related(Circuit, 'provider_account') + ) filterset = filtersets.ProviderAccountFilterSet table = tables.ProviderAccountTable diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index 81ff6e912..b7b3f81c5 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -16,7 +16,11 @@ - + + + + + diff --git a/netbox/templates/circuits/circuittermination_edit.html b/netbox/templates/circuits/circuittermination_edit.html index f171ecc1b..2b00f6ab4 100644 --- a/netbox/templates/circuits/circuittermination_edit.html +++ b/netbox/templates/circuits/circuittermination_edit.html @@ -8,6 +8,7 @@
Circuit Termination
{% render_field form.provider %} + {% render_field form.provider_account %} {% render_field form.circuit %} {% render_field form.term_side %} {% render_field form.tags %} diff --git a/netbox/templates/circuits/provideraccount.html b/netbox/templates/circuits/provideraccount.html index ec9efaf55..f051cb2ce 100644 --- a/netbox/templates/circuits/provideraccount.html +++ b/netbox/templates/circuits/provideraccount.html @@ -43,5 +43,16 @@ {% include 'inc/panels/contacts.html' %} {% plugin_right_page object %} + +
+
+
Circuits
+
+
+ {% plugin_full_width_page object %} +
{% endblock %}
Provider{{ object.provider|linkify }}{{ object.provider_account.provider|linkify }}
Provider Account{{ object.provider_account|linkify }}
Circuit ID