mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #181: Implemented support for bulk IP address creation
This commit is contained in:
@ -5,7 +5,8 @@ from dcim.models import Site, Rack, Device, Interface
|
|||||||
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
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 (
|
from .models import (
|
||||||
@ -339,6 +340,14 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
|||||||
self.fields['nat_inside'].choices = []
|
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):
|
class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
||||||
site = forms.ModelChoiceField(queryset=Site.objects.all(), label='Site', required=False,
|
site = forms.ModelChoiceField(queryset=Site.objects.all(), label='Site', required=False,
|
||||||
widget=forms.Select(attrs={'filter-for': 'rack'}))
|
widget=forms.Select(attrs={'filter-for': 'rack'}))
|
||||||
|
@ -51,6 +51,7 @@ urlpatterns = [
|
|||||||
# IP addresses
|
# IP addresses
|
||||||
url(r'^ip-addresses/$', views.IPAddressListView.as_view(), name='ipaddress_list'),
|
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/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/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/edit/$', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
|
||||||
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
||||||
|
@ -12,7 +12,7 @@ from dcim.models import Device
|
|||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.paginator import EnhancedPaginator
|
from utilities.paginator import EnhancedPaginator
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
@ -613,6 +613,14 @@ class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
|||||||
redirect_url = 'ipam:ipaddress_list'
|
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):
|
class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||||
permission_required = 'ipam.add_ipaddress'
|
permission_required = 'ipam.add_ipaddress'
|
||||||
form = forms.IPAddressImportForm
|
form = forms.IPAddressImportForm
|
||||||
|
4
netbox/templates/ipam/inc/ipadress_edit_header.html
Normal file
4
netbox/templates/ipam/inc/ipadress_edit_header.html
Normal file
@ -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
netbox/templates/ipam/ipaddress_bulk_add.html
Normal file
22
netbox/templates/ipam/ipaddress_bulk_add.html
Normal file
@ -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 %}
|
@ -2,6 +2,12 @@
|
|||||||
{% load static from staticfiles %}
|
{% load static from staticfiles %}
|
||||||
{% load form_helpers %}
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block tabs %}
|
||||||
|
{% if not obj.pk %}
|
||||||
|
{% include 'ipam/inc/ipadress_edit_header.html' with active_tab='add' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>IP Address</strong></div>
|
<div class="panel-heading"><strong>IP Address</strong></div>
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends '_base.html' %}
|
||||||
{% load form_helpers %}
|
{% load form_helpers %}
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% if obj.pk %}Editing {{ obj_type }} {{ obj }}{% else %}Add a new {{ obj_type }}{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="." method="post" class="form form-horizontal">
|
<form action="." method="post" class="form form-horizontal">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -13,7 +9,8 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<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 %}
|
{% if form.non_field_errors %}
|
||||||
<div class="panel panel-danger">
|
<div class="panel panel-danger">
|
||||||
<div class="panel-heading"><strong>Errors</strong></div>
|
<div class="panel-heading"><strong>Errors</strong></div>
|
||||||
|
@ -3,7 +3,7 @@ from django_tables2 import RequestConfig
|
|||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.contenttypes.models import ContentType
|
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.core.urlresolvers import reverse
|
||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction, IntegrityError
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
@ -254,6 +254,57 @@ class ObjectDeleteView(View):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
class BulkImportView(View):
|
||||||
form = None
|
form = None
|
||||||
table = None
|
table = None
|
||||||
|
Reference in New Issue
Block a user