diff --git a/netbox/extras/migrations/0108_convert_reports_to_scripts.py b/netbox/extras/migrations/0108_convert_reports_to_scripts.py index 07235355013..b547c41c32a 100644 --- a/netbox/extras/migrations/0108_convert_reports_to_scripts.py +++ b/netbox/extras/migrations/0108_convert_reports_to_scripts.py @@ -25,7 +25,4 @@ class Migration(migrations.Migration): migrations.DeleteModel( name='Report', ), - migrations.DeleteModel( - name='ReportModule', - ), ] diff --git a/netbox/extras/migrations/0109_script_model.py b/netbox/extras/migrations/0109_script_model.py index 89b343a8222..7570077a771 100644 --- a/netbox/extras/migrations/0109_script_model.py +++ b/netbox/extras/migrations/0109_script_model.py @@ -82,10 +82,12 @@ def update_scripts(apps, schema_editor): ContentType = apps.get_model('contenttypes', 'ContentType') Script = apps.get_model('extras', 'Script') ScriptModule = apps.get_model('extras', 'ScriptModule') + ReportModule = apps.get_model('extras', 'ReportModule') Job = apps.get_model('core', 'Job') - script_ct = ContentType.objects.get_for_model(Script) - scriptmodule_ct = ContentType.objects.get_for_model(ScriptModule) + script_ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) + scriptmodule_ct = ContentType.objects.get_for_model(ScriptModule, for_concrete_model=False) + reportmodule_ct = ContentType.objects.get_for_model(ReportModule, for_concrete_model=False) for module in ScriptModule.objects.all(): for script_name in get_module_scripts(module): @@ -96,10 +98,16 @@ def update_scripts(apps, schema_editor): # Update all Jobs associated with this ScriptModule & script name to point to the new Script object Job.objects.filter( - object_type=scriptmodule_ct, + object_type_id=scriptmodule_ct.id, + object_id=module.pk, + name=script_name + ).update(object_type_id=script_ct.id, object_id=script.pk) + # Update all Jobs associated with this ScriptModule & script name to point to the new Script object + Job.objects.filter( + object_type_id=reportmodule_ct.id, object_id=module.pk, name=script_name - ).update(object_type=script_ct, object_id=script.pk) + ).update(object_type_id=script_ct.id, object_id=script.pk) def update_event_rules(apps, schema_editor): diff --git a/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py b/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py index 9103524622f..b7373bdce23 100644 --- a/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py +++ b/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py @@ -12,4 +12,7 @@ class Migration(migrations.Migration): model_name='eventrule', name='action_parameters', ), + migrations.DeleteModel( + name='ReportModule', + ), ] diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 8482c5e24a9..a9d80783c56 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _ from extras.models import * -from netbox.tables import NetBoxTable, columns +from netbox.tables import BaseTable, NetBoxTable, columns from .template_code import * __all__ = ( @@ -21,6 +21,8 @@ 'JournalEntryTable', 'ObjectChangeTable', 'SavedFilterTable', + 'ReportResultsTable', + 'ScriptResultsTable', 'TaggedItemTable', 'TagTable', 'WebhookTable', @@ -507,3 +509,61 @@ class Meta(NetBoxTable.Meta): default_columns = ( 'pk', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments' ) + + +class ScriptResultsTable(BaseTable): + index = tables.Column( + verbose_name=_('Line') + ) + time = tables.Column( + verbose_name=_('Time') + ) + status = tables.TemplateColumn( + template_code="""{% load log_levels %}{% log_level record.status %}""", + verbose_name=_('Level') + ) + message = tables.Column( + verbose_name=_('Message') + ) + + class Meta(BaseTable.Meta): + empty_text = _('No results found') + fields = ( + 'index', 'time', 'status', 'message', + ) + + +class ReportResultsTable(BaseTable): + index = tables.Column( + verbose_name=_('Line') + ) + method = tables.Column( + verbose_name=_('Method') + ) + time = tables.Column( + verbose_name=_('Time') + ) + status = tables.Column( + empty_values=(), + verbose_name=_('Level') + ) + status = tables.TemplateColumn( + template_code="""{% load log_levels %}{% log_level record.status %}""", + verbose_name=_('Level') + ) + + object = tables.Column( + verbose_name=_('Object') + ) + url = tables.Column( + verbose_name=_('URL') + ) + message = tables.Column( + verbose_name=_('Message') + ) + + class Meta(BaseTable.Meta): + empty_text = _('No results found') + fields = ( + 'index', 'method', 'time', 'status', 'object', 'url', 'message', + ) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 56840f7a9aa..a0370544f4c 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -17,6 +17,7 @@ from extras.dashboard.utils import get_widget_class from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic +from netbox.views.generic.mixins import TableMixin from utilities.forms import ConfirmationForm, get_field_value from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.rqworker import get_workers_for_queue @@ -26,6 +27,7 @@ from . import filtersets, forms, tables from .models import * from .scripts import run_script +from .tables import ReportResultsTable, ScriptResultsTable # @@ -1143,19 +1145,72 @@ def get(self, request, module, name, path=''): return redirect(f'{url}{path}') -class ScriptResultView(generic.ObjectView): +class ScriptResultView(TableMixin, generic.ObjectView): queryset = Job.objects.all() def get_required_permission(self): return 'extras.view_script' + def get_table(self, job, request, bulk_actions=True): + data = [] + tests = None + table = None + index = 0 + if job.data: + if 'log' in job.data: + if 'tests' in job.data: + tests = job.data['tests'] + + for log in job.data['log']: + index += 1 + result = { + 'index': index, + 'time': log.get('time'), + 'status': log.get('status'), + 'message': log.get('message'), + } + data.append(result) + + table = ScriptResultsTable(data, user=request.user) + table.configure(request) + else: + # for legacy reports + tests = job.data + + if tests: + for method, test_data in tests.items(): + if 'log' in test_data: + for time, status, obj, url, message in test_data['log']: + index += 1 + result = { + 'index': index, + 'method': method, + 'time': time, + 'status': status, + 'object': obj, + 'url': url, + 'message': message, + } + data.append(result) + + table = ReportResultsTable(data, user=request.user) + table.configure(request) + + return table + def get(self, request, **kwargs): + table = None job = get_object_or_404(Job.objects.all(), pk=kwargs.get('job_pk')) + if job.completed: + table = self.get_table(job, request, bulk_actions=False) + context = { 'script': job.object, 'job': job, + 'table': table, } + if job.data and 'log' in job.data: # Script context['tests'] = job.data.get('tests', {}) diff --git a/netbox/templates/extras/htmx/script_result.html b/netbox/templates/extras/htmx/script_result.html index ed5dd9cbd29..e532e07e14f 100644 --- a/netbox/templates/extras/htmx/script_result.html +++ b/netbox/templates/extras/htmx/script_result.html @@ -3,124 +3,63 @@ {% load log_levels %} {% load i18n %} -
- {% if job.started %} - {% trans "Started" %}: {{ job.started|annotated_date }} - {% elif job.scheduled %} - {% trans "Scheduled for" %}: {{ job.scheduled|annotated_date }} ({{ job.scheduled|naturaltime }}) - {% else %} - {% trans "Created" %}: {{ job.created|annotated_date }} - {% endif %} +
+ {% if job.started %} + {% trans "Started" %}: {{ job.started|annotated_date }} + {% elif job.scheduled %} + {% trans "Scheduled for" %}: {{ job.scheduled|annotated_date }} ({{ job.scheduled|naturaltime }}) + {% else %} + {% trans "Created" %}: {{ job.created|annotated_date }} + {% endif %} + {% if job.completed %} + {% trans "Duration" %}: {{ job.duration }} + {% endif %} + {% badge job.get_status_display job.get_status_color %} +
{% if job.completed %} - {% trans "Duration" %}: {{ job.duration }} - {% endif %} - {% badge job.get_status_display job.get_status_color %} - -{% if job.completed %} - - {# Script log. Legacy reports will not have this. #} - {% if 'log' in job.data %} -{% trans "Line" %} | -{% trans "Time" %} | -{% trans "Level" %} | -{% trans "Message" %} | -
---|
{{ forloop.counter }} | -{{ log.time|placeholder }} | -{% log_level log.status %} | -{{ log.message|markdown }} | +{{ test }} | ++ {{ data.success }} + {{ data.info }} + {{ data.warning }} + {{ data.failure }} + |
{{ job.data.output }}- {% else %} -
{{ test }} | -- {{ data.success }} - {{ data.info }} - {{ data.warning }} - {{ data.failure }} - | -
{% trans "Time" %} | -{% trans "Level" %} | -{% trans "Object" %} | -{% trans "Message" %} | -
---|---|---|---|
- {{ test }} - | -|||
{{ time }} | -- - | -- {% if obj and url %} - {{ obj }} - {% elif obj %} - {{ obj }} - {% else %} - {{ ''|placeholder }} - {% endif %} - | -{{ message|markdown }} | -
{{ job.data.output }}+ {% else %} +
{{ script.filename }}
{{ script.source }}-