diff --git a/docs/core-functionality/circuits.md b/docs/core-functionality/circuits.md index 67388dba4..51261858c 100644 --- a/docs/core-functionality/circuits.md +++ b/docs/core-functionality/circuits.md @@ -1,7 +1,7 @@ # Circuits {!docs/models/circuits/provider.md!} -{!docs/models/circuits/cloud.md!} +{!docs/models/circuits/providernetwork.md!} --- diff --git a/docs/models/circuits/circuittermination.md b/docs/models/circuits/circuittermination.md index c1ec09cae..beea2f85a 100644 --- a/docs/models/circuits/circuittermination.md +++ b/docs/models/circuits/circuittermination.md @@ -2,9 +2,9 @@ The association of a circuit with a particular site and/or device is modeled separately as a circuit termination. A circuit may have up to two terminations, labeled A and Z. A single-termination circuit can be used when you don't know (or care) about the far end of a circuit (for example, an Internet access circuit which connects to a transit provider). A dual-termination circuit is useful for tracking circuits which connect two sites. -Each circuit termination is attached to either a site or a cloud. Site terminations may optionally be connected via a cable to a specific device interface or port within that site. Each termination must be assigned a port speed, and can optionally be assigned an upstream speed if it differs from the downstream speed (a common scenario with e.g. DOCSIS cable modems). Fields are also available to track cross-connect and patch panel details. +Each circuit termination is attached to either a site or to a provider network. Site terminations may optionally be connected via a cable to a specific device interface or port within that site. Each termination must be assigned a port speed, and can optionally be assigned an upstream speed if it differs from the downstream speed (a common scenario with e.g. DOCSIS cable modems). Fields are also available to track cross-connect and patch panel details. In adherence with NetBox's philosophy of closely modeling the real world, a circuit may be connected only to a physical interface. For example, circuits may not terminate to LAG interfaces, which are virtual in nature. In such cases, a separate physical circuit is associated with each LAG member interface and each needs to be modeled discretely. !!! note - A circuit in NetBox represents a physical link, and cannot have more than two endpoints. When modeling a multi-point topology, each leg of the topology must be defined as a discrete circuit, with one end terminating within the provider's infrastructure. The cloud model is ideal for representing these networks. + A circuit in NetBox represents a physical link, and cannot have more than two endpoints. When modeling a multi-point topology, each leg of the topology must be defined as a discrete circuit, with one end terminating within the provider's infrastructure. The provider network model is ideal for representing these networks. diff --git a/docs/models/circuits/cloud.md b/docs/models/circuits/cloud.md deleted file mode 100644 index c4b3cec5e..000000000 --- a/docs/models/circuits/cloud.md +++ /dev/null @@ -1,5 +0,0 @@ -# Clouds - -A cloud represents an abstract portion of network topology, just like in a topology diagram. For example, a cloud may be used to represent a provider's MPLS network. - -Each cloud must be assigned to a provider. A circuit may terminate to either a cloud or to a site. diff --git a/docs/models/circuits/providernetwork.md b/docs/models/circuits/providernetwork.md new file mode 100644 index 000000000..970a9f8a8 --- /dev/null +++ b/docs/models/circuits/providernetwork.md @@ -0,0 +1,5 @@ +# Provider Network + +This model can be used to represent the boundary of a provider network, the details of which are unknown or unimportant to the NetBox user. For example, it might represent a provider's regional MPLS network to which multiple circuits provide connectivity. + +Each provider network must be assigned to a provider. A circuit may terminate to either a provider network or to a site. diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index f0a61bd0a..2043b02e9 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -78,9 +78,9 @@ This release introduces the new SiteGroup model, which can be used to organize s The ObjectChange model (which is used to record the creation, modification, and deletion of NetBox objects) now explicitly records the pre-change and post-change state of each object, rather than only the post-change state. This was done to present a more clear depiction of each change being made, and to prevent the erroneous association of a previous unlogged change with its successor. -#### Cloud Modeling for Circuits ([#5986](https://github.com/netbox-community/netbox/issues/5986)) +#### Provider Network Modeling ([#5986](https://github.com/netbox-community/netbox/issues/5986)) -A new Cloud model has been introduced to represent the boundary of a network that exists outside the scope of NetBox. This is analogous to using a cloud icon on a topology drawing to represent an abstracted network. Each cloud must be assigned to a provider, and circuits can terminate to either clouds or sites. The use of this model will likely be extended by future releases to support overlay and virtual circuit modeling. +A new provider network model has been introduced to represent the boundary of a network that exists outside the scope of NetBox. Each instance of this model must be assigned to a provider, and circuits can now terminate to either provider networks or to sites. The use of this model will likely be extended by future releases to support overlay and virtual circuit modeling. ### Enhancements @@ -130,9 +130,9 @@ A new Cloud model has been introduced to represent the boundary of a network tha * Renamed RackGroup to Location * The `/dcim/rack-groups/` endpoint is now `/dcim/locations/` * circuits.CircuitTermination - * Added the `cloud` field -* circuits.Cloud - * Added the `/api/circuits/clouds/` endpoint + * Added the `provider_network` field +* circuits.ProviderNetwork + * Added the `/api/circuits/provider-networks/` endpoint * dcim.Device * Added the `location` field * dcim.Interface diff --git a/netbox/circuits/api/nested_serializers.py b/netbox/circuits/api/nested_serializers.py index 0fd07d31b..fccf4a8b6 100644 --- a/netbox/circuits/api/nested_serializers.py +++ b/netbox/circuits/api/nested_serializers.py @@ -7,17 +7,17 @@ __all__ = [ 'NestedCircuitSerializer', 'NestedCircuitTerminationSerializer', 'NestedCircuitTypeSerializer', - 'NestedCloudSerializer', + 'NestedProviderNetworkSerializer', 'NestedProviderSerializer', ] # -# Clouds +# Provider networks # -class NestedCloudSerializer(WritableNestedSerializer): - url = serializers.HyperlinkedIdentityField(view_name='circuits-api:cloud-detail') +class NestedProviderNetworkSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail') class Meta: model = Provider diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index 5469049db..794235dee 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -29,15 +29,15 @@ class ProviderSerializer(PrimaryModelSerializer): # -# Clouds +# Provider networks # -class CloudSerializer(PrimaryModelSerializer): - url = serializers.HyperlinkedIdentityField(view_name='circuits-api:cloud-detail') +class ProviderNetworkSerializer(PrimaryModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail') provider = NestedProviderSerializer() class Meta: - model = Cloud + model = ProviderNetwork fields = [ 'id', 'url', 'display', 'provider', 'name', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', @@ -63,12 +63,12 @@ class CircuitTypeSerializer(OrganizationalModelSerializer): class CircuitCircuitTerminationSerializer(WritableNestedSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail') site = NestedSiteSerializer() - cloud = NestedCloudSerializer() + provider_network = NestedProviderNetworkSerializer() class Meta: model = CircuitTermination fields = [ - 'id', 'url', 'display', 'site', 'cloud', 'port_speed', 'upstream_speed', 'xconnect_id', + 'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', ] @@ -95,13 +95,13 @@ class CircuitTerminationSerializer(BaseModelSerializer, CableTerminationSerializ url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail') circuit = NestedCircuitSerializer() site = NestedSiteSerializer(required=False) - cloud = NestedCloudSerializer(required=False) + provider_network = NestedProviderNetworkSerializer(required=False) cable = NestedCableSerializer(read_only=True) class Meta: model = CircuitTermination fields = [ - 'id', 'url', 'display', 'circuit', 'term_side', 'site', 'cloud', 'port_speed', 'upstream_speed', + 'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', '_occupied', ] diff --git a/netbox/circuits/api/urls.py b/netbox/circuits/api/urls.py index 4f31806bd..5389e0bde 100644 --- a/netbox/circuits/api/urls.py +++ b/netbox/circuits/api/urls.py @@ -13,8 +13,8 @@ router.register('circuit-types', views.CircuitTypeViewSet) router.register('circuits', views.CircuitViewSet) router.register('circuit-terminations', views.CircuitTerminationViewSet) -# Clouds -router.register('clouds', views.CloudViewSet) +# Provider networks +router.register('provider-networks', views.ProviderNetworkViewSet) app_name = 'circuits-api' urlpatterns = router.urls diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py index 0adbfcb0e..83c4a8fa6 100644 --- a/netbox/circuits/api/views.py +++ b/netbox/circuits/api/views.py @@ -68,10 +68,10 @@ class CircuitTerminationViewSet(PathEndpointMixin, ModelViewSet): # -# Clouds +# Provider networks # -class CloudViewSet(CustomFieldModelViewSet): - queryset = Cloud.objects.prefetch_related('tags') - serializer_class = serializers.CloudSerializer - filterset_class = filters.CloudFilterSet +class ProviderNetworkViewSet(CustomFieldModelViewSet): + queryset = ProviderNetwork.objects.prefetch_related('tags') + serializer_class = serializers.ProviderNetworkSerializer + filterset_class = filters.ProviderNetworkFilterSet diff --git a/netbox/circuits/filters.py b/netbox/circuits/filters.py index 0efd2f331..fa9f964c2 100644 --- a/netbox/circuits/filters.py +++ b/netbox/circuits/filters.py @@ -15,7 +15,7 @@ __all__ = ( 'CircuitFilterSet', 'CircuitTerminationFilterSet', 'CircuitTypeFilterSet', - 'CloudFilterSet', + 'ProviderNetworkFilterSet', 'ProviderFilterSet', ) @@ -80,7 +80,7 @@ class ProviderFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdated ) -class CloudFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet): +class ProviderNetworkFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet): q = django_filters.CharFilter( method='search', label='Search', @@ -98,7 +98,7 @@ class CloudFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFil tag = TagFilter() class Meta: - model = Cloud + model = ProviderNetwork fields = ['id', 'name'] def search(self, queryset, name, value): @@ -132,10 +132,10 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldModelFilterSet, TenancyFilterSe to_field_name='slug', label='Provider (slug)', ) - cloud_id = django_filters.ModelMultipleChoiceFilter( - field_name='terminations__cloud', - queryset=Cloud.objects.all(), - label='Cloud (ID)', + provider_network_id = django_filters.ModelMultipleChoiceFilter( + field_name='terminations__provider_network', + queryset=ProviderNetwork.objects.all(), + label='ProviderNetwork (ID)', ) type_id = django_filters.ModelMultipleChoiceFilter( queryset=CircuitType.objects.all(), @@ -226,9 +226,9 @@ class CircuitTerminationFilterSet(BaseFilterSet, CableTerminationFilterSet, Path to_field_name='slug', label='Site (slug)', ) - cloud_id = django_filters.ModelMultipleChoiceFilter( - queryset=Cloud.objects.all(), - label='Cloud (ID)', + provider_network_id = django_filters.ModelMultipleChoiceFilter( + queryset=ProviderNetwork.objects.all(), + label='ProviderNetwork (ID)', ) class Meta: diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index d818ec0f6..1b3eb3242 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -129,10 +129,10 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm): # -# Clouds +# Provider networks # -class CloudForm(BootstrapMixin, CustomFieldModelForm): +class ProviderNetworkForm(BootstrapMixin, CustomFieldModelForm): provider = DynamicModelChoiceField( queryset=Provider.objects.all() ) @@ -143,16 +143,16 @@ class CloudForm(BootstrapMixin, CustomFieldModelForm): ) class Meta: - model = Cloud + model = ProviderNetwork fields = [ 'provider', 'name', 'description', 'comments', 'tags', ] fieldsets = ( - ('Cloud', ('provider', 'name', 'description', 'tags')), + ('Provider Network', ('provider', 'name', 'description', 'tags')), ) -class CloudCSVForm(CustomFieldModelCSVForm): +class ProviderNetworkCSVForm(CustomFieldModelCSVForm): provider = CSVModelChoiceField( queryset=Provider.objects.all(), to_field_name='name', @@ -160,15 +160,15 @@ class CloudCSVForm(CustomFieldModelCSVForm): ) class Meta: - model = Cloud + model = ProviderNetwork fields = [ 'provider', 'name', 'description', 'comments', ] -class CloudBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm): +class ProviderNetworkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm): pk = forms.ModelMultipleChoiceField( - queryset=Cloud.objects.all(), + queryset=ProviderNetwork.objects.all(), widget=forms.MultipleHiddenInput ) provider = DynamicModelChoiceField( @@ -190,8 +190,8 @@ class CloudBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFo ] -class CloudFilterForm(BootstrapMixin, CustomFieldFilterForm): - model = Cloud +class ProviderNetworkFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = ProviderNetwork field_order = ['q', 'provider_id'] q = forms.CharField( required=False, @@ -357,7 +357,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): model = Circuit field_order = [ - 'q', 'type_id', 'provider_id', 'cloud_id', 'status', 'region_id', 'site_id', 'tenant_group_id', 'tenant_id', + 'q', 'type_id', 'provider_id', 'provider_network_id', 'status', 'region_id', 'site_id', 'tenant_group_id', 'tenant_id', 'commit_rate', ] q = forms.CharField( @@ -374,13 +374,13 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm required=False, label=_('Provider') ) - cloud_id = DynamicModelMultipleChoiceField( - queryset=Cloud.objects.all(), + provider_network_id = DynamicModelMultipleChoiceField( + queryset=ProviderNetwork.objects.all(), required=False, query_params={ 'provider_id': '$provider_id' }, - label=_('Cloud') + label=_('Provider network') ) status = forms.MultipleChoiceField( choices=CircuitStatusChoices, @@ -435,16 +435,16 @@ class CircuitTerminationForm(BootstrapMixin, forms.ModelForm): }, required=False ) - cloud = DynamicModelChoiceField( - queryset=Cloud.objects.all(), + provider_network = DynamicModelChoiceField( + queryset=ProviderNetwork.objects.all(), required=False ) class Meta: model = CircuitTermination fields = [ - 'term_side', 'region', 'site_group', 'site', 'cloud', 'mark_connected', 'port_speed', 'upstream_speed', - 'xconnect_id', 'pp_info', 'description', + 'term_side', 'region', 'site_group', 'site', 'provider_network', 'mark_connected', 'port_speed', + 'upstream_speed', 'xconnect_id', 'pp_info', 'description', ] help_texts = { 'port_speed': "Physical circuit speed", @@ -460,4 +460,4 @@ class CircuitTerminationForm(BootstrapMixin, forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['cloud'].widget.add_query_param('provider_id', self.instance.circuit.provider_id) + self.fields['provider_network'].widget.add_query_param('provider_id', self.instance.circuit.provider_id) diff --git a/netbox/circuits/migrations/0027_cloud.py b/netbox/circuits/migrations/0027_providernetwork.py similarity index 85% rename from netbox/circuits/migrations/0027_cloud.py rename to netbox/circuits/migrations/0027_providernetwork.py index 893371f8f..e8fbdb8d4 100644 --- a/netbox/circuits/migrations/0027_cloud.py +++ b/netbox/circuits/migrations/0027_providernetwork.py @@ -12,9 +12,9 @@ class Migration(migrations.Migration): ] operations = [ - # Create the new Cloud model + # Create the new ProviderNetwork model migrations.CreateModel( - name='Cloud', + name='ProviderNetwork', fields=[ ('created', models.DateField(auto_now_add=True, null=True)), ('last_updated', models.DateTimeField(auto_now=True, null=True)), @@ -23,7 +23,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100)), ('description', models.CharField(blank=True, max_length=200)), ('comments', models.TextField(blank=True)), - ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='clouds', to='circuits.provider')), + ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='networks', to='circuits.provider')), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -31,19 +31,19 @@ class Migration(migrations.Migration): }, ), migrations.AddConstraint( - model_name='cloud', - constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_cloud_provider_name'), + model_name='providernetwork', + constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_provider_name'), ), migrations.AlterUniqueTogether( - name='cloud', + name='providernetwork', unique_together={('provider', 'name')}, ), - # Add cloud FK to CircuitTermination + # Add ProviderNetwork FK to CircuitTermination migrations.AddField( model_name='circuittermination', - name='cloud', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='circuits.cloud'), + name='provider_network', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='circuits.providernetwork'), ), migrations.AlterField( model_name='circuittermination', diff --git a/netbox/circuits/migrations/0028_cache_circuit_terminations.py b/netbox/circuits/migrations/0028_cache_circuit_terminations.py index 49631da07..23734348e 100644 --- a/netbox/circuits/migrations/0028_cache_circuit_terminations.py +++ b/netbox/circuits/migrations/0028_cache_circuit_terminations.py @@ -26,7 +26,7 @@ def cache_circuit_terminations(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('circuits', '0027_cloud'), + ('circuits', '0027_providernetwork'), ] operations = [ diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index b163834e6..10534d1cc 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -15,7 +15,7 @@ __all__ = ( 'Circuit', 'CircuitTermination', 'CircuitType', - 'Cloud', + 'ProviderNetwork', 'Provider', ) @@ -93,18 +93,22 @@ class Provider(PrimaryModel): # -# Clouds +# Provider networks # @extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') -class Cloud(PrimaryModel): +class ProviderNetwork(PrimaryModel): + """ + This represents a provider network which exists outside of NetBox, the details of which are unknown or + unimportant to the user. + """ name = models.CharField( max_length=100 ) provider = models.ForeignKey( to='circuits.Provider', on_delete=models.PROTECT, - related_name='clouds' + related_name='networks' ) description = models.CharField( max_length=200, @@ -125,7 +129,7 @@ class Cloud(PrimaryModel): constraints = ( models.UniqueConstraint( fields=('provider', 'name'), - name='circuits_cloud_provider_name' + name='circuits_providernetwork_provider_name' ), ) unique_together = ('provider', 'name') @@ -134,7 +138,7 @@ class Cloud(PrimaryModel): return self.name def get_absolute_url(self): - return reverse('circuits:cloud', args=[self.pk]) + return reverse('circuits:providernetwork', args=[self.pk]) def to_csv(self): return ( @@ -308,8 +312,8 @@ class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination): blank=True, null=True ) - cloud = models.ForeignKey( - to=Cloud, + provider_network = models.ForeignKey( + to=ProviderNetwork, on_delete=models.PROTECT, related_name='circuit_terminations', blank=True, @@ -348,23 +352,21 @@ class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination): unique_together = ['circuit', 'term_side'] def __str__(self): - if self.site: - return str(self.site) - return str(self.cloud) + return str(self.site or self.provider_network) def get_absolute_url(self): if self.site: return self.site.get_absolute_url() - return self.cloud.get_absolute_url() + return self.provider_network.get_absolute_url() def clean(self): super().clean() - # Must define either site *or* cloud - if self.site is None and self.cloud is None: - raise ValidationError("A circuit termination must attach to either a site or a cloud.") - if self.site and self.cloud: - raise ValidationError("A circuit termination cannot attach to both a site and a cloud.") + # Must define either site *or* provider network + if self.site is None and self.provider_network is None: + raise ValidationError("A circuit termination must attach to either a site or a provider network.") + if self.site and self.provider_network: + raise ValidationError("A circuit termination cannot attach to both a site and a provider network.") def to_objectchange(self, action): # Annotate the parent Circuit diff --git a/netbox/circuits/tables.py b/netbox/circuits/tables.py index ba113de8c..326897415 100644 --- a/netbox/circuits/tables.py +++ b/netbox/circuits/tables.py @@ -30,10 +30,10 @@ class ProviderTable(BaseTable): # -# Clouds +# Provider networks # -class CloudTable(BaseTable): +class ProviderNetworkTable(BaseTable): pk = ToggleColumn() name = tables.Column( linkify=True @@ -42,11 +42,11 @@ class CloudTable(BaseTable): linkify=True ) tags = TagColumn( - url_name='circuits:cloud_list' + url_name='circuits:providernetwork_list' ) class Meta(BaseTable.Meta): - model = Cloud + model = ProviderNetwork fields = ('pk', 'name', 'provider', 'description', 'tags') default_columns = ('pk', 'name', 'provider', 'description') diff --git a/netbox/circuits/tests/test_api.py b/netbox/circuits/tests/test_api.py index 01e228f76..424b13d40 100644 --- a/netbox/circuits/tests/test_api.py +++ b/netbox/circuits/tests/test_api.py @@ -180,8 +180,8 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): } -class CloudTest(APIViewTestCases.APIViewTestCase): - model = Cloud +class ProviderNetworkTest(APIViewTestCases.APIViewTestCase): + model = ProviderNetwork brief_fields = ['display', 'id', 'name', 'url'] @classmethod @@ -192,24 +192,24 @@ class CloudTest(APIViewTestCases.APIViewTestCase): ) Provider.objects.bulk_create(providers) - clouds = ( - Cloud(name='Cloud 1', provider=providers[0]), - Cloud(name='Cloud 2', provider=providers[0]), - Cloud(name='Cloud 3', provider=providers[0]), + provider_networks = ( + ProviderNetwork(name='Provider Network 1', provider=providers[0]), + ProviderNetwork(name='Provider Network 2', provider=providers[0]), + ProviderNetwork(name='Provider Network 3', provider=providers[0]), ) - Cloud.objects.bulk_create(clouds) + ProviderNetwork.objects.bulk_create(provider_networks) cls.create_data = [ { - 'name': 'Cloud 4', + 'name': 'Provider Network 4', 'provider': providers[0].pk, }, { - 'name': 'Cloud 5', + 'name': 'Provider Network 5', 'provider': providers[0].pk, }, { - 'name': 'Cloud 6', + 'name': 'Provider Network 6', 'provider': providers[0].pk, }, ] diff --git a/netbox/circuits/tests/test_filters.py b/netbox/circuits/tests/test_filters.py index 880139baf..dca6b317d 100644 --- a/netbox/circuits/tests/test_filters.py +++ b/netbox/circuits/tests/test_filters.py @@ -186,12 +186,12 @@ class CircuitTestCase(TestCase): ) Provider.objects.bulk_create(providers) - clouds = ( - Cloud(name='Cloud 1', provider=providers[1]), - Cloud(name='Cloud 2', provider=providers[1]), - Cloud(name='Cloud 3', provider=providers[1]), + provider_networks = ( + ProviderNetwork(name='Provider Network 1', provider=providers[1]), + ProviderNetwork(name='Provider Network 2', provider=providers[1]), + ProviderNetwork(name='Provider Network 3', provider=providers[1]), ) - Cloud.objects.bulk_create(clouds) + 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', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE), @@ -207,9 +207,9 @@ class CircuitTestCase(TestCase): CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A'), CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A'), CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A'), - CircuitTermination(circuit=circuits[3], cloud=clouds[0], term_side='A'), - CircuitTermination(circuit=circuits[4], cloud=clouds[1], term_side='A'), - CircuitTermination(circuit=circuits[5], cloud=clouds[2], term_side='A'), + CircuitTermination(circuit=circuits[3], provider_network=provider_networks[0], term_side='A'), + CircuitTermination(circuit=circuits[4], provider_network=provider_networks[1], term_side='A'), + CircuitTermination(circuit=circuits[5], provider_network=provider_networks[2], term_side='A'), )) CircuitTermination.objects.bulk_create(circuit_terminations) @@ -236,9 +236,9 @@ class CircuitTestCase(TestCase): params = {'provider': [provider.slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) - def test_cloud(self): - clouds = Cloud.objects.all()[:2] - params = {'cloud_id': [clouds[0].pk, clouds[1].pk]} + def test_provider_network(self): + provider_networks = ProviderNetwork.objects.all()[:2] + params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_type(self): @@ -312,12 +312,12 @@ class CircuitTerminationTestCase(TestCase): ) Provider.objects.bulk_create(providers) - clouds = ( - Cloud(name='Cloud 1', provider=providers[0]), - Cloud(name='Cloud 2', provider=providers[0]), - Cloud(name='Cloud 3', provider=providers[0]), + provider_networks = ( + ProviderNetwork(name='Provider Network 1', provider=providers[0]), + ProviderNetwork(name='Provider Network 2', provider=providers[0]), + ProviderNetwork(name='Provider Network 3', provider=providers[0]), ) - Cloud.objects.bulk_create(clouds) + ProviderNetwork.objects.bulk_create(provider_networks) circuits = ( Circuit(provider=providers[0], type=circuit_types[0], cid='Circuit 1'), @@ -336,9 +336,9 @@ class CircuitTerminationTestCase(TestCase): CircuitTermination(circuit=circuits[1], site=sites[2], term_side='Z', port_speed=2000, upstream_speed=2000, xconnect_id='JKL'), CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=3000, upstream_speed=3000, xconnect_id='MNO'), CircuitTermination(circuit=circuits[2], site=sites[0], term_side='Z', port_speed=3000, upstream_speed=3000, xconnect_id='PQR'), - CircuitTermination(circuit=circuits[3], cloud=clouds[0], term_side='A'), - CircuitTermination(circuit=circuits[4], cloud=clouds[1], term_side='A'), - CircuitTermination(circuit=circuits[5], cloud=clouds[2], term_side='A'), + CircuitTermination(circuit=circuits[3], provider_network=provider_networks[0], term_side='A'), + CircuitTermination(circuit=circuits[4], provider_network=provider_networks[1], term_side='A'), + CircuitTermination(circuit=circuits[5], provider_network=provider_networks[2], term_side='A'), )) CircuitTermination.objects.bulk_create(circuit_terminations) @@ -372,9 +372,9 @@ class CircuitTerminationTestCase(TestCase): params = {'site': [sites[0].slug, sites[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) - def test_cloud(self): - clouds = Cloud.objects.all()[:2] - params = {'cloud_id': [clouds[0].pk, clouds[1].pk]} + def test_provider_network(self): + provider_networks = ProviderNetwork.objects.all()[:2] + params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_cabled(self): @@ -388,9 +388,9 @@ class CircuitTerminationTestCase(TestCase): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7) -class CloudTestCase(TestCase): - queryset = Cloud.objects.all() - filterset = CloudFilterSet +class ProviderNetworkTestCase(TestCase): + queryset = ProviderNetwork.objects.all() + filterset = ProviderNetworkFilterSet @classmethod def setUpTestData(cls): @@ -402,19 +402,19 @@ class CloudTestCase(TestCase): ) Provider.objects.bulk_create(providers) - clouds = ( - Cloud(name='Cloud 1', provider=providers[0]), - Cloud(name='Cloud 2', provider=providers[1]), - Cloud(name='Cloud 3', provider=providers[2]), + provider_networks = ( + ProviderNetwork(name='Provider Network 1', provider=providers[0]), + ProviderNetwork(name='Provider Network 2', provider=providers[1]), + ProviderNetwork(name='Provider Network 3', provider=providers[2]), ) - Cloud.objects.bulk_create(clouds) + ProviderNetwork.objects.bulk_create(provider_networks) def test_id(self): params = {'id': self.queryset.values_list('pk', flat=True)[:2]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_name(self): - params = {'name': ['Cloud 1', 'Cloud 2']} + params = {'name': ['Provider Network 1', 'Provider Network 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_provider(self): diff --git a/netbox/circuits/tests/test_views.py b/netbox/circuits/tests/test_views.py index ba2d4fc22..20f7b4d83 100644 --- a/netbox/circuits/tests/test_views.py +++ b/netbox/circuits/tests/test_views.py @@ -135,8 +135,8 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): } -class CloudTestCase(ViewTestCases.PrimaryObjectViewTestCase): - model = Cloud +class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = ProviderNetwork @classmethod def setUpTestData(cls): @@ -147,27 +147,27 @@ class CloudTestCase(ViewTestCases.PrimaryObjectViewTestCase): ) Provider.objects.bulk_create(providers) - Cloud.objects.bulk_create([ - Cloud(name='Cloud 1', provider=providers[0]), - Cloud(name='Cloud 2', provider=providers[0]), - Cloud(name='Cloud 3', provider=providers[0]), + ProviderNetwork.objects.bulk_create([ + ProviderNetwork(name='Provider Network 1', provider=providers[0]), + ProviderNetwork(name='Provider Network 2', provider=providers[0]), + ProviderNetwork(name='Provider Network 3', provider=providers[0]), ]) tags = cls.create_tags('Alpha', 'Bravo', 'Charlie') cls.form_data = { - 'name': 'Cloud X', + 'name': 'Provider Network X', 'provider': providers[1].pk, - 'description': 'A new cloud', + 'description': 'A new provider network', 'comments': 'Longer description goes here', 'tags': [t.pk for t in tags], } cls.csv_data = ( "name,provider,description", - "Cloud 4,Provider 1,Foo", - "Cloud 5,Provider 1,Bar", - "Cloud 6,Provider 1,Baz", + "Provider Network 4,Provider 1,Foo", + "Provider Network 5,Provider 1,Bar", + "Provider Network 6,Provider 1,Baz", ) cls.bulk_edit_data = { diff --git a/netbox/circuits/urls.py b/netbox/circuits/urls.py index 58f52bb42..e634eeeb4 100644 --- a/netbox/circuits/urls.py +++ b/netbox/circuits/urls.py @@ -20,17 +20,17 @@ urlpatterns = [ path('providers//changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}), path('providers//journal/', ObjectJournalView.as_view(), name='provider_journal', kwargs={'model': Provider}), - # Clouds - path('clouds/', views.CloudListView.as_view(), name='cloud_list'), - path('clouds/add/', views.CloudEditView.as_view(), name='cloud_add'), - path('clouds/import/', views.CloudBulkImportView.as_view(), name='cloud_import'), - path('clouds/edit/', views.CloudBulkEditView.as_view(), name='cloud_bulk_edit'), - path('clouds/delete/', views.CloudBulkDeleteView.as_view(), name='cloud_bulk_delete'), - path('clouds//', views.CloudView.as_view(), name='cloud'), - path('clouds//edit/', views.CloudEditView.as_view(), name='cloud_edit'), - path('clouds//delete/', views.CloudDeleteView.as_view(), name='cloud_delete'), - path('clouds//changelog/', ObjectChangeLogView.as_view(), name='cloud_changelog', kwargs={'model': Cloud}), - path('clouds//journal/', ObjectJournalView.as_view(), name='cloud_journal', kwargs={'model': Cloud}), + # Provider networks + path('provider-networks/', views.ProviderNetworkListView.as_view(), name='providernetwork_list'), + path('provider-networks/add/', views.ProviderNetworkEditView.as_view(), name='providernetwork_add'), + path('provider-networks/import/', views.ProviderNetworkBulkImportView.as_view(), name='providernetwork_import'), + path('provider-networks/edit/', views.ProviderNetworkBulkEditView.as_view(), name='providernetwork_bulk_edit'), + path('provider-networks/delete/', views.ProviderNetworkBulkDeleteView.as_view(), name='providernetwork_bulk_delete'), + path('provider-networks//', views.ProviderNetworkView.as_view(), name='providernetwork'), + path('provider-networks//edit/', views.ProviderNetworkEditView.as_view(), name='providernetwork_edit'), + path('provider-networks//delete/', views.ProviderNetworkDeleteView.as_view(), name='providernetwork_delete'), + path('provider-networks//changelog/', ObjectChangeLogView.as_view(), name='providernetwork_changelog', kwargs={'model': ProviderNetwork}), + path('provider-networks//journal/', ObjectJournalView.as_view(), name='providernetwork_journal', kwargs={'model': ProviderNetwork}), # Circuit types path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'), diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 6f3c5b2be..f0aefd346 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -77,23 +77,23 @@ class ProviderBulkDeleteView(generic.BulkDeleteView): # -# Clouds +# Provider networks # -class CloudListView(generic.ObjectListView): - queryset = Cloud.objects.all() - filterset = filters.CloudFilterSet - filterset_form = forms.CloudFilterForm - table = tables.CloudTable +class ProviderNetworkListView(generic.ObjectListView): + queryset = ProviderNetwork.objects.all() + filterset = filters.ProviderNetworkFilterSet + filterset_form = forms.ProviderNetworkFilterForm + table = tables.ProviderNetworkTable -class CloudView(generic.ObjectView): - queryset = Cloud.objects.all() +class ProviderNetworkView(generic.ObjectView): + queryset = ProviderNetwork.objects.all() def get_extra_context(self, request, instance): circuits = Circuit.objects.restrict(request.user, 'view').filter( - Q(termination_a__cloud=instance.pk) | - Q(termination_z__cloud=instance.pk) + Q(termination_a__provider_network=instance.pk) | + Q(termination_z__provider_network=instance.pk) ).prefetch_related( 'type', 'tenant', 'terminations__site' ) @@ -108,32 +108,32 @@ class CloudView(generic.ObjectView): } -class CloudEditView(generic.ObjectEditView): - queryset = Cloud.objects.all() - model_form = forms.CloudForm +class ProviderNetworkEditView(generic.ObjectEditView): + queryset = ProviderNetwork.objects.all() + model_form = forms.ProviderNetworkForm -class CloudDeleteView(generic.ObjectDeleteView): - queryset = Cloud.objects.all() +class ProviderNetworkDeleteView(generic.ObjectDeleteView): + queryset = ProviderNetwork.objects.all() -class CloudBulkImportView(generic.BulkImportView): - queryset = Cloud.objects.all() - model_form = forms.CloudCSVForm - table = tables.CloudTable +class ProviderNetworkBulkImportView(generic.BulkImportView): + queryset = ProviderNetwork.objects.all() + model_form = forms.ProviderNetworkCSVForm + table = tables.ProviderNetworkTable -class CloudBulkEditView(generic.BulkEditView): - queryset = Cloud.objects.all() - filterset = filters.CloudFilterSet - table = tables.CloudTable - form = forms.CloudBulkEditForm +class ProviderNetworkBulkEditView(generic.BulkEditView): + queryset = ProviderNetwork.objects.all() + filterset = filters.ProviderNetworkFilterSet + table = tables.ProviderNetworkTable + form = forms.ProviderNetworkBulkEditForm -class CloudBulkDeleteView(generic.BulkDeleteView): - queryset = Cloud.objects.all() - filterset = filters.CloudFilterSet - table = tables.CloudTable +class ProviderNetworkBulkDeleteView(generic.BulkDeleteView): + queryset = ProviderNetwork.objects.all() + filterset = filters.ProviderNetworkFilterSet + table = tables.ProviderNetworkTable # diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 9e5aea725..b20fc7080 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -242,14 +242,14 @@ class Cable(PrimaryModel): ): raise ValidationError("A front port cannot be connected to it corresponding rear port") - # A CircuitTermination attached to a Cloud cannot have a Cable - if isinstance(self.termination_a, CircuitTermination) and self.termination_a.cloud is not None: + # A CircuitTermination attached to a ProviderNetwork cannot have a Cable + if isinstance(self.termination_a, CircuitTermination) and self.termination_a.provider_network is not None: raise ValidationError({ - 'termination_a_id': "Circuit terminations attached to a cloud may not be cabled." + 'termination_a_id': "Circuit terminations attached to a provider network may not be cabled." }) - if isinstance(self.termination_b, CircuitTermination) and self.termination_b.cloud is not None: + if isinstance(self.termination_b, CircuitTermination) and self.termination_b.provider_network is not None: raise ValidationError({ - 'termination_b_id': "Circuit terminations attached to a cloud may not be cabled." + 'termination_b_id': "Circuit terminations attached to a provider network may not be cabled." }) # Check for an existing Cable connected to either termination object diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index b4454aa8a..ae280365e 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -479,13 +479,13 @@ class CableTestCase(TestCase): device=self.patch_pannel, name='FP4', type='8p8c', rear_port=self.rear_port4, rear_port_position=1 ) self.provider = Provider.objects.create(name='Provider 1', slug='provider-1') - cloud = Cloud.objects.create(name='Cloud 1', provider=self.provider) + provider_network = ProviderNetwork.objects.create(name='Provider Network 1', provider=self.provider) self.circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') self.circuit1 = Circuit.objects.create(provider=self.provider, type=self.circuittype, cid='1') self.circuit2 = Circuit.objects.create(provider=self.provider, type=self.circuittype, cid='2') self.circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit1, site=site, term_side='A') self.circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit1, site=site, term_side='Z') - self.circuittermination3 = CircuitTermination.objects.create(circuit=self.circuit2, cloud=cloud, term_side='A') + self.circuittermination3 = CircuitTermination.objects.create(circuit=self.circuit2, provider_network=provider_network, term_side='A') def test_cable_creation(self): """ @@ -555,9 +555,9 @@ class CableTestCase(TestCase): with self.assertRaises(ValidationError): cable.clean() - def test_cable_cannot_terminate_to_a_cloud_circuittermination(self): + def test_cable_cannot_terminate_to_a_provider_network_circuittermination(self): """ - Neither side of a cable can be terminated to a CircuitTermination which is attached to a Cloud + Neither side of a cable can be terminated to a CircuitTermination which is attached to a ProviderNetwork """ cable = Cable(termination_a=self.interface3, termination_b=self.circuittermination3) with self.assertRaises(ValidationError): diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index 797a11965..5568f4e70 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -1,8 +1,8 @@ from collections import OrderedDict -from circuits.filters import CircuitFilterSet, CloudFilterSet, ProviderFilterSet -from circuits.models import Circuit, Cloud, Provider -from circuits.tables import CircuitTable, CloudTable, ProviderTable +from circuits.filters import CircuitFilterSet, ProviderFilterSet, ProviderNetworkFilterSet +from circuits.models import Circuit, ProviderNetwork, Provider +from circuits.tables import CircuitTable, ProviderNetworkTable, ProviderTable from dcim.filters import ( CableFilterSet, DeviceFilterSet, DeviceTypeFilterSet, PowerFeedFilterSet, RackFilterSet, LocationFilterSet, SiteFilterSet, VirtualChassisFilterSet, @@ -45,11 +45,11 @@ SEARCH_TYPES = OrderedDict(( 'table': CircuitTable, 'url': 'circuits:circuit_list', }), - ('cloud', { - 'queryset': Cloud.objects.prefetch_related('provider'), - 'filterset': CloudFilterSet, - 'table': CloudTable, - 'url': 'circuits:cloud_list', + ('providernetwork', { + 'queryset': ProviderNetwork.objects.prefetch_related('provider'), + 'filterset': ProviderNetworkFilterSet, + 'table': ProviderNetworkTable, + 'url': 'circuits:providernetwork_list', }), # DCIM ('site', { diff --git a/netbox/templates/circuits/circuittermination_edit.html b/netbox/templates/circuits/circuittermination_edit.html index ebad75976..4034695d5 100644 --- a/netbox/templates/circuits/circuittermination_edit.html +++ b/netbox/templates/circuits/circuittermination_edit.html @@ -26,19 +26,19 @@

{{ form.term_side.value }}

- {% with cloud_tab_active=form.initial.cloud %} + {% with providernetwork_tab_active=form.initial.provider_network %}
-
+
{% render_field form.region %} {% render_field form.site_group %} {% render_field form.site %}
-
- {% render_field form.cloud %} +
+ {% render_field form.provider_network %}
{% endwith %} diff --git a/netbox/templates/circuits/inc/circuit_termination.html b/netbox/templates/circuits/inc/circuit_termination.html index acfc4ee22..5a5a4788d 100644 --- a/netbox/templates/circuits/inc/circuit_termination.html +++ b/netbox/templates/circuits/inc/circuit_termination.html @@ -85,9 +85,9 @@ {% else %} - Cloud + Provider Network - {{ termination.cloud }} + {{ termination.provider_network }} {% endif %} diff --git a/netbox/templates/circuits/cloud.html b/netbox/templates/circuits/providernetwork.html similarity index 86% rename from netbox/templates/circuits/cloud.html rename to netbox/templates/circuits/providernetwork.html index 532118bf8..5bc3ef271 100644 --- a/netbox/templates/circuits/cloud.html +++ b/netbox/templates/circuits/providernetwork.html @@ -4,8 +4,8 @@ {% load plugins %} {% block breadcrumbs %} -
  • Clouds
  • -
  • {{ object.provider }}
  • +
  • Provider Networks
  • +
  • {{ object.provider }}
  • {{ object }}
  • {% endblock %} @@ -14,7 +14,7 @@
    - Cloud + Provider Network
    @@ -46,7 +46,7 @@ {% include 'inc/custom_fields_panel.html' %} - {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='circuits:cloud_list' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='circuits:providernetwork_list' %} {% plugin_left_page object %}
    diff --git a/netbox/templates/inc/nav_menu.html b/netbox/templates/inc/nav_menu.html index fa44da417..0ac1ce393 100644 --- a/netbox/templates/inc/nav_menu.html +++ b/netbox/templates/inc/nav_menu.html @@ -465,14 +465,14 @@
    {% endif %} Providers - - {% if perms.circuits.add_cloud %} + + {% if perms.circuits.add_providernetwork %}
    - - + +
    {% endif %} - Clouds + Provider Networks