mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Rename Cloud to ProviderNetwork
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
# Circuits
|
||||
|
||||
{!docs/models/circuits/provider.md!}
|
||||
{!docs/models/circuits/cloud.md!}
|
||||
{!docs/models/circuits/providernetwork.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.
|
||||
|
@ -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.
|
5
docs/models/circuits/providernetwork.md
Normal file
5
docs/models/circuits/providernetwork.md
Normal file
@ -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.
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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',
|
@ -26,7 +26,7 @@ def cache_circuit_terminations(apps, schema_editor):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0027_cloud'),
|
||||
('circuits', '0027_providernetwork'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
]
|
||||
|
@ -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):
|
||||
|
@ -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 = {
|
||||
|
@ -20,17 +20,17 @@ urlpatterns = [
|
||||
path('providers/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||
path('providers/<int:pk>/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/<int:pk>/', views.CloudView.as_view(), name='cloud'),
|
||||
path('clouds/<int:pk>/edit/', views.CloudEditView.as_view(), name='cloud_edit'),
|
||||
path('clouds/<int:pk>/delete/', views.CloudDeleteView.as_view(), name='cloud_delete'),
|
||||
path('clouds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cloud_changelog', kwargs={'model': Cloud}),
|
||||
path('clouds/<int:pk>/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/<int:pk>/', views.ProviderNetworkView.as_view(), name='providernetwork'),
|
||||
path('provider-networks/<int:pk>/edit/', views.ProviderNetworkEditView.as_view(), name='providernetwork_edit'),
|
||||
path('provider-networks/<int:pk>/delete/', views.ProviderNetworkDeleteView.as_view(), name='providernetwork_delete'),
|
||||
path('provider-networks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providernetwork_changelog', kwargs={'model': ProviderNetwork}),
|
||||
path('provider-networks/<int:pk>/journal/', ObjectJournalView.as_view(), name='providernetwork_journal', kwargs={'model': ProviderNetwork}),
|
||||
|
||||
# Circuit types
|
||||
path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
|
@ -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
|
||||
|
||||
|
||||
#
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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', {
|
||||
|
@ -26,19 +26,19 @@
|
||||
<p class="form-control-static">{{ form.term_side.value }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% with cloud_tab_active=form.initial.cloud %}
|
||||
{% with providernetwork_tab_active=form.initial.provider_network %}
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation"{% if not cloud_tab_active %} class="active"{% endif %}><a href="#site" role="tab" data-toggle="tab">Site</a></li>
|
||||
<li role="presentation"{% if cloud_tab_active %} class="active"{% endif %}><a href="#cloud" role="tab" data-toggle="tab">Cloud</a></li>
|
||||
<li role="presentation"{% if not providernetwork_tab_active %} class="active"{% endif %}><a href="#site" role="tab" data-toggle="tab">Site</a></li>
|
||||
<li role="presentation"{% if providernetwork_tab_active %} class="active"{% endif %}><a href="#providernetwork" role="tab" data-toggle="tab">Provider Network</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane{% if not cloud_tab_active %} active{% endif %}" id="site">
|
||||
<div class="tab-pane{% if not providernetwork_tab_active %} active{% endif %}" id="site">
|
||||
{% render_field form.region %}
|
||||
{% render_field form.site_group %}
|
||||
{% render_field form.site %}
|
||||
</div>
|
||||
<div class="tab-pane{% if cloud_tab_active %} active{% endif %}" id="cloud">
|
||||
{% render_field form.cloud %}
|
||||
<div class="tab-pane{% if providernetwork_tab_active %} active{% endif %}" id="providernetwork">
|
||||
{% render_field form.provider_network %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
|
@ -85,9 +85,9 @@
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td>Cloud</td>
|
||||
<td>Provider Network</td>
|
||||
<td>
|
||||
<a href="{{ termination.cloud.get_absolute_url }}">{{ termination.cloud }}</a>
|
||||
<a href="{{ termination.provider_network.get_absolute_url }}">{{ termination.provider_network }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
@ -4,8 +4,8 @@
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'circuits:cloud_list' %}">Clouds</a></li>
|
||||
<li><a href="{% url 'circuits:cloud_list' %}?provider_id={{ object.provider_id }}">{{ object.provider }}</a></li>
|
||||
<li><a href="{% url 'circuits:providernetwork_list' %}">Provider Networks</a></li>
|
||||
<li><a href="{% url 'circuits:providernetwork_list' %}?provider_id={{ object.provider_id }}">{{ object.provider }}</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Cloud</strong>
|
||||
<strong>Provider Network</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
@ -465,14 +465,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'circuits:provider_list' %}">Providers</a>
|
||||
<li{% if not perms.circuits.view_cloud %} class="disabled"{% endif %}>
|
||||
{% if perms.circuits.add_cloud %}
|
||||
<li{% if not perms.circuits.view_providernetwork %} class="disabled"{% endif %}>
|
||||
{% if perms.circuits.add_providernetwork %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'circuits:cloud_add' %}" class="btn btn-xs btn-success" title="Add"><i class="mdi mdi-plus-thick"></i></a>
|
||||
<a href="{% url 'circuits:cloud_import' %}" class="btn btn-xs btn-info" title="Import"><i class="mdi mdi-database-import-outline"></i></a>
|
||||
<a href="{% url 'circuits:providernetwork_add' %}" class="btn btn-xs btn-success" title="Add"><i class="mdi mdi-plus-thick"></i></a>
|
||||
<a href="{% url 'circuits:providernetwork_import' %}" class="btn btn-xs btn-info" title="Import"><i class="mdi mdi-database-import-outline"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'circuits:cloud_list' %}">Clouds</a>
|
||||
<a href="{% url 'circuits:providernetwork_list' %}">Provider Networks</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
Reference in New Issue
Block a user