mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 12:12:53 -06:00
Extended reports API
This commit is contained in:
parent
88c57d002d
commit
d35a2b0faa
@ -7,7 +7,7 @@ from rest_framework import serializers
|
|||||||
from dcim.api.serializers import NestedDeviceSerializer, NestedRackSerializer, NestedSiteSerializer
|
from dcim.api.serializers import NestedDeviceSerializer, NestedRackSerializer, NestedSiteSerializer
|
||||||
from dcim.models import Device, Rack, Site
|
from dcim.models import Device, Rack, Site
|
||||||
from extras.models import (
|
from extras.models import (
|
||||||
ACTION_CHOICES, ExportTemplate, Graph, GRAPH_TYPE_CHOICES, ImageAttachment, TopologyMap, UserAction,
|
ACTION_CHOICES, ExportTemplate, Graph, GRAPH_TYPE_CHOICES, ImageAttachment, ReportResult, TopologyMap, UserAction,
|
||||||
)
|
)
|
||||||
from users.api.serializers import NestedUserSerializer
|
from users.api.serializers import NestedUserSerializer
|
||||||
from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer, ValidatedModelSerializer
|
from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer, ValidatedModelSerializer
|
||||||
@ -127,6 +127,36 @@ class WritableImageAttachmentSerializer(ValidatedModelSerializer):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Reports
|
||||||
|
#
|
||||||
|
|
||||||
|
class ReportResultSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ReportResult
|
||||||
|
fields = ['created', 'user', 'failed', 'data']
|
||||||
|
|
||||||
|
|
||||||
|
class NestedReportResultSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ReportResult
|
||||||
|
fields = ['created', 'user', 'failed']
|
||||||
|
|
||||||
|
|
||||||
|
class ReportSerializer(serializers.Serializer):
|
||||||
|
module = serializers.CharField(max_length=255)
|
||||||
|
name = serializers.CharField(max_length=255)
|
||||||
|
description = serializers.CharField(max_length=255, required=False)
|
||||||
|
test_methods = serializers.ListField(child=serializers.CharField(max_length=255))
|
||||||
|
result = NestedReportResultSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
class ReportDetailSerializer(ReportSerializer):
|
||||||
|
result = ReportResultSerializer()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# User actions
|
# User actions
|
||||||
#
|
#
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from rest_framework.decorators import detail_route
|
from rest_framework.decorators import detail_route
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet
|
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.http import HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from extras import filters
|
from extras import filters
|
||||||
from extras.models import ExportTemplate, Graph, ImageAttachment, TopologyMap, UserAction
|
from extras.models import ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
|
||||||
from extras.reports import get_reports
|
from extras.reports import get_report, get_reports
|
||||||
from utilities.api import WritableSerializerMixin
|
from utilities.api import WritableSerializerMixin
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
@ -94,23 +93,76 @@ class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
|
|||||||
class ReportViewSet(ViewSet):
|
class ReportViewSet(ViewSet):
|
||||||
_ignore_model_permissions = True
|
_ignore_model_permissions = True
|
||||||
exclude_from_schema = True
|
exclude_from_schema = True
|
||||||
|
lookup_value_regex = '[^/]+' # Allow dots
|
||||||
|
|
||||||
def list(self, request):
|
def list(self, request):
|
||||||
|
|
||||||
ret_list = []
|
# Compile all reports
|
||||||
|
report_list = []
|
||||||
for module_name, reports in get_reports():
|
for module_name, reports in get_reports():
|
||||||
for report_name, report_cls in reports:
|
for report_name, report_cls in reports:
|
||||||
report = OrderedDict((
|
data = {
|
||||||
('module', module_name),
|
'module': module_name,
|
||||||
('name', report_name),
|
'name': report_name,
|
||||||
('description', report_cls.description),
|
'description': report_cls.description,
|
||||||
('test_methods', report_cls().test_methods),
|
'test_methods': report_cls().test_methods,
|
||||||
))
|
'result': None,
|
||||||
ret_list.append(report)
|
}
|
||||||
|
try:
|
||||||
|
result = ReportResult.objects.defer('data').get(report='{}.{}'.format(module_name, report_name))
|
||||||
|
data['result'] = result
|
||||||
|
except ReportResult.DoesNotExist:
|
||||||
|
pass
|
||||||
|
report_list.append(data)
|
||||||
|
|
||||||
return Response(ret_list)
|
serializer = serializers.ReportSerializer(report_list, many=True, context={'request': request})
|
||||||
|
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def retrieve(self, request, pk):
|
||||||
|
|
||||||
|
# Retrieve report by <module>.<report>
|
||||||
|
if '.' not in pk:
|
||||||
|
raise Http404
|
||||||
|
module_name, report_name = pk.split('.', 1)
|
||||||
|
report_cls = get_report(module_name, report_name)
|
||||||
|
data = {
|
||||||
|
'module': module_name,
|
||||||
|
'name': report_name,
|
||||||
|
'description': report_cls.description,
|
||||||
|
'test_methods': report_cls().test_methods,
|
||||||
|
'result': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Attach report result
|
||||||
|
try:
|
||||||
|
result = ReportResult.objects.get(report='{}.{}'.format(module_name, report_name))
|
||||||
|
data['result'] = result
|
||||||
|
except ReportResult.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
serializer = serializers.ReportDetailSerializer(data)
|
||||||
|
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@detail_route()
|
||||||
|
def run(self, request, pk):
|
||||||
|
|
||||||
|
# Retrieve report by <module>.<report>
|
||||||
|
if '.' not in pk:
|
||||||
|
raise Http404
|
||||||
|
module_name, report_name = pk.split('.', 1)
|
||||||
|
report_cls = get_report(module_name, report_name)
|
||||||
|
|
||||||
|
# Run the report
|
||||||
|
report = report_cls()
|
||||||
|
result = report.run()
|
||||||
|
|
||||||
|
# Save the ReportResult
|
||||||
|
ReportResult.objects.filter(report=pk).delete()
|
||||||
|
ReportResult(report=pk, failed=report.failed, data=result).save()
|
||||||
|
|
||||||
|
return Response('Report completed.')
|
||||||
|
|
||||||
|
|
||||||
class RecentActivityViewSet(ReadOnlyModelViewSet):
|
class RecentActivityViewSet(ReadOnlyModelViewSet):
|
||||||
|
@ -30,15 +30,15 @@ class Command(BaseCommand):
|
|||||||
"[{:%H:%M:%S}] Running {}.{}...".format(timezone.now(), module_name, report_name)
|
"[{:%H:%M:%S}] Running {}.{}...".format(timezone.now(), module_name, report_name)
|
||||||
)
|
)
|
||||||
report = report_cls()
|
report = report_cls()
|
||||||
results = report.run()
|
result = report.run()
|
||||||
|
|
||||||
# Record the results
|
# Record the results
|
||||||
ReportResult.objects.filter(report=report_name_full).delete()
|
ReportResult.objects.filter(report=report_name_full).delete()
|
||||||
ReportResult(report=report_name_full, failed=report.failed, data=results).save()
|
ReportResult(report=report_name_full, failed=report.failed, data=result).save()
|
||||||
|
|
||||||
# Report on success/failure
|
# Report on success/failure
|
||||||
status = self.style.ERROR('FAILED') if report.failed else self.style.SUCCESS('SUCCESS')
|
status = self.style.ERROR('FAILED') if report.failed else self.style.SUCCESS('SUCCESS')
|
||||||
for test_name, attrs in results.items():
|
for test_name, attrs in result.items():
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
"\t{}: {} success, {} info, {} warning, {} failed".format(
|
"\t{}: {} success, {} info, {} warning, {} failed".format(
|
||||||
test_name, attrs['success'], attrs['info'], attrs['warning'], attrs['failed']
|
test_name, attrs['success'], attrs['info'], attrs['warning'], attrs['failed']
|
||||||
|
@ -18,6 +18,14 @@ def is_report(obj):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_report(module_name, report_name):
|
||||||
|
"""
|
||||||
|
Return a specific report from within a module.
|
||||||
|
"""
|
||||||
|
module = importlib.import_module('reports.{}'.format(module_name))
|
||||||
|
return getattr(module, report_name)
|
||||||
|
|
||||||
|
|
||||||
def get_reports():
|
def get_reports():
|
||||||
"""
|
"""
|
||||||
Compile a list of all reports available across all modules in the reports path. Returns a list of tuples:
|
Compile a list of all reports available across all modules in the reports path. Returns a list of tuples:
|
||||||
|
Loading…
Reference in New Issue
Block a user