diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index b68a1dba054..501859f558d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1715,7 +1715,7 @@ class InterfaceConnectionsListView(ObjectListView): # IP addresses # -@permission_required('ipam.add_ipaddress') +@permission_required(['dcim.change_device', 'ipam.add_ipaddress']) def ipaddress_assign(request, pk): device = get_object_or_404(Device, pk=pk) @@ -1727,8 +1727,7 @@ def ipaddress_assign(request, pk): ipaddress = form.save(commit=False) ipaddress.interface = form.cleaned_data['interface'] ipaddress.save() - messages.success(request, "Added new IP address {0} to interface {1}".format(ipaddress, - ipaddress.interface)) + messages.success(request, "Added new IP address {} to interface {}".format(ipaddress, ipaddress.interface)) if form.cleaned_data['set_as_primary']: if ipaddress.family == 4: diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index cc67db47c70..039475d036b 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -1,7 +1,7 @@ from django import forms from django.db.models import Count -from dcim.models import Site, Device, Interface +from dcim.models import Site, Rack, Device, Interface from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from tenancy.models import Tenant from utilities.forms import ( @@ -336,6 +336,29 @@ def __init__(self, *args, **kwargs): self.fields['nat_inside'].choices = [] +class IPAddressAssignForm(BootstrapMixin, forms.Form): + site = forms.ModelChoiceField(queryset=Site.objects.all(), label='Site', required=False, + widget=forms.Select(attrs={'filter-for': 'rack'})) + rack = forms.ModelChoiceField(queryset=Rack.objects.all(), label='Rack', required=False, + widget=APISelect(api_url='/api/dcim/racks/?site_id={{site}}', display_field='display_name', attrs={'filter-for': 'device'})) + device = forms.ModelChoiceField(queryset=Device.objects.all(), label='Device', required=False, + widget=APISelect(api_url='/api/dcim/devices/?rack_id={{rack}}', display_field='display_name', attrs={'filter-for': 'interface'})) + livesearch = forms.CharField(required=False, label='Device', widget=Livesearch( + query_key='q', query_url='dcim-api:device_list', field_to_update='device') + ) + interface = forms.ModelChoiceField(queryset=Interface.objects.all(), label='Interface', + widget=APISelect(api_url='/api/dcim/devices/{{device}}/interfaces/')) + set_as_primary = forms.BooleanField(label='Set as primary IP for device', required=False) + + def __init__(self, *args, **kwargs): + + super(IPAddressAssignForm, self).__init__(*args, **kwargs) + + self.fields['rack'].choices = [] + self.fields['device'].choices = [] + self.fields['interface'].choices = [] + + class IPAddressFromCSVForm(forms.ModelForm): vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd', error_messages={'invalid_choice': 'VRF not found.'}) diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 22c4cd512c9..dc5fcc96460 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -56,6 +56,8 @@ url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'), url(r'^ip-addresses/(?P\d+)/$', views.ipaddress, name='ipaddress'), url(r'^ip-addresses/(?P\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'), + url(r'^ip-addresses/(?P\d+)/assign/$', views.ipaddress_assign, name='ipaddress_assign'), + url(r'^ip-addresses/(?P\d+)/remove/$', views.ipaddress_remove, name='ipaddress_remove'), url(r'^ip-addresses/(?P\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'), # VLAN groups diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index c7c5a46c6e7..646dbde8a4d 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1,11 +1,15 @@ import netaddr from django_tables2 import RequestConfig +from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib import messages +from django.core.urlresolvers import reverse from django.db.models import Count, Q -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, redirect, render from dcim.models import Device +from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, @@ -446,6 +450,73 @@ def ipaddress(request, pk): }) +@permission_required(['dcim.change_device', 'ipam.change_ipaddress']) +def ipaddress_assign(request, pk): + + ipaddress = get_object_or_404(IPAddress, pk=pk) + + if request.method == 'POST': + form = forms.IPAddressAssignForm(request.POST) + if form.is_valid(): + + interface = form.cleaned_data['interface'] + ipaddress.interface = interface + ipaddress.save() + messages.success(request, "Assigned IP address {} to interface {}".format(ipaddress, ipaddress.interface)) + + if form.cleaned_data['set_as_primary']: + device = interface.device + if ipaddress.family == 4: + device.primary_ip4 = ipaddress + elif ipaddress.family == 6: + device.primary_ip6 = ipaddress + device.save() + + return redirect('ipam:ipaddress', pk=ipaddress.pk) + + else: + form = forms.IPAddressAssignForm() + + return render(request, 'ipam/ipaddress_assign.html', { + 'ipaddress': ipaddress, + 'form': form, + 'cancel_url': reverse('ipam:ipaddress', kwargs={'pk': ipaddress.pk}), + }) + + +@permission_required(['dcim.change_device', 'ipam.change_ipaddress']) +def ipaddress_remove(request, pk): + + ipaddress = get_object_or_404(IPAddress, pk=pk) + + if request.method == 'POST': + form = ConfirmationForm(request.POST) + if form.is_valid(): + + device = ipaddress.interface.device + ipaddress.interface = None + ipaddress.save() + messages.success(request, "Removed IP address {} from {}".format(ipaddress, device)) + + if device.primary_ip4 == ipaddress.pk: + device.primary_ip4 = None + device.save() + elif device.primary_ip6 == ipaddress.pk: + device.primary_ip6 = None + device.save() + + return redirect('ipam:ipaddress', pk=ipaddress.pk) + + else: + form = ConfirmationForm() + + return render(request, 'ipam/ipaddress_unassign.html', { + 'ipaddress': ipaddress, + 'form': form, + 'cancel_url': reverse('ipam:ipaddress', kwargs={'pk': ipaddress.pk}), + }) + + class IPAddressEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'ipam.change_ipaddress' model = IPAddress diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index b85b98c931a..2392e462beb 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -97,8 +97,14 @@

{{ ipaddress }}

{% if ipaddress.interface %} {{ ipaddress.interface.device }} ({{ ipaddress.interface }}) + {% if perms.dcim.change_device and perms.ipam.change_ipaddress %} + Remove + {% endif %} {% else %} None + {% if perms.dcim.change_device and perms.ipam.change_ipaddress %} + Assign + {% endif %} {% endif %} diff --git a/netbox/templates/ipam/ipaddress_assign.html b/netbox/templates/ipam/ipaddress_assign.html new file mode 100644 index 00000000000..4143dc3eeaa --- /dev/null +++ b/netbox/templates/ipam/ipaddress_assign.html @@ -0,0 +1,56 @@ +{% extends '_base.html' %} +{% load static from staticfiles %} +{% load form_helpers %} + +{% block title %}Assign IP Address{% endblock %} + +{% block content %} +
+ {% csrf_token %} +
+
+ {% if form.non_field_errors %} +
+
Errors
+
+ {{ form.non_field_errors }} +
+
+ {% endif %} +
+
+ Assign IP Address {{ ipaddress }} ({% if ipaddress.vrf %}VRF {{ ipaddress.vrf }}{% else %}Global Table{% endif %}) +
+
+ +
+ +
+ {% render_field form.site %} + {% render_field form.rack %} + {% render_field form.device %} +
+
+ {% render_field form.interface %} + {% render_field form.set_as_primary %} +
+
+
+
+ + Cancel +
+
+
+
+
+{% endblock %} + +{% block javascript %} + +{% endblock %} diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index e5e7f0b5b59..65d0678b77b 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -17,8 +17,12 @@

{% if obj.interface %} {{ obj.interface.device }} + Remove {% else %} - None + None + {% if obj.pk %} + Assign + {% endif %} {% endif %}

@@ -26,7 +30,13 @@
-

{{ obj.interface }}

+

+ {% if obj.interface %} + {{ obj.interface }} + {% else %} + None + {% endif %} +

{% endif %} diff --git a/netbox/templates/ipam/ipaddress_unassign.html b/netbox/templates/ipam/ipaddress_unassign.html new file mode 100644 index 00000000000..5cd83abb956 --- /dev/null +++ b/netbox/templates/ipam/ipaddress_unassign.html @@ -0,0 +1,8 @@ +{% extends 'utilities/confirmation_form.html' %} +{% load form_helpers %} + +{% block title %}Remove {{ ipaddress }} from {{ ipaddress.interface }}?{% endblock %} + +{% block message %} +

Are you sure you want to remove this IP address from {{ ipaddress.interface.device }} {{ ipaddress.interface }}?

+{% endblock %}