Sfoglia il codice sorgente

Fixes #6289: Fix assignment of VC member interfaces to LAG interfaces

jeremystretch 4 anni fa
parent
commit
9a588231c5

+ 1 - 0
docs/release-notes/version-2.11.md

@@ -17,6 +17,7 @@
 * [#6254](https://github.com/netbox-community/netbox/issues/6254) - Disable ordering of space column in racks table
 * [#6258](https://github.com/netbox-community/netbox/issues/6258) - Fix parent assignment for SiteGroup API serializer
 * [#6267](https://github.com/netbox-community/netbox/issues/6267) - Fix cable tracing API endpoint for circuit terminations
+* [#6289](https://github.com/netbox-community/netbox/issues/6289) - Fix assignment of VC member interfaces to LAG interfaces
 
 ---
 

+ 2 - 2
netbox/dcim/filters.py

@@ -984,7 +984,7 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminati
             devices = Device.objects.filter(**{'{}__in'.format(name): value})
             vc_interface_ids = []
             for device in devices:
-                vc_interface_ids.extend(device.vc_interfaces.values_list('id', flat=True))
+                vc_interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
             return queryset.filter(pk__in=vc_interface_ids)
         except Device.DoesNotExist:
             return queryset.none()
@@ -995,7 +995,7 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminati
         try:
             devices = Device.objects.filter(pk__in=id_list)
             for device in devices:
-                vc_interface_ids += device.vc_interfaces.values_list('id', flat=True)
+                vc_interface_ids += device.vc_interfaces().values_list('id', flat=True)
             return queryset.filter(pk__in=vc_interface_ids)
         except Device.DoesNotExist:
             return queryset.none()

+ 1 - 1
netbox/dcim/forms.py

@@ -2153,7 +2153,7 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
                 ip_choices = [(None, '---------')]
 
                 # Gather PKs of all interfaces belonging to this Device or a peer VirtualChassis member
-                interface_ids = self.instance.vc_interfaces.values_list('pk', flat=True)
+                interface_ids = self.instance.vc_interfaces().values_list('pk', flat=True)
 
                 # Collect interface IPs
                 interface_ips = IPAddress.objects.filter(

+ 11 - 4
netbox/dcim/models/devices.py

@@ -716,7 +716,7 @@ class Device(PrimaryModel, ConfigContextModel):
                 pass
 
         # Validate primary IP addresses
-        vc_interfaces = self.vc_interfaces.all()
+        vc_interfaces = self.vc_interfaces()
         if self.primary_ip4:
             if self.primary_ip4.family != 4:
                 raise ValidationError({
@@ -854,20 +854,27 @@ class Device(PrimaryModel, ConfigContextModel):
         else:
             return None
 
+    @property
+    def interfaces_count(self):
+        if self.virtual_chassis and self.virtual_chassis.master == self:
+            return self.vc_interfaces().count()
+        return self.interfaces.count()
+
     def get_vc_master(self):
         """
         If this Device is a VirtualChassis member, return the VC master. Otherwise, return None.
         """
         return self.virtual_chassis.master if self.virtual_chassis else None
 
-    @property
-    def vc_interfaces(self):
+    def vc_interfaces(self, if_master=False):
         """
         Return a QuerySet matching all Interfaces assigned to this Device or, if this Device is a VC master, to another
         Device belonging to the same VirtualChassis.
+
+        :param if_master: If True, return VC member interfaces only if this Device is the VC master.
         """
         filter = Q(device=self)
-        if self.virtual_chassis and self.virtual_chassis.master == self:
+        if self.virtual_chassis and (not if_master or self.virtual_chassis.master == self):
             filter |= Q(device__virtual_chassis=self.virtual_chassis, mgmt_only=False)
         return Interface.objects.filter(filter)
 

+ 2 - 2
netbox/dcim/views.py

@@ -1405,7 +1405,7 @@ class DeviceInterfacesView(generic.ObjectView):
     template_name = 'dcim/device/interfaces.html'
 
     def get_extra_context(self, request, instance):
-        interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
+        interfaces = instance.vc_interfaces(if_master=True).restrict(request.user, 'view').prefetch_related(
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
             Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
             'lag', 'cable', '_path__destination', 'tags',
@@ -1527,7 +1527,7 @@ class DeviceLLDPNeighborsView(generic.ObjectView):
     template_name = 'dcim/device/lldp_neighbors.html'
 
     def get_extra_context(self, request, instance):
-        interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
+        interfaces = instance.vc_interfaces(if_master=True).restrict(request.user, 'view').prefetch_related(
             '_path__destination'
         ).exclude(
             type__in=NONCONNECTABLE_IFACE_TYPES

+ 1 - 1
netbox/ipam/filters.py

@@ -515,7 +515,7 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilter
             return queryset.none()
         interface_ids = []
         for device in devices:
-            interface_ids.extend(device.vc_interfaces.values_list('id', flat=True))
+            interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
         return queryset.filter(
             interface__in=interface_ids
         )

+ 1 - 1
netbox/ipam/forms.py

@@ -1561,7 +1561,7 @@ class ServiceForm(BootstrapMixin, CustomFieldModelForm):
         # Limit IP address choices to those assigned to interfaces of the parent device/VM
         if self.instance.device:
             self.fields['ipaddresses'].queryset = IPAddress.objects.filter(
-                interface__in=self.instance.device.vc_interfaces.values_list('id', flat=True)
+                interface__in=self.instance.device.vc_interfaces().values_list('id', flat=True)
             )
         elif self.instance.virtual_machine:
             self.fields['ipaddresses'].queryset = IPAddress.objects.filter(

+ 1 - 1
netbox/templates/dcim/device/base.html

@@ -68,7 +68,7 @@
         <li role="presentation" {% if active_tab == 'device' %} class="active"{% endif %}>
             <a href="{% url 'dcim:device' pk=object.pk %}">Device</a>
         </li>
-        {% with interface_count=object.vc_interfaces.count %}
+        {% with interface_count=object.interfaces_count %}
             {% if interface_count %}
                 <li role="presentation" {% if active_tab == 'interfaces' %} class="active"{% endif %}>
                     <a href="{% url 'dcim:device_interfaces' pk=object.pk %}">Interfaces {% badge interface_count %}</a>