900 lines
30 KiB
Python
900 lines
30 KiB
Python
from netaddr import IPNetwork, IPSet
|
|
from netaddr.core import AddrFormatError
|
|
|
|
from django_tables2 import RequestConfig
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import permission_required
|
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
|
from django.core.urlresolvers import reverse
|
|
from django.db.models import ProtectedError
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.utils.http import urlencode
|
|
|
|
from dcim.models import Device
|
|
from extras.models import ExportTemplate
|
|
from utilities.error_handlers import handle_protectederror
|
|
from utilities.forms import ConfirmationForm
|
|
from utilities.paginator import EnhancedPaginator
|
|
from utilities.views import BulkImportView, BulkEditView, BulkDeleteView
|
|
|
|
from .filters import AggregateFilter, PrefixFilter, IPAddressFilter, VLANFilter, VRFFilter
|
|
from .forms import AggregateForm, AggregateImportForm, AggregateBulkEditForm, AggregateBulkDeleteForm, \
|
|
AggregateFilterForm, PrefixForm, PrefixImportForm, PrefixBulkEditForm, PrefixBulkDeleteForm, PrefixFilterForm, \
|
|
IPAddressForm, IPAddressImportForm, IPAddressBulkEditForm, IPAddressBulkDeleteForm, IPAddressFilterForm, VLANForm, \
|
|
VLANImportForm, VLANBulkEditForm, VLANBulkDeleteForm, VRFForm, VRFImportForm, VRFBulkEditForm, VRFBulkDeleteForm, \
|
|
VLANFilterForm
|
|
from .models import VRF, Aggregate, Prefix, VLAN
|
|
from .tables import AggregateTable, AggregateBulkEditTable, PrefixTable, PrefixBriefTable, PrefixBulkEditTable, \
|
|
IPAddress, IPAddressBriefTable, IPAddressTable, IPAddressBulkEditTable, VLANTable, VLANBulkEditTable, VRFTable, \
|
|
VRFBulkEditTable
|
|
|
|
|
|
def add_available_prefixes(parent, prefix_list):
|
|
"""
|
|
Create fake Prefix objects for all unallocated space within a prefix.
|
|
"""
|
|
|
|
# Find all unallocated space
|
|
available_prefixes = IPSet(parent) ^ IPSet([p.prefix for p in prefix_list])
|
|
available_prefixes = [Prefix(prefix=p) for p in available_prefixes.iter_cidrs()]
|
|
|
|
# Concatenate and sort complete list of children
|
|
prefix_list = list(prefix_list) + available_prefixes
|
|
prefix_list.sort(key=lambda p: p.prefix)
|
|
|
|
return prefix_list
|
|
|
|
|
|
#
|
|
# VRFs
|
|
#
|
|
|
|
def vrf_list(request):
|
|
|
|
queryset = VRF.objects.all()
|
|
queryset = VRFFilter(request.GET, queryset).qs
|
|
# annotate_depth(queryset)
|
|
|
|
# Export
|
|
if 'export' in request.GET:
|
|
et = get_object_or_404(ExportTemplate, content_type__model='vrf', name=request.GET.get('export'))
|
|
response = et.to_response(context_dict={'queryset': queryset}, filename='netbox_vrfs')
|
|
return response
|
|
|
|
if request.user.has_perm('ipam.change_vrf') or request.user.has_perm('ipam.delete_vrf'):
|
|
vrf_table = VRFBulkEditTable(queryset)
|
|
else:
|
|
vrf_table = VRFTable(queryset)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator}).configure(vrf_table)
|
|
|
|
export_templates = ExportTemplate.objects.filter(content_type__model='vrf')
|
|
|
|
return render(request, 'ipam/vrf_list.html', {
|
|
'vrf_table': vrf_table,
|
|
'export_templates': export_templates,
|
|
})
|
|
|
|
|
|
def vrf(request, pk):
|
|
|
|
vrf = get_object_or_404(VRF.objects.all(), pk=pk)
|
|
prefixes = Prefix.objects.filter(vrf=vrf)
|
|
|
|
return render(request, 'ipam/vrf.html', {
|
|
'vrf': vrf,
|
|
'prefixes': prefixes,
|
|
})
|
|
|
|
|
|
@permission_required('ipam.add_vrf')
|
|
def vrf_add(request):
|
|
|
|
if request.method == 'POST':
|
|
form = VRFForm(request.POST)
|
|
if form.is_valid():
|
|
vrf = form.save()
|
|
messages.success(request, "Added new VRF: {0}".format(vrf))
|
|
if '_addanother' in request.POST:
|
|
return redirect('ipam:vrf_add')
|
|
else:
|
|
return redirect('ipam:vrf', pk=vrf.pk)
|
|
|
|
else:
|
|
form = VRFForm()
|
|
|
|
return render(request, 'ipam/vrf_edit.html', {
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vrf_list'),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.change_vrf')
|
|
def vrf_edit(request, pk):
|
|
|
|
vrf = get_object_or_404(VRF, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = VRFForm(request.POST, instance=vrf)
|
|
if form.is_valid():
|
|
vrf = form.save()
|
|
messages.success(request, "Modified VRF {0}".format(vrf))
|
|
return redirect('ipam:vrf', pk=vrf.pk)
|
|
|
|
else:
|
|
form = VRFForm(instance=vrf)
|
|
|
|
return render(request, 'ipam/vrf_edit.html', {
|
|
'vrf': vrf,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vrf', kwargs={'pk': vrf.pk}),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.delete_vrf')
|
|
def vrf_delete(request, pk):
|
|
|
|
vrf = get_object_or_404(VRF, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ConfirmationForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
vrf.delete()
|
|
messages.success(request, "VRF {0} has been deleted".format(vrf))
|
|
return redirect('ipam:vrf_list')
|
|
except ProtectedError, e:
|
|
handle_protectederror(vrf, request, e)
|
|
return redirect('ipam:vrf', pk=vrf.pk)
|
|
|
|
else:
|
|
form = ConfirmationForm()
|
|
|
|
return render(request, 'ipam/vrf_delete.html', {
|
|
'vrf': vrf,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vrf', kwargs={'pk': vrf.pk})
|
|
})
|
|
|
|
|
|
class VRFBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|
permission_required = 'ipam.add_vrf'
|
|
form = VRFImportForm
|
|
table = VRFTable
|
|
template_name = 'ipam/vrf_import.html'
|
|
obj_list_url = 'ipam:vrf_list'
|
|
|
|
|
|
class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|
permission_required = 'ipam.change_vrf'
|
|
cls = VRF
|
|
form = VRFBulkEditForm
|
|
template_name = 'ipam/vrf_bulk_edit.html'
|
|
redirect_url = 'ipam:vrf_list'
|
|
|
|
def update_objects(self, pk_list, form):
|
|
|
|
fields_to_update = {}
|
|
for field in ['description']:
|
|
if form.cleaned_data[field]:
|
|
fields_to_update[field] = form.cleaned_data[field]
|
|
|
|
updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
|
|
messages.success(self.request, "Updated {} VRFs".format(updated_count))
|
|
|
|
|
|
class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|
permission_required = 'ipam.delete_vrf'
|
|
cls = VRF
|
|
form = VRFBulkDeleteForm
|
|
template_name = 'ipam/vrf_bulk_delete.html'
|
|
redirect_url = 'ipam:vrf_list'
|
|
|
|
|
|
#
|
|
# Aggregates
|
|
#
|
|
|
|
def aggregate_list(request):
|
|
|
|
queryset = Aggregate.objects.select_related('rir').extra(
|
|
select = {
|
|
'child_count': 'SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix',
|
|
}
|
|
)
|
|
queryset = AggregateFilter(request.GET, queryset).qs
|
|
|
|
# Export
|
|
if 'export' in request.GET:
|
|
et = get_object_or_404(ExportTemplate, content_type__model='aggregate', name=request.GET.get('export'))
|
|
response = et.to_response(context_dict={'queryset': queryset}, filename='netbox_aggregates')
|
|
return response
|
|
|
|
if request.user.has_perm('ipam.change_aggregate') or request.user.has_perm('ipam.delete_aggregate'):
|
|
aggregate_table = AggregateBulkEditTable(queryset)
|
|
else:
|
|
aggregate_table = AggregateTable(queryset)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator})\
|
|
.configure(aggregate_table)
|
|
|
|
export_templates = ExportTemplate.objects.filter(content_type__model='aggregate')
|
|
|
|
return render(request, 'ipam/aggregate_list.html', {
|
|
'aggregate_table': aggregate_table,
|
|
'export_templates': export_templates,
|
|
'filter_form': AggregateFilterForm(request.GET, label_suffix=''),
|
|
})
|
|
|
|
|
|
def aggregate(request, pk):
|
|
|
|
aggregate = get_object_or_404(Aggregate, pk=pk)
|
|
|
|
# Find all child prefixes contained by this aggregate
|
|
child_prefixes = Prefix.objects.filter(prefix__net_contained_or_equal=str(aggregate.prefix))\
|
|
.select_related('site', 'status', 'role').annotate_depth(limit=0)
|
|
child_prefixes = add_available_prefixes(aggregate.prefix, child_prefixes)
|
|
|
|
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
|
|
prefix_table = PrefixBulkEditTable(child_prefixes)
|
|
else:
|
|
prefix_table = PrefixTable(child_prefixes)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator})\
|
|
.configure(prefix_table)
|
|
|
|
return render(request, 'ipam/aggregate.html', {
|
|
'aggregate': aggregate,
|
|
'prefix_table': prefix_table,
|
|
})
|
|
|
|
|
|
@permission_required('ipam.add_aggregate')
|
|
def aggregate_add(request):
|
|
|
|
if request.method == 'POST':
|
|
form = AggregateForm(request.POST)
|
|
if form.is_valid():
|
|
aggregate = form.save()
|
|
messages.success(request, "Added new aggregate: {0}".format(aggregate.prefix))
|
|
if '_addanother' in request.POST:
|
|
return redirect('ipam:aggregate_add')
|
|
else:
|
|
return redirect('ipam:aggregate', pk=aggregate.pk)
|
|
|
|
else:
|
|
form = AggregateForm()
|
|
|
|
return render(request, 'ipam/aggregate_edit.html', {
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:aggregate_list'),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.change_aggregate')
|
|
def aggregate_edit(request, pk):
|
|
|
|
aggregate = get_object_or_404(Aggregate, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = AggregateForm(request.POST, instance=aggregate)
|
|
if form.is_valid():
|
|
aggregate = form.save()
|
|
messages.success(request, "Modified aggregate {0}".format(aggregate.prefix))
|
|
return redirect('ipam:aggregate', pk=aggregate.pk)
|
|
|
|
else:
|
|
form = AggregateForm(instance=aggregate)
|
|
|
|
return render(request, 'ipam/aggregate_edit.html', {
|
|
'aggregate': aggregate,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:aggregate', kwargs={'pk': aggregate.pk}),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.delete_aggregate')
|
|
def aggregate_delete(request, pk):
|
|
|
|
aggregate = get_object_or_404(Aggregate, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ConfirmationForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
aggregate.delete()
|
|
messages.success(request, "Aggregate {0} has been deleted".format(aggregate))
|
|
return redirect('ipam:aggregate_list')
|
|
except ProtectedError, e:
|
|
handle_protectederror(aggregate, request, e)
|
|
return redirect('ipam:aggregate', pk=aggregate.pk)
|
|
|
|
else:
|
|
form = ConfirmationForm()
|
|
|
|
return render(request, 'ipam/aggregate_delete.html', {
|
|
'aggregate': aggregate,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:aggregate', kwargs={'pk': aggregate.pk})
|
|
})
|
|
|
|
|
|
class AggregateBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|
permission_required = 'ipam.add_aggregate'
|
|
form = AggregateImportForm
|
|
table = AggregateTable
|
|
template_name = 'ipam/aggregate_import.html'
|
|
obj_list_url = 'ipam:aggregate_list'
|
|
|
|
|
|
class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|
permission_required = 'ipam.change_aggregate'
|
|
cls = Aggregate
|
|
form = AggregateBulkEditForm
|
|
template_name = 'ipam/aggregate_bulk_edit.html'
|
|
redirect_url = 'ipam:aggregate_list'
|
|
|
|
def update_objects(self, pk_list, form):
|
|
|
|
fields_to_update = {}
|
|
for field in ['rir', 'date_added', 'description']:
|
|
if form.cleaned_data[field]:
|
|
fields_to_update[field] = form.cleaned_data[field]
|
|
|
|
updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
|
|
messages.success(self.request, "Updated {} aggregates".format(updated_count))
|
|
|
|
|
|
class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|
permission_required = 'ipam.delete_aggregate'
|
|
cls = Aggregate
|
|
form = AggregateBulkDeleteForm
|
|
template_name = 'ipam/aggregate_bulk_delete.html'
|
|
redirect_url = 'ipam:aggregate_list'
|
|
|
|
|
|
#
|
|
# Prefixes
|
|
#
|
|
|
|
def prefix_list(request):
|
|
|
|
queryset = Prefix.objects.select_related('site', 'status', 'role')
|
|
queryset = PrefixFilter(request.GET, queryset).qs
|
|
|
|
# Export
|
|
if 'export' in request.GET:
|
|
et = get_object_or_404(ExportTemplate, content_type__model='prefix', name=request.GET.get('export'))
|
|
response = et.to_response(context_dict={'queryset': queryset}, filename='netbox_prefixes')
|
|
return response
|
|
|
|
# Show only top-level prefixes by default
|
|
limit = None if request.GET.get('expand') else 0
|
|
prefixes = queryset.annotate_depth(limit=limit)
|
|
|
|
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
|
|
prefix_table = PrefixBulkEditTable(prefixes)
|
|
else:
|
|
prefix_table = PrefixTable(prefixes)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator}).configure(prefix_table)
|
|
|
|
export_templates = ExportTemplate.objects.filter(content_type__model='prefix')
|
|
|
|
return render(request, 'ipam/prefix_list.html', {
|
|
'prefix_table': prefix_table,
|
|
'export_templates': export_templates,
|
|
'filter_form': PrefixFilterForm(request.GET, label_suffix=''),
|
|
})
|
|
|
|
|
|
def prefix(request, pk):
|
|
|
|
prefix = get_object_or_404(Prefix.objects.select_related('site', 'vlan', 'status', 'role'), pk=pk)
|
|
|
|
try:
|
|
aggregate = Aggregate.objects.get(prefix__net_contains_or_equals=str(prefix.prefix))
|
|
except Aggregate.DoesNotExist:
|
|
aggregate = None
|
|
|
|
# Count child IP addresses
|
|
ipaddress_count = IPAddress.objects.filter(address__net_contained_or_equal=str(prefix.prefix)).count()
|
|
|
|
# Parent prefixes table
|
|
parent_prefixes = Prefix.objects.filter(vrf=prefix.vrf, prefix__net_contains=str(prefix.prefix))\
|
|
.select_related('site', 'status', 'role').annotate_depth()
|
|
parent_prefix_table = PrefixBriefTable(parent_prefixes)
|
|
|
|
# Duplicate prefixes table
|
|
duplicate_prefixes = Prefix.objects.filter(vrf=prefix.vrf, prefix=str(prefix.prefix)).exclude(pk=prefix.pk)\
|
|
.select_related('site', 'status', 'role')
|
|
duplicate_prefix_table = PrefixBriefTable(duplicate_prefixes)
|
|
|
|
# Child prefixes table
|
|
child_prefixes = Prefix.objects.filter(vrf=prefix.vrf, prefix__net_contained=str(prefix.prefix))\
|
|
.select_related('site', 'status', 'role').annotate_depth(limit=0)
|
|
if child_prefixes:
|
|
child_prefixes = add_available_prefixes(prefix.prefix, child_prefixes)
|
|
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
|
|
child_prefix_table = PrefixBulkEditTable(child_prefixes)
|
|
else:
|
|
child_prefix_table = PrefixTable(child_prefixes)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator})\
|
|
.configure(child_prefix_table)
|
|
|
|
return render(request, 'ipam/prefix.html', {
|
|
'prefix': prefix,
|
|
'aggregate': aggregate,
|
|
'ipaddress_count': ipaddress_count,
|
|
'parent_prefix_table': parent_prefix_table,
|
|
'child_prefix_table': child_prefix_table,
|
|
'duplicate_prefix_table': duplicate_prefix_table,
|
|
})
|
|
|
|
|
|
@permission_required('ipam.add_prefix')
|
|
def prefix_add(request):
|
|
|
|
if request.method == 'POST':
|
|
form = PrefixForm(request.POST)
|
|
if form.is_valid():
|
|
prefix = form.save()
|
|
messages.success(request, "Added new prefix: {0}".format(prefix.prefix))
|
|
if '_addanother' in request.POST:
|
|
return redirect('ipam:prefix_add')
|
|
else:
|
|
return redirect('ipam:prefix', pk=prefix.pk)
|
|
|
|
else:
|
|
form = PrefixForm(initial={
|
|
'site': request.GET.get('site'),
|
|
'prefix': request.GET.get('prefix'),
|
|
})
|
|
|
|
return render(request, 'ipam/prefix_edit.html', {
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:prefix_list'),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.change_prefix')
|
|
def prefix_edit(request, pk):
|
|
|
|
prefix = get_object_or_404(Prefix, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = PrefixForm(request.POST, instance=prefix)
|
|
if form.is_valid():
|
|
prefix = form.save()
|
|
messages.success(request, "Modified prefix {0}".format(prefix.prefix))
|
|
return redirect('ipam:prefix', pk=prefix.pk)
|
|
|
|
else:
|
|
form = PrefixForm(instance=prefix)
|
|
|
|
return render(request, 'ipam/prefix_edit.html', {
|
|
'prefix': prefix,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:prefix', kwargs={'pk': prefix.pk}),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.delete_prefix')
|
|
def prefix_delete(request, pk):
|
|
|
|
prefix = get_object_or_404(Prefix, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ConfirmationForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
prefix.delete()
|
|
messages.success(request, "Prefix {0} has been deleted".format(prefix))
|
|
return redirect('ipam:prefix_list')
|
|
except ProtectedError, e:
|
|
handle_protectederror(prefix, request, e)
|
|
return redirect('ipam:prefix', pk=prefix.pk)
|
|
|
|
else:
|
|
form = ConfirmationForm()
|
|
|
|
return render(request, 'ipam/prefix_delete.html', {
|
|
'prefix': prefix,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:prefix', kwargs={'pk': prefix.pk})
|
|
})
|
|
|
|
|
|
class PrefixBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|
permission_required = 'ipam.add_prefix'
|
|
form = PrefixImportForm
|
|
table = PrefixTable
|
|
template_name = 'ipam/prefix_import.html'
|
|
obj_list_url = 'ipam:prefix_list'
|
|
|
|
|
|
class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|
permission_required = 'ipam.change_prefix'
|
|
cls = Prefix
|
|
form = PrefixBulkEditForm
|
|
template_name = 'ipam/prefix_bulk_edit.html'
|
|
redirect_url = 'ipam:prefix_list'
|
|
|
|
def update_objects(self, pk_list, form):
|
|
|
|
fields_to_update = {}
|
|
if form.cleaned_data['vrf']:
|
|
fields_to_update['vrf'] = form.cleaned_data['vrf']
|
|
elif form.cleaned_data['vrf_global']:
|
|
fields_to_update['vrf'] = None
|
|
for field in ['site', 'status', 'role', 'description']:
|
|
if form.cleaned_data[field]:
|
|
fields_to_update[field] = form.cleaned_data[field]
|
|
|
|
updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
|
|
messages.success(self.request, "Updated {} prefixes".format(updated_count))
|
|
|
|
|
|
class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|
permission_required = 'ipam.delete_prefix'
|
|
cls = Prefix
|
|
form = PrefixBulkDeleteForm
|
|
template_name = 'ipam/prefix_bulk_delete.html'
|
|
redirect_url = 'ipam:prefix_list'
|
|
|
|
|
|
def prefix_ipaddresses(request, pk):
|
|
|
|
prefix = get_object_or_404(Prefix.objects.all(), pk=pk)
|
|
|
|
# Find all IPAddresses belonging to this Prefix
|
|
ipaddresses = IPAddress.objects.filter(address__net_contained_or_equal=str(prefix.prefix))\
|
|
.select_related('vrf', 'interface__device', 'primary_for')
|
|
|
|
if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
|
|
ip_table = IPAddressBulkEditTable(ipaddresses)
|
|
else:
|
|
ip_table = IPAddressTable(ipaddresses)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator})\
|
|
.configure(ip_table)
|
|
|
|
return render(request, 'ipam/prefix_ipaddresses.html', {
|
|
'prefix': prefix,
|
|
'ip_table': ip_table,
|
|
})
|
|
|
|
|
|
#
|
|
# IP addresses
|
|
#
|
|
|
|
def ipaddress_list(request):
|
|
|
|
queryset = IPAddress.objects.select_related('vrf', 'interface__device', 'primary_for')
|
|
queryset = IPAddressFilter(request.GET, queryset).qs
|
|
|
|
# Export
|
|
if 'export' in request.GET:
|
|
et = get_object_or_404(ExportTemplate, content_type__model='ipaddress', name=request.GET.get('export'))
|
|
response = et.to_response(context_dict={'queryset': queryset}, filename='netbox_ips')
|
|
return response
|
|
|
|
if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
|
|
ip_table = IPAddressBulkEditTable(queryset)
|
|
else:
|
|
ip_table = IPAddressTable(queryset)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator}).configure(ip_table)
|
|
|
|
export_templates = ExportTemplate.objects.filter(content_type__model='ipaddress')
|
|
|
|
# If searching and no IPAddresses were found, include a list of parent prefixes matching the query
|
|
prefix_table = None
|
|
if request.GET.get('q') and not queryset:
|
|
try:
|
|
ip = str(IPNetwork(request.GET.get('q')))
|
|
prefix_table = PrefixTable(Prefix.objects.filter(prefix__net_contains_or_equals=ip))
|
|
RequestConfig(request).configure(prefix_table)
|
|
except AddrFormatError:
|
|
pass
|
|
|
|
return render(request, 'ipam/ipaddress_list.html', {
|
|
'ip_table': ip_table,
|
|
'prefix_table': prefix_table,
|
|
'export_templates': export_templates,
|
|
'filter_form': IPAddressFilterForm(request.GET, label_suffix=''),
|
|
})
|
|
|
|
|
|
def ipaddress(request, pk):
|
|
|
|
ipaddress = get_object_or_404(IPAddress.objects.select_related('interface__device'), pk=pk)
|
|
|
|
parent_prefixes = Prefix.objects.filter(vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip))
|
|
related_ips = IPAddress.objects.select_related('interface__device').exclude(pk=ipaddress.pk).filter(vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address))
|
|
|
|
related_ips_table = IPAddressBriefTable(related_ips)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator}).configure(related_ips_table)
|
|
|
|
return render(request, 'ipam/ipaddress.html', {
|
|
'ipaddress': ipaddress,
|
|
'parent_prefixes': parent_prefixes,
|
|
'related_ips_table': related_ips_table,
|
|
})
|
|
|
|
|
|
@permission_required('ipam.add_ipaddress')
|
|
def ipaddress_add(request):
|
|
|
|
if request.method == 'POST':
|
|
form = IPAddressForm(request.POST)
|
|
if form.is_valid():
|
|
ipaddress = form.save()
|
|
messages.success(request, "Created new IP Address: {0}".format(ipaddress))
|
|
if '_addanother' in request.POST:
|
|
return redirect('ipam:ipaddress_add')
|
|
else:
|
|
return redirect('ipam:ipaddress', pk=ipaddress.pk)
|
|
|
|
else:
|
|
form = IPAddressForm(initial={
|
|
'ipaddress': request.GET.get('ipaddress', None),
|
|
})
|
|
|
|
return render(request, 'ipam/ipaddress_edit.html', {
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:ipaddress_list'),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.change_ipaddress')
|
|
def ipaddress_edit(request, pk):
|
|
|
|
ipaddress = get_object_or_404(IPAddress, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = IPAddressForm(request.POST, instance=ipaddress)
|
|
if form.is_valid():
|
|
ipaddress = form.save()
|
|
messages.success(request, "Modified IP address {0}".format(ipaddress))
|
|
return redirect('ipam:ipaddress', pk=ipaddress.pk)
|
|
|
|
else:
|
|
form = IPAddressForm(instance=ipaddress)
|
|
|
|
return render(request, 'ipam/ipaddress_edit.html', {
|
|
'ipaddress': ipaddress,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:ipaddress', kwargs={'pk': ipaddress.pk}),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.delete_ipaddress')
|
|
def ipaddress_delete(request, pk):
|
|
|
|
ipaddress = get_object_or_404(IPAddress, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ConfirmationForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
ipaddress.delete()
|
|
messages.success(request, "IP address {0} has been deleted".format(ipaddress))
|
|
if ipaddress.interface:
|
|
return redirect('dcim:device', pk=ipaddress.interface.device.pk)
|
|
else:
|
|
return redirect('ipam:ipaddress_list')
|
|
except ProtectedError, e:
|
|
handle_protectederror(ipaddress, request, e)
|
|
return redirect('ipam:ipaddress', pk=ipaddress.pk)
|
|
|
|
else:
|
|
form = ConfirmationForm()
|
|
|
|
# Upon cancellation, redirect to the assigned device if one exists
|
|
if ipaddress.interface:
|
|
cancel_url = reverse('dcim:device', kwargs={'pk': ipaddress.interface.device.pk})
|
|
else:
|
|
cancel_url = reverse('ipam:ipaddress_list')
|
|
|
|
return render(request, 'ipam/ipaddress_delete.html', {
|
|
'ipaddress': ipaddress,
|
|
'form': form,
|
|
'cancel_url': cancel_url,
|
|
})
|
|
|
|
|
|
class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|
permission_required = 'ipam.add_ipaddress'
|
|
form = IPAddressImportForm
|
|
table = IPAddressTable
|
|
template_name = 'ipam/ipaddress_import.html'
|
|
obj_list_url = 'ipam:ipaddress_list'
|
|
|
|
def save_obj(self, obj):
|
|
obj.save()
|
|
# Update primary IP for device if needed
|
|
try:
|
|
device = obj.primary_for
|
|
device.primary_ip = obj
|
|
device.save()
|
|
except Device.DoesNotExist:
|
|
pass
|
|
|
|
|
|
class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|
permission_required = 'ipam.change_ipaddress'
|
|
cls = IPAddress
|
|
form = IPAddressBulkEditForm
|
|
template_name = 'ipam/ipaddress_bulk_edit.html'
|
|
redirect_url = 'ipam:ipaddress_list'
|
|
|
|
def update_objects(self, pk_list, form):
|
|
|
|
fields_to_update = {}
|
|
if form.cleaned_data['vrf']:
|
|
fields_to_update['vrf'] = form.cleaned_data['vrf']
|
|
elif form.cleaned_data['vrf_global']:
|
|
fields_to_update['vrf'] = None
|
|
for field in ['description']:
|
|
if form.cleaned_data[field]:
|
|
fields_to_update[field] = form.cleaned_data[field]
|
|
|
|
updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
|
|
messages.success(self.request, "Updated {} IP addresses".format(updated_count))
|
|
|
|
|
|
class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|
permission_required = 'ipam.delete_ipaddress'
|
|
cls = IPAddress
|
|
form = IPAddressBulkDeleteForm
|
|
template_name = 'ipam/ipaddress_bulk_delete.html'
|
|
redirect_url = 'ipam:ipaddress_list'
|
|
|
|
|
|
#
|
|
# VLANs
|
|
#
|
|
|
|
def vlan_list(request):
|
|
|
|
queryset = VLAN.objects.select_related('site', 'status', 'role')
|
|
queryset = VLANFilter(request.GET, queryset).qs
|
|
|
|
# Export
|
|
if 'export' in request.GET:
|
|
et = get_object_or_404(ExportTemplate, content_type__model='vlan', name=request.GET.get('export'))
|
|
response = et.to_response(context_dict={'queryset': queryset}, filename='netbox_vlans')
|
|
return response
|
|
|
|
if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
|
|
vlan_table = VLANBulkEditTable(queryset)
|
|
else:
|
|
vlan_table = VLANTable(queryset)
|
|
RequestConfig(request, paginate={'per_page': settings.PAGINATE_COUNT, 'klass': EnhancedPaginator}).configure(vlan_table)
|
|
|
|
export_templates = ExportTemplate.objects.filter(content_type__model='vlan')
|
|
|
|
return render(request, 'ipam/vlan_list.html', {
|
|
'vlan_table': vlan_table,
|
|
'export_templates': export_templates,
|
|
'filter_form': VLANFilterForm(request.GET, label_suffix=''),
|
|
})
|
|
|
|
|
|
def vlan(request, pk):
|
|
|
|
vlan = get_object_or_404(VLAN.objects.select_related('site', 'status', 'role'), pk=pk)
|
|
prefixes = Prefix.objects.filter(vlan=vlan)
|
|
|
|
return render(request, 'ipam/vlan.html', {
|
|
'vlan': vlan,
|
|
'prefixes': prefixes,
|
|
})
|
|
|
|
|
|
@permission_required('ipam.add_vlan')
|
|
def vlan_add(request):
|
|
|
|
if request.method == 'POST':
|
|
form = VLANForm(request.POST)
|
|
if form.is_valid():
|
|
vlan = form.save()
|
|
messages.success(request, "Added new VLAN: {0}".format(vlan))
|
|
if '_addanother' in request.POST:
|
|
base_url = reverse('ipam:vlan_add')
|
|
params = urlencode({
|
|
'site': vlan.site.pk,
|
|
})
|
|
return HttpResponseRedirect('{}?{}'.format(base_url, params))
|
|
else:
|
|
return redirect('ipam:vlan', pk=vlan.pk)
|
|
|
|
else:
|
|
form = VLANForm()
|
|
|
|
return render(request, 'ipam/vlan_edit.html', {
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vlan_list'),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.change_vlan')
|
|
def vlan_edit(request, pk):
|
|
|
|
vlan = get_object_or_404(VLAN, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = VLANForm(request.POST, instance=vlan)
|
|
if form.is_valid():
|
|
vlan = form.save()
|
|
messages.success(request, "Modified VLAN {0}".format(vlan))
|
|
return redirect('ipam:vlan', pk=vlan.pk)
|
|
|
|
else:
|
|
form = VLANForm(instance=vlan)
|
|
|
|
return render(request, 'ipam/vlan_edit.html', {
|
|
'vlan': vlan,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vlan', kwargs={'pk': vlan.pk}),
|
|
})
|
|
|
|
|
|
@permission_required('ipam.delete_vlan')
|
|
def vlan_delete(request, pk):
|
|
|
|
vlan = get_object_or_404(VLAN, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ConfirmationForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
vlan.delete()
|
|
messages.success(request, "VLAN {0} has been deleted".format(vlan))
|
|
return redirect('ipam:vlan_list')
|
|
except ProtectedError, e:
|
|
handle_protectederror(vlan, request, e)
|
|
return redirect('ipam:vlan', pk=vlan.pk)
|
|
|
|
else:
|
|
form = ConfirmationForm()
|
|
|
|
return render(request, 'ipam/vlan_delete.html', {
|
|
'vlan': vlan,
|
|
'form': form,
|
|
'cancel_url': reverse('ipam:vlan', kwargs={'pk': vlan.pk})
|
|
})
|
|
|
|
|
|
class VLANBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|
permission_required = 'ipam.add_vlan'
|
|
form = VLANImportForm
|
|
table = VLANTable
|
|
template_name = 'ipam/vlan_import.html'
|
|
obj_list_url = 'ipam:vlan_list'
|
|
|
|
|
|
class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|
permission_required = 'ipam.change_vlan'
|
|
cls = VLAN
|
|
form = VLANBulkEditForm
|
|
template_name = 'ipam/vlan_bulk_edit.html'
|
|
redirect_url = 'ipam:vlan_list'
|
|
|
|
def update_objects(self, pk_list, form):
|
|
|
|
fields_to_update = {}
|
|
for field in ['site', 'status', 'role']:
|
|
if form.cleaned_data[field]:
|
|
fields_to_update[field] = form.cleaned_data[field]
|
|
|
|
updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
|
|
messages.success(self.request, "Updated {} VLANs".format(updated_count))
|
|
|
|
|
|
class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|
permission_required = 'ipam.delete_vlan'
|
|
cls = VLAN
|
|
form = VLANBulkDeleteForm
|
|
template_name = 'ipam/vlan_bulk_delete.html'
|
|
redirect_url = 'ipam:vlan_list'
|