mirror of
				https://github.com/netbox-community/netbox.git
				synced 2024-05-10 07:54:54 +00:00 
			
		
		
		
	Remove legacy connected endpoint fields
This commit is contained in:
		@@ -46,9 +46,7 @@ class CircuitTypeViewSet(ModelViewSet):
 | 
			
		||||
 | 
			
		||||
class CircuitViewSet(CustomFieldModelViewSet):
 | 
			
		||||
    queryset = Circuit.objects.prefetch_related(
 | 
			
		||||
        Prefetch('terminations', queryset=CircuitTermination.objects.prefetch_related(
 | 
			
		||||
            'site', 'connected_endpoint__device'
 | 
			
		||||
        )),
 | 
			
		||||
        Prefetch('terminations', queryset=CircuitTermination.objects.prefetch_related('site')),
 | 
			
		||||
        'type', 'tenant', 'provider',
 | 
			
		||||
    ).prefetch_related('tags')
 | 
			
		||||
    serializer_class = serializers.CircuitSerializer
 | 
			
		||||
@@ -61,7 +59,7 @@ class CircuitViewSet(CustomFieldModelViewSet):
 | 
			
		||||
 | 
			
		||||
class CircuitTerminationViewSet(ModelViewSet):
 | 
			
		||||
    queryset = CircuitTermination.objects.prefetch_related(
 | 
			
		||||
        'circuit', 'site', 'connected_endpoint__device', 'cable'
 | 
			
		||||
        'circuit', 'site', 'cable'
 | 
			
		||||
    )
 | 
			
		||||
    serializer_class = serializers.CircuitTerminationSerializer
 | 
			
		||||
    filterset_class = filters.CircuitTerminationFilterSet
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								netbox/circuits/migrations/0022_drop_connected_endpoint.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								netbox/circuits/migrations/0022_drop_connected_endpoint.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
# Generated by Django 3.1 on 2020-10-05 13:56
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('circuits', '0021_cablepath'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='circuittermination',
 | 
			
		||||
            name='connected_endpoint',
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -248,13 +248,6 @@ class CircuitTermination(PathEndpoint, CableTermination):
 | 
			
		||||
        on_delete=models.PROTECT,
 | 
			
		||||
        related_name='circuit_terminations'
 | 
			
		||||
    )
 | 
			
		||||
    connected_endpoint = models.OneToOneField(
 | 
			
		||||
        to='dcim.Interface',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connection_status = models.BooleanField(
 | 
			
		||||
        choices=CONNECTION_STATUS_CHOICES,
 | 
			
		||||
        blank=True,
 | 
			
		||||
 
 | 
			
		||||
@@ -131,7 +131,7 @@ class CircuitView(ObjectView):
 | 
			
		||||
        circuit = get_object_or_404(self.queryset, pk=pk)
 | 
			
		||||
 | 
			
		||||
        termination_a = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
 | 
			
		||||
            'site__region', 'connected_endpoint__device'
 | 
			
		||||
            'site__region'
 | 
			
		||||
        ).filter(
 | 
			
		||||
            circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
 | 
			
		||||
        ).first()
 | 
			
		||||
@@ -139,7 +139,7 @@ class CircuitView(ObjectView):
 | 
			
		||||
            termination_a.ip_addresses = termination_a.connected_endpoint.ip_addresses.restrict(request.user, 'view')
 | 
			
		||||
 | 
			
		||||
        termination_z = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
 | 
			
		||||
            'site__region', 'connected_endpoint__device'
 | 
			
		||||
            'site__region'
 | 
			
		||||
        ).filter(
 | 
			
		||||
            circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
 | 
			
		||||
        ).first()
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,7 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
 | 
			
		||||
 | 
			
		||||
    def get_connected_endpoint_type(self, obj):
 | 
			
		||||
        if obj.path is not None:
 | 
			
		||||
            destination = obj.path.destination
 | 
			
		||||
            return f'{destination._meta.app_label}.{destination._meta.model_name}'
 | 
			
		||||
            return f'{obj.connected_endpoint._meta.app_label}.{obj.connected_endpoint._meta.model_name}'
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @swagger_serializer_method(serializer_or_field=serializers.DictField)
 | 
			
		||||
@@ -44,7 +43,7 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
 | 
			
		||||
        Return the appropriate serializer for the type of connected object.
 | 
			
		||||
        """
 | 
			
		||||
        if obj.path is not None:
 | 
			
		||||
            serializer = get_serializer_for_model(obj.path.destination, prefix='Nested')
 | 
			
		||||
            serializer = get_serializer_for_model(obj.connected_endpoint, prefix='Nested')
 | 
			
		||||
            context = {'request': self.context['request']}
 | 
			
		||||
            return serializer(obj.path.destination, context=context).data
 | 
			
		||||
        return None
 | 
			
		||||
 
 | 
			
		||||
@@ -470,37 +470,31 @@ class DeviceViewSet(CustomFieldModelViewSet):
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class ConsolePortViewSet(PathEndpointMixin, ModelViewSet):
 | 
			
		||||
    queryset = ConsolePort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
 | 
			
		||||
    queryset = ConsolePort.objects.prefetch_related('device', '_path', 'cable', 'tags')
 | 
			
		||||
    serializer_class = serializers.ConsolePortSerializer
 | 
			
		||||
    filterset_class = filters.ConsolePortFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConsoleServerPortViewSet(PathEndpointMixin, ModelViewSet):
 | 
			
		||||
    queryset = ConsoleServerPort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
 | 
			
		||||
    queryset = ConsoleServerPort.objects.prefetch_related('device', '_path', 'cable', 'tags')
 | 
			
		||||
    serializer_class = serializers.ConsoleServerPortSerializer
 | 
			
		||||
    filterset_class = filters.ConsoleServerPortFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PowerPortViewSet(PathEndpointMixin, ModelViewSet):
 | 
			
		||||
    queryset = PowerPort.objects.prefetch_related(
 | 
			
		||||
        'device', '_connected_poweroutlet__device', '_connected_powerfeed', 'cable', 'tags'
 | 
			
		||||
    )
 | 
			
		||||
    queryset = PowerPort.objects.prefetch_related('device', '_path', 'cable', 'tags')
 | 
			
		||||
    serializer_class = serializers.PowerPortSerializer
 | 
			
		||||
    filterset_class = filters.PowerPortFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PowerOutletViewSet(PathEndpointMixin, ModelViewSet):
 | 
			
		||||
    queryset = PowerOutlet.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
 | 
			
		||||
    queryset = PowerOutlet.objects.prefetch_related('device', '_path', 'cable', 'tags')
 | 
			
		||||
    serializer_class = serializers.PowerOutletSerializer
 | 
			
		||||
    filterset_class = filters.PowerOutletFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InterfaceViewSet(PathEndpointMixin, ModelViewSet):
 | 
			
		||||
    queryset = Interface.objects.prefetch_related(
 | 
			
		||||
        'device', '_connected_interface', '_connected_circuittermination', 'cable', 'ip_addresses', 'tags'
 | 
			
		||||
    ).filter(
 | 
			
		||||
        device__isnull=False
 | 
			
		||||
    )
 | 
			
		||||
    queryset = Interface.objects.prefetch_related('device', '_path', 'cable', 'ip_addresses', 'tags')
 | 
			
		||||
    serializer_class = serializers.InterfaceSerializer
 | 
			
		||||
    filterset_class = filters.InterfaceFilterSet
 | 
			
		||||
 | 
			
		||||
@@ -534,32 +528,26 @@ class InventoryItemViewSet(ModelViewSet):
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
 | 
			
		||||
    queryset = ConsolePort.objects.prefetch_related(
 | 
			
		||||
        'device', 'connected_endpoint__device'
 | 
			
		||||
    ).filter(
 | 
			
		||||
        connected_endpoint__isnull=False
 | 
			
		||||
    queryset = ConsolePort.objects.prefetch_related('device', '_path').filter(
 | 
			
		||||
        _path__destination_id__isnull=False
 | 
			
		||||
    )
 | 
			
		||||
    serializer_class = serializers.ConsolePortSerializer
 | 
			
		||||
    filterset_class = filters.ConsoleConnectionFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
 | 
			
		||||
    queryset = PowerPort.objects.prefetch_related(
 | 
			
		||||
        'device', 'connected_endpoint__device'
 | 
			
		||||
    ).filter(
 | 
			
		||||
        _connected_poweroutlet__isnull=False
 | 
			
		||||
    queryset = PowerPort.objects.prefetch_related('device', '_path').filter(
 | 
			
		||||
        _path__destination_id__isnull=False
 | 
			
		||||
    )
 | 
			
		||||
    serializer_class = serializers.PowerPortSerializer
 | 
			
		||||
    filterset_class = filters.PowerConnectionFilterSet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
 | 
			
		||||
    queryset = Interface.objects.prefetch_related(
 | 
			
		||||
        'device', '_connected_interface__device'
 | 
			
		||||
    ).filter(
 | 
			
		||||
    queryset = Interface.objects.prefetch_related('device', '_path').filter(
 | 
			
		||||
        # Avoid duplicate connections by only selecting the lower PK in a connected pair
 | 
			
		||||
        _connected_interface__isnull=False,
 | 
			
		||||
        pk__lt=F('_connected_interface')
 | 
			
		||||
        _path__destination_id__isnull=False,
 | 
			
		||||
        pk__lt=F('_path__destination_id')
 | 
			
		||||
    )
 | 
			
		||||
    serializer_class = serializers.InterfaceConnectionSerializer
 | 
			
		||||
    filterset_class = filters.InterfaceConnectionFilterSet
 | 
			
		||||
@@ -664,7 +652,7 @@ class ConnectedDeviceViewSet(ViewSet):
 | 
			
		||||
            device__name=peer_device_name,
 | 
			
		||||
            name=peer_interface_name
 | 
			
		||||
        )
 | 
			
		||||
        local_interface = peer_interface._connected_interface
 | 
			
		||||
        local_interface = peer_interface.connected_endpoint
 | 
			
		||||
 | 
			
		||||
        if local_interface is None:
 | 
			
		||||
            return Response()
 | 
			
		||||
 
 | 
			
		||||
@@ -1171,18 +1171,19 @@ class ConsoleConnectionFilterSet(BaseFilterSet):
 | 
			
		||||
        model = ConsolePort
 | 
			
		||||
        fields = ['name', 'connection_status']
 | 
			
		||||
 | 
			
		||||
    def filter_site(self, queryset, name, value):
 | 
			
		||||
        if not value.strip():
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(connected_endpoint__device__site__slug=value)
 | 
			
		||||
 | 
			
		||||
    def filter_device(self, queryset, name, value):
 | 
			
		||||
        if not value:
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(
 | 
			
		||||
            Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
            Q(**{'connected_endpoint__{}__in'.format(name): value})
 | 
			
		||||
        )
 | 
			
		||||
    # TODO: Fix filters
 | 
			
		||||
    # def filter_site(self, queryset, name, value):
 | 
			
		||||
    #     if not value.strip():
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(connected_endpoint__device__site__slug=value)
 | 
			
		||||
    #
 | 
			
		||||
    # def filter_device(self, queryset, name, value):
 | 
			
		||||
    #     if not value:
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(
 | 
			
		||||
    #         Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
    #         Q(**{'connected_endpoint__{}__in'.format(name): value})
 | 
			
		||||
    #     )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PowerConnectionFilterSet(BaseFilterSet):
 | 
			
		||||
@@ -1202,18 +1203,19 @@ class PowerConnectionFilterSet(BaseFilterSet):
 | 
			
		||||
        model = PowerPort
 | 
			
		||||
        fields = ['name', 'connection_status']
 | 
			
		||||
 | 
			
		||||
    def filter_site(self, queryset, name, value):
 | 
			
		||||
        if not value.strip():
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(_connected_poweroutlet__device__site__slug=value)
 | 
			
		||||
 | 
			
		||||
    def filter_device(self, queryset, name, value):
 | 
			
		||||
        if not value:
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(
 | 
			
		||||
            Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
            Q(**{'_connected_poweroutlet__{}__in'.format(name): value})
 | 
			
		||||
        )
 | 
			
		||||
    # TODO: Fix filters
 | 
			
		||||
    # def filter_site(self, queryset, name, value):
 | 
			
		||||
    #     if not value.strip():
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(_connected_poweroutlet__device__site__slug=value)
 | 
			
		||||
    #
 | 
			
		||||
    # def filter_device(self, queryset, name, value):
 | 
			
		||||
    #     if not value:
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(
 | 
			
		||||
    #         Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
    #         Q(**{'_connected_poweroutlet__{}__in'.format(name): value})
 | 
			
		||||
    #     )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InterfaceConnectionFilterSet(BaseFilterSet):
 | 
			
		||||
@@ -1233,21 +1235,22 @@ class InterfaceConnectionFilterSet(BaseFilterSet):
 | 
			
		||||
        model = Interface
 | 
			
		||||
        fields = ['connection_status']
 | 
			
		||||
 | 
			
		||||
    def filter_site(self, queryset, name, value):
 | 
			
		||||
        if not value.strip():
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(
 | 
			
		||||
            Q(device__site__slug=value) |
 | 
			
		||||
            Q(_connected_interface__device__site__slug=value)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def filter_device(self, queryset, name, value):
 | 
			
		||||
        if not value:
 | 
			
		||||
            return queryset
 | 
			
		||||
        return queryset.filter(
 | 
			
		||||
            Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
            Q(**{'_connected_interface__{}__in'.format(name): value})
 | 
			
		||||
        )
 | 
			
		||||
    # TODO: Fix filters
 | 
			
		||||
    # def filter_site(self, queryset, name, value):
 | 
			
		||||
    #     if not value.strip():
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(
 | 
			
		||||
    #         Q(device__site__slug=value) |
 | 
			
		||||
    #         Q(_connected_interface__device__site__slug=value)
 | 
			
		||||
    #     )
 | 
			
		||||
    #
 | 
			
		||||
    # def filter_device(self, queryset, name, value):
 | 
			
		||||
    #     if not value:
 | 
			
		||||
    #         return queryset
 | 
			
		||||
    #     return queryset.filter(
 | 
			
		||||
    #         Q(**{'{}__in'.format(name): value}) |
 | 
			
		||||
    #         Q(**{'_connected_interface__{}__in'.format(name): value})
 | 
			
		||||
    #     )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PowerPanelFilterSet(BaseFilterSet):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								netbox/dcim/migrations/0121_drop_connected_endpoint.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								netbox/dcim/migrations/0121_drop_connected_endpoint.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
# Generated by Django 3.1 on 2020-10-05 13:56
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('dcim', '0120_cablepath'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='consoleport',
 | 
			
		||||
            name='connected_endpoint',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='interface',
 | 
			
		||||
            name='_connected_circuittermination',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='interface',
 | 
			
		||||
            name='_connected_interface',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='powerfeed',
 | 
			
		||||
            name='connected_endpoint',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='powerport',
 | 
			
		||||
            name='_connected_powerfeed',
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name='powerport',
 | 
			
		||||
            name='_connected_poweroutlet',
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -150,6 +150,15 @@ class PathEndpoint(models.Model):
 | 
			
		||||
        # Return the path as a list of three-tuples (A termination, cable, B termination)
 | 
			
		||||
        return list(zip(*[iter(path)] * 3))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def connected_endpoint(self):
 | 
			
		||||
        """
 | 
			
		||||
        Caching accessor for the attached CablePath's destination (if any)
 | 
			
		||||
        """
 | 
			
		||||
        if not hasattr(self, '_connected_endpoint'):
 | 
			
		||||
            self._connected_endpoint = self._path.destination if self._path else None
 | 
			
		||||
        return self._connected_endpoint
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Console ports
 | 
			
		||||
@@ -166,13 +175,6 @@ class ConsolePort(CableTermination, PathEndpoint, ComponentModel):
 | 
			
		||||
        blank=True,
 | 
			
		||||
        help_text='Physical port type'
 | 
			
		||||
    )
 | 
			
		||||
    connected_endpoint = models.OneToOneField(
 | 
			
		||||
        to='dcim.ConsoleServerPort',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='connected_endpoint',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connection_status = models.BooleanField(
 | 
			
		||||
        choices=CONNECTION_STATUS_CHOICES,
 | 
			
		||||
        blank=True,
 | 
			
		||||
@@ -267,20 +269,6 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
 | 
			
		||||
        validators=[MinValueValidator(1)],
 | 
			
		||||
        help_text="Allocated power draw (watts)"
 | 
			
		||||
    )
 | 
			
		||||
    _connected_poweroutlet = models.OneToOneField(
 | 
			
		||||
        to='dcim.PowerOutlet',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='connected_endpoint',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    _connected_powerfeed = models.OneToOneField(
 | 
			
		||||
        to='dcim.PowerFeed',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connection_status = models.BooleanField(
 | 
			
		||||
        choices=CONNECTION_STATUS_CHOICES,
 | 
			
		||||
        blank=True,
 | 
			
		||||
@@ -308,43 +296,6 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel):
 | 
			
		||||
            self.description,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def connected_endpoint(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return the connected PowerOutlet, if it exists, or the connected PowerFeed, if it exists. We have to check for
 | 
			
		||||
        ObjectDoesNotExist in case the referenced object has been deleted from the database.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            if self._connected_poweroutlet:
 | 
			
		||||
                return self._connected_poweroutlet
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        try:
 | 
			
		||||
            if self._connected_powerfeed:
 | 
			
		||||
                return self._connected_powerfeed
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @connected_endpoint.setter
 | 
			
		||||
    def connected_endpoint(self, value):
 | 
			
		||||
        # TODO: Fix circular import
 | 
			
		||||
        from . import PowerFeed
 | 
			
		||||
 | 
			
		||||
        if value is None:
 | 
			
		||||
            self._connected_poweroutlet = None
 | 
			
		||||
            self._connected_powerfeed = None
 | 
			
		||||
        elif isinstance(value, PowerOutlet):
 | 
			
		||||
            self._connected_poweroutlet = value
 | 
			
		||||
            self._connected_powerfeed = None
 | 
			
		||||
        elif isinstance(value, PowerFeed):
 | 
			
		||||
            self._connected_poweroutlet = None
 | 
			
		||||
            self._connected_powerfeed = value
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Connected endpoint must be a PowerOutlet or PowerFeed, not {}.".format(type(value))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def get_power_draw(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return the allocated and maximum power draw (in VA) and child PowerOutlet count for this PowerPort.
 | 
			
		||||
@@ -497,20 +448,6 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
 | 
			
		||||
        max_length=100,
 | 
			
		||||
        blank=True
 | 
			
		||||
    )
 | 
			
		||||
    _connected_interface = models.OneToOneField(
 | 
			
		||||
        to='self',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    _connected_circuittermination = models.OneToOneField(
 | 
			
		||||
        to='circuits.CircuitTermination',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connection_status = models.BooleanField(
 | 
			
		||||
        choices=CONNECTION_STATUS_CHOICES,
 | 
			
		||||
        blank=True,
 | 
			
		||||
@@ -631,42 +568,6 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface):
 | 
			
		||||
 | 
			
		||||
        return super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def connected_endpoint(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return the connected Interface, if it exists, or the connected CircuitTermination, if it exists. We have to
 | 
			
		||||
        check for ObjectDoesNotExist in case the referenced object has been deleted from the database.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            if self._connected_interface:
 | 
			
		||||
                return self._connected_interface
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        try:
 | 
			
		||||
            if self._connected_circuittermination:
 | 
			
		||||
                return self._connected_circuittermination
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @connected_endpoint.setter
 | 
			
		||||
    def connected_endpoint(self, value):
 | 
			
		||||
        from circuits.models import CircuitTermination
 | 
			
		||||
 | 
			
		||||
        if value is None:
 | 
			
		||||
            self._connected_interface = None
 | 
			
		||||
            self._connected_circuittermination = None
 | 
			
		||||
        elif isinstance(value, Interface):
 | 
			
		||||
            self._connected_interface = value
 | 
			
		||||
            self._connected_circuittermination = None
 | 
			
		||||
        elif isinstance(value, CircuitTermination):
 | 
			
		||||
            self._connected_interface = None
 | 
			
		||||
            self._connected_circuittermination = value
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Connected endpoint must be an Interface or CircuitTermination, not {}.".format(type(value))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def parent(self):
 | 
			
		||||
        return self.device
 | 
			
		||||
 
 | 
			
		||||
@@ -88,13 +88,6 @@ class PowerFeed(ChangeLoggedModel, PathEndpoint, CableTermination, CustomFieldMo
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connected_endpoint = models.OneToOneField(
 | 
			
		||||
        to='dcim.PowerPort',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    connection_status = models.BooleanField(
 | 
			
		||||
        choices=CONNECTION_STATUS_CHOICES,
 | 
			
		||||
        blank=True,
 | 
			
		||||
 
 | 
			
		||||
@@ -1122,10 +1122,8 @@ class DeviceLLDPNeighborsView(ObjectView):
 | 
			
		||||
    def get(self, request, pk):
 | 
			
		||||
 | 
			
		||||
        device = get_object_or_404(self.queryset, pk=pk)
 | 
			
		||||
        interfaces = device.vc_interfaces.restrict(request.user, 'view').exclude(
 | 
			
		||||
        interfaces = device.vc_interfaces.restrict(request.user, 'view').prefetch_related('_path').exclude(
 | 
			
		||||
            type__in=NONCONNECTABLE_IFACE_TYPES
 | 
			
		||||
        ).prefetch_related(
 | 
			
		||||
            '_connected_interface__device'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return render(request, 'dcim/device_lldp_neighbors.html', {
 | 
			
		||||
@@ -1483,8 +1481,6 @@ class InterfaceView(ObjectView):
 | 
			
		||||
 | 
			
		||||
        return render(request, 'dcim/interface.html', {
 | 
			
		||||
            'instance': interface,
 | 
			
		||||
            'connected_interface': interface._connected_interface,
 | 
			
		||||
            'connected_circuittermination': interface._connected_circuittermination,
 | 
			
		||||
            'ipaddress_table': ipaddress_table,
 | 
			
		||||
            'vlan_table': vlan_table,
 | 
			
		||||
        })
 | 
			
		||||
@@ -2137,7 +2133,7 @@ class InterfaceConnectionsListView(ObjectListView):
 | 
			
		||||
    ).filter(
 | 
			
		||||
        # Avoid duplicate connections by only selecting the lower PK in a connected pair
 | 
			
		||||
        _path__isnull=False,
 | 
			
		||||
        pk__lt=F('_connected_interface')
 | 
			
		||||
        pk__lt=F('_path__destination_id')
 | 
			
		||||
    ).order_by('device')
 | 
			
		||||
    filterset = filters.InterfaceConnectionFilterSet
 | 
			
		||||
    filterset_form = forms.InterfaceConnectionFilterForm
 | 
			
		||||
 
 | 
			
		||||
@@ -190,15 +190,15 @@ class HomeView(View):
 | 
			
		||||
 | 
			
		||||
    def get(self, request):
 | 
			
		||||
 | 
			
		||||
        connected_consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(
 | 
			
		||||
            connected_endpoint__isnull=False
 | 
			
		||||
        connected_consoleports = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
 | 
			
		||||
            _path__destination_id__isnull=False
 | 
			
		||||
        )
 | 
			
		||||
        connected_powerports = PowerPort.objects.restrict(request.user, 'view').filter(
 | 
			
		||||
            _connected_poweroutlet__isnull=False
 | 
			
		||||
        connected_powerports = PowerPort.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
 | 
			
		||||
            _path__destination_id__isnull=False
 | 
			
		||||
        )
 | 
			
		||||
        connected_interfaces = Interface.objects.restrict(request.user, 'view').filter(
 | 
			
		||||
            _connected_interface__isnull=False,
 | 
			
		||||
            pk__lt=F('_connected_interface')
 | 
			
		||||
        connected_interfaces = Interface.objects.restrict(request.user, 'view').prefetch_related('_path').filter(
 | 
			
		||||
            _path__destination_id__isnull=False,
 | 
			
		||||
            pk__lt=F('_path__destination_id')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Report Results
 | 
			
		||||
 
 | 
			
		||||
@@ -77,61 +77,63 @@
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% if instance.cable %}
 | 
			
		||||
                        <table class="table table-hover panel-body attr-table">
 | 
			
		||||
                            {% if connected_interface %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>Device</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <a href="{{ connected_interface.device.get_absolute_url }}">{{ connected_interface.device }}</a>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>Name</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <a href="{{ connected_interface.get_absolute_url }}">{{ connected_interface.name }}</a>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>Type</td>
 | 
			
		||||
                                    <td>{{ connected_interface.get_type_display }}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>Enabled</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% if connected_interface.enabled %}
 | 
			
		||||
                                            <span class="text-success"><i class="fa fa-check"></i></span>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <span class="text-danger"><i class="fa fa-close"></i></span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>LAG</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% if connected_interface.lag%}
 | 
			
		||||
                                            <a href="{{ connected_interface.lag.get_absolute_url }}">{{ connected_interface.lag }}</a>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <span class="text-muted">None</span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>Description</td>
 | 
			
		||||
                                    <td>{{ connected_interface.description|placeholder }}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>MTU</td>
 | 
			
		||||
                                    <td>{{ connected_interface.mtu|placeholder }}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>MAC Address</td>
 | 
			
		||||
                                    <td>{{ connected_interface.mac_address|placeholder }}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>802.1Q Mode</td>
 | 
			
		||||
                                    <td>{{ connected_interface.get_mode_display }}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% elif connected_circuittermination %}
 | 
			
		||||
                                {% with ct=connected_circuittermination %}
 | 
			
		||||
                            {% if instance.connected_endpoint.device %}
 | 
			
		||||
                                {% with iface=instance.connected_endpoint %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Device</td>
 | 
			
		||||
                                        <td>
 | 
			
		||||
                                            <a href="{{ iface.device.get_absolute_url }}">{{ iface.device }}</a>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Name</td>
 | 
			
		||||
                                        <td>
 | 
			
		||||
                                            <a href="{{ iface.get_absolute_url }}">{{ iface.name }}</a>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Type</td>
 | 
			
		||||
                                        <td>{{ iface.get_type_display }}</td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Enabled</td>
 | 
			
		||||
                                        <td>
 | 
			
		||||
                                            {% if iface.enabled %}
 | 
			
		||||
                                                <span class="text-success"><i class="fa fa-check"></i></span>
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                <span class="text-danger"><i class="fa fa-close"></i></span>
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>LAG</td>
 | 
			
		||||
                                        <td>
 | 
			
		||||
                                            {% if iface.lag%}
 | 
			
		||||
                                                <a href="{{ iface.lag.get_absolute_url }}">{{ iface.lag }}</a>
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                <span class="text-muted">None</span>
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Description</td>
 | 
			
		||||
                                        <td>{{ iface.description|placeholder }}</td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>MTU</td>
 | 
			
		||||
                                        <td>{{ iface.mtu|placeholder }}</td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>MAC Address</td>
 | 
			
		||||
                                        <td>{{ iface.mac_address|placeholder }}</td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>802.1Q Mode</td>
 | 
			
		||||
                                        <td>{{ iface.get_mode_display }}</td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                {% endwith %}
 | 
			
		||||
                            {% elif instance.connected_endpoint.circuit %}
 | 
			
		||||
                                {% with ct=instance.connected_endpoint %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>Provider</td>
 | 
			
		||||
                                        <td><a href="{{ ct.circuit.provider.get_absolute_url }}">{{ ct.circuit.provider }}</a></td>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user