Browse Source

Incorporated stats into RIR list view

Jeremy Stretch 8 years ago
parent
commit
d891c8c981

+ 26 - 2
netbox/ipam/tables.py

@@ -6,6 +6,25 @@ from utilities.tables import BaseTable, ToggleColumn
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
 
 
+RIR_UTILIZATION = """
+<div class="progress">
+    {% if record.stats.total %}
+        <div class="progress-bar" role="progressbar" style="width: {{ record.stats.percentages.active }}%;">
+            <span class="sr-only">{{ record.stats.percentages.active }}%</span>
+        </div>
+        <div class="progress-bar progress-bar-info" role="progressbar" style="width: {{ record.stats.percentages.reserved }}%;">
+            <span class="sr-only">{{ record.stats.percentages.reserved }}%</span>
+        </div>
+        <div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{ record.stats.percentages.deprecated }}%;">
+            <span class="sr-only">{{ record.stats.percentages.deprecated }}%</span>
+        </div>
+        <div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ record.stats.percentages.available }}%;">
+            <span class="sr-only">{{ record.stats.percentages.available }}%</span>
+        </div>
+    {% endif %}
+</div>
+"""
+
 RIR_ACTIONS = """
 {% if perms.ipam.change_rir %}
     <a href="{% url 'ipam:rir_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
@@ -108,12 +127,17 @@ class RIRTable(BaseTable):
     pk = ToggleColumn()
     name = tables.LinkColumn(verbose_name='Name')
     aggregate_count = tables.Column(verbose_name='Aggregates')
-    slug = tables.Column(verbose_name='Slug')
+    stats_total = tables.Column(accessor='stats.total', verbose_name='Total')
+    stats_active = tables.Column(accessor='stats.active', verbose_name='Active')
+    stats_reserved = tables.Column(accessor='stats.reserved', verbose_name='Reserved')
+    stats_deprecated = tables.Column(accessor='stats.deprecated', verbose_name='Deprecated')
+    stats_available = tables.Column(accessor='stats.available', verbose_name='Available')
+    utilization = tables.TemplateColumn(template_code=RIR_UTILIZATION, verbose_name='Utilization')
     actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
 
     class Meta(BaseTable.Meta):
         model = RIR
-        fields = ('pk', 'name', 'aggregate_count', 'slug', 'actions')
+        fields = ('pk', 'name', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved', 'stats_deprecated', 'stats_available', 'utilization', 'actions')
 
 
 #

+ 0 - 2
netbox/ipam/urls.py

@@ -19,8 +19,6 @@ urlpatterns = [
     url(r'^rirs/$', views.RIRListView.as_view(), name='rir_list'),
     url(r'^rirs/add/$', views.RIREditView.as_view(), name='rir_add'),
     url(r'^rirs/delete/$', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
-    url(r'^rirs/stats/$', views.rir_stats, name='rir_stats'),
-    url(r'^rirs/stats/ipv6/$', views.rir_stats, kwargs={'family': 6}, name='rir_stats_ipv6'),
     url(r'^rirs/(?P<slug>[\w-]+)/edit/$', views.RIREditView.as_view(), name='rir_edit'),
 
     # Aggregates

+ 73 - 73
netbox/ipam/views.py

@@ -158,6 +158,79 @@ class RIRListView(ObjectListView):
     edit_permissions = ['ipam.change_rir', 'ipam.delete_rir']
     template_name = 'ipam/rir_list.html'
 
+    def alter_queryset(self, request):
+
+        # Count /64s for IPv6 rather than individual IPs
+        family = 4
+        denominator = 2 ** 64 if family == 6 else 1
+
+        rirs = []
+        for rir in self.queryset:
+
+            stats = {
+                'total': 0,
+                'active': 0,
+                'reserved': 0,
+                'deprecated': 0,
+                'available': 0,
+            }
+            aggregate_list = Aggregate.objects.filter(family=family, rir=rir)
+            for aggregate in aggregate_list:
+
+                queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(aggregate.prefix))
+
+                # Find all consumed space for each prefix status (we ignore containers for this purpose).
+                active_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_ACTIVE)])
+                reserved_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_RESERVED)])
+                deprecated_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_DEPRECATED)])
+
+                # Find all available prefixes by subtracting each of the existing prefix sets from the aggregate prefix.
+                available_prefixes = (
+                    netaddr.IPSet([aggregate.prefix])
+                    - netaddr.IPSet(active_prefixes)
+                    - netaddr.IPSet(reserved_prefixes)
+                    - netaddr.IPSet(deprecated_prefixes)
+                )
+
+                # Add the size of each metric to the RIR total.
+                stats['total'] += aggregate.prefix.size / denominator
+                stats['active'] += netaddr.IPSet(active_prefixes).size / denominator
+                stats['reserved'] += netaddr.IPSet(reserved_prefixes).size / denominator
+                stats['deprecated'] += netaddr.IPSet(deprecated_prefixes).size / denominator
+                stats['available'] += available_prefixes.size / denominator
+
+            # Calculate the percentage of total space for each prefix status.
+            total = float(stats['total'])
+            stats['percentages'] = {
+                'active': float('{:.2f}'.format(stats['active'] / total * 100)) if total else 0,
+                'reserved': float('{:.2f}'.format(stats['reserved'] / total * 100)) if total else 0,
+                'deprecated': float('{:.2f}'.format(stats['deprecated'] / total * 100)) if total else 0,
+            }
+            stats['percentages']['available'] = (
+                100
+                - stats['percentages']['active']
+                - stats['percentages']['reserved']
+                - stats['percentages']['deprecated']
+            )
+            rir.stats = stats
+            rirs.append(rir)
+
+        return rirs
+
+    def extra_context(self):
+
+        totals = {
+            'total': sum([rir.stats['total'] for rir in self.queryset]),
+            'active': sum([rir.stats['active'] for rir in self.queryset]),
+            'reserved': sum([rir.stats['reserved'] for rir in self.queryset]),
+            'deprecated': sum([rir.stats['deprecated'] for rir in self.queryset]),
+            'available': sum([rir.stats['available'] for rir in self.queryset]),
+        }
+
+        return {
+            'totals': totals,
+        }
+
 
 class RIREditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_rir'
@@ -656,76 +729,3 @@ class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vlan'
     cls = VLAN
     default_redirect_url = 'ipam:vlan_list'
-
-
-#
-# Miscellaneous
-#
-
-def rir_stats(request, family=4):
-
-    denominator = 2 ** 64 if family == 6 else 1
-
-    stats = OrderedDict()
-    for rir in RIR.objects.all():
-
-        stats[rir] = {
-            'total': 0,
-            'active': 0,
-            'reserved': 0,
-            'deprecated': 0,
-            'available': 0,
-        }
-        aggregate_list = Aggregate.objects.filter(family=family, rir=rir)
-        for aggregate in aggregate_list:
-
-            queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(aggregate.prefix))
-
-            # Find all consumed space for each prefix status (we ignore containers for this purpose).
-            active_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_ACTIVE)])
-            reserved_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_RESERVED)])
-            deprecated_prefixes = netaddr.cidr_merge([p.prefix for p in queryset.filter(status=PREFIX_STATUS_DEPRECATED)])
-
-            # Find all available prefixes by subtracting each of the existing prefix sets from the aggregate prefix.
-            available_prefixes = (
-                netaddr.IPSet([aggregate.prefix])
-                - netaddr.IPSet(active_prefixes)
-                - netaddr.IPSet(reserved_prefixes)
-                - netaddr.IPSet(deprecated_prefixes)
-            )
-
-            # Add the size of each metric to the RIR total.
-            stats[rir]['total'] += aggregate.prefix.size / denominator
-            stats[rir]['active'] += netaddr.IPSet(active_prefixes).size / denominator
-            stats[rir]['reserved'] += netaddr.IPSet(reserved_prefixes).size / denominator
-            stats[rir]['deprecated'] += netaddr.IPSet(deprecated_prefixes).size / denominator
-            stats[rir]['available'] += available_prefixes.size / denominator
-
-        # Calculate the percentage of total space for each prefix status.
-        total = float(stats[rir]['total'])
-        stats[rir]['percentages'] = {
-            'active': float('{:.2f}'.format(stats[rir]['active'] / total * 100)) if total else 0,
-            'reserved': float('{:.2f}'.format(stats[rir]['reserved'] / total * 100)) if total else 0,
-            'deprecated': float('{:.2f}'.format(stats[rir]['deprecated'] / total * 100)) if total else 0,
-            'available': float('{:.2f}'.format(stats[rir]['available'] / total * 100)) if total else 0,
-        }
-        stats[rir]['percentages']['available'] = (
-            100
-            - stats[rir]['percentages']['active']
-            - stats[rir]['percentages']['reserved']
-            - stats[rir]['percentages']['deprecated']
-        )
-
-    totals = {
-        'total': sum([counts['total'] for rir, counts in stats.items()]),
-        'active': sum([counts['active'] for rir, counts in stats.items()]),
-        'reserved': sum([counts['reserved'] for rir, counts in stats.items()]),
-        'deprecated': sum([counts['deprecated'] for rir, counts in stats.items()]),
-        'available': sum([counts['available'] for rir, counts in stats.items()]),
-    }
-
-    return render(request, 'ipam/stats.html', {
-        'stats': stats,
-        'totals': totals,
-        'family': family,
-    })

+ 26 - 0
netbox/templates/ipam/rir_list.html

@@ -1,4 +1,5 @@
 {% extends '_base.html' %}
+{% load humanize %}
 {% load helpers %}
 
 {% block title %}RIRs{% endblock %}
@@ -18,4 +19,29 @@
         {% include 'utilities/obj_table.html' with bulk_delete_url='ipam:rir_bulk_delete' %}
     </div>
 </div>
+<div class="row">
+    <div class="col-md-2">
+        <h3>Totals</h3>
+    </div>
+    <div class="col-md-2 text-center">
+        <h3>{{ totals.total|intcomma }}</h3>
+        All IPv4 space
+    </div>
+    <div class="col-md-2 text-center">
+        <h3>{{ totals.active|intcomma }}</h3>
+        Active
+    </div>
+    <div class="col-md-2 text-center">
+        <h3>{{ totals.reserved|intcomma }}</h3>
+        Reserved
+    </div>
+    <div class="col-md-2 text-center">
+        <h3>{{ totals.deprecated|intcomma }}</h3>
+        Deprecated
+    </div>
+    <div class="col-md-2 text-center">
+        <h3>{{ totals.available|intcomma }}</h3>
+        Available
+    </div>
+</div>
 {% endblock %}

+ 0 - 90
netbox/templates/ipam/stats.html

@@ -1,90 +0,0 @@
-{% extends '_base.html' %}
-{% load humanize %}
-{% load render_table from django_tables2 %}
-
-{% block title %}RIR Statistics{% endblock %}
-
-{% block content %}
-<h1>RIR Statistics</h1>
-<div class="row">
-    <div class="col-md-9">
-        <ul class="nav nav-tabs" style="margin-bottom: 20px">
-            <li role="presentation"{% if family == 4 %} class="active"{% endif %}><a href="{% url 'ipam:rir_stats' %}">IPv4</a></li>
-            <li role="presentation"{% if family == 6 %} class="active"{% endif %}><a href="{% url 'ipam:rir_stats_ipv6' %}">IPv6</a></li>
-        </ul>
-        {% if family == 6 %}
-            <div class="alert alert-info alert-dismissible" role="alert">
-                <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                <strong>Note:</strong> Numbers shown here indicate equivalent /64 prefixes, not individual IP addresses.
-            </div>
-        {% endif %}
-        {% for rir, counts in stats.items %}
-            <h3>{{ rir }}</h3>
-            <div class="progress">
-                {% if counts.total %}
-                    <div class="progress-bar" role="progressbar" style="width: {{ counts.percentages.active }}%;">
-                        <span class="sr-only">{{ counts.percentages.active }}%</span>
-                    </div>
-                    <div class="progress-bar progress-bar-info" role="progressbar" style="width: {{ counts.percentages.reserved }}%;">
-                        <span class="sr-only">{{ counts.percentages.reserved }}%</span>
-                    </div>
-                    <div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{ counts.percentages.deprecated }}%;">
-                        <span class="sr-only">{{ counts.percentages.deprecated }}%</span>
-                    </div>
-                    <div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ counts.percentages.available }}%;">
-                        <span class="sr-only">{{ counts.percentages.available }}%</span>
-                    </div>
-                {% endif %}
-            </div>
-            <div class="row">
-                <div class="col-md-2 col-md-offset-2 text-center">
-                    <h4><span class="label label-default">{{ counts.total|intcomma }}</span></h4>
-                    Total
-                </div>
-                <div class="col-md-2 text-center">
-                    <h4><span class="label label-primary">{{ counts.active|intcomma }}</span></h4>
-                    Active
-                </div>
-                <div class="col-md-2 text-center">
-                    <h4><span class="label label-info">{{ counts.reserved|intcomma }}</span></h4>
-                    Reserved
-                </div>
-                <div class="col-md-2 text-center">
-                    <h4><span class="label label-danger">{{ counts.deprecated|intcomma }}</span></h4>
-                    Deprecated
-                </div>
-                <div class="col-md-2 text-center">
-                    <h4><span class="label label-success">{{ counts.available|intcomma }}</span></h4>
-                    Available
-                </div>
-            </div>
-        {% endfor %}
-        <hr />
-        <div class="row">
-            <div class="col-md-2">
-                <h3>Totals</h3>
-            </div>
-            <div class="col-md-2 text-center">
-                <h3>{{ totals.total|intcomma }}</h3>
-                All IPv{{ family }} space
-            </div>
-            <div class="col-md-2 text-center">
-                <h3>{{ totals.active|intcomma }}</h3>
-                Active
-            </div>
-            <div class="col-md-2 text-center">
-                <h3>{{ totals.reserved|intcomma }}</h3>
-                Reserved
-            </div>
-            <div class="col-md-2 text-center">
-                <h3>{{ totals.deprecated|intcomma }}</h3>
-                Deprecated
-            </div>
-            <div class="col-md-2 text-center">
-                <h3>{{ totals.available|intcomma }}</h3>
-                Available
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock %}