diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 82c68c86d..63003bdf2 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -159,7 +159,7 @@ class ReportViewSet(ViewSet): # Read the PK as "." 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 diff --git a/netbox/extras/management/commands/runreport.py b/netbox/extras/management/commands/runreport.py index ee166ae6a..38d435613 100644 --- a/netbox/extras/management/commands/runreport.py +++ b/netbox/extras/management/commands/runreport.py @@ -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 diff --git a/netbox/extras/reports.py b/netbox/extras/reports.py index 43d916aff..32e4efc2d 100644 --- a/netbox/extras/reports.py +++ b/netbox/extras/reports.py @@ -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 diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 6e4478304..23a778789 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -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 diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index 4c23adb0f..ced3bd4b9 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -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/./', views.ReportView.as_view(), name='report'), path('reports/results//', views.ReportResultView.as_view(), name='report_result'), + re_path(r'^reports/(?P.([^.]+)).(?P.(.+))/', views.ReportView.as_view(), name='report'), # Scripts path('scripts/', views.ScriptListView.as_view(), name='script_list'), - path('scripts/./', views.ScriptView.as_view(), name='script'), path('scripts/results//', views.ScriptResultView.as_view(), name='script_result'), + re_path(r'^scripts/(?P.([^.]+)).(?P.(.+))/', views.ScriptView.as_view(), name='script'), ] diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 30f48f817..d8a015bb0 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -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 diff --git a/netbox/templates/extras/script_list.html b/netbox/templates/extras/script_list.html index 8884ff77c..1f34f4d5e 100644 --- a/netbox/templates/extras/script_list.html +++ b/netbox/templates/extras/script_list.html @@ -34,7 +34,7 @@ {% for class_name, script in module_scripts.items %} - {{ script.name }} + {{ script.name }} {% include 'extras/inc/job_label.html' with result=script.result %}