Merge pull request #10290 from kkthxbye-code/10258-nested-module-scripts

Fixes #10258 - Allow running scripts nested in modules/packages
This commit is contained in:
Jeremy Stretch 2022-09-15 10:06:28 -04:00 committed by GitHub
commit 7b4f5252f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 38 additions and 24 deletions

View File

@ -159,7 +159,7 @@ class ReportViewSet(ViewSet):
# Read the PK as "<module>.<report>"
if '.' not in pk:
raise Http404
module_name, report_name = pk.split('.', 1)
module_name, report_name = pk.split('.', maxsplit=1)
# Raise a 404 on an invalid Report module/name
report = get_report(module_name, report_name)
@ -183,8 +183,8 @@ class ReportViewSet(ViewSet):
}
# Iterate through all available Reports.
for module_name, reports in get_reports():
for report in reports:
for module_name, reports in get_reports().items():
for report in reports.values():
# Attach the relevant JobResult (if any) to each Report.
report.result = results.get(report.full_name, None)
@ -257,7 +257,7 @@ class ScriptViewSet(ViewSet):
lookup_value_regex = '[^/]+' # Allow dots
def _get_script(self, pk):
module_name, script_name = pk.split('.')
module_name, script_name = pk.split('.', maxsplit=1)
script = get_script(module_name, script_name)
if script is None:
raise Http404

View File

@ -21,8 +21,8 @@ class Command(BaseCommand):
reports = get_reports()
# Run reports
for module_name, report_list in reports:
for report in report_list:
for module_name, report_list in reports.items():
for report in report_list.values():
if module_name in options['reports'] or report.full_name in options['reports']:
# Run the report and create a new JobResult

View File

@ -26,20 +26,18 @@ def get_report(module_name, report_name):
"""
Return a specific report from within a module.
"""
file_path = '{}/{}.py'.format(settings.REPORTS_ROOT, module_name)
reports = get_reports()
module = reports.get(module_name)
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(module)
except FileNotFoundError:
if module is None:
return None
report = getattr(module, report_name, None)
report = module.get(report_name)
if report is None:
return None
return report()
return report
def get_reports():
@ -52,7 +50,7 @@ def get_reports():
...
]
"""
module_list = []
module_list = {}
# Iterate through all modules within the reports path. These are the user-created files in which reports are
# defined.
@ -61,7 +59,16 @@ def get_reports():
report_order = getattr(module, "report_order", ())
ordered_reports = [cls() for cls in report_order if is_report(cls)]
unordered_reports = [cls() for _, cls in inspect.getmembers(module, is_report) if cls not in report_order]
module_list.append((module_name, [*ordered_reports, *unordered_reports]))
module_reports = {}
for cls in [*ordered_reports, *unordered_reports]:
# For reports in submodules use the full import path w/o the root module as the name
report_name = cls.full_name.split(".", maxsplit=1)[1]
module_reports[report_name] = cls
if module_reports:
module_list[module_name] = module_reports
return module_list

View File

@ -299,6 +299,10 @@ class BaseScript:
def module(cls):
return cls.__module__
@classmethod
def root_module(cls):
return cls.__module__.split(".")[0]
@classproperty
def job_timeout(self):
return getattr(self.Meta, 'job_timeout', None)
@ -514,7 +518,9 @@ def get_scripts(use_names=False):
ordered_scripts = [cls for cls in script_order if is_script(cls)]
unordered_scripts = [cls for _, cls in inspect.getmembers(module, is_script) if cls not in script_order]
for cls in [*ordered_scripts, *unordered_scripts]:
module_scripts[cls.__name__] = cls
# For scripts in submodules use the full import path w/o the root module as the name
script_name = cls.full_name.split(".", maxsplit=1)[1]
module_scripts[script_name] = cls
if module_scripts:
scripts[module_name] = module_scripts

View File

@ -1,4 +1,4 @@
from django.urls import path
from django.urls import path, re_path
from extras import models, views
from netbox.views.generic import ObjectChangeLogView
@ -100,12 +100,12 @@ urlpatterns = [
# Reports
path('reports/', views.ReportListView.as_view(), name='report_list'),
path('reports/<str:module>.<str:name>/', views.ReportView.as_view(), name='report'),
path('reports/results/<int:job_result_pk>/', views.ReportResultView.as_view(), name='report_result'),
re_path(r'^reports/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ReportView.as_view(), name='report'),
# Scripts
path('scripts/', views.ScriptListView.as_view(), name='script_list'),
path('scripts/<str:module>.<str:name>/', views.ScriptView.as_view(), name='script'),
path('scripts/results/<int:job_result_pk>/', views.ScriptResultView.as_view(), name='script_result'),
re_path(r'^scripts/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ScriptView.as_view(), name='script'),
]

View File

@ -534,9 +534,10 @@ class ReportListView(ContentTypePermissionRequiredMixin, View):
}
ret = []
for module, report_list in reports:
for module, report_list in reports.items():
module_reports = []
for report in report_list:
for report in report_list.values():
report.result = results.get(report.full_name, None)
module_reports.append(report)
ret.append((module, module_reports))
@ -613,7 +614,7 @@ class ReportResultView(ContentTypePermissionRequiredMixin, View):
result = get_object_or_404(JobResult.objects.all(), pk=job_result_pk, obj_type=report_content_type)
# Retrieve the Report and attach the JobResult to it
module, report_name = result.name.split('.')
module, report_name = result.name.split('.', maxsplit=1)
report = get_report(module, report_name)
report.result = result

View File

@ -34,7 +34,7 @@
{% for class_name, script in module_scripts.items %}
<tr>
<td>
<a href="{% url 'extras:script' module=script.module name=class_name %}" name="script.{{ class_name }}">{{ script.name }}</a>
<a href="{% url 'extras:script' module=script.root_module name=class_name %}" name="script.{{ class_name }}">{{ script.name }}</a>
</td>
<td>
{% include 'extras/inc/job_label.html' with result=script.result %}