Repository navigation
Description
NetBox Edition
NetBox Community
NetBox Version
v4.7.4
Python Version
3.14
Steps to Reproduce
- Create a minimal plugin which registers an event type in
ready().docs/plugins/development/event-types.mdsays this "can be done anywhere within the plugin":
# probe_plugin/__init__.py
from netbox.plugins import PluginConfig
class ProbeConfig(PluginConfig):
name = 'probe_plugin'
verbose_name = 'Probe'
version = '0.0.1'
def ready(self):
super().ready()
from netbox.events import EventType
EventType('probe_event', 'Probe event').register()
config = ProbeConfig- Enable it with
PLUGINS = ['probe_plugin']and start NetBox. - Go to Operations > Integrations > Event Rules, click Add, and open the Event types list.
- Go to Operations > Integrations > Event Rules, click Import, and import this CSV:
name,object_types,event_types,action_type,action_object
probe,dcim.site,probe_event,webhook,<an existing webhook name>
Expected Behavior
"Probe event" is listed in the Event types field of the add/edit form and of the bulk edit form, and the CSV import accepts probe_event, as on NetBox v4.6.
Observed Behavior
"Probe event" is not listed, and the CSV import fails with:
Select a valid choice. probe_event is not one of the available choices.
The type is registered (registry['event_types'] contains it), and the REST API accepts it, because the model field resolves its choices on each call. The same happens with the event types of netbox-branching (branch_provisioned, branch_deprovisioned, branch_synced, branch_merged, branch_reverted), so a UI user cannot create an event rule on any plugin event type.
| Version | Plugin event type in the event rule form |
|---|---|
| v4.6.9, v4.6.10 | Yes |
v4.7.0, v4.7.1, v4.7.4, main (f810ba8) |
No |
Suspected Cause
EventRuleForm, EventRuleBulkEditForm and EventRuleImportForm call get_event_type_choices() in the class body, so the list of choices is fixed when extras.forms is first imported.
Since v4.7.0, extras.forms is imported by the extras app's own ready(), which runs before the ready() of any plugin:
extras/apps.py ready()
-> extras/signals.py from .cache import ...
-> extras/cache.py from extras.jobs import RenderConfigContextJob
-> extras/jobs.py from extras.scripts import _UNSET
-> extras/scripts.py from .forms import ScriptForm
-> extras/forms/__init__.py -> model_forms.py, bulk_edit.py, bulk_import.py
No single commit is at fault. Two links were added on different branches, and each branch alone is correct:
- 288c47d (Pre-render and cache config context data #21025,
feature) addedsignals.py -> cache.py -> extras.jobs. - a39d562 (Invalid Custom Script Meta values raise unhandled exceptions #22872,
main, released in v4.6.10) addedextras.jobs -> extras.scripts. - The merge 56693d6 ("Merge branch 'main' into feature") joins them. Both of its parents list the plugin event type; the merge does not.
The forms only worked before because nothing imported them before the plugins were ready.
Proposed Fix
Resolve the choices when the form is instantiated, which is how EventRuleFilterForm.event_type and the action_type field of EventRuleForm already work:
EventRuleFormandEventRuleBulkEditForm: passchoices=get_event_type_choices(the callable) instead ofchoices=get_event_type_choices(). Django evaluates a callable each time the choices are read.EventRuleImportForm:CSVChoicesMixinpasses its choices tounpack_grouped_choices()at init, which cannot iterate a callable, so set the choices in__init__()instead. The import page builds a form instance to list the accepted values, so the choices table is correct too.
diff --git a/netbox/extras/forms/bulk_edit.py b/netbox/extras/forms/bulk_edit.py
index a376e794a1..5bed256f5b 100644
--- a/netbox/extras/forms/bulk_edit.py
+++ b/netbox/extras/forms/bulk_edit.py
@@ -302,7 +302,7 @@ class EventRuleBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
widget=BulkEditNullBooleanSelect()
)
event_types = forms.MultipleChoiceField(
- choices=get_event_type_choices(),
+ choices=get_event_type_choices,
required=False,
label=_('Event types')
)
diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py
index 06ded421ab..790566a670 100644
--- a/netbox/extras/forms/bulk_import.py
+++ b/netbox/extras/forms/bulk_import.py
@@ -317,7 +317,6 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
help_text=_("One or more assigned object types")
)
event_types = CSVMultipleChoiceField(
- choices=get_event_type_choices(),
label=_('Event types'),
help_text=_('The event type(s) which will trigger this rule')
)
@@ -337,6 +336,11 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
'owner', 'comments', 'tags'
)
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ # Resolved per instance because plugins register event types after this module is imported
+ self.fields['event_types'].choices = get_event_type_choices()
+
def clean(self):
super().clean()
diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py
index 8f02f88e1b..385e6a63dd 100644
--- a/netbox/extras/forms/model_forms.py
+++ b/netbox/extras/forms/model_forms.py
@@ -607,7 +607,7 @@ class EventRuleForm(OwnerMixin, NetBoxModelForm):
queryset=ObjectType.objects.with_feature('event_rules'),
)
event_types = MultipleChoiceField(
- choices=get_event_type_choices(),
+ choices=get_event_type_choices,
label=_('Event types')
)
action_choice = ChoiceField(Tested on main (f810ba8) with the probe plugin above: the three forms list probe_event, the CSV import of the rule above is valid, the import field still rejects an unknown value, and an event type registered after the forms are imported is listed by the three forms. On v4.7.4 without the patch, none of these forms list probe_event and the CSV import fails with the error above.
A regression test can register an event type after the forms are imported and check that the three forms list it, removing it from registry['event_types'] in cleanup.
Volunteer
- I volunteer to submit a fix for this bug if it is accepted
Activity