diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 0c7a411cd..538984b6e 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -192,13 +192,15 @@ class PrefixFromCSVForm(forms.ModelForm): error_messages={'invalid_choice': 'VRF not found.'}) site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, to_field_name='name', error_messages={'invalid_choice': 'Site not found.'}) + vlan = forms.ModelChoiceField(queryset=VLAN.objects.all(), required=False, to_field_name='name', + error_messages={'invalid_choice': 'VLAN not found.'}) status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in PREFIX_STATUS_CHOICES]) 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', 'site', 'status_name', 'role', 'description'] + fields = ['prefix', 'vrf', 'site', 'vlan', 'status_name', 'role', 'description'] def save(self, *args, **kwargs): m = super(PrefixFromCSVForm, self).save(commit=False) @@ -238,6 +240,9 @@ def prefix_site_choices(): site_choices = Site.objects.annotate(prefix_count=Count('prefixes')) return [(s.slug, '{} ({})'.format(s.name, s.prefix_count)) for s in site_choices] +def prefix_vlan_choices(): + vlan_choices = VLAN.objects.annotate(prefix_count=Count('prefixes')) + return [(v.id, '{} ({})'.format(v.display_name, v.prefix_count)) for v in vlan_choices] def prefix_status_choices(): status_counts = {} @@ -256,9 +261,11 @@ class PrefixFilterForm(forms.Form, BootstrapMixin): vrf = forms.ChoiceField(required=False, choices=prefix_vrf_choices, label='VRF') status = forms.MultipleChoiceField(required=False, choices=prefix_status_choices) site = forms.MultipleChoiceField(required=False, choices=prefix_site_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + widget=forms.SelectMultiple(attrs={'size': 6})) + vlan_id = forms.MultipleChoiceField(required=False, choices=prefix_vlan_choices, label='VLAN', + widget=forms.SelectMultiple(attrs={'size': 8})) role = forms.MultipleChoiceField(required=False, choices=prefix_role_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + widget=forms.SelectMultiple(attrs={'size': 6})) expand = forms.BooleanField(required=False, label='Expand prefix hierarchy') diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index 2267b5deb..89d095d2e 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -127,12 +127,13 @@ class PrefixTable(BaseTable): prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix') vrf = tables.Column(orderable=False, default='Global', verbose_name='VRF') site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') + vlan = tables.Column(accessor='vlan.display_name', verbose_name='VLAN') role = tables.Column(verbose_name='Role') description = tables.Column(orderable=False, verbose_name='Description') class Meta(BaseTable.Meta): model = Prefix - fields = ('pk', 'prefix', 'status', 'vrf', 'site', 'role', 'description') + fields = ('pk', 'prefix', 'status', 'vrf', 'site', 'vlan', 'role', 'description') class PrefixBriefTable(BaseTable): diff --git a/netbox/templates/ipam/prefix_bulk_edit.html b/netbox/templates/ipam/prefix_bulk_edit.html index 03e358725..00dfcefe2 100644 --- a/netbox/templates/ipam/prefix_bulk_edit.html +++ b/netbox/templates/ipam/prefix_bulk_edit.html @@ -9,6 +9,7 @@
192.168.42.0/24,65000:123,HQ,Active,Customer,7th floor WiFi+
192.168.42.0/24,65000:123,HQ,Security,Active,Customer,7th floor WiFi{% endblock %} diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 9855b9273..190745b7c 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse_lazy from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.safestring import mark_safe - +from django.core.exceptions import MultipleObjectsReturned EXPANSION_PATTERN = '\[(\d+-\d+)\]' @@ -244,18 +244,20 @@ class BulkImportForm(forms.Form): return obj_list = [] - for i, record in enumerate(records, start=1): obj_form = self.fields['csv'].csv_form(data=record) - if obj_form.is_valid(): - obj = obj_form.save(commit=False) - obj_list.append(obj) - else: - for field, errors in obj_form.errors.items(): - for e in errors: - if field == '__all__': - self.add_error('csv', "Record {}: {}".format(i, e)) - else: - self.add_error('csv', "Record {} ({}): {}".format(i, field, e)) + try: + if obj_form.is_valid(): + obj = obj_form.save(commit=False) + obj_list.append(obj) + else: + for field, errors in obj_form.errors.items(): + for e in errors: + if field == '__all__': + self.add_error('csv', "Record {}: {}".format(i, e)) + else: + self.add_error('csv', "Record {} ({}): {}".format(i, field, e)) + except MultipleObjectsReturned as e: + self.add_error('csv', '%s' % (e.message)) - self.cleaned_data['csv'] = obj_list + self.cleaned_data['csv'] = obj_list