From f9328d53b4297992d784924fb7ed9f6b55fcd603 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 17 May 2017 17:16:02 -0400 Subject: [PATCH] Fixes #1197: Fixed status assignment during bulk import of devices, prefixes, IPs, and VLANs --- netbox/dcim/forms.py | 18 ++++++---- netbox/ipam/forms.py | 49 ++++++++++++++++---------- netbox/templates/ipam/vlan_import.html | 2 +- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 414e54306..4978bfeb6 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -674,7 +674,7 @@ class BaseDeviceFromCSVForm(forms.ModelForm): queryset=Platform.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Invalid platform.'} ) - status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in STATUS_CHOICES]) + status = forms.CharField() class Meta: fields = [] @@ -692,8 +692,12 @@ class BaseDeviceFromCSVForm(forms.ModelForm): except DeviceType.DoesNotExist: self.add_error('model_name', "Invalid device type ({} {})".format(manufacturer, model_name)) - def clean_status_name(self): - return dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']] + def clean_status(self): + status_choices = {s[1].lower(): s[0] for s in STATUS_CHOICES} + try: + return status_choices[self.cleaned_data['status'].lower()] + except KeyError: + raise ValidationError("Invalid status: {}".format(self.cleaned_data['status'])) class DeviceFromCSVForm(BaseDeviceFromCSVForm): @@ -707,8 +711,8 @@ class DeviceFromCSVForm(BaseDeviceFromCSVForm): class Meta(BaseDeviceFromCSVForm.Meta): fields = [ - 'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', - 'status_name', 'site', 'rack_name', 'position', 'face', + 'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status', + 'site', 'rack_name', 'position', 'face', ] def clean(self): @@ -751,8 +755,8 @@ class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm): class Meta(BaseDeviceFromCSVForm.Meta): fields = [ - 'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', - 'status_name', 'parent', 'device_bay_name', + 'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status', + 'parent', 'device_bay_name', ] def clean(self): diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 53984c464..e45543479 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import ValidationError from django.db.models import Count from dcim.models import Site, Rack, Device, Interface @@ -195,14 +196,16 @@ class PrefixFromCSVForm(forms.ModelForm): error_messages={'invalid_choice': 'Site not found.'}) vlan_group_name = forms.CharField(required=False) vlan_vid = forms.IntegerField(required=False) - status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in PREFIX_STATUS_CHOICES]) + status = forms.CharField() role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Invalid role.'}) class Meta: model = Prefix - fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status_name', 'role', 'is_pool', - 'description'] + fields = [ + 'prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status', 'role', 'is_pool', + 'description', + ] def clean(self): @@ -237,12 +240,12 @@ class PrefixFromCSVForm(forms.ModelForm): except VLAN.MultipleObjectsReturned: self.add_error('vlan_vid', "Multiple VLANs found ({} - VID {})".format(site, vlan_vid)) - def save(self, *args, **kwargs): - - # Assign Prefix status by name - self.instance.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']] - - return super(PrefixFromCSVForm, self).save(*args, **kwargs) + def clean_status(self): + status_choices = {s[1].lower(): s[0] for s in PREFIX_STATUS_CHOICES} + try: + return status_choices[self.cleaned_data['status'].lower()] + except KeyError: + raise ValidationError("Invalid status: {}".format(self.cleaned_data['status'])) class PrefixImportForm(BootstrapMixin, BulkImportForm): @@ -491,7 +494,7 @@ class IPAddressFromCSVForm(forms.ModelForm): error_messages={'invalid_choice': 'VRF not found.'}) tenant = forms.ModelChoiceField(Tenant.objects.all(), to_field_name='name', required=False, error_messages={'invalid_choice': 'Tenant not found.'}) - status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in IPADDRESS_STATUS_CHOICES]) + status = forms.CharField() device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Device not found.'}) interface_name = forms.CharField(required=False) @@ -499,7 +502,7 @@ class IPAddressFromCSVForm(forms.ModelForm): class Meta: model = IPAddress - fields = ['address', 'vrf', 'tenant', 'status_name', 'device', 'interface_name', 'is_primary', 'description'] + fields = ['address', 'vrf', 'tenant', 'status', 'device', 'interface_name', 'is_primary', 'description'] def clean(self): @@ -522,10 +525,14 @@ class IPAddressFromCSVForm(forms.ModelForm): if is_primary and not device: self.add_error('is_primary', "No device specified; cannot set as primary IP") - def save(self, *args, **kwargs): + def clean_status(self): + status_choices = {s[1].lower(): s[0] for s in IPADDRESS_STATUS_CHOICES} + try: + return status_choices[self.cleaned_data['status'].lower()] + except KeyError: + raise ValidationError("Invalid status: {}".format(self.cleaned_data['status'])) - # Assign status by name - self.instance.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']] + def save(self, *args, **kwargs): # Set interface if self.cleaned_data['device'] and self.cleaned_data['interface_name']: @@ -650,7 +657,7 @@ class VLANFromCSVForm(forms.ModelForm): Tenant.objects.all(), to_field_name='name', required=False, error_messages={'invalid_choice': 'Tenant not found.'} ) - status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in VLAN_STATUS_CHOICES]) + status = forms.CharField() role = forms.ModelChoiceField( queryset=Role.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Invalid role.'} @@ -658,7 +665,7 @@ class VLANFromCSVForm(forms.ModelForm): class Meta: model = VLAN - fields = ['site', 'group_name', 'vid', 'name', 'tenant', 'status_name', 'role', 'description'] + fields = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description'] def clean(self): @@ -672,6 +679,13 @@ class VLANFromCSVForm(forms.ModelForm): except VLANGroup.DoesNotExist: self.add_error('group_name', "Invalid VLAN group {}.".format(group_name)) + def clean_status(self): + status_choices = {s[1].lower(): s[0] for s in VLAN_STATUS_CHOICES} + try: + return status_choices[self.cleaned_data['status'].lower()] + except KeyError: + raise ValidationError("Invalid status: {}".format(self.cleaned_data['status'])) + def save(self, *args, **kwargs): vlan = super(VLANFromCSVForm, self).save(commit=False) @@ -680,9 +694,6 @@ class VLANFromCSVForm(forms.ModelForm): if self.cleaned_data['group_name']: vlan.group = VLANGroup.objects.get(site=self.cleaned_data['site'], name=self.cleaned_data['group_name']) - # Assign VLAN status by name - vlan.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']] - if kwargs.get('commit'): vlan.save() return vlan diff --git a/netbox/templates/ipam/vlan_import.html b/netbox/templates/ipam/vlan_import.html index 46b2f6c64..7c9081c24 100644 --- a/netbox/templates/ipam/vlan_import.html +++ b/netbox/templates/ipam/vlan_import.html @@ -15,7 +15,7 @@ Site - Name of assigned site + Name of assigned site (optional) LAS2