Skip to content

Commit

Permalink
Closes netbox-community#181: Implemented support for bulk IP address …
Browse files Browse the repository at this point in the history
…creation
  • Loading branch information
jeremystretch committed Dec 20, 2016
1 parent ce78ae0 commit 9a8d41d
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 8 deletions.
11 changes: 10 additions & 1 deletion netbox/ipam/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
from tenancy.models import Tenant
from utilities.forms import (
APISelect, BootstrapMixin, CSVDataField, BulkImportForm, FilterChoiceField, Livesearch, SlugField, add_blank_choice,
APISelect, BootstrapMixin, BulkImportForm, CSVDataField, ExpandableIPAddressField, FilterChoiceField, Livesearch,
SlugField, add_blank_choice,
)

from .models import (
Expand Down Expand Up @@ -339,6 +340,14 @@ def __init__(self, *args, **kwargs):
self.fields['nat_inside'].choices = []


class IPAddressBulkAddForm(forms.Form, BootstrapMixin):
address = ExpandableIPAddressField()
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF')
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
status = forms.ChoiceField(choices=IPADDRESS_STATUS_CHOICES)
description = forms.CharField(max_length=100, required=False)


class IPAddressAssignForm(BootstrapMixin, forms.Form):
site = forms.ModelChoiceField(queryset=Site.objects.all(), label='Site', required=False,
widget=forms.Select(attrs={'filter-for': 'rack'}))
Expand Down
1 change: 1 addition & 0 deletions netbox/ipam/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
# IP addresses
url(r'^ip-addresses/$', views.IPAddressListView.as_view(), name='ipaddress_list'),
url(r'^ip-addresses/add/$', views.IPAddressEditView.as_view(), name='ipaddress_add'),
url(r'^ip-addresses/bulk-add/$', views.IPAddressBulkAddView.as_view(), name='ipaddress_bulk_add'),
url(r'^ip-addresses/import/$', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
url(r'^ip-addresses/edit/$', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
Expand Down
10 changes: 9 additions & 1 deletion netbox/ipam/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator
from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
)

from . import filters, forms, tables
Expand Down Expand Up @@ -613,6 +613,14 @@ class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView):
redirect_url = 'ipam:ipaddress_list'


class IPAddressBulkAddView(PermissionRequiredMixin, BulkAddView):
permission_required = 'ipam.add_ipaddress'
form = forms.IPAddressBulkAddForm
model = IPAddress
template_name = 'ipam/ipaddress_bulk_add.html'
redirect_url = 'ipam:ipaddress_list'


class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
permission_required = 'ipam.add_ipaddress'
form = forms.IPAddressImportForm
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/ipam/inc/ipadress_edit_header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<ul class="nav nav-tabs" style="margin-bottom: 20px">
<li role="presentation"{% if active_tab == 'add' %} class="active"{% endif %}><a href="{% url 'ipam:ipaddress_add' %}">Individual</a></li>
<li role="presentation"{% if active_tab == 'bulk_add' %} class="active"{% endif %}><a href="{% url 'ipam:ipaddress_bulk_add' %}">Bulk</a></li>
</ul>
22 changes: 22 additions & 0 deletions netbox/templates/ipam/ipaddress_bulk_add.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% extends 'utilities/obj_edit.html' %}
{% load static from staticfiles %}
{% load form_helpers %}

{% block title %}Bulk Add IP Addresses{% endblock %}

{% block tabs %}
{% include 'ipam/inc/ipadress_edit_header.html' with active_tab='bulk_add' %}
{% endblock %}

{% block form %}
<div class="panel panel-default">
<div class="panel-heading"><strong>IP Address</strong></div>
<div class="panel-body">
{% render_field form.address %}
{% render_field form.vrf %}
{% render_field form.tenant %}
{% render_field form.status %}
{% render_field form.description %}
</div>
</div>
{% endblock %}
6 changes: 6 additions & 0 deletions netbox/templates/ipam/ipaddress_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
{% load static from staticfiles %}
{% load form_helpers %}

{% block tabs %}
{% if not obj.pk %}
{% include 'ipam/inc/ipadress_edit_header.html' with active_tab='add' %}
{% endif %}
{% endblock %}

{% block form %}
<div class="panel panel-default">
<div class="panel-heading"><strong>IP Address</strong></div>
Expand Down
7 changes: 2 additions & 5 deletions netbox/templates/utilities/obj_edit.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
{% extends '_base.html' %}
{% load form_helpers %}

{% block title %}
{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}
{% endblock %}

{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
Expand All @@ -13,7 +9,8 @@
{% endfor %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}</h3>
<h3>{% block title %}{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}{% endblock %}</h3>
{% block tabs %}{% endblock %}
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
Expand Down
53 changes: 52 additions & 1 deletion netbox/utilities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.urlresolvers import reverse
from django.db import transaction, IntegrityError
from django.db.models import ProtectedError
Expand Down Expand Up @@ -254,6 +254,57 @@ def post(self, request, *args, **kwargs):
})


class BulkAddView(View):
form = None
model = None
template_name = None
redirect_url = None

def get(self, request):

form = self.form()

return render(request, self.template_name, {
'obj_type': self.model._meta.verbose_name,
'form': form,
'cancel_url': reverse(self.redirect_url),
})

def post(self, request):

form = self.form(request.POST)
if form.is_valid():

# The first field will be used as the pattern
pattern_field = form.fields.keys()[0]
pattern = form.cleaned_data[pattern_field]

# All other fields will be copied as object attributes
kwargs = {k: form.cleaned_data[k] for k in form.fields.keys()[1:]}

new_objs = []
try:
with transaction.atomic():
for value in pattern:
obj = self.model(**kwargs)
setattr(obj, pattern_field, value)
obj.full_clean()
obj.save()
new_objs.append(obj)
except ValidationError as e:
form.add_error(None, e)

if not form.errors:
messages.success(request, u"Added {} {}.".format(len(new_objs), self.model._meta.verbose_name_plural))
return redirect(self.redirect_url)

return render(request, self.template_name, {
'form': form,
'obj_type': self.model._meta.verbose_name,
'cancel_url': reverse(self.redirect_url),
})


class BulkImportView(View):
form = None
table = None
Expand Down

0 comments on commit 9a8d41d

Please sign in to comment.