mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Incorporated stats into RIR list view
This commit is contained in:
parent
13cdc44caf
commit
d891c8c981
@ -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')
|
||||
|
||||
|
||||
#
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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 %}
|
||||
|
@ -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">×</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 %}
|
Loading…
Reference in New Issue
Block a user