Skip to content

Commit

Permalink
Closes #211: Allow device assignment and removal from IP address view
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Oct 24, 2016
1 parent f44a322 commit e22eafc
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 7 deletions.
5 changes: 2 additions & 3 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
Expand Down
25 changes: 24 additions & 1 deletion netbox/ipam/forms.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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.'})
Expand Down
2 changes: 2 additions & 0 deletions netbox/ipam/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
url(r'^ip-addresses/(?P<pk>\d+)/$', views.ipaddress, name='ipaddress'),
url(r'^ip-addresses/(?P<pk>\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
url(r'^ip-addresses/(?P<pk>\d+)/assign/$', views.ipaddress_assign, name='ipaddress_assign'),
url(r'^ip-addresses/(?P<pk>\d+)/remove/$', views.ipaddress_remove, name='ipaddress_remove'),
url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),

# VLAN groups
Expand Down
73 changes: 72 additions & 1 deletion netbox/ipam/views.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions netbox/templates/ipam/ipaddress.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,14 @@ <h1>{{ ipaddress }}</h1>
<td>
{% if ipaddress.interface %}
<span><a href="{% url 'dcim:device' pk=ipaddress.interface.device.pk %}">{{ ipaddress.interface.device }}</a> ({{ ipaddress.interface }})</span>
{% if perms.dcim.change_device and perms.ipam.change_ipaddress %}
<a href="{% url 'ipam:ipaddress_remove' pk=ipaddress.pk %}" class="btn btn-xs btn-danger"><i class="glyphicon glyphicon-remove"></i> Remove</a>
{% endif %}
{% else %}
<span class="text-muted">None</span>
{% if perms.dcim.change_device and perms.ipam.change_ipaddress %}
<a href="{% url 'ipam:ipaddress_assign' pk=ipaddress.pk %}" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-plus"></i> Assign</a>
{% endif %}
{% endif %}
</td>
</tr>
Expand Down
56 changes: 56 additions & 0 deletions netbox/templates/ipam/ipaddress_assign.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{% extends '_base.html' %}
{% load static from staticfiles %}
{% load form_helpers %}

{% block title %}Assign IP Address{% endblock %}

{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Assign IP Address {{ ipaddress }} ({% if ipaddress.vrf %}VRF {{ ipaddress.vrf }}{% else %}Global Table{% endif %})</strong>
</div>
<div class="panel-body">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>
<li role="presentation"><a href="#select" aria-controls="home" role="tab" data-toggle="tab">Select</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="search">
{% render_field form.livesearch %}
</div>
<div class="tab-pane" id="select">
{% render_field form.site %}
{% render_field form.rack %}
{% render_field form.device %}
</div>
</div>
{% render_field form.interface %}
{% render_field form.set_as_primary %}
</div>
</div>
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<button type="submit" name="_assign" class="btn btn-primary">Assign</button>
<a href="{{ cancel_url }}" class="btn btn-default">Cancel</a>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

{% block javascript %}
<script src="{% static 'js/livesearch.js' %}"></script>
{% endblock %}
14 changes: 12 additions & 2 deletions netbox/templates/ipam/ipaddress_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,26 @@
<p class="form-control-static">
{% if obj.interface %}
<a href="{% url 'dcim:device' pk=obj.interface.device.pk %}">{{ obj.interface.device }}</a>
<a href="{% url 'ipam:ipaddress_remove' pk=obj.pk %}" class="btn btn-xs btn-danger"><i class="glyphicon glyphicon-remove"></i> Remove</a>
{% else %}
<span>None</span>
<span class="text-muted">None</span>
{% if obj.pk %}
<a href="{% url 'ipam:ipaddress_assign' pk=obj.pk %}" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-plus"></i> Assign</a>
{% endif %}
{% endif %}
</p>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">Interface</label>
<div class="col-md-9">
<p class="form-control-static">{{ obj.interface }}</p>
<p class="form-control-static">
{% if obj.interface %}
{{ obj.interface }}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</p>
</div>
</div>
{% endif %}
Expand Down
8 changes: 8 additions & 0 deletions netbox/templates/ipam/ipaddress_unassign.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends 'utilities/confirmation_form.html' %}
{% load form_helpers %}

{% block title %}Remove {{ ipaddress }} from {{ ipaddress.interface }}?{% endblock %}

{% block message %}
<p>Are you sure you want to remove this IP address from <strong>{{ ipaddress.interface.device }} {{ ipaddress.interface }}</strong>?</p>
{% endblock %}

0 comments on commit e22eafc

Please sign in to comment.