Support VLAN in Import Prefixes, Add VLAN column to Prefixes page

catch MultipleObjectsReturned Error & Cleanup

Fix VLAN Filter on Prefixes page
This commit is contained in:
Gelob 2016-06-28 11:12:36 -04:00
parent 5e9090a03a
commit 4ea70763e1
5 changed files with 34 additions and 18 deletions

View File

@ -192,13 +192,15 @@ class PrefixFromCSVForm(forms.ModelForm):
error_messages={'invalid_choice': 'VRF not found.'}) error_messages={'invalid_choice': 'VRF not found.'})
site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, to_field_name='name', site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, to_field_name='name',
error_messages={'invalid_choice': 'Site not found.'}) 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]) 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', role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False, to_field_name='name',
error_messages={'invalid_choice': 'Invalid role.'}) error_messages={'invalid_choice': 'Invalid role.'})
class Meta: class Meta:
model = Prefix model = Prefix
fields = ['prefix', 'vrf', 'site', 'status_name', 'role', 'description'] fields = ['prefix', 'vrf', 'site', 'vlan', 'status_name', 'role', 'description']
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
m = super(PrefixFromCSVForm, self).save(commit=False) m = super(PrefixFromCSVForm, self).save(commit=False)
@ -238,6 +240,9 @@ def prefix_site_choices():
site_choices = Site.objects.annotate(prefix_count=Count('prefixes')) site_choices = Site.objects.annotate(prefix_count=Count('prefixes'))
return [(s.slug, '{} ({})'.format(s.name, s.prefix_count)) for s in site_choices] 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(): def prefix_status_choices():
status_counts = {} status_counts = {}
@ -256,9 +261,11 @@ class PrefixFilterForm(forms.Form, BootstrapMixin):
vrf = forms.ChoiceField(required=False, choices=prefix_vrf_choices, label='VRF') vrf = forms.ChoiceField(required=False, choices=prefix_vrf_choices, label='VRF')
status = forms.MultipleChoiceField(required=False, choices=prefix_status_choices) status = forms.MultipleChoiceField(required=False, choices=prefix_status_choices)
site = forms.MultipleChoiceField(required=False, choices=prefix_site_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, 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') expand = forms.BooleanField(required=False, label='Expand prefix hierarchy')

View File

@ -127,12 +127,13 @@ class PrefixTable(BaseTable):
prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix') prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix')
vrf = tables.Column(orderable=False, default='Global', verbose_name='VRF') vrf = tables.Column(orderable=False, default='Global', verbose_name='VRF')
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') 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') role = tables.Column(verbose_name='Role')
description = tables.Column(orderable=False, verbose_name='Description') description = tables.Column(orderable=False, verbose_name='Description')
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Prefix model = Prefix
fields = ('pk', 'prefix', 'status', 'vrf', 'site', 'role', 'description') fields = ('pk', 'prefix', 'status', 'vrf', 'site', 'vlan', 'role', 'description')
class PrefixBriefTable(BaseTable): class PrefixBriefTable(BaseTable):

View File

@ -9,6 +9,7 @@
<td><a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a></td> <td><a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a></td>
<td>{{ prefix.vrf|default:"Global" }}</td> <td>{{ prefix.vrf|default:"Global" }}</td>
<td>{{ prefix.site }}</td> <td>{{ prefix.site }}</td>
<td>{{ prefix.vlan }}</td>
<td>{{ prefix.status }}</td> <td>{{ prefix.status }}</td>
<td>{{ prefix.role }}</td> <td>{{ prefix.role }}</td>
<td>{{ prefix.description }}</td> <td>{{ prefix.description }}</td>

View File

@ -43,6 +43,11 @@
<td>Name of assigned site (optional)</td> <td>Name of assigned site (optional)</td>
<td>HQ</td> <td>HQ</td>
</tr> </tr>
<tr>
<td>VLAN</td>
<td>VLAN Name (optional)</td>
<td>Security</td>
</tr>
<tr> <tr>
<td>Status</td> <td>Status</td>
<td>Current status</td> <td>Current status</td>
@ -61,7 +66,7 @@
</tbody> </tbody>
</table> </table>
<h4>Example</h4> <h4>Example</h4>
<pre>192.168.42.0/24,65000:123,HQ,Active,Customer,7th floor WiFi</pre> <pre>192.168.42.0/24,65000:123,HQ,Security,Active,Customer,7th floor WiFi</pre>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse_lazy
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.core.exceptions import MultipleObjectsReturned
EXPANSION_PATTERN = '\[(\d+-\d+)\]' EXPANSION_PATTERN = '\[(\d+-\d+)\]'
@ -244,18 +244,20 @@ class BulkImportForm(forms.Form):
return return
obj_list = [] obj_list = []
for i, record in enumerate(records, start=1): for i, record in enumerate(records, start=1):
obj_form = self.fields['csv'].csv_form(data=record) obj_form = self.fields['csv'].csv_form(data=record)
if obj_form.is_valid(): try:
obj = obj_form.save(commit=False) if obj_form.is_valid():
obj_list.append(obj) obj = obj_form.save(commit=False)
else: obj_list.append(obj)
for field, errors in obj_form.errors.items(): else:
for e in errors: for field, errors in obj_form.errors.items():
if field == '__all__': for e in errors:
self.add_error('csv', "Record {}: {}".format(i, e)) if field == '__all__':
else: self.add_error('csv', "Record {}: {}".format(i, e))
self.add_error('csv', "Record {} ({}): {}".format(i, field, 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