15093 Allow plugins to register events pipeline (#17717)

* 15093 add events_pipeline registration to plugins

* 15093 use list

* 15093 add documentation

* Update docs/configuration/index.md

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* Update docs/configuration/miscellaneous.md

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* 15093 review changes

* 15093 review changes

* Formatting & readability

* 15093 review changes

* 15093 add test

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
Arthur Hanson 2024-10-18 09:47:16 -07:00 committed by GitHub
parent a8ec06687a
commit b9c6def8ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 36 additions and 2 deletions

View File

@ -106,6 +106,15 @@ By default, NetBox will prevent the creation of duplicate prefixes and IP addres
--- ---
## EVENTS_PIPELINE
Default: `['extras.events.process_event_queue',]`
NetBox will call dotted paths to the functions listed here for events (create, update, delete) on models as well as when custom EventRules are fired.
---
## FILE_UPLOAD_MAX_MEMORY_SIZE ## FILE_UPLOAD_MAX_MEMORY_SIZE
Default: `2621440` (2.5 MB) Default: `2621440` (2.5 MB)

View File

@ -114,6 +114,7 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
| `max_version` | Maximum version of NetBox with which the plugin is compatible | | `max_version` | Maximum version of NetBox with which the plugin is compatible |
| `middleware` | A list of middleware classes to append after NetBox's build-in middleware | | `middleware` | A list of middleware classes to append after NetBox's build-in middleware |
| `queues` | A list of custom background task queues to create | | `queues` | A list of custom background task queues to create |
| `events_pipeline` | A list of handlers to add to [`EVENTS_PIPELINE`](./miscellaneous.md#events_pipeline), identified by dotted paths |
| `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) | | `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) |
| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) | | `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) |
| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) | | `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) |

View File

@ -78,6 +78,7 @@ class PluginConfig(AppConfig):
menu_items = None menu_items = None
template_extensions = None template_extensions = None
user_preferences = None user_preferences = None
events_pipeline = []
def _load_resource(self, name): def _load_resource(self, name):
# Import from the configured path, if defined. # Import from the configured path, if defined.

View File

@ -110,9 +110,9 @@ DEFAULT_PERMISSIONS = getattr(configuration, 'DEFAULT_PERMISSIONS', {
DEVELOPER = getattr(configuration, 'DEVELOPER', False) DEVELOPER = getattr(configuration, 'DEVELOPER', False)
DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs')) DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs'))
EMAIL = getattr(configuration, 'EMAIL', {}) EMAIL = getattr(configuration, 'EMAIL', {})
EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', ( EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', [
'extras.events.process_event_queue', 'extras.events.process_event_queue',
)) ])
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {}) FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {})
FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440) FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440)
@ -787,6 +787,10 @@ STRAWBERRY_DJANGO = {
PLUGIN_CATALOG_URL = 'https://api.netbox.oss.netboxlabs.com/v1/plugins' PLUGIN_CATALOG_URL = 'https://api.netbox.oss.netboxlabs.com/v1/plugins'
EVENTS_PIPELINE = list(EVENTS_PIPELINE)
if 'extras.events.process_event_queue' not in EVENTS_PIPELINE:
EVENTS_PIPELINE.insert(0, 'extras.events.process_event_queue')
# Register any configured plugins # Register any configured plugins
for plugin_name in PLUGINS: for plugin_name in PLUGINS:
try: try:
@ -857,6 +861,13 @@ for plugin_name in PLUGINS:
f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues
}) })
events_pipeline = plugin_config.events_pipeline
if events_pipeline:
if type(events_pipeline) in (list, tuple):
EVENTS_PIPELINE.extend(events_pipeline)
else:
raise ImproperlyConfigured(f"events_pipline in plugin: {plugin_name} must be a list or tuple")
# UNSUPPORTED FUNCTIONALITY: Import any local overrides. # UNSUPPORTED FUNCTIONALITY: Import any local overrides.
try: try:
from .local_settings import * from .local_settings import *

View File

@ -17,6 +17,9 @@ class DummyPluginConfig(PluginConfig):
'testing-medium', 'testing-medium',
'testing-high' 'testing-high'
] ]
events_pipeline = [
'netbox.tests.dummy_plugin.events.process_events_queue'
]
config = DummyPluginConfig config = DummyPluginConfig

View File

@ -0,0 +1,2 @@
def process_events_queue(events):
pass

View File

@ -203,3 +203,10 @@ class PluginTest(TestCase):
self.assertEqual(get_plugin_config(plugin, 'foo'), 123) self.assertEqual(get_plugin_config(plugin, 'foo'), 123)
self.assertEqual(get_plugin_config(plugin, 'bar'), None) self.assertEqual(get_plugin_config(plugin, 'bar'), None)
self.assertEqual(get_plugin_config(plugin, 'bar', default=456), 456) self.assertEqual(get_plugin_config(plugin, 'bar', default=456), 456)
def test_events_pipeline(self):
"""
Check that events pipeline is registered.
"""
self.assertIn('netbox.tests.dummy_plugin.events.process_events_queue', settings.EVENTS_PIPELINE)