#9047 - Link Circuits to Provider Account instead of Provider

This commit is contained in:
Daniel Sheppard 2023-03-22 13:05:52 -05:00
parent 22fdd6160c
commit 1a6bd726eb
19 changed files with 245 additions and 75 deletions

View File

@ -105,7 +105,7 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
class CircuitSerializer(NetBoxModelSerializer): class CircuitSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail') url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
provider = NestedProviderSerializer() provider_account = NestedProviderAccountSerializer()
status = ChoiceField(choices=CircuitStatusChoices, required=False) status = ChoiceField(choices=CircuitStatusChoices, required=False)
type = NestedCircuitTypeSerializer() type = NestedCircuitTypeSerializer()
tenant = NestedTenantSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True)
@ -115,7 +115,7 @@ class CircuitSerializer(NetBoxModelSerializer):
class Meta: class Meta:
model = Circuit model = Circuit
fields = [ 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', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'created', 'last_updated',
] ]

View File

@ -22,7 +22,7 @@ class CircuitsRootView(APIRootView):
class ProviderViewSet(NetBoxModelViewSet): class ProviderViewSet(NetBoxModelViewSet):
queryset = Provider.objects.prefetch_related('asns', 'tags').annotate( 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 serializer_class = serializers.ProviderSerializer
filterset_class = filtersets.ProviderFilterSet filterset_class = filtersets.ProviderFilterSet
@ -46,7 +46,7 @@ class CircuitTypeViewSet(NetBoxModelViewSet):
class CircuitViewSet(NetBoxModelViewSet): class CircuitViewSet(NetBoxModelViewSet):
queryset = Circuit.objects.prefetch_related( 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') ).prefetch_related('tags')
serializer_class = serializers.CircuitSerializer serializer_class = serializers.CircuitSerializer
filterset_class = filtersets.CircuitFilterSet filterset_class = filtersets.CircuitFilterSet

View File

@ -24,37 +24,37 @@ __all__ = (
class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter( region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(), queryset=Region.objects.all(),
field_name='circuits__terminations__site__region', field_name='accounts__circuits__terminations__site__region',
lookup_expr='in', lookup_expr='in',
label=_('Region (ID)'), label=_('Region (ID)'),
) )
region = TreeNodeMultipleChoiceFilter( region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(), queryset=Region.objects.all(),
field_name='circuits__terminations__site__region', field_name='accounts__circuits__terminations__site__region',
lookup_expr='in', lookup_expr='in',
to_field_name='slug', to_field_name='slug',
label=_('Region (slug)'), label=_('Region (slug)'),
) )
site_group_id = TreeNodeMultipleChoiceFilter( site_group_id = TreeNodeMultipleChoiceFilter(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
field_name='circuits__terminations__site__group', field_name='accounts__circuits__terminations__site__group',
lookup_expr='in', lookup_expr='in',
label=_('Site group (ID)'), label=_('Site group (ID)'),
) )
site_group = TreeNodeMultipleChoiceFilter( site_group = TreeNodeMultipleChoiceFilter(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
field_name='circuits__terminations__site__group', field_name='accounts__circuits__terminations__site__group',
lookup_expr='in', lookup_expr='in',
to_field_name='slug', to_field_name='slug',
label=_('Site group (slug)'), label=_('Site group (slug)'),
) )
site_id = django_filters.ModelMultipleChoiceFilter( site_id = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site', field_name='accounts__circuits__terminations__site',
queryset=Site.objects.all(), queryset=Site.objects.all(),
label=_('Site'), label=_('Site'),
) )
site = django_filters.ModelMultipleChoiceFilter( site = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site__slug', field_name='accounts__circuits__terminations__site__slug',
queryset=Site.objects.all(), queryset=Site.objects.all(),
to_field_name='slug', to_field_name='slug',
label=_('Site (slug)'), label=_('Site (slug)'),
@ -142,15 +142,21 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter( provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='provider_account__provider',
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
label=_('Provider (ID)'), label=_('Provider (ID)'),
) )
provider = django_filters.ModelMultipleChoiceFilter( provider = django_filters.ModelMultipleChoiceFilter(
field_name='provider__slug', field_name='provider_account__provider__slug',
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
to_field_name='slug', to_field_name='slug',
label=_('Provider (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( provider_network_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__provider_network', field_name='terminations__provider_network',
queryset=ProviderNetwork.objects.all(), queryset=ProviderNetwork.objects.all(),

View File

@ -118,6 +118,13 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
required=False required=False
) )
provider_account = DynamicModelChoiceField(
queryset=ProviderAccount.objects.all(),
required=False,
query_params={
'provider': '$provider'
}
)
status = forms.ChoiceField( status = forms.ChoiceField(
choices=add_blank_choice(CircuitStatusChoices), choices=add_blank_choice(CircuitStatusChoices),
required=False, required=False,

View File

@ -68,10 +68,10 @@ class CircuitTypeImportForm(NetBoxModelImportForm):
class CircuitImportForm(NetBoxModelImportForm): class CircuitImportForm(NetBoxModelImportForm):
provider = CSVModelChoiceField( provider_account = CSVModelChoiceField(
queryset=Provider.objects.all(), queryset=ProviderAccount.objects.all(),
to_field_name='name', to_field_name='name',
help_text=_('Assigned provider') help_text=_('Assigned provider account')
) )
type = CSVModelChoiceField( type = CSVModelChoiceField(
queryset=CircuitType.objects.all(), queryset=CircuitType.objects.all(),
@ -92,7 +92,7 @@ class CircuitImportForm(NetBoxModelImportForm):
class Meta: class Meta:
model = Circuit model = Circuit
fields = [ 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' 'description', 'comments', 'tags'
] ]

View File

@ -118,6 +118,14 @@ class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFi
required=False, required=False,
label=_('Provider') label=_('Provider')
) )
provider_account_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(),
required=False,
query_params={
'provider_id': '$provider_id'
},
label=_('Provider')
)
provider_network_id = DynamicModelMultipleChoiceField( provider_network_id = DynamicModelMultipleChoiceField(
queryset=ProviderNetwork.objects.all(), queryset=ProviderNetwork.objects.all(),
required=False, required=False,

View File

@ -44,6 +44,9 @@ class ProviderForm(NetBoxModelForm):
class ProviderAccountForm(NetBoxModelForm): class ProviderAccountForm(NetBoxModelForm):
provider = DynamicModelChoiceField(
queryset=Provider.objects.all()
)
comments = CommentField() comments = CommentField()
class Meta: class Meta:
@ -88,7 +91,20 @@ class CircuitTypeForm(NetBoxModelForm):
class CircuitForm(TenancyForm, NetBoxModelForm): class CircuitForm(TenancyForm, NetBoxModelForm):
provider = DynamicModelChoiceField( 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( type = DynamicModelChoiceField(
queryset=CircuitType.objects.all() queryset=CircuitType.objects.all()
@ -96,7 +112,7 @@ class CircuitForm(TenancyForm, NetBoxModelForm):
comments = CommentField() comments = CommentField()
fieldsets = ( 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')), ('Service Parameters', ('install_date', 'termination_date', 'commit_rate')),
('Tenancy', ('tenant_group', 'tenant')), ('Tenancy', ('tenant_group', 'tenant')),
) )
@ -104,8 +120,8 @@ class CircuitForm(TenancyForm, NetBoxModelForm):
class Meta: class Meta:
model = Circuit model = Circuit
fields = [ fields = [
'cid', 'type', 'provider', 'status', 'install_date', 'termination_date', 'commit_rate', 'description', 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
'tenant_group', 'tenant', 'comments', 'tags', 'description', 'tenant_group', 'tenant', 'comments', 'tags',
] ]
help_texts = { help_texts = {
'cid': _("Unique circuit ID"), 'cid': _("Unique circuit ID"),
@ -123,8 +139,18 @@ class CircuitTerminationForm(NetBoxModelForm):
provider = DynamicModelChoiceField( provider = DynamicModelChoiceField(
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
required=False, required=False,
initial_params={
'accounts': '$provider_account'
}
)
provider_account = DynamicModelChoiceField(
queryset=ProviderAccount.objects.all(),
required=False,
initial_params={ initial_params={
'circuits': '$circuit' 'circuits': '$circuit'
},
query_params={
'provider': '$provider',
} }
) )
circuit = DynamicModelChoiceField( circuit = DynamicModelChoiceField(
@ -174,9 +200,9 @@ class CircuitTerminationForm(NetBoxModelForm):
class Meta: class Meta:
model = CircuitTermination model = CircuitTermination
fields = [ fields = [
'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network_provider', 'provider', 'provider_account', 'circuit', 'term_side', 'region', 'site_group', 'site',
'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'provider_network_provider', 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed',
'description', 'tags', 'xconnect_id', 'pp_info', 'description', 'tags',
] ]
help_texts = { help_texts = {
'port_speed': _("Physical circuit speed"), 'port_speed': _("Physical circuit speed"),

View File

@ -34,6 +34,22 @@ def revert_provideraccounts_from_providers(apps, schema_editor):
provideraccount.provider.save() 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): class Migration(migrations.Migration):
dependencies = [ dependencies = [
@ -75,4 +91,39 @@ class Migration(migrations.Migration):
model_name='provider', model_name='provider',
name='account', 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',
),
] ]

View File

@ -38,8 +38,8 @@ class Circuit(PrimaryModel):
max_length=100, max_length=100,
verbose_name='Circuit ID' verbose_name='Circuit ID'
) )
provider = models.ForeignKey( provider_account = models.ForeignKey(
to='circuits.Provider', to='circuits.ProviderAccount',
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='circuits' related_name='circuits'
) )
@ -102,18 +102,18 @@ class Circuit(PrimaryModel):
) )
clone_fields = ( 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 = ( prerequisite_models = (
'circuits.CircuitType', 'circuits.CircuitType',
'circuits.Provider', 'circuits.ProviderAccount',
) )
class Meta: class Meta:
ordering = ['provider', 'cid'] ordering = ['provider_account', 'cid']
constraints = ( constraints = (
models.UniqueConstraint( models.UniqueConstraint(
fields=('provider', 'cid'), fields=('provider_account', 'cid'),
name='%(app_label)s_%(class)s_unique_provider_cid' name='%(app_label)s_%(class)s_unique_provider_cid'
), ),
) )

View File

@ -90,6 +90,7 @@ class ProviderAccount(PrimaryModel):
def __str__(self): def __str__(self):
if self.name: if self.name:
return f'{self.account} ({self.name})' return f'{self.account} ({self.name})'
return f'{self.account}'
def get_absolute_url(self): def get_absolute_url(self):
return reverse('circuits:provideraccount', args=[self.pk]) return reverse('circuits:provideraccount', args=[self.pk])

View File

@ -1,4 +1,6 @@
import django_tables2 as tables import django_tables2 as tables
from django_tables2.utils import Accessor
from circuits.models import * from circuits.models import *
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
@ -48,6 +50,10 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
verbose_name='Circuit ID' verbose_name='Circuit ID'
) )
provider = tables.Column( provider = tables.Column(
accessor=Accessor('provider_account__provider'),
linkify=True
)
provider_account = tables.Column(
linkify=True linkify=True
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
@ -68,7 +74,7 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = Circuit model = Circuit
fields = ( 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', 'install_date', 'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created',
'last_updated', 'last_updated',
) )

View File

@ -57,10 +57,10 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable):
class ProviderAccountTable(ContactsColumnMixin, NetBoxTable): class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
name = tables.Column( account = tables.Column(
linkify=True linkify=True
) )
account = tables.Column() name = tables.Column()
provider = tables.Column( provider = tables.Column(
linkify=True linkify=True
) )
@ -72,9 +72,9 @@ class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = ProviderAccount model = ProviderAccount
fields = ( 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): class ProviderNetworkTable(NetBoxTable):

View File

@ -106,6 +106,12 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
) )
Provider.objects.bulk_create(providers) 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 = ( circuit_types = (
CircuitType(name='Circuit Type 1', slug='circuit-type-1'), CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
CircuitType(name='Circuit Type 2', slug='circuit-type-2'), CircuitType(name='Circuit Type 2', slug='circuit-type-2'),
@ -113,26 +119,26 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
CircuitType.objects.bulk_create(circuit_types) CircuitType.objects.bulk_create(circuit_types)
circuits = ( circuits = (
Circuit(cid='Circuit 1', 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=providers[0], type=circuit_types[0]), Circuit(cid='Circuit 2', provider_account=provider_accounts[0], type=circuit_types[0]),
Circuit(cid='Circuit 3', provider=providers[0], type=circuit_types[0]), Circuit(cid='Circuit 3', provider_account=provider_accounts[0], type=circuit_types[0]),
) )
Circuit.objects.bulk_create(circuits) Circuit.objects.bulk_create(circuits)
cls.create_data = [ cls.create_data = [
{ {
'cid': 'Circuit 4', 'cid': 'Circuit 4',
'provider': providers[1].pk, 'provider_account': provider_accounts[1].pk,
'type': circuit_types[1].pk, 'type': circuit_types[1].pk,
}, },
{ {
'cid': 'Circuit 5', 'cid': 'Circuit 5',
'provider': providers[1].pk, 'provider_account': provider_accounts[1].pk,
'type': circuit_types[1].pk, 'type': circuit_types[1].pk,
}, },
{ {
'cid': 'Circuit 6', 'cid': 'Circuit 6',
'provider': providers[1].pk, 'provider_account': provider_accounts[1].pk,
'type': circuit_types[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') provider = Provider.objects.create(name='Provider 1', slug='provider-1')
circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-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 = ( sites = (
Site(name='Site 1', slug='site-1'), Site(name='Site 1', slug='site-1'),
@ -163,9 +174,9 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
ProviderNetwork.objects.bulk_create(provider_networks) ProviderNetwork.objects.bulk_create(provider_networks)
circuits = ( circuits = (
Circuit(cid='Circuit 1', provider=provider, type=circuit_type), Circuit(cid='Circuit 1', provider_account=provider_account, type=circuit_type),
Circuit(cid='Circuit 2', provider=provider, type=circuit_type), Circuit(cid='Circuit 2', provider_account=provider_account, type=circuit_type),
Circuit(cid='Circuit 3', provider=provider, type=circuit_type), Circuit(cid='Circuit 3', provider_account=provider_account, type=circuit_type),
) )
Circuit.objects.bulk_create(circuits) Circuit.objects.bulk_create(circuits)

View File

@ -192,6 +192,12 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
Provider.objects.bulk_create(providers) 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 = ( provider_networks = (
ProviderNetwork(name='Provider Network 1', provider=providers[1]), ProviderNetwork(name='Provider Network 1', provider=providers[1]),
ProviderNetwork(name='Provider Network 2', 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) ProviderNetwork.objects.bulk_create(provider_networks)
circuits = ( 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_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=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_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=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_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=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_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=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_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=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[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) Circuit.objects.bulk_create(circuits)
@ -242,6 +248,11 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'provider': [provider.slug]} params = {'provider': [provider.slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) 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): def test_provider_network(self):
provider_networks = ProviderNetwork.objects.all()[:2] provider_networks = ProviderNetwork.objects.all()[:2]
params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} 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.objects.bulk_create(providers)
provider_accounts = (
ProviderAccount(name='Provider Account 1', provider=providers[0], account='1234'),
)
ProviderAccount.objects.bulk_create(provider_accounts)
provider_networks = ( provider_networks = (
ProviderNetwork(name='Provider Network 1', provider=providers[0]), ProviderNetwork(name='Provider Network 1', provider=providers[0]),
ProviderNetwork(name='Provider Network 2', 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) ProviderNetwork.objects.bulk_create(provider_networks)
circuits = ( circuits = (
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 1'), Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 1'),
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 2'), Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 2'),
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 3'), Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 3'),
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 4'), Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 4'),
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 5'), Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Circuit 5'),
Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 6'), Circuit(provider_account=provider_accounts[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 7'),
) )
Circuit.objects.bulk_create(circuits) Circuit.objects.bulk_create(circuits)

View File

@ -122,6 +122,12 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
) )
Provider.objects.bulk_create(providers) 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 = ( circuittypes = (
CircuitType(name='Circuit Type 1', slug='circuit-type-1'), CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
CircuitType(name='Circuit Type 2', slug='circuit-type-2'), CircuitType(name='Circuit Type 2', slug='circuit-type-2'),
@ -129,9 +135,9 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
CircuitType.objects.bulk_create(circuittypes) CircuitType.objects.bulk_create(circuittypes)
circuits = ( circuits = (
Circuit(cid='Circuit 1', provider=providers[0], type=circuittypes[0]), Circuit(cid='Circuit 1', provider_account=provider_accounts[0], type=circuittypes[0]),
Circuit(cid='Circuit 2', provider=providers[0], type=circuittypes[0]), Circuit(cid='Circuit 2', provider_account=provider_accounts[0], type=circuittypes[0]),
Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]), Circuit(cid='Circuit 3', provider_account=provider_accounts[0], type=circuittypes[0]),
) )
Circuit.objects.bulk_create(circuits) Circuit.objects.bulk_create(circuits)
@ -140,7 +146,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
cls.form_data = { cls.form_data = {
'cid': 'Circuit X', 'cid': 'Circuit X',
'provider': providers[1].pk, 'provider_account': provider_accounts[1].pk,
'type': circuittypes[1].pk, 'type': circuittypes[1].pk,
'status': CircuitStatusChoices.STATUS_DECOMMISSIONED, 'status': CircuitStatusChoices.STATUS_DECOMMISSIONED,
'tenant': None, 'tenant': None,
@ -153,10 +159,10 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
} }
cls.csv_data = ( cls.csv_data = (
"cid,provider,type,status", "cid,provider_account,type,status",
"Circuit 4,Provider 1,Circuit Type 1,active", "Circuit 4,Provider Account 1,Circuit Type 1,active",
"Circuit 5,Provider 1,Circuit Type 1,active", "Circuit 5,Provider Account 1,Circuit Type 1,active",
"Circuit 6,Provider 1,Circuit Type 1,active", "Circuit 6,Provider Account 1,Circuit Type 1,active",
) )
cls.csv_update_data = ( cls.csv_update_data = (
@ -167,7 +173,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
) )
cls.bulk_edit_data = { cls.bulk_edit_data = {
'provider': providers[1].pk, 'provider_account': provider_accounts[1].pk,
'type': circuittypes[1].pk, 'type': circuittypes[1].pk,
'status': CircuitStatusChoices.STATUS_DECOMMISSIONED, 'status': CircuitStatusChoices.STATUS_DECOMMISSIONED,
'tenant': None, 'tenant': None,
@ -297,11 +303,12 @@ class CircuitTerminationTestCase(
Site.objects.bulk_create(sites) Site.objects.bulk_create(sites)
provider = Provider.objects.create(name='Provider 1', slug='provider-1') provider = Provider.objects.create(name='Provider 1', slug='provider-1')
circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-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 = ( circuits = (
Circuit(cid='Circuit 1', provider=provider, type=circuittype), Circuit(cid='Circuit 1', provider_account=account, type=circuittype),
Circuit(cid='Circuit 2', provider=provider, type=circuittype), Circuit(cid='Circuit 2', provider_account=account, type=circuittype),
Circuit(cid='Circuit 3', provider=provider, type=circuittype), Circuit(cid='Circuit 3', provider_account=account, type=circuittype),
) )
Circuit.objects.bulk_create(circuits) Circuit.objects.bulk_create(circuits)

View File

@ -18,7 +18,7 @@ from .models import *
class ProviderListView(generic.ObjectListView): class ProviderListView(generic.ObjectListView):
queryset = Provider.objects.annotate( queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider') count_circuits=count_related(Circuit, 'provider_account__provider')
) )
filterset = filtersets.ProviderFilterSet filterset = filtersets.ProviderFilterSet
filterset_form = forms.ProviderFilterForm filterset_form = forms.ProviderFilterForm
@ -32,7 +32,7 @@ class ProviderView(generic.ObjectView):
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
related_models = ( related_models = (
(ProviderAccount.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'), (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 { return {
@ -59,7 +59,7 @@ class ProviderBulkImportView(generic.BulkImportView):
class ProviderBulkEditView(generic.BulkEditView): class ProviderBulkEditView(generic.BulkEditView):
queryset = Provider.objects.annotate( queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider') count_circuits=count_related(Circuit, 'provider_account__provider')
) )
filterset = filtersets.ProviderFilterSet filterset = filtersets.ProviderFilterSet
table = tables.ProviderTable table = tables.ProviderTable
@ -68,7 +68,7 @@ class ProviderBulkEditView(generic.BulkEditView):
class ProviderBulkDeleteView(generic.BulkDeleteView): class ProviderBulkDeleteView(generic.BulkDeleteView):
queryset = Provider.objects.annotate( queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider') count_circuits=count_related(Circuit, 'provider_account__provider')
) )
filterset = filtersets.ProviderFilterSet filterset = filtersets.ProviderFilterSet
table = tables.ProviderTable table = tables.ProviderTable
@ -79,7 +79,9 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
# #
class ProviderAccountListView(generic.ObjectListView): class ProviderAccountListView(generic.ObjectListView):
queryset = ProviderAccount.objects.all() queryset = ProviderAccount.objects.annotate(
count_circuits=count_related(Circuit, 'provider_account')
)
filterset = filtersets.ProviderAccountFilterSet filterset = filtersets.ProviderAccountFilterSet
filterset_form = forms.ProviderAccountFilterForm filterset_form = forms.ProviderAccountFilterForm
table = tables.ProviderAccountTable table = tables.ProviderAccountTable
@ -89,6 +91,15 @@ class ProviderAccountListView(generic.ObjectListView):
class ProviderAccountView(generic.ObjectView): class ProviderAccountView(generic.ObjectView):
queryset = ProviderAccount.objects.all() 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') @register_model_view(ProviderAccount, 'edit')
class ProviderAccountEditView(generic.ObjectEditView): class ProviderAccountEditView(generic.ObjectEditView):
@ -108,14 +119,18 @@ class ProviderAccountBulkImportView(generic.BulkImportView):
class ProviderAccountBulkEditView(generic.BulkEditView): class ProviderAccountBulkEditView(generic.BulkEditView):
queryset = ProviderAccount.objects.all() queryset = ProviderAccount.objects.annotate(
count_circuits=count_related(Circuit, 'provider_account')
)
filterset = filtersets.ProviderAccountFilterSet filterset = filtersets.ProviderAccountFilterSet
table = tables.ProviderAccountTable table = tables.ProviderAccountTable
form = forms.ProviderAccountBulkEditForm form = forms.ProviderAccountBulkEditForm
class ProviderAccountBulkDeleteView(generic.BulkDeleteView): class ProviderAccountBulkDeleteView(generic.BulkDeleteView):
queryset = ProviderAccount.objects.all() queryset = ProviderAccount.objects.annotate(
count_circuits=count_related(Circuit, 'provider_account')
)
filterset = filtersets.ProviderAccountFilterSet filterset = filtersets.ProviderAccountFilterSet
table = tables.ProviderAccountTable table = tables.ProviderAccountTable

View File

@ -16,7 +16,11 @@
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
<tr> <tr>
<th scope="row">Provider</th> <th scope="row">Provider</th>
<td>{{ object.provider|linkify }}</td> <td>{{ object.provider_account.provider|linkify }}</td>
</tr>
<tr>
<th scope="row">Provider Account</th>
<td>{{ object.provider_account|linkify }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Circuit ID</th> <th scope="row">Circuit ID</th>

View File

@ -8,6 +8,7 @@
<h5 class="offset-sm-3">Circuit Termination</h5> <h5 class="offset-sm-3">Circuit Termination</h5>
</div> </div>
{% render_field form.provider %} {% render_field form.provider %}
{% render_field form.provider_account %}
{% render_field form.circuit %} {% render_field form.circuit %}
{% render_field form.term_side %} {% render_field form.term_side %}
{% render_field form.tags %} {% render_field form.tags %}

View File

@ -43,5 +43,16 @@
{% include 'inc/panels/contacts.html' %} {% include 'inc/panels/contacts.html' %}
{% plugin_right_page object %} {% plugin_right_page object %}
</div> </div>
<div class="col col-md-12">
<div class="card">
<h5 class="card-header">Circuits</h5>
<div class="card-body htmx-container table-responsive"
hx-get="{% url 'circuits:circuit_list' %}?provider_id={{ object.pk }}"
hx-trigger="load"
></div>
</div>
{% plugin_full_width_page object %}
</div>
</div> </div>
{% endblock %} {% endblock %}