mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-11 21:10:29 +01:00
Improve handling of plugin loading errors #3820
Closed
opened 2025-12-29 18:31:22 +01:00 by adam
·
2 comments
No Branch/Tag Specified
main
update-changelog-comments-docs
feature-removal-issue-type
20911-dropdown
20239-plugin-menu-classes-mutable-state
21097-graphql-id-lookups
feature
fix_module_substitution
20923-dcim-templates
20044-elevation-stuck-lightmode
feature-ip-prefix-link
v4.5-beta1-release
20068-import-moduletype-attrs
20766-fix-german-translation-code-literals
20378-del-script
7604-filter-modifiers-v3
circuit-swap
12318-case-insensitive-uniqueness
20637-improve-device-q-filter
20660-script-load
19724-graphql
20614-update-ruff
14884-script
02496-max-page
19720-macaddress-interface-generic-relation
19408-circuit-terminations-export-templates
20203-openapi-check
fix-19669-api-image-download
7604-filter-modifiers
19275-fixes-interface-bulk-edit
fix-17794-get_field_value_return_list
11507-show-aggregate-and-rir-on-api
9583-add_column_specific_search_field_to_tables
v4.5.0
v4.4.10
v4.4.9
v4.5.0-beta1
v4.4.8
v4.4.7
v4.4.6
v4.4.5
v4.4.4
v4.4.3
v4.4.2
v4.4.1
v4.4.0
v4.3.7
v4.4.0-beta1
v4.3.6
v4.3.5
v4.3.4
v4.3.3
v4.3.2
v4.3.1
v4.3.0
v4.2.9
v4.3.0-beta2
v4.2.8
v4.3.0-beta1
v4.2.7
v4.2.6
v4.2.5
v4.2.4
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.11
v4.1.10
v4.1.9
v4.1.8
v4.2-beta1
v4.1.7
v4.1.6
v4.1.5
v4.1.4
v4.1.3
v4.1.2
v4.1.1
v4.1.0
v4.0.11
v4.0.10
v4.0.9
v4.1-beta1
v4.0.8
v4.0.7
v4.0.6
v4.0.5
v4.0.3
v4.0.2
v4.0.1
v4.0.0
v3.7.8
v3.7.7
v4.0-beta2
v3.7.6
v3.7.5
v4.0-beta1
v3.7.4
v3.7.3
v3.7.2
v3.7.1
v3.7.0
v3.6.9
v3.6.8
v3.6.7
v3.7-beta1
v3.6.6
v3.6.5
v3.6.4
v3.6.3
v3.6.2
v3.6.1
v3.6.0
v3.5.9
v3.6-beta2
v3.5.8
v3.6-beta1
v3.5.7
v3.5.6
v3.5.5
v3.5.4
v3.5.3
v3.5.2
v3.5.1
v3.5.0
v3.4.10
v3.4.9
v3.5-beta2
v3.4.8
v3.5-beta1
v3.4.7
v3.4.6
v3.4.5
v3.4.4
v3.4.3
v3.4.2
v3.4.1
v3.4.0
v3.3.10
v3.3.9
v3.4-beta1
v3.3.8
v3.3.7
v3.3.6
v3.3.5
v3.3.4
v3.3.3
v3.3.2
v3.3.1
v3.3.0
v3.2.9
v3.2.8
v3.3-beta2
v3.2.7
v3.3-beta1
v3.2.6
v3.2.5
v3.2.4
v3.2.3
v3.2.2
v3.2.1
v3.2.0
v3.1.11
v3.1.10
v3.2-beta2
v3.1.9
v3.2-beta1
v3.1.8
v3.1.7
v3.1.6
v3.1.5
v3.1.4
v3.1.3
v3.1.2
v3.1.1
v3.1.0
v3.0.12
v3.0.11
v3.0.10
v3.1-beta1
v3.0.9
v3.0.8
v3.0.7
v3.0.6
v3.0.5
v3.0.4
v3.0.3
v3.0.2
v3.0.1
v3.0.0
v2.11.12
v3.0-beta2
v2.11.11
v2.11.10
v3.0-beta1
v2.11.9
v2.11.8
v2.11.7
v2.11.6
v2.11.5
v2.11.4
v2.11.3
v2.11.2
v2.11.1
v2.11.0
v2.10.10
v2.10.9
v2.11-beta1
v2.10.8
v2.10.7
v2.10.6
v2.10.5
v2.10.4
v2.10.3
v2.10.2
v2.10.1
v2.10.0
v2.9.11
v2.10-beta2
v2.9.10
v2.10-beta1
v2.9.9
v2.9.8
v2.9.7
v2.9.6
v2.9.5
v2.9.4
v2.9.3
v2.9.2
v2.9.1
v2.9.0
v2.9-beta2
v2.8.9
v2.9-beta1
v2.8.8
v2.8.7
v2.8.6
v2.8.5
v2.8.4
v2.8.3
v2.8.2
v2.8.1
v2.8.0
v2.7.12
v2.7.11
v2.7.10
v2.7.9
v2.7.8
v2.7.7
v2.7.6
v2.7.5
v2.7.4
v2.7.3
v2.7.2
v2.7.1
v2.7.0
v2.6.12
v2.6.11
v2.6.10
v2.6.9
v2.7-beta1
Solcon-2020-01-06
v2.6.8
v2.6.7
v2.6.6
v2.6.5
v2.6.4
v2.6.3
v2.6.2
v2.6.1
v2.6.0
v2.5.13
v2.5.12
v2.6-beta1
v2.5.11
v2.5.10
v2.5.9
v2.5.8
v2.5.7
v2.5.6
v2.5.5
v2.5.4
v2.5.3
v2.5.2
v2.5.1
v2.5.0
v2.4.9
v2.5-beta2
v2.4.8
v2.5-beta1
v2.4.7
v2.4.6
v2.4.5
v2.4.4
v2.4.3
v2.4.2
v2.4.1
v2.4.0
v2.3.7
v2.4-beta1
v2.3.6
v2.3.5
v2.3.4
v2.3.3
v2.3.2
v2.3.1
v2.3.0
v2.2.10
v2.3-beta2
v2.2.9
v2.3-beta1
v2.2.8
v2.2.7
v2.2.6
v2.2.5
v2.2.4
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.6
v2.2-beta2
v2.1.5
v2.2-beta1
v2.1.4
v2.1.3
v2.1.2
v2.1.1
v2.1.0
v2.0.10
v2.1-beta1
v2.0.9
v2.0.8
v2.0.7
v2.0.6
v2.0.5
v2.0.4
v2.0.3
v2.0.2
v2.0.1
v2.0.0
v2.0-beta3
v1.9.6
v1.9.5
v2.0-beta2
v1.9.4-r1
v1.9.3
v2.0-beta1
v1.9.2
v1.9.1
v1.9.0-r1
v1.8.4
v1.8.3
v1.8.2
v1.8.1
v1.8.0
v1.7.3
v1.7.2-r1
v1.7.1
v1.7.0
v1.6.3
v1.6.2-r1
v1.6.1-r1
1.6.1
v1.6.0
v1.5.2
v1.5.1
v1.5.0
v1.4.2
v1.4.1
v1.4.0
v1.3.2
v1.3.1
v1.3.0
v1.2.2
v1.2.1
v1.2.0
v1.1.0
v1.0.7-r1
v1.0.7
v1.0.6
v1.0.5
v1.0.4
v1.0.3-r1
v1.0.3
1.0.0
Labels
Clear labels
beta
breaking change
complexity: high
complexity: low
complexity: medium
needs milestone
netbox
pending closure
plugin candidate
pull-request
severity: high
severity: low
severity: medium
status: accepted
status: backlog
status: blocked
status: duplicate
status: needs owner
status: needs triage
status: revisions needed
status: under review
topic: GraphQL
topic: Internationalization
topic: OpenAPI
topic: UI/UX
topic: cabling
topic: event rules
topic: htmx navigation
topic: industrialization
topic: migrations
topic: plugins
topic: scripts
topic: templating
topic: testing
type: bug
type: deprecation
type: documentation
type: feature
type: housekeeping
type: translation
Mirrored from GitHub Pull Request
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/netbox#3820
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @glennmatthews on GitHub (Jun 30, 2020).
Environment
Proposed Functionality
Currently, when developing a plugin, errors in the plugin code under development can cause undesirable NetBox behavior. If the plugin code is severely broken (such as a
SyntaxError, then NetBox as a whole will die when attempting to load the plugin. In other cases, such as where the plugin code throws anImportErrordue to a missing or incorrectly specified dependency, NetBox will silently ignore the error, the admin "Installed Plugins" page will still show the broken plugin, but attempts to access the plugin later (via its URLs, views, etc.) will report a confusing error such asNoReverseMatch at / 'plugin_name' is not a registered namespace inside 'plugins'.See also #4628.
Currently NetBox loads plugins via three primary functions:
importlib.import_module(),django.apps.apps.get_app_config()anddjango.utils.module_loading.import_string().importlib.import_module()
This is called in
netbox/netbox/settings.py:In the case of an
ImportError, this should re-raise anImproperlyConfigurederror, but in practice it appears that Django is silently ignoring that exception. (Possible NetBox bug of some sort?) This then causes errors downstream whenget_app_configis called - see below.In the case of any other exception being thrown by the plugin, the entire Django startup process aborts.
Proposal: add a blanket
except Exceptionto thistryblock, and log the exception and continue without the plugin, so that NetBox itself remains operational.django.apps.apps.get_app_config()
This is called in
netbox/extras/plugins/urls.pyandnetbox/extras/plugins/views.py, and will raise aLookupErrorif the requested plugin is not present (such as due to an earlier error insettings.pycallingimport_module()above), which is not handled by NetBox, causing the entire application to abort.Proposal: catch the LookupError in each case, print an appropriate error message to the Django console, and skip over the affected plugin. This will fix the case where broken plugins are still listed under the admin "Installed Plugins" view and the
api/plugins/installed-plugins/API view.Proposal: A possible further enhancement would be for these two views to, rather than simply skipping broken plugins, separately report any plugins that are configured but could not be loaded successfully.
django.utils.module_loading.import_string()
This method raises an
ImportErrorif:module.submodule.object)ImportError, because either:a. it does not exist
b. it exists but fails to load, perhaps because it itself contains an
importstatement that fails (the case described in this issue)In the case of plugin loading where we are using
import_string(extras/plugins/__init__.py,extras/plugins/views.py,extras/plugins/urls.py) we avoid case 1. by properly constructing the string ourselves. We want case 2.a. (and 3.?) to fail silently because, for example, a plugin very well may not implement aplugin_name.urlsmodule (or have aurlpatternsdeclaration in said module?).Proposal: Case 2.b. should result in a reported error message, and possibly also this failure to be reported in some fashion under the /admin/plugins/installed-plugins/ view. This could probably be accomplished by rolling our own implementation of
import_string, which is a relatively simple function.Use Cases
Database Changes
None are proposed.
External Dependencies
None identified.
@jeremystretch commented on GitHub (Jun 30, 2020):
What would be the benefit of allowing NetBox to run with an incorrectly installed plugin? A configuration which references a missing or broken plugin should cause the NetBox process to fail and report the error, calling the user's attention to the problem. Suppressing the error will only lead to confusion, especially if the user does not have adequate logging configured.
I cannot reproduce this on NetBox v2.8.6. Attempting to import an invalid plugin, or intentionally raising an
ImportErrorwithin a valid plugin, raises anImproperlyConfiguredwith the expected error message.There's no requirement that a plugin must define these modules: Even though the path is correct, the file may not necessarily exist.
I agree that in "case 2b" it would be ideal to raise
ImportErrorsresulting from the plugin itself, however that will require introducing separate logic to check for the existence of each optional plugin module (template_extensions.py,urlpatterns.py, etc.) prior to attempting to import it. For example, instead of:We'd do something like:
This avoids intercepting any ImportErrors. However, it should be noted that exceptions remain the responsibility of the plugin author: If they make it to NetBox, they will cause NetBox to fail just as they do in core.
@glennmatthews commented on GitHub (Jun 30, 2020):
Fair enough, I can understand where you're coming from as far as wanting to fail fast if a plugin error is present. That honestly simplifies the solution a fair bit.
It's very weird. In my installation, only this specific
ImproperlyConfiguredappears to be being ignored - if I raise one earlier insettings.py, it gets reported as expected; if I change this to aRuntimeErrorit gets reported as expected; but as implemented, anImproperlyConfiguredraised at this specific point just causes the execution ofsettings.pyto end at this point with no error being reported. I'll assume it's just something bizarre about my particular setup.Understood and agreed.
Agreed. That's what I think we would benefit from here.