Przeglądaj źródła

Enable scheduling_enabled parameter for reports

jeremystretch 2 lat temu
rodzic
commit
b167153186

+ 4 - 0
docs/customization/reports.md

@@ -91,6 +91,10 @@ As you can see, reports are completely customizable. Validation logic can be as
 
 A human-friendly description of what your report does.
 
+### `scheduling_enabled`
+
+By default, a report can be scheduled for execution at a later time. Setting `scheduling_enabled` to False disables this ability: Only immediate execution will be possible. (This also disables the ability to set a recurring execution interval.)
+
 ### `job_timeout`
 
 Set the maximum allowed runtime for the report. If not set, `RQ_DEFAULT_TIMEOUT` will be used.

+ 1 - 0
docs/release-notes/version-3.5.md

@@ -57,6 +57,7 @@ Two new webhook trigger events have been introduced: `job_start` and `job_end`.
 * [#10729](https://github.com/netbox-community/netbox/issues/10729) - Add date & time custom field type
 * [#11029](https://github.com/netbox-community/netbox/issues/11029) - Enable change logging for cable terminations
 * [#11254](https://github.com/netbox-community/netbox/issues/11254) - Introduce the `X-Request-ID` HTTP header to annotate the unique ID of each request for change logging
+* [#11255](https://github.com/netbox-community/netbox/issues/11255) - Introduce the `scheduling_enabled` settings for reports & scripts
 * [#11291](https://github.com/netbox-community/netbox/issues/11291) - Optimized GraphQL API request handling
 * [#11440](https://github.com/netbox-community/netbox/issues/11440) - Add an `enabled` field for device type interfaces
 * [#11494](https://github.com/netbox-community/netbox/issues/11494) - Enable filtering objects by create/update request IDs

+ 10 - 0
netbox/extras/api/serializers.py

@@ -443,6 +443,16 @@ class ReportInputSerializer(serializers.Serializer):
     schedule_at = serializers.DateTimeField(required=False, allow_null=True)
     interval = serializers.IntegerField(required=False, allow_null=True)
 
+    def validate_schedule_at(self, value):
+        if value and not self.context['report'].scheduling_enabled:
+            raise serializers.ValidationError("Scheduling is not enabled for this report.")
+        return value
+
+    def validate_interval(self, value):
+        if value and not self.context['report'].scheduling_enabled:
+            raise serializers.ValidationError("Scheduling is not enabled for this report.")
+        return value
+
 
 #
 # Scripts

+ 6 - 2
netbox/extras/api/views.py

@@ -244,8 +244,12 @@ class ReportViewSet(ViewSet):
             raise RQWorkerNotRunningException()
 
         # Retrieve and run the Report. This will create a new Job.
-        module, report = self._get_report(pk)
-        input_serializer = serializers.ReportInputSerializer(data=request.data)
+        module, report_cls = self._get_report(pk)
+        report = report_cls()
+        input_serializer = serializers.ReportInputSerializer(
+            data=request.data,
+            context={'report': report}
+        )
 
         if input_serializer.is_valid():
             report.result = Job.enqueue(

+ 15 - 10
netbox/extras/forms/reports.py

@@ -25,20 +25,25 @@ class ReportForm(BootstrapMixin, forms.Form):
         help_text=_("Interval at which this report is re-run (in minutes)")
     )
 
+    def __init__(self, *args, scheduling_enabled=True, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Annotate the current system time for reference
+        now = local_now().strftime('%Y-%m-%d %H:%M:%S')
+        self.fields['schedule_at'].help_text += f' (current time: <strong>{now}</strong>)'
+
+        # Remove scheduling fields if scheduling is disabled
+        if not scheduling_enabled:
+            self.fields.pop('schedule_at')
+            self.fields.pop('interval')
+
     def clean(self):
-        scheduled_time = self.cleaned_data['schedule_at']
+        scheduled_time = self.cleaned_data.get('schedule_at')
         if scheduled_time and scheduled_time < local_now():
             raise forms.ValidationError(_('Scheduled time must be in the future.'))
 
-        # When interval is used without schedule at, raise an exception
-        if self.cleaned_data['interval'] and not scheduled_time:
+        # When interval is used without schedule at, schedule for the current time
+        if self.cleaned_data.get('interval') and not scheduled_time:
             self.cleaned_data['schedule_at'] = local_now()
 
         return self.cleaned_data
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-
-        # Annotate the current system time for reference
-        now = local_now().strftime('%Y-%m-%d %H:%M:%S')
-        self.fields['schedule_at'].help_text += f' (current time: <strong>{now}</strong>)'

+ 2 - 2
netbox/extras/forms/scripts.py

@@ -44,12 +44,12 @@ class ScriptForm(BootstrapMixin, forms.Form):
             self.fields.pop('_interval')
 
     def clean(self):
-        scheduled_time = self.cleaned_data['_schedule_at']
+        scheduled_time = self.cleaned_data.get('_schedule_at')
         if scheduled_time and scheduled_time < local_now():
             raise forms.ValidationError(_('Scheduled time must be in the future.'))
 
         # When interval is used without schedule at, schedule for the current time
-        if self.cleaned_data['_interval'] and not scheduled_time:
+        if self.cleaned_data.get('_interval') and not scheduled_time:
             self.cleaned_data['_schedule_at'] = local_now()
 
         return self.cleaned_data

+ 1 - 0
netbox/extras/reports.py

@@ -83,6 +83,7 @@ class Report(object):
     }
     """
     description = None
+    scheduling_enabled = True
     job_timeout = None
 
     def __init__(self):

+ 2 - 2
netbox/extras/views.py

@@ -876,7 +876,7 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
         return render(request, 'extras/report.html', {
             'module': module,
             'report': report,
-            'form': ReportForm(),
+            'form': ReportForm(scheduling_enabled=report.scheduling_enabled),
         })
 
     def post(self, request, module, name):
@@ -885,7 +885,7 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
 
         module = get_object_or_404(ReportModule.objects.restrict(request.user), file_path__startswith=module)
         report = module.reports[name]()
-        form = ReportForm(request.POST)
+        form = ReportForm(request.POST, scheduling_enabled=report.scheduling_enabled)
 
         if form.is_valid():