mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Remove link peer fields from cable termination models
This commit is contained in:
@ -109,6 +109,6 @@ class CircuitTerminationSerializer(ValidatedModelSerializer, LinkTerminationSeri
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
|
||||
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type',
|
||||
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'link_peers', 'link_peers_type',
|
||||
'_occupied', 'created', 'last_updated',
|
||||
]
|
||||
|
@ -58,7 +58,7 @@ class CircuitViewSet(NetBoxModelViewSet):
|
||||
|
||||
class CircuitTerminationViewSet(PassThroughPortMixin, NetBoxModelViewSet):
|
||||
queryset = CircuitTermination.objects.prefetch_related(
|
||||
'circuit', 'site', 'provider_network', 'cable'
|
||||
'circuit', 'site', 'provider_network', 'cable__terminations'
|
||||
)
|
||||
serializer_class = serializers.CircuitTerminationSerializer
|
||||
filterset_class = filtersets.CircuitTerminationFilterSet
|
||||
|
@ -224,7 +224,7 @@ class CircuitTerminationFilterSet(ChangeLoggedModelFilterSet, CabledObjectFilter
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = ['id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description']
|
||||
fields = ['id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'cable_end']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
|
16
netbox/circuits/migrations/0036_new_cabling_models.py
Normal file
16
netbox/circuits/migrations/0036_new_cabling_models.py
Normal file
@ -0,0 +1,16 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0035_provider_asns'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='circuittermination',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
]
|
20
netbox/circuits/migrations/0037_cabling_cleanup.py
Normal file
20
netbox/circuits/migrations/0037_cabling_cleanup.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0036_new_cabling_models'),
|
||||
('dcim', '0157_populate_cable_ends'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='circuittermination',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='circuittermination',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
]
|
@ -27,25 +27,37 @@ from .nested_serializers import *
|
||||
|
||||
|
||||
class LinkTerminationSerializer(serializers.ModelSerializer):
|
||||
link_peer_type = serializers.SerializerMethodField(read_only=True)
|
||||
link_peer = serializers.SerializerMethodField(read_only=True)
|
||||
link_peers_type = serializers.SerializerMethodField(read_only=True)
|
||||
link_peers = serializers.SerializerMethodField(read_only=True)
|
||||
_occupied = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def get_link_peer_type(self, obj):
|
||||
if obj._link_peer is not None:
|
||||
return f'{obj._link_peer._meta.app_label}.{obj._link_peer._meta.model_name}'
|
||||
def get_link_peers_type(self, obj):
|
||||
"""
|
||||
Return the type of the peer link terminations, or None.
|
||||
"""
|
||||
if not obj.cable:
|
||||
return None
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
||||
def get_link_peer(self, obj):
|
||||
if obj.link_peers:
|
||||
return f'{obj.link_peers[0]._meta.app_label}.{obj.link_peers[0]._meta.model_name}'
|
||||
|
||||
return None
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=serializers.ListField)
|
||||
def get_link_peers(self, obj):
|
||||
"""
|
||||
Return the appropriate serializer for the link termination model.
|
||||
"""
|
||||
if obj._link_peer is not None:
|
||||
serializer = get_serializer_for_model(obj._link_peer, prefix='Nested')
|
||||
if not obj.cable:
|
||||
return []
|
||||
|
||||
# Return serialized peer termination objects
|
||||
if obj.link_peers:
|
||||
serializer = get_serializer_for_model(obj.link_peers[0], prefix='Nested')
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj._link_peer, context=context).data
|
||||
return None
|
||||
return serializer(obj.link_peers, context=context, many=True).data
|
||||
|
||||
return []
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=serializers.BooleanField)
|
||||
def get__occupied(self, obj):
|
||||
@ -96,7 +108,7 @@ class ConnectedEndpointsSerializer(serializers.ModelSerializer):
|
||||
if endpoints:
|
||||
return f'{endpoints[0]._meta.app_label}.{endpoints[0]._meta.model_name}'
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
||||
@swagger_serializer_method(serializer_or_field=serializers.ListField)
|
||||
def get_connected_endpoints(self, obj):
|
||||
"""
|
||||
Return the appropriate serializer for the type of connected object.
|
||||
@ -715,7 +727,7 @@ class ConsoleServerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializ
|
||||
model = ConsoleServerPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||
'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoints', 'connected_endpoints_type',
|
||||
'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'connected_endpoints', 'connected_endpoints_type',
|
||||
'connected_endpoints_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||
]
|
||||
|
||||
@ -743,7 +755,7 @@ class ConsolePortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, Co
|
||||
model = ConsolePort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||
'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoints', 'connected_endpoints_type',
|
||||
'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'connected_endpoints', 'connected_endpoints_type',
|
||||
'connected_endpoints_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||
]
|
||||
|
||||
@ -777,7 +789,7 @@ class PowerOutletSerializer(NetBoxModelSerializer, LinkTerminationSerializer, Co
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg',
|
||||
'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoints',
|
||||
'description', 'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
@ -801,7 +813,7 @@ class PowerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, Conn
|
||||
model = PowerPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
|
||||
'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoints',
|
||||
'description', 'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
@ -847,7 +859,7 @@ class InterfaceSerializer(NetBoxModelSerializer, LinkTerminationSerializer, Conn
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag',
|
||||
'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected',
|
||||
'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'vrf', 'connected_endpoints',
|
||||
'cable', 'wireless_link', 'link_peers', 'link_peers_type', 'wireless_lans', 'vrf', 'connected_endpoints',
|
||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
|
||||
]
|
||||
@ -880,7 +892,7 @@ class RearPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
|
||||
model = RearPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description',
|
||||
'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields', 'created',
|
||||
'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
|
||||
@ -911,7 +923,7 @@ class FrontPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
|
||||
model = FrontPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
|
||||
'rear_port_position', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags',
|
||||
'rear_port_position', 'description', 'mark_connected', 'cable', 'link_peers', 'link_peers_type', 'tags',
|
||||
'custom_fields', 'created', 'last_updated', '_occupied',
|
||||
]
|
||||
|
||||
@ -1193,7 +1205,7 @@ class PowerFeedSerializer(NetBoxModelSerializer, LinkTerminationSerializer, Conn
|
||||
model = PowerFeed
|
||||
fields = [
|
||||
'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
|
||||
'amperage', 'max_utilization', 'comments', 'mark_connected', 'cable', 'link_peer', 'link_peer_type',
|
||||
'amperage', 'max_utilization', 'comments', 'mark_connected', 'cable', 'link_peers', 'link_peers_type',
|
||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', '_occupied',
|
||||
]
|
||||
|
@ -546,7 +546,7 @@ class ModuleViewSet(NetBoxModelViewSet):
|
||||
|
||||
class ConsolePortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = ConsolePort.objects.prefetch_related(
|
||||
'device', 'module__module_bay', '_path', 'cable', '_link_peer', 'tags'
|
||||
'device', 'module__module_bay', '_path', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.ConsolePortSerializer
|
||||
filterset_class = filtersets.ConsolePortFilterSet
|
||||
@ -555,7 +555,7 @@ class ConsolePortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
|
||||
class ConsoleServerPortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = ConsoleServerPort.objects.prefetch_related(
|
||||
'device', 'module__module_bay', '_path', 'cable', '_link_peer', 'tags'
|
||||
'device', 'module__module_bay', '_path', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.ConsoleServerPortSerializer
|
||||
filterset_class = filtersets.ConsoleServerPortFilterSet
|
||||
@ -564,7 +564,7 @@ class ConsoleServerPortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
|
||||
class PowerPortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = PowerPort.objects.prefetch_related(
|
||||
'device', 'module__module_bay', '_path', 'cable', '_link_peer', 'tags'
|
||||
'device', 'module__module_bay', '_path', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerPortSerializer
|
||||
filterset_class = filtersets.PowerPortFilterSet
|
||||
@ -573,7 +573,7 @@ class PowerPortViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
|
||||
class PowerOutletViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = PowerOutlet.objects.prefetch_related(
|
||||
'device', 'module__module_bay', '_path', 'cable', '_link_peer', 'tags'
|
||||
'device', 'module__module_bay', '_path', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerOutletSerializer
|
||||
filterset_class = filtersets.PowerOutletFilterSet
|
||||
@ -582,8 +582,8 @@ class PowerOutletViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
|
||||
class InterfaceViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = Interface.objects.prefetch_related(
|
||||
'device', 'module__module_bay', 'parent', 'bridge', 'lag', '_path', 'cable', '_link_peer',
|
||||
'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'ip_addresses', 'fhrp_group_assignments', 'tags'
|
||||
'device', 'module__module_bay', 'parent', 'bridge', 'lag', '_path', 'cable__terminations', 'wireless_lans',
|
||||
'untagged_vlan', 'tagged_vlans', 'vrf', 'ip_addresses', 'fhrp_group_assignments', 'tags'
|
||||
)
|
||||
serializer_class = serializers.InterfaceSerializer
|
||||
filterset_class = filtersets.InterfaceFilterSet
|
||||
@ -592,7 +592,7 @@ class InterfaceViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
|
||||
class FrontPortViewSet(PassThroughPortMixin, NetBoxModelViewSet):
|
||||
queryset = FrontPort.objects.prefetch_related(
|
||||
'device__device_type__manufacturer', 'module__module_bay', 'rear_port', 'cable', 'tags'
|
||||
'device__device_type__manufacturer', 'module__module_bay', 'rear_port', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.FrontPortSerializer
|
||||
filterset_class = filtersets.FrontPortFilterSet
|
||||
@ -601,7 +601,7 @@ class FrontPortViewSet(PassThroughPortMixin, NetBoxModelViewSet):
|
||||
|
||||
class RearPortViewSet(PassThroughPortMixin, NetBoxModelViewSet):
|
||||
queryset = RearPort.objects.prefetch_related(
|
||||
'device__device_type__manufacturer', 'module__module_bay', 'cable', 'tags'
|
||||
'device__device_type__manufacturer', 'module__module_bay', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.RearPortSerializer
|
||||
filterset_class = filtersets.RearPortFilterSet
|
||||
@ -691,7 +691,7 @@ class PowerPanelViewSet(NetBoxModelViewSet):
|
||||
|
||||
class PowerFeedViewSet(PathEndpointMixin, NetBoxModelViewSet):
|
||||
queryset = PowerFeed.objects.prefetch_related(
|
||||
'power_panel', 'rack', '_path', 'cable', '_link_peer', 'tags'
|
||||
'power_panel', 'rack', '_path', 'cable__terminations', 'tags'
|
||||
)
|
||||
serializer_class = serializers.PowerFeedSerializer
|
||||
filterset_class = filtersets.PowerFeedFilterSet
|
||||
|
@ -1226,7 +1226,8 @@ class CableEndChoices(ChoiceSet):
|
||||
|
||||
CHOICES = (
|
||||
(SIDE_A, 'A'),
|
||||
(SIDE_B, 'B')
|
||||
(SIDE_B, 'B'),
|
||||
('', ''),
|
||||
)
|
||||
|
||||
|
||||
|
@ -1141,7 +1141,7 @@ class ConsolePortFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = ['id', 'name', 'label', 'description']
|
||||
fields = ['id', 'name', 'label', 'description', 'cable_end']
|
||||
|
||||
|
||||
class ConsoleServerPortFilterSet(
|
||||
@ -1157,7 +1157,7 @@ class ConsoleServerPortFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = ['id', 'name', 'label', 'description']
|
||||
fields = ['id', 'name', 'label', 'description', 'cable_end']
|
||||
|
||||
|
||||
class PowerPortFilterSet(
|
||||
@ -1173,7 +1173,7 @@ class PowerPortFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = ['id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description']
|
||||
fields = ['id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description', 'cable_end']
|
||||
|
||||
|
||||
class PowerOutletFilterSet(
|
||||
@ -1193,7 +1193,7 @@ class PowerOutletFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = ['id', 'name', 'label', 'feed_leg', 'description']
|
||||
fields = ['id', 'name', 'label', 'feed_leg', 'description', 'cable_end']
|
||||
|
||||
|
||||
class InterfaceFilterSet(
|
||||
@ -1273,7 +1273,7 @@ class InterfaceFilterSet(
|
||||
model = Interface
|
||||
fields = [
|
||||
'id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'rf_role', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'cable_end',
|
||||
]
|
||||
|
||||
def filter_device(self, queryset, name, value):
|
||||
@ -1336,7 +1336,7 @@ class FrontPortFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = FrontPort
|
||||
fields = ['id', 'name', 'label', 'type', 'color', 'description']
|
||||
fields = ['id', 'name', 'label', 'type', 'color', 'description', 'cable_end']
|
||||
|
||||
|
||||
class RearPortFilterSet(
|
||||
@ -1351,7 +1351,7 @@ class RearPortFilterSet(
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description']
|
||||
fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description', 'cable_end']
|
||||
|
||||
|
||||
class ModuleBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
|
||||
@ -1679,7 +1679,9 @@ class PowerFeedFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet, PathEndpoi
|
||||
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = ['id', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization']
|
||||
fields = [
|
||||
'id', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', 'cable_end',
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
|
@ -1,30 +0,0 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('dcim', '0153_created_datetimefield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CableTermination',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('cable_end', models.CharField(max_length=1)),
|
||||
('termination_id', models.PositiveBigIntegerField()),
|
||||
('cable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable')),
|
||||
('termination_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('cable', 'cable_end', 'pk'),
|
||||
},
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='cabletermination',
|
||||
constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='unique_termination'),
|
||||
),
|
||||
]
|
91
netbox/dcim/migrations/0154_new_cabling_models.py
Normal file
91
netbox/dcim/migrations/0154_new_cabling_models.py
Normal file
@ -0,0 +1,91 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('dcim', '0153_created_datetimefield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Create CableTermination model
|
||||
migrations.CreateModel(
|
||||
name='CableTermination',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('cable_end', models.CharField(max_length=1)),
|
||||
('termination_id', models.PositiveBigIntegerField()),
|
||||
('cable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable')),
|
||||
('termination_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('cable', 'cable_end', 'pk'),
|
||||
},
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='cabletermination',
|
||||
constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='unique_termination'),
|
||||
),
|
||||
|
||||
# Update CablePath model
|
||||
migrations.RenameField(
|
||||
model_name='cablepath',
|
||||
old_name='path',
|
||||
new_name='_nodes',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cablepath',
|
||||
name='path',
|
||||
field=models.JSONField(default=list),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cablepath',
|
||||
name='is_complete',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
|
||||
# Add cable_end field to cable termination models
|
||||
migrations.AddField(
|
||||
model_name='consoleport',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='consoleserverport',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='frontport',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerfeed',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poweroutlet',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerport',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rearport',
|
||||
name='cable_end',
|
||||
field=models.CharField(blank=True, max_length=1),
|
||||
),
|
||||
]
|
@ -40,7 +40,7 @@ def populate_cable_terminations(apps, schema_editor):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0154_cabletermination'),
|
||||
('dcim', '0154_new_cabling_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -1,37 +0,0 @@
|
||||
# Generated by Django 4.0.4 on 2022-04-29 14:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0155_populate_cable_terminations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='cable',
|
||||
options={'ordering': ('pk',)},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cable',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_a_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_a_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_b_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_b_type',
|
||||
),
|
||||
]
|
@ -39,7 +39,7 @@ def populate_cable_paths(apps, schema_editor):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0157_cablepath'),
|
||||
('dcim', '0155_populate_cable_terminations'),
|
||||
]
|
||||
|
||||
operations = [
|
@ -1,28 +0,0 @@
|
||||
import dcim.fields
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0156_cable_remove_terminations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='cablepath',
|
||||
old_name='path',
|
||||
new_name='_nodes',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cablepath',
|
||||
name='path',
|
||||
field=models.JSONField(default=list),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cablepath',
|
||||
name='is_complete',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
42
netbox/dcim/migrations/0157_populate_cable_ends.py
Normal file
42
netbox/dcim/migrations/0157_populate_cable_ends.py
Normal file
@ -0,0 +1,42 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def populate_cable_terminations(apps, schema_editor):
|
||||
Cable = apps.get_model('dcim', 'Cable')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
|
||||
cable_termination_models = (
|
||||
apps.get_model('dcim', 'ConsolePort'),
|
||||
apps.get_model('dcim', 'ConsoleServerPort'),
|
||||
apps.get_model('dcim', 'PowerPort'),
|
||||
apps.get_model('dcim', 'PowerOutlet'),
|
||||
apps.get_model('dcim', 'Interface'),
|
||||
apps.get_model('dcim', 'FrontPort'),
|
||||
apps.get_model('dcim', 'RearPort'),
|
||||
apps.get_model('dcim', 'PowerFeed'),
|
||||
apps.get_model('circuits', 'CircuitTermination'),
|
||||
)
|
||||
|
||||
for model in cable_termination_models:
|
||||
ct = ContentType.objects.get_for_model(model)
|
||||
model.objects.filter(
|
||||
id__in=Cable.objects.filter(termination_a_type=ct).values_list('termination_a_id', flat=True)
|
||||
).update(cable_end='A')
|
||||
model.objects.filter(
|
||||
id__in=Cable.objects.filter(termination_b_type=ct).values_list('termination_b_id', flat=True)
|
||||
).update(cable_end='B')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('circuits', '0036_new_cabling_models'),
|
||||
('dcim', '0156_populate_cable_paths'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=populate_cable_terminations,
|
||||
reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
]
|
126
netbox/dcim/migrations/0158_cabling_cleanup.py
Normal file
126
netbox/dcim/migrations/0158_cabling_cleanup.py
Normal file
@ -0,0 +1,126 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0157_populate_cable_ends'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
||||
# Remove old fields from Cable
|
||||
migrations.AlterModelOptions(
|
||||
name='cable',
|
||||
options={'ordering': ('pk',)},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cable',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_a_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_a_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_b_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cable',
|
||||
name='termination_b_type',
|
||||
),
|
||||
|
||||
# Remove old fields from CablePath
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cablepath',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='destination_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='destination_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='origin_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='origin_type',
|
||||
),
|
||||
|
||||
# Remove link peer type/ID fields from cable termination models
|
||||
migrations.RemoveField(
|
||||
model_name='consoleport',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleport',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleserverport',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='consoleserverport',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='frontport',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='frontport',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='interface',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='interface',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerfeed',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerfeed',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='poweroutlet',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='poweroutlet',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerport',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='powerport',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rearport',
|
||||
name='_link_peer_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='rearport',
|
||||
name='_link_peer_type',
|
||||
),
|
||||
|
||||
]
|
@ -1,33 +0,0 @@
|
||||
# Generated by Django 4.0.4 on 2022-05-03 14:50
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0158_cablepath_populate_path'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cablepath',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='destination_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='destination_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='origin_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cablepath',
|
||||
name='origin_type',
|
||||
),
|
||||
]
|
@ -307,7 +307,10 @@ class CableTermination(models.Model):
|
||||
|
||||
# Set the cable on the terminating object
|
||||
termination_model = self.termination._meta.model
|
||||
termination_model.objects.filter(pk=self.termination_id).update(cable=self.cable)
|
||||
termination_model.objects.filter(pk=self.termination_id).update(
|
||||
cable=self.cable,
|
||||
cable_end=self.cable_end
|
||||
)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Sum
|
||||
@ -105,12 +105,7 @@ class ModularComponentModel(ComponentModel):
|
||||
|
||||
class LinkTermination(models.Model):
|
||||
"""
|
||||
An abstract model inherited by all models to which a Cable, WirelessLink, or other such link can terminate. Examples
|
||||
include most device components, CircuitTerminations, and PowerFeeds. The `cable` and `wireless_link` fields
|
||||
reference the attached Cable or WirelessLink instance, respectively.
|
||||
|
||||
`_link_peer` is a GenericForeignKey used to cache the far-end LinkTermination on the local instance; this is a
|
||||
shortcut to referencing `instance.link.termination_b`, for example.
|
||||
An abstract model inherited by all models to which a Cable can terminate.
|
||||
"""
|
||||
cable = models.ForeignKey(
|
||||
to='dcim.Cable',
|
||||
@ -119,20 +114,10 @@ class LinkTermination(models.Model):
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
_link_peer_type = models.ForeignKey(
|
||||
to=ContentType,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='+',
|
||||
cable_end = models.CharField(
|
||||
max_length=1,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
_link_peer_id = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
_link_peer = GenericForeignKey(
|
||||
ct_field='_link_peer_type',
|
||||
fk_field='_link_peer_id'
|
||||
choices=CableEndChoices
|
||||
)
|
||||
mark_connected = models.BooleanField(
|
||||
default=False,
|
||||
@ -145,13 +130,23 @@ class LinkTermination(models.Model):
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
if self.cable and not self.cable_end:
|
||||
raise ValidationError({
|
||||
"cable_end": "Must specify cable end (A or B) when attaching a cable."
|
||||
})
|
||||
|
||||
if self.mark_connected and self.cable_id:
|
||||
raise ValidationError({
|
||||
"mark_connected": "Cannot mark as connected with a cable attached."
|
||||
})
|
||||
|
||||
def get_link_peer(self):
|
||||
return self._link_peer
|
||||
@property
|
||||
def link_peers(self):
|
||||
# TODO: Support WirelessLinks
|
||||
if not self.cable:
|
||||
return []
|
||||
peer_terminations = self.cable.terminations.exclude(cable_end=self.cable_end).prefetch_related('termination')
|
||||
return [ct.termination for ct in peer_terminations]
|
||||
|
||||
@property
|
||||
def _occupied(self):
|
||||
|
@ -132,4 +132,4 @@ def nullify_connected_endpoints(instance, **kwargs):
|
||||
Disassociate the Cable from the termination object.
|
||||
"""
|
||||
model = instance.termination_type.model_class()
|
||||
model.objects.filter(pk=instance.termination_id).update(_link_peer_type=None, _link_peer_id=None)
|
||||
model.objects.filter(pk=instance.termination_id).update(cable=None, cable_end='')
|
||||
|
@ -503,8 +503,12 @@ class CableTestCase(TestCase):
|
||||
"""
|
||||
self.interface1.refresh_from_db()
|
||||
self.interface2.refresh_from_db()
|
||||
self.assertEqual(self.interface1._link_peer, self.interface2)
|
||||
self.assertEqual(self.interface2._link_peer, self.interface1)
|
||||
self.assertEqual(self.interface1.cable, self.cable)
|
||||
self.assertEqual(self.interface2.cable, self.cable)
|
||||
self.assertEqual(self.interface1.cable_end, 'A')
|
||||
self.assertEqual(self.interface2.cable_end, 'B')
|
||||
self.assertEqual(self.interface1.link_peers, [self.interface2])
|
||||
self.assertEqual(self.interface2.link_peers, [self.interface1])
|
||||
|
||||
def test_cable_deletion(self):
|
||||
"""
|
||||
@ -516,10 +520,10 @@ class CableTestCase(TestCase):
|
||||
self.assertNotEqual(str(self.cable), '#None')
|
||||
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||
self.assertIsNone(interface1.cable)
|
||||
self.assertIsNone(interface1._link_peer)
|
||||
self.assertListEqual(interface1.link_peers, [])
|
||||
interface2 = Interface.objects.get(pk=self.interface2.pk)
|
||||
self.assertIsNone(interface2.cable)
|
||||
self.assertIsNone(interface2._link_peer)
|
||||
self.assertListEqual(interface2.link_peers, [])
|
||||
|
||||
def test_cable_validates_same_parent_object(self):
|
||||
"""
|
||||
|
@ -44,16 +44,15 @@
|
||||
<span class="text-success"><i class="mdi mdi-check-bold"></i></span>
|
||||
<span class="text-muted">Marked as connected</span>
|
||||
{% elif termination.cable %}
|
||||
<a class="d-block d-md-inline" href="{{ termination.cable.get_absolute_url }}">{{ termination.cable }}</a>
|
||||
{% with peer=termination.get_link_peer %}
|
||||
to
|
||||
<a class="d-block d-md-inline" href="{{ termination.cable.get_absolute_url }}">{{ termination.cable }}</a> to
|
||||
{% for peer in termination.link_peers %}
|
||||
{% if peer.device %}
|
||||
{{ peer.device|linkify }}<br/>
|
||||
{% elif peer.circuit %}
|
||||
{{ peer.circuit|linkify }}<br/>
|
||||
{% endif %}
|
||||
{{ peer|linkify }}
|
||||
{% endwith %}
|
||||
{{ peer|linkify }}{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
<div class="mt-1">
|
||||
<a href="{% url 'circuits:circuittermination_trace' pk=termination.pk %}" class="btn btn-primary btn-sm lh-1" title="Trace">
|
||||
<i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i> Trace
|
||||
|
@ -25,12 +25,10 @@ def update_connected_interfaces(instance, created, raw=False, **kwargs):
|
||||
if instance.interface_a.wireless_link != instance:
|
||||
logger.debug(f"Updating interface A for wireless link {instance}")
|
||||
instance.interface_a.wireless_link = instance
|
||||
instance.interface_a._link_peer = instance.interface_b
|
||||
instance.interface_a.save()
|
||||
if instance.interface_b.cable != instance:
|
||||
logger.debug(f"Updating interface B for wireless link {instance}")
|
||||
instance.interface_b.wireless_link = instance
|
||||
instance.interface_b._link_peer = instance.interface_a
|
||||
instance.interface_b.save()
|
||||
|
||||
# Create/update cable paths
|
||||
@ -48,18 +46,10 @@ def nullify_connected_interfaces(instance, **kwargs):
|
||||
|
||||
if instance.interface_a is not None:
|
||||
logger.debug(f"Nullifying interface A for wireless link {instance}")
|
||||
Interface.objects.filter(pk=instance.interface_a.pk).update(
|
||||
wireless_link=None,
|
||||
_link_peer_type=None,
|
||||
_link_peer_id=None
|
||||
)
|
||||
Interface.objects.filter(pk=instance.interface_a.pk).update(wireless_link=None)
|
||||
if instance.interface_b is not None:
|
||||
logger.debug(f"Nullifying interface B for wireless link {instance}")
|
||||
Interface.objects.filter(pk=instance.interface_b.pk).update(
|
||||
wireless_link=None,
|
||||
_link_peer_type=None,
|
||||
_link_peer_id=None
|
||||
)
|
||||
Interface.objects.filter(pk=instance.interface_b.pk).update(wireless_link=None)
|
||||
|
||||
# Delete and retrace any dependent cable paths
|
||||
for cablepath in CablePath.objects.filter(_nodes__contains=instance):
|
||||
|
Reference in New Issue
Block a user