Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes: #18535 - Skip incompatible plugins during startup and remove from PLUGINS #18537

Open
wants to merge 1 commit into
base: feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions netbox/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
from django.core.exceptions import ImproperlyConfigured


class SyncError(Exception):
pass


class IncompatiblePluginError(ImproperlyConfigured):
pass
5 changes: 3 additions & 2 deletions netbox/netbox/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.utils.module_loading import import_string
from packaging import version

from core.exceptions import IncompatiblePluginError
from netbox.registry import registry
from netbox.search import register_search
from netbox.utils import register_data_backend
Expand Down Expand Up @@ -138,14 +139,14 @@ def validate(cls, user_config, netbox_version):
if cls.min_version is not None:
min_version = version.parse(cls.min_version)
if current_version < min_version:
raise ImproperlyConfigured(
raise IncompatiblePluginError(
f"Plugin {cls.__module__} requires NetBox minimum version {cls.min_version} (current: "
f"{netbox_version})."
)
if cls.max_version is not None:
max_version = version.parse(cls.max_version)
if current_version > max_version:
raise ImproperlyConfigured(
raise IncompatiblePluginError(
f"Plugin {cls.__module__} requires NetBox maximum version {cls.max_version} (current: "
f"{netbox_version})."
)
Expand Down
20 changes: 15 additions & 5 deletions netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.core.validators import URLValidator
from django.utils.translation import gettext_lazy as _

from core.exceptions import IncompatiblePluginError
from netbox.config import PARAMS as CONFIG_PARAMS
from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW
from netbox.plugins import PluginConfig
Expand Down Expand Up @@ -792,6 +793,7 @@ def _setting(name, default=None):
EVENTS_PIPELINE.insert(0, 'extras.events.process_event_queue')

# Register any configured plugins
incompatible_plugins = []
for plugin_name in PLUGINS:
try:
# Import the plugin module
Expand All @@ -813,6 +815,16 @@ def _setting(name, default=None):
f"__init__.py file and point to the PluginConfig subclass."
)

# Validate version compatibility and user-provided configuration settings and assign defaults
if plugin_name not in PLUGINS_CONFIG:
PLUGINS_CONFIG[plugin_name] = {}
try:
plugin_config.validate(PLUGINS_CONFIG[plugin_name], RELEASE.version)
except IncompatiblePluginError as e:
print(f'Unable to load plugin {plugin_name}: {e}')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about adding a print here, as it would be the first of its kind in this file. Is there a better way to emit this warning?

incompatible_plugins.append(plugin_name)
continue

plugin_module = "{}.{}".format(plugin_config.__module__, plugin_config.__name__) # type: ignore

# Gather additional apps to load alongside this plugin
Expand Down Expand Up @@ -842,11 +854,6 @@ def _setting(name, default=None):
sorted_apps = reversed(list(dict.fromkeys(reversed(INSTALLED_APPS))))
INSTALLED_APPS = list(sorted_apps)

# Validate user-provided configuration settings and assign defaults
if plugin_name not in PLUGINS_CONFIG:
PLUGINS_CONFIG[plugin_name] = {}
plugin_config.validate(PLUGINS_CONFIG[plugin_name], RELEASE.version)

# Add middleware
plugin_middleware = plugin_config.middleware
if plugin_middleware and type(plugin_middleware) in (list, tuple):
Expand All @@ -868,6 +875,9 @@ def _setting(name, default=None):
else:
raise ImproperlyConfigured(f"events_pipline in plugin: {plugin_name} must be a list or tuple")

[PLUGINS.remove(x) for x in incompatible_plugins]


# UNSUPPORTED FUNCTIONALITY: Import any local overrides.
try:
from .local_settings import *
Expand Down