Explorar el Código

Closes #11152: Add support to abort custom script gracefully (#11621)

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
Maximilian Wilhelm hace 3 años
padre
commit
699edd049c

+ 13 - 0
docs/customization/custom-scripts.md

@@ -142,6 +142,19 @@ obj.full_clean()
 obj.save()
 obj.save()
 ```
 ```
 
 
+## Error handling
+
+Sometimes things go wrong and a script will run into an `Exception`. If that happens and an uncaught exception is raised by the custom script, the execution is aborted and a full stack trace is reported.
+
+Although this is helpful for debugging, in some situations it might be required to cleanly abort the execution of a custom script (e.g. because of invalid input data) and thereby make sure no changes are performed on the database. In this case the script can throw an `AbortScript` exception, which will prevent the stack trace from being reported, but still terminating the script's execution and reporting a given error message.
+
+```python
+from utilities.exceptions import AbortScript
+
+if some_error:
+    raise AbortScript("Some meaningful error message")
+```
+
 ## Variable Reference
 ## Variable Reference
 
 
 ### Default Options
 ### Default Options

+ 9 - 1
netbox/extras/scripts.py

@@ -21,7 +21,7 @@ from extras.models import JobResult
 from extras.signals import clear_webhooks
 from extras.signals import clear_webhooks
 from ipam.formfields import IPAddressFormField, IPNetworkFormField
 from ipam.formfields import IPAddressFormField, IPNetworkFormField
 from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator
 from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator
-from utilities.exceptions import AbortTransaction
+from utilities.exceptions import AbortScript, AbortTransaction
 from utilities.forms import add_blank_choice, DynamicModelChoiceField, DynamicModelMultipleChoiceField
 from utilities.forms import add_blank_choice, DynamicModelChoiceField, DynamicModelMultipleChoiceField
 from .context_managers import change_logging
 from .context_managers import change_logging
 from .forms import ScriptForm
 from .forms import ScriptForm
@@ -470,6 +470,14 @@ def run_script(data, request, commit=True, *args, **kwargs):
         except AbortTransaction:
         except AbortTransaction:
             script.log_info("Database changes have been reverted automatically.")
             script.log_info("Database changes have been reverted automatically.")
             clear_webhooks.send(request)
             clear_webhooks.send(request)
+        except AbortScript as e:
+            script.log_failure(
+                f"Script aborted with error: {e}"
+            )
+            script.log_info("Database changes have been reverted due to error.")
+            logger.error(f"Script aborted with error: {e}")
+            job_result.set_status(JobResultStatusChoices.STATUS_ERRORED)
+            clear_webhooks.send(request)
         except Exception as e:
         except Exception as e:
             stacktrace = traceback.format_exc()
             stacktrace = traceback.format_exc()
             script.log_failure(
             script.log_failure(

+ 7 - 0
netbox/utilities/exceptions.py

@@ -24,6 +24,13 @@ class AbortRequest(Exception):
         self.message = message
         self.message = message
 
 
 
 
+class AbortScript(Exception):
+    """
+    Raised to cleanly abort a script.
+    """
+    pass
+
+
 class PermissionsViolation(Exception):
 class PermissionsViolation(Exception):
     """
     """
     Raised when an operation was prevented because it would violate the
     Raised when an operation was prevented because it would violate the