Merged release v2.3.7

This commit is contained in:
Jeremy Stretch
2018-07-27 11:43:27 -04:00
13 changed files with 268 additions and 145 deletions

View File

@@ -203,17 +203,35 @@ class RIRTable(BaseTable):
class RIRDetailTable(RIRTable):
stats_total = tables.Column(accessor='stats.total', verbose_name='Total',
footer=lambda table: sum(r.stats['total'] for r in table.data))
stats_active = tables.Column(accessor='stats.active', verbose_name='Active',
footer=lambda table: sum(r.stats['active'] for r in table.data))
stats_reserved = tables.Column(accessor='stats.reserved', verbose_name='Reserved',
footer=lambda table: sum(r.stats['reserved'] for r in table.data))
stats_deprecated = tables.Column(accessor='stats.deprecated', verbose_name='Deprecated',
footer=lambda table: sum(r.stats['deprecated'] for r in table.data))
stats_available = tables.Column(accessor='stats.available', verbose_name='Available',
footer=lambda table: sum(r.stats['available'] for r in table.data))
utilization = tables.TemplateColumn(template_code=RIR_UTILIZATION, verbose_name='Utilization')
stats_total = tables.Column(
accessor='stats.total',
verbose_name='Total',
footer=lambda table: sum(r.stats['total'] for r in table.data)
)
stats_active = tables.Column(
accessor='stats.active',
verbose_name='Active',
footer=lambda table: sum(r.stats['active'] for r in table.data)
)
stats_reserved = tables.Column(
accessor='stats.reserved',
verbose_name='Reserved',
footer=lambda table: sum(r.stats['reserved'] for r in table.data)
)
stats_deprecated = tables.Column(
accessor='stats.deprecated',
verbose_name='Deprecated',
footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
)
stats_available = tables.Column(
accessor='stats.available',
verbose_name='Available',
footer=lambda table: sum(r.stats['available'] for r in table.data)
)
utilization = tables.TemplateColumn(
template_code=RIR_UTILIZATION,
verbose_name='Utilization'
)
class Meta(RIRTable.Meta):
fields = (

View File

@@ -189,9 +189,15 @@ class RIRListView(ObjectListView):
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)])
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 = (
@@ -202,11 +208,11 @@ class RIRListView(ObjectListView):
)
# 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
stats['total'] += int(aggregate.prefix.size / denominator)
stats['active'] += int(netaddr.IPSet(active_prefixes).size / denominator)
stats['reserved'] += int(netaddr.IPSet(reserved_prefixes).size / denominator)
stats['deprecated'] += int(netaddr.IPSet(deprecated_prefixes).size / denominator)
stats['available'] += int(available_prefixes.size / denominator)
# Calculate the percentage of total space for each prefix status.
total = float(stats['total'])
@@ -226,20 +232,6 @@ class RIRListView(ObjectListView):
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 RIRCreateView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'ipam.add_rir'

View File

@@ -80,3 +80,5 @@ if settings.DEBUG:
urlpatterns = [
url(r'^{}'.format(settings.BASE_PATH), include(_patterns))
]
handler500 = 'utilities.views.server_error'

View File

@@ -376,12 +376,19 @@ table.reports td.method {
font-family: monospace;
padding-left: 30px;
}
table.reports td.stats label {
td.report-stats label {
display: inline-block;
line-height: 14px;
margin-bottom: 0;
min-width: 40px;
}
table.report th {
position: relative;
}
table.report th a {
position: absolute;
top: -51px;
}
/* AJAX loader */
.loading {

View File

@@ -29,63 +29,73 @@
<p class="lead">{{ report.description }}</p>
{% endif %}
{% if report.result %}
<p>Last run: {{ report.result.created }}</p>
{% else %}
<p class="text-muted">Last run: Never</p>
<p>Last run: <strong>{{ report.result.created }}</strong></p>
{% endif %}
</div>
<div class="col-md-9">
{% if report.result %}
<table class="table table-hover">
<thead>
<tr>
<th>Time</th>
<th>Level</th>
<th>Object</th>
<th>Message</th>
</tr>
</thead>
{% for method, data in report.result.data.items %}
<tr>
<th colspan="4"><a name="{{ method }}"></a>{{ method }}</th>
</tr>
{% for time, level, obj, url, message in data.log %}
<tr class="{% if level == 'failure' %}danger{% elif level %}{{ level }}{% endif %}">
<td>{{ time }}</td>
<td>
<label class="label label-{% if level == 'failure' %}danger{% else %}{{ level }}{% endif %}">{{ level|title }}</label>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Report Methods</strong>
</div>
<table class="table table-hover panel-body">
{% for method, data in report.result.data.items %}
<tr>
<td><code><a href="#{{ method }}">{{ method }}</a></code></td>
<td class="text-right report-stats">
<label class="label label-success">{{ data.success }}</label>
<label class="label label-info">{{ data.info }}</label>
<label class="label label-warning">{{ data.warning }}</label>
<label class="label label-danger">{{ data.failure }}</label>
</td>
<td>
{% if obj and url %}
<a href="{{ url }}">{{ obj }}</a>
{% elif obj %}
{{ obj }}
{% endif %}
</td>
<td>{{ message }}</td>
</tr>
{% endfor %}
{% endfor %}
</table>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Report Results</strong>
</div>
<table class="table table-hover panel-body report">
<thead>
<tr class="table-headings">
<th>Time</th>
<th>Level</th>
<th>Object</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{% for method, data in report.result.data.items %}
<tr>
<th colspan="4" style="font-family: monospace">
<a name="{{ method }}"></a>{{ method }}
</th>
</tr>
{% for time, level, obj, url, message in data.log %}
<tr class="{% if level == 'failure' %}danger{% elif level %}{{ level }}{% endif %}">
<td>{{ time }}</td>
<td>
<label class="label label-{% if level == 'failure' %}danger{% else %}{{ level }}{% endif %}">{{ level|title }}</label>
</td>
<td>
{% if obj and url %}
<a href="{{ url }}">{{ obj }}</a>
{% elif obj %}
{{ obj }}
{% endif %}
</td>
<td>{{ message }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="well">No results are available for this report. Please run the report first.</div>
{% endif %}
</div>
<div class="col-md-3">
{% if report.result %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Methods</strong>
</div>
<ul class="list-group">
{% for method, data in report.result.data.items %}
<li class="list-group-item">
<a href="#{{ method }}">{{ method }}</a>
<span class="badge">{{ data.log|length }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</div>

View File

@@ -38,7 +38,7 @@
<td colspan="3" class="method">
{{ method }}
</td>
<td class="text-right stats">
<td class="text-right report-stats">
<label class="label label-success">{{ stats.success }}</label>
<label class="label label-info">{{ stats.info }}</label>
<label class="label label-warning">{{ stats.warning }}</label>
@@ -69,7 +69,7 @@
<a href="#report.{{ report.name }}" class="list-group-item">
<i class="fa fa-list-alt"></i> {{ report.name }}
<div class="pull-right">
{% include 'extras/inc/report_label.html' %}
{% include 'extras/inc/report_label.html' with result=report.result %}
</div>
</a>
{% endfor %}

View File

@@ -5,9 +5,10 @@ import sys
from django.conf import settings
from django.db import ProgrammingError
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .views import server_error
BASE_PATH = getattr(settings, 'BASE_PATH', False)
LOGIN_REQUIRED = getattr(settings, 'LOGIN_REQUIRED', False)
@@ -65,23 +66,19 @@ class ExceptionHandlingMiddleware(object):
if isinstance(exception, Http404):
return
# Determine the type of exception
# Determine the type of exception. If it's a common issue, return a custom error page with instructions.
custom_template = None
if isinstance(exception, ProgrammingError):
template_name = 'exceptions/programming_error.html'
custom_template = 'exceptions/programming_error.html'
elif isinstance(exception, ImportError):
template_name = 'exceptions/import_error.html'
custom_template = 'exceptions/import_error.html'
elif (
sys.version_info[0] >= 3 and isinstance(exception, PermissionError)
) or (
isinstance(exception, OSError) and exception.errno == 13
):
template_name = 'exceptions/permission_error.html'
else:
template_name = '500.html'
custom_template = 'exceptions/permission_error.html'
# Return an error message
type_, error, traceback = sys.exc_info()
return render(request, template_name, {
'exception': str(type_),
'error': error,
}, status=500)
# Return a custom error message, or fall back to Django's default 500 error handling
if custom_template:
return server_error(request, template_name=custom_template)

View File

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
from collections import OrderedDict
from copy import deepcopy
import sys
from django.conf import settings
from django.contrib import messages
@@ -10,12 +11,16 @@ from django.core.exceptions import ValidationError
from django.db import transaction, IntegrityError
from django.db.models import Count, ProtectedError
from django.forms import CharField, Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
from django.http import HttpResponseServerError
from django.shortcuts import get_object_or_404, redirect, render
from django.template.exceptions import TemplateSyntaxError
from django.template import loader
from django.template.exceptions import TemplateDoesNotExist, TemplateSyntaxError
from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME
from django.views.generic import View
from django_tables2 import RequestConfig
@@ -844,3 +849,20 @@ class BulkComponentCreateView(GetReturnURLMixin, View):
'table': table,
'return_url': self.get_return_url(request),
})
@requires_csrf_token
def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
"""
Custom 500 handler to provide additional context when rendering 500.html.
"""
try:
template = loader.get_template(template_name)
except TemplateDoesNotExist:
return HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
type_, error, traceback = sys.exc_info()
return HttpResponseServerError(template.render({
'exception': str(type_),
'error': error,
}))