Closes #19499 - Add WirelessLink Bulk Import Support by Device and Interface Names (#19679)

This commit is contained in:
Martin Hauser 2025-06-16 20:19:56 +02:00 committed by GitHub
parent a9af541e81
commit e6c1cebd34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 15 deletions

View File

@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
from dcim.forms.mixins import ScopedImportForm from dcim.forms.mixins import ScopedImportForm
from dcim.models import Interface from dcim.models import Device, Interface, Site
from ipam.models import VLAN from ipam.models import VLAN
from netbox.choices import * from netbox.choices import *
from netbox.forms import NetBoxModelImportForm from netbox.forms import NetBoxModelImportForm
@ -85,18 +85,53 @@ class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
class WirelessLinkImportForm(NetBoxModelImportForm): class WirelessLinkImportForm(NetBoxModelImportForm):
status = CSVChoiceField( # Termination A
label=_('Status'), site_a = CSVModelChoiceField(
choices=LinkStatusChoices, label=_('Site A'),
help_text=_('Connection status') queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text=_('Site of parent device A (if any)'),
)
device_a = CSVModelChoiceField(
label=_('Device A'),
queryset=Device.objects.all(),
to_field_name='name',
help_text=_('Parent device of assigned interface A'),
) )
interface_a = CSVModelChoiceField( interface_a = CSVModelChoiceField(
label=_('Interface A'), label=_('Interface A'),
queryset=Interface.objects.all() queryset=Interface.objects.all(),
to_field_name='name',
help_text=_('Assigned interface A'),
)
# Termination B
site_b = CSVModelChoiceField(
label=_('Site B'),
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text=_('Site of parent device B (if any)'),
)
device_b = CSVModelChoiceField(
label=_('Device B'),
queryset=Device.objects.all(),
to_field_name='name',
help_text=_('Parent device of assigned interface B'),
) )
interface_b = CSVModelChoiceField( interface_b = CSVModelChoiceField(
label=_('Interface B'), label=_('Interface B'),
queryset=Interface.objects.all() queryset=Interface.objects.all(),
to_field_name='name',
help_text=_('Assigned interface B'),
)
# WirelessLink attributes
status = CSVChoiceField(
label=_('Status'),
choices=LinkStatusChoices,
help_text=_('Connection status'),
) )
tenant = CSVModelChoiceField( tenant = CSVModelChoiceField(
label=_('Tenant'), label=_('Tenant'),
@ -127,6 +162,28 @@ class WirelessLinkImportForm(NetBoxModelImportForm):
class Meta: class Meta:
model = WirelessLink model = WirelessLink
fields = ( fields = (
'interface_a', 'interface_b', 'ssid', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'site_a', 'device_a', 'interface_a', 'site_b', 'device_b', 'interface_b', 'status', 'ssid', 'tenant',
'distance', 'distance_unit', 'description', 'comments', 'tags', 'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'comments', 'tags',
) )
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
if data:
# Limit choices for interface_a to the assigned device_a
interface_a_params = {f'device__{self.fields["device_a"].to_field_name}': data.get('device_a')}
# Limit choices for device_a to the assigned site_a
if site_a := data.get('site_a'):
device_a_params = {f'site__{self.fields["site_a"].to_field_name}': site_a}
self.fields['device_a'].queryset = self.fields['device_a'].queryset.filter(**device_a_params)
interface_a_params.update({f'device__site__{self.fields["site_a"].to_field_name}': site_a})
self.fields['interface_a'].queryset = self.fields['interface_a'].queryset.filter(**interface_a_params)
# Limit choices for interface_b to the assigned device_b
interface_b_params = {f'device__{self.fields["device_b"].to_field_name}': data.get('device_b')}
# Limit choices for device_b to the assigned site_b
if site_b := data.get('site_b'):
device_b_params = {f'site__{self.fields["site_b"].to_field_name}': site_b}
self.fields['device_b'].queryset = self.fields['device_b'].queryset.filter(**device_b_params)
interface_b_params.update({f'device__site__{self.fields["site_b"].to_field_name}': site_b})
self.fields['interface_b'].queryset = self.fields['interface_b'].queryset.filter(**interface_b_params)

View File

@ -198,13 +198,13 @@ class WirelessLink(WirelessAuthenticationBase, DistanceMixin, PrimaryModel):
super().clean() super().clean()
# Validate interface types # Validate interface types
if self.interface_a.type not in WIRELESS_IFACE_TYPES: if hasattr(self, "interface_a") and self.interface_a.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({ raise ValidationError({
'interface_a': _( 'interface_a': _(
"{type} is not a wireless interface." "{type} is not a wireless interface."
).format(type=self.interface_a.get_type_display()) ).format(type=self.interface_a.get_type_display())
}) })
if self.interface_b.type not in WIRELESS_IFACE_TYPES: if hasattr(self, "interface_b") and self.interface_b.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({ raise ValidationError({
'interface_b': _( 'interface_b': _(
"{type} is not a wireless interface." "{type} is not a wireless interface."

View File

@ -198,10 +198,10 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
} }
cls.csv_data = ( cls.csv_data = (
"interface_a,interface_b,status,tenant", "device_a,interface_a,device_b,interface_b,status,tenant",
f"{interfaces[6].pk},{interfaces[7].pk},connected,{tenants[0].name}", f"{interfaces[6].device.name},{interfaces[6].name},{interfaces[7].device.name},{interfaces[7].name},connected,{tenants[0].name}",
f"{interfaces[8].pk},{interfaces[9].pk},connected,{tenants[1].name}", f"{interfaces[8].device.name},{interfaces[8].name},{interfaces[9].device.name},{interfaces[9].name},connected,{tenants[1].name}",
f"{interfaces[10].pk},{interfaces[11].pk},connected,{tenants[2].name}", f"{interfaces[10].device.name},{interfaces[10].name},{interfaces[11].device.name},{interfaces[11].name},connected,{tenants[2].name}",
) )
cls.csv_update_data = ( cls.csv_update_data = (