Arthur il y a 18 heures
Parent
commit
c12c6cedda

+ 7 - 0
netbox/dcim/forms/model_forms.py

@@ -2230,6 +2230,13 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
         if 'lag_hash_policy' not in self.fields:
             self.instance.lag_hash_policy = None
 
+        # Clear LAG attributes left over when changing a LAG interface to another type
+        if self.instance.is_lag and self.cleaned_data.get('type') != InterfaceTypeChoices.TYPE_LAG:
+            self.cleaned_data['lag_mode'] = None
+            self.instance.lag_hash_policy = None
+            if 'lag_hash_policy' in self.fields:
+                self.cleaned_data['lag_hash_policy'] = None
+
 
 class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
     type = ChoiceField(

+ 2 - 2
netbox/dcim/migrations/0253_interface_lag_hash_policy_interface_lag_mode.py → netbox/dcim/migrations/0254_interface_lag_hash_policy_interface_lag_mode.py

@@ -1,4 +1,4 @@
-# Generated by Django 6.1.3 on 2026-11-12 19:28
+# Generated by Django 6.1.3 on 2026-11-13 22:03
 
 from django.db import migrations, models
 
@@ -6,7 +6,7 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 
     dependencies = [
-        ("dcim", "0252_generated_fields"),
+        ("dcim", "0253_macblock"),
     ]
 
     operations = [

+ 37 - 0
netbox/dcim/tests/test_forms.py

@@ -1328,6 +1328,43 @@ class InterfaceTestCase(TestCase):
         self.assertEqual(lag.lag_mode, InterfaceLAGModeChoices.MODE_ACTIVE_BACKUP)
         self.assertIsNone(lag.lag_hash_policy)
 
+    def test_lag_attributes_cleared_when_changing_from_lag_type(self):
+        """Changing a LAG interface to another type clears its LAG mode and hash policy."""
+        lag = Interface.objects.create(
+            device=self.device,
+            name='bond0',
+            type=InterfaceTypeChoices.TYPE_LAG,
+            lag_mode=InterfaceLAGModeChoices.MODE_LACP,
+            lag_hash_policy=InterfaceLAGHashPolicyChoices.POLICY_LAYER3_4,
+        )
+        data = {
+            'device': self.device.pk,
+            'name': 'bond0',
+            'type': InterfaceTypeChoices.TYPE_1GE_FIXED,
+            'lag_mode': InterfaceLAGModeChoices.MODE_LACP,
+            'lag_hash_policy': InterfaceLAGHashPolicyChoices.POLICY_LAYER3_4,
+        }
+        form = InterfaceForm(data, instance=lag)
+        self.assertTrue(form.is_valid(), form.errors)
+        form.save()
+
+        lag.refresh_from_db()
+        self.assertEqual(lag.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
+        self.assertIsNone(lag.lag_mode)
+        self.assertIsNone(lag.lag_hash_policy)
+
+    def test_lag_mode_on_non_lag_interface_rejected(self):
+        """A LAG mode chosen for a non-LAG interface is rejected rather than silently dropped."""
+        data = {
+            'device': self.device.pk,
+            'name': 'eth9',
+            'type': InterfaceTypeChoices.TYPE_1GE_FIXED,
+            'lag_mode': InterfaceLAGModeChoices.MODE_LACP,
+        }
+        form = InterfaceForm(data)
+        self.assertFalse(form.is_valid())
+        self.assertIn('lag_mode', form.errors)
+
     def test_child_interface_preselects_every_parent_vdc(self):
         """A child interface's form pre-selects every virtual device context of its parent."""
         vdcs = (