mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 12:06:53 -06:00
Enable attaching circuit terminations to clouds
This commit is contained in:
parent
6ff8a267e9
commit
574a43fff7
@ -63,12 +63,13 @@ class CircuitTypeSerializer(OrganizationalModelSerializer):
|
|||||||
class CircuitCircuitTerminationSerializer(WritableNestedSerializer, ConnectedEndpointSerializer):
|
class CircuitCircuitTerminationSerializer(WritableNestedSerializer, ConnectedEndpointSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
|
cloud = NestedCloudSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'site', 'port_speed', 'upstream_speed', 'xconnect_id', 'connected_endpoint',
|
'id', 'url', 'display', 'site', 'cloud', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||||
'connected_endpoint_type', 'connected_endpoint_reachable',
|
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -93,13 +94,14 @@ class CircuitSerializer(PrimaryModelSerializer):
|
|||||||
class CircuitTerminationSerializer(BaseModelSerializer, CableTerminationSerializer, ConnectedEndpointSerializer):
|
class CircuitTerminationSerializer(BaseModelSerializer, CableTerminationSerializer, ConnectedEndpointSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
||||||
circuit = NestedCircuitSerializer()
|
circuit = NestedCircuitSerializer()
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer(required=False)
|
||||||
|
cloud = NestedCloudSerializer(required=False)
|
||||||
cable = NestedCableSerializer(read_only=True)
|
cable = NestedCableSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'port_speed', 'upstream_speed', 'xconnect_id',
|
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'cloud', 'port_speed', 'upstream_speed',
|
||||||
'pp_info', 'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint',
|
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type',
|
||||||
'connected_endpoint_type', 'connected_endpoint_reachable', '_occupied',
|
'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', '_occupied',
|
||||||
]
|
]
|
||||||
|
@ -221,6 +221,10 @@ class CircuitTerminationFilterSet(BaseFilterSet, CableTerminationFilterSet, Path
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label='Site (slug)',
|
||||||
)
|
)
|
||||||
|
cloud_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=Cloud.objects.all(),
|
||||||
|
label='Cloud (ID)',
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
|
@ -423,13 +423,18 @@ class CircuitTerminationForm(BootstrapMixin, forms.ModelForm):
|
|||||||
query_params={
|
query_params={
|
||||||
'region_id': '$region',
|
'region_id': '$region',
|
||||||
'group_id': '$site_group',
|
'group_id': '$site_group',
|
||||||
}
|
},
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
cloud = DynamicModelChoiceField(
|
||||||
|
queryset=Cloud.objects.all(),
|
||||||
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
fields = [
|
fields = [
|
||||||
'term_side', 'region', 'site_group', 'site', 'mark_connected', 'port_speed', 'upstream_speed',
|
'term_side', 'region', 'site_group', 'site', 'cloud', 'mark_connected', 'port_speed', 'upstream_speed',
|
||||||
'xconnect_id', 'pp_info', 'description',
|
'xconnect_id', 'pp_info', 'description',
|
||||||
]
|
]
|
||||||
help_texts = {
|
help_texts = {
|
||||||
@ -442,3 +447,8 @@ class CircuitTerminationForm(BootstrapMixin, forms.ModelForm):
|
|||||||
'port_speed': SelectSpeedWidget(),
|
'port_speed': SelectSpeedWidget(),
|
||||||
'upstream_speed': SelectSpeedWidget(),
|
'upstream_speed': SelectSpeedWidget(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['cloud'].widget.add_query_param('provider_id', self.instance.circuit.provider_id)
|
||||||
|
@ -37,4 +37,14 @@ class Migration(migrations.Migration):
|
|||||||
name='cloud',
|
name='cloud',
|
||||||
unique_together={('provider', 'name')},
|
unique_together={('provider', 'name')},
|
||||||
),
|
),
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='circuittermination',
|
||||||
|
name='site',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.site'),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
@ -300,7 +301,16 @@ class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination):
|
|||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='circuit_terminations'
|
related_name='circuit_terminations',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
cloud = models.ForeignKey(
|
||||||
|
to=Cloud,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='circuit_terminations',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
)
|
)
|
||||||
port_speed = models.PositiveIntegerField(
|
port_speed = models.PositiveIntegerField(
|
||||||
verbose_name='Port speed (Kbps)',
|
verbose_name='Port speed (Kbps)',
|
||||||
@ -335,7 +345,16 @@ class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination):
|
|||||||
unique_together = ['circuit', 'term_side']
|
unique_together = ['circuit', 'term_side']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Side {}'.format(self.get_term_side_display())
|
return f"Side {self.get_term_side_display()}"
|
||||||
|
|
||||||
|
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.")
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
# Annotate the parent Circuit
|
# Annotate the parent Circuit
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>Location</strong></div>
|
<div class="panel-heading"><strong>Termination</strong></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-3 control-label">Provider</label>
|
<label class="col-md-3 control-label">Provider</label>
|
||||||
@ -26,9 +26,22 @@
|
|||||||
<p class="form-control-static">{{ form.term_side.value }}</p>
|
<p class="form-control-static">{{ form.term_side.value }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% with cloud_tab_active=form.initial.cloud %}
|
||||||
|
<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>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane{% if not cloud_tab_active %} active{% endif %}" id="site">
|
||||||
{% render_field form.region %}
|
{% render_field form.region %}
|
||||||
{% render_field form.site_group %}
|
{% render_field form.site_group %}
|
||||||
{% render_field form.site %}
|
{% render_field form.site %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane{% if cloud_tab_active %} active{% endif %}" id="cloud">
|
||||||
|
{% render_field form.cloud %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
{% render_field form.mark_connected %}
|
{% render_field form.mark_connected %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
<strong>Cloud</strong>
|
<strong>Cloud</strong>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover panel-body attr-table">
|
<table class="table table-hover panel-body attr-table">
|
||||||
|
<tr>
|
||||||
|
<td>Provider</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ object.provider.get_absolute_url }}">{{ object.provider }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Name</td>
|
<td>Name</td>
|
||||||
<td>{{ object.name }}</td>
|
<td>{{ object.name }}</td>
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if termination %}
|
{% if termination %}
|
||||||
<table class="table table-hover panel-body attr-table">
|
<table class="table table-hover panel-body attr-table">
|
||||||
|
{% if termination.site %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Site</td>
|
<td>Site</td>
|
||||||
<td>
|
<td>
|
||||||
@ -82,6 +83,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td>Cloud</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ termination.cloud.get_absolute_url }}">{{ termination.cloud }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Speed</td>
|
<td>Speed</td>
|
||||||
<td>
|
<td>
|
||||||
|
Loading…
Reference in New Issue
Block a user