L2VPNTerminationImportForm bulk update validation fails when interface/vlan fields omitted #541

Closed
opened 2026-04-05 16:41:35 +02:00 by MrUnknownDE · 0 comments
Owner

Originally created by @adionit7 on 1/17/2026

NetBox Version

v4.2+ (tested on main branch, issue exists since the validation was added)

Python Version

3.12

Steps to Reproduce

  1. Create prerequisite objects:

    • Create a Site and Device with at least 3 interfaces
  2. Create two L2VPNs:

    • L2VPN-A: name="L2VPN Source", type=VXLAN
    • L2VPN-B: name="L2VPN Target", type=VXLAN
  3. Create 3 L2VPN Terminations on L2VPN-A:

    • Termination 1 (id=1): L2VPN-A, assigned to interface eth0
    • Termination 2 (id=2): L2VPN-A, assigned to interface eth1
    • Termination 3 (id=3): L2VPN-A, assigned to interface eth2
  4. Prepare CSV for bulk update (moving terminations to L2VPN-B):
    id,l2vpn
    1,L2VPN Target
    2,L2VPN Target
    3,L2VPN Target

    Note: interface and vlan columns are intentionally omitted - existing assignments should be preserved.

  5. Import via: VPN → L2VPN Terminations → Import

Expected Behavior

The bulk update should succeed, moving all three terminations to L2VPN-B while preserving their existing interface assignments.

Observed Behavior

Validation error: "Each termination must specify either an interface or a VLAN."

Root Cause

In netbox/vpn/forms/bulk_import.py, line 340:

if not self.instance and not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')):

Should be:

if not self.instance.pk and not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')):

This matches the pattern used in RackImportForm (dcim/forms/bulk_import.py:332).

Additional Context

  • The test at vpn/tests/test_views.py:683-685 is currently skipped with a TODO: "Fix L2VPNTerminationImportForm validation to support bulk updates"
  • The test setup already creates the necessary data structure (csv_update_data at line 674-679)
*Originally created by @adionit7 on 1/17/2026* ### NetBox Version v4.2+ (tested on main branch, issue exists since the validation was added) ### Python Version 3.12 ### Steps to Reproduce 1. **Create prerequisite objects:** - Create a Site and Device with at least 3 interfaces 2. **Create two L2VPNs:** - L2VPN-A: name="L2VPN Source", type=VXLAN - L2VPN-B: name="L2VPN Target", type=VXLAN 3. **Create 3 L2VPN Terminations on L2VPN-A:** - Termination 1 (id=1): L2VPN-A, assigned to interface eth0 - Termination 2 (id=2): L2VPN-A, assigned to interface eth1 - Termination 3 (id=3): L2VPN-A, assigned to interface eth2 4. **Prepare CSV for bulk update (moving terminations to L2VPN-B):** id,l2vpn 1,L2VPN Target 2,L2VPN Target 3,L2VPN Target Note: `interface` and `vlan` columns are intentionally omitted - existing assignments should be preserved. 5. **Import via:** VPN → L2VPN Terminations → Import ### Expected Behavior The bulk update should succeed, moving all three terminations to L2VPN-B while preserving their existing interface assignments. ### Observed Behavior Validation error: "Each termination must specify either an interface or a VLAN." ### Root Cause In `netbox/vpn/forms/bulk_import.py`, line 340: `if not self.instance and not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')):` Should be: `if not self.instance.pk and not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')):` This matches the pattern used in `RackImportForm` (dcim/forms/bulk_import.py:332). ## Additional Context - The test at `vpn/tests/test_views.py:683-685` is currently skipped with a TODO: "Fix L2VPNTerminationImportForm validation to support bulk updates" - The test setup already creates the necessary data structure (csv_update_data at line 674-679)
MrUnknownDE added the type: bugnetboxtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetbox labels 2026-04-05 16:41:46 +02:00
Sign in to join this conversation.
No Label netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github/netbox#541