From 29a46d2c18091b48d9d99a51ba31e62b1c8063c7 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 6 Jul 2022 12:46:26 -0400 Subject: [PATCH] Add support for VM interface termination via bulk import --- netbox/ipam/forms/bulk_import.py | 37 ++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index 5b94f6c8e..b8dd1c54c 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -453,49 +453,58 @@ class L2VPNTerminationCSVForm(NetBoxModelCSVForm): to_field_name='name', label='L2VPN', ) - device = CSVModelChoiceField( queryset=Device.objects.all(), required=False, to_field_name='name', - help_text='Required if assigned to a interface' + help_text='Parent device (for interface)' ) - - interface = CSVModelChoiceField( - queryset=Interface.objects.all(), + virtual_machine = CSVModelChoiceField( + queryset=VirtualMachine.objects.all(), required=False, to_field_name='name', - help_text='Required if not assigned to a vlan' + help_text='Parent virtual machine (for interface)' + ) + interface = CSVModelChoiceField( + queryset=Interface.objects.none(), # Can also refer to VMInterface + required=False, + to_field_name='name', + help_text='Assigned interface (device or VM)' ) - vlan = CSVModelChoiceField( queryset=VLAN.objects.all(), required=False, to_field_name='name', - help_text='Required if not assigned to a interface' + help_text='Assigned VLAN' ) class Meta: model = L2VPNTermination - fields = ('l2vpn', 'device', 'interface', 'vlan') + fields = ('l2vpn', 'device', 'virtual_machine', 'interface', 'vlan') def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) + if data: - # Limit interface queryset by assigned device + + # Limit interface queryset by device or VM if data.get('device'): self.fields['interface'].queryset = Interface.objects.filter( **{f"device__{self.fields['device'].to_field_name}": data['device']} ) + elif data.get('virtual_machine'): + self.fields['interface'].queryset = VMInterface.objects.filter( + **{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']} + ) def clean(self): super().clean() + if self.cleaned_data.get('device') and self.cleaned_data.get('virtual_machine'): + raise ValidationError('Cannot import device and VM interface terminations simultaneously.') if not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')): - raise ValidationError('You must have either a interface or a VLAN') - + raise ValidationError('Each termination must specify either an interface or a VLAN.') if self.cleaned_data.get('interface') and self.cleaned_data.get('vlan'): - raise ValidationError('Cannot assign both a interface and vlan') + raise ValidationError('Cannot assign both an interface and a VLAN.') - # Set Assigned Object self.instance.assigned_object = self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')