Browse Source

Closes #1512: Added a view to search for an IP address being assigned to an interface

Jeremy Stretch 7 years ago
parent
commit
a0bb7b08bd

+ 5 - 0
netbox/ipam/forms.py

@@ -688,6 +688,11 @@ class IPAddressBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
         nullable_fields = ['vrf', 'role', 'tenant', 'description']
 
 
+class IPAddressAssignForm(BootstrapMixin, forms.Form):
+    vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF')
+    address = forms.CharField(label='IP Address')
+
+
 def ipaddress_status_choices():
     status_counts = {}
     for status in IPAddress.objects.values('status').annotate(count=Count('status')).order_by('status'):

+ 17 - 1
netbox/ipam/tables.py

@@ -76,6 +76,10 @@ IPADDRESS_LINK = """
 {% endif %}
 """
 
+IPADDRESS_ASSIGN_LINK = """
+<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ request.GET.interface }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
+"""
+
 IPADDRESS_PARENT = """
 {% if record.interface %}
     <a href="{{ record.interface.parent.get_absolute_url }}">{{ record.interface.parent }}</a>
@@ -268,8 +272,8 @@ class PrefixDetailTable(PrefixTable):
 class IPAddressTable(BaseTable):
     pk = ToggleColumn()
     address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
-    status = tables.TemplateColumn(STATUS_LABEL)
     vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
+    status = tables.TemplateColumn(STATUS_LABEL)
     tenant = tables.TemplateColumn(TENANT_LINK)
     parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False)
     interface = tables.Column(orderable=False)
@@ -293,6 +297,18 @@ class IPAddressDetailTable(IPAddressTable):
         )
 
 
+class IPAddressAssignTable(BaseTable):
+    address = tables.TemplateColumn(IPADDRESS_ASSIGN_LINK, verbose_name='IP Address')
+    status = tables.TemplateColumn(STATUS_LABEL)
+    parent = tables.TemplateColumn(IPADDRESS_PARENT, orderable=False)
+    interface = tables.Column(orderable=False)
+
+    class Meta(BaseTable.Meta):
+        model = IPAddress
+        fields = ('address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface')
+        orderable = False
+
+
 #
 # VLAN groups
 #

+ 1 - 0
netbox/ipam/urls.py

@@ -60,6 +60,7 @@ urlpatterns = [
     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'),
+    url(r'^ip-addresses/assign/$', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
     url(r'^ip-addresses/(?P<pk>\d+)/$', views.IPAddressView.as_view(), name='ipaddress'),
     url(r'^ip-addresses/(?P<pk>\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
     url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),

+ 46 - 1
netbox/ipam/views.py

@@ -4,7 +4,7 @@ import netaddr
 from django.conf import settings
 from django.contrib.auth.mixins import PermissionRequiredMixin
 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 django.urls import reverse
 from django.views.generic import View
 from django_tables2 import RequestConfig
@@ -686,6 +686,51 @@ class IPAddressEditView(IPAddressCreateView):
     permission_required = 'ipam.change_ipaddress'
 
 
+class IPAddressAssignView(PermissionRequiredMixin, View):
+    """
+    Search for IPAddresses to be assigned to an Interface.
+    """
+    permission_required = 'ipam.change_ipaddress'
+
+    def dispatch(self, request, *args, **kwargs):
+
+        # Redirect user if an interface has not been provided
+        if 'interface' not in request.GET:
+            return redirect('ipam:ipaddress_add')
+
+        return super(IPAddressAssignView, self).dispatch(request, *args, **kwargs)
+
+    def get(self, request):
+
+        form = forms.IPAddressAssignForm()
+
+        return render(request, 'ipam/ipaddress_assign.html', {
+            'form': form,
+            'return_url': request.GET.get('return_url', ''),
+        })
+
+    def post(self, request):
+
+        form = forms.IPAddressAssignForm(request.POST)
+        table = None
+
+        if form.is_valid():
+
+            queryset = IPAddress.objects.select_related(
+                'vrf', 'tenant', 'interface__device', 'interface__virtual_machine'
+            ).filter(
+                vrf=form.cleaned_data['vrf'],
+                address__net_host=form.cleaned_data['address'],
+            )
+            table = tables.IPAddressAssignTable(queryset)
+
+        return render(request, 'ipam/ipaddress_assign.html', {
+            'form': form,
+            'table': table,
+            'return_url': request.GET.get('return_url', ''),
+        })
+
+
 class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView):
     permission_required = 'ipam.delete_ipaddress'
     model = IPAddress

+ 14 - 2
netbox/templates/ipam/inc/ipadress_edit_header.html

@@ -1,4 +1,16 @@
+{% load helpers %}
+
 <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>
+    <li role="presentation"{% if active_tab == 'add' %} class="active"{% endif %}>
+        <a href="{% url 'ipam:ipaddress_add' %}{% querystring request %}">New IP</a>
+    </li>
+    {% if 'interface' in request.GET %}
+        <li role="presentation"{% if active_tab == 'assign' %} class="active"{% endif %}>
+            <a href="{% url 'ipam:ipaddress_assign' %}{% querystring request %}">Assign IP</a>
+        </li>
+    {% else %}
+        <li role="presentation"{% if active_tab == 'bulk_add' %} class="active"{% endif %}>
+            <a href="{% url 'ipam:ipaddress_bulk_add' %}{% querystring request %}">Bulk Create</a>
+        </li>
+    {% endif %}
 </ul>

+ 48 - 0
netbox/templates/ipam/ipaddress_assign.html

@@ -0,0 +1,48 @@
+{% extends 'utilities/obj_edit.html' %}
+{% load static from staticfiles %}
+{% load form_helpers %}
+{% load helpers %}
+
+{% block content %}
+    <form action="{% querystring request %}" method="post" class="form form-horizontal">
+        {% csrf_token %}
+        {% for field in form.hidden_fields %}
+            {{ field }}
+        {% endfor %}
+        <div class="row">
+            <div class="col-md-6 col-md-offset-3">
+                <h3>Assign an IP Address</h3>
+                {% include 'ipam/inc/ipadress_edit_header.html' with active_tab='assign' %}
+                {% 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>Select IP Address</strong></div>
+                <div class="panel-body">
+                    {% render_field form.vrf %}
+                    {% render_field form.address %}
+                </div>
+            </div>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-md-6 col-md-offset-3 text-right">
+                <button type="submit" class="btn btn-primary">Search</button>
+                <a href="{{ return_url }}" class="btn btn-default">Cancel</a>
+            </div>
+        </div>
+    </form>
+    {% if table %}
+        <div class="row">
+            <div class="col-md-10 col-md-offset-1" style="margin-top: 20px">
+                <h3>Search Results</h3>
+                {% include 'utilities/obj_table.html' with table_template='panel_table.html' %}
+            </div>
+        </div>
+    {% endif %}
+{% endblock %}

+ 1 - 1
netbox/utilities/templatetags/helpers.py

@@ -132,7 +132,7 @@ def querystring(request, **kwargs):
             querydict[k] = v
         elif k in querydict:
             querydict.pop(k)
-    querystring = querydict.urlencode()
+    querystring = querydict.urlencode(safe='/')
     if querystring:
         return '?' + querystring
     else: