Explorar o código

Closes #13368: Report installed plugins during server error (#13387)

* Introduce get_installed_plugins() utility

* Extend 500 error template to list installed plugins

* Move get_plugin_config() to extras.plugins.utils
Jeremy Stretch %!s(int64=2) %!d(string=hai) anos
pai
achega
f5a1f83f9f

+ 0 - 21
netbox/extras/plugins/__init__.py

@@ -2,7 +2,6 @@ import collections
 from importlib import import_module
 
 from django.apps import AppConfig
-from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.module_loading import import_string
 from packaging import version
@@ -146,23 +145,3 @@ class PluginConfig(AppConfig):
         for setting, value in cls.default_settings.items():
             if setting not in user_config:
                 user_config[setting] = value
-
-
-#
-# Utilities
-#
-
-def get_plugin_config(plugin_name, parameter, default=None):
-    """
-    Return the value of the specified plugin configuration parameter.
-
-    Args:
-        plugin_name: The name of the plugin
-        parameter: The name of the configuration parameter
-        default: The value to return if the parameter is not defined (default: None)
-    """
-    try:
-        plugin_config = settings.PLUGINS_CONFIG[plugin_name]
-        return plugin_config.get(parameter, default)
-    except KeyError:
-        raise ImproperlyConfigured(f"Plugin {plugin_name} is not registered.")

+ 37 - 0
netbox/extras/plugins/utils.py

@@ -0,0 +1,37 @@
+from django.apps import apps
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+
+__all__ = (
+    'get_installed_plugins',
+    'get_plugin_config',
+)
+
+
+def get_installed_plugins():
+    """
+    Return a dictionary mapping the names of installed plugins to their versions.
+    """
+    plugins = {}
+    for plugin_name in settings.PLUGINS:
+        plugin_name = plugin_name.rsplit('.', 1)[-1]
+        plugin_config = apps.get_app_config(plugin_name)
+        plugins[plugin_name] = getattr(plugin_config, 'version', None)
+
+    return dict(sorted(plugins.items()))
+
+
+def get_plugin_config(plugin_name, parameter, default=None):
+    """
+    Return the value of the specified plugin configuration parameter.
+
+    Args:
+        plugin_name: The name of the plugin
+        parameter: The name of the configuration parameter
+        default: The value to return if the parameter is not defined (default: None)
+    """
+    try:
+        plugin_config = settings.PLUGINS_CONFIG[plugin_name]
+        return plugin_config.get(parameter, default)
+    except KeyError:
+        raise ImproperlyConfigured(f"Plugin {plugin_name} is not registered.")

+ 2 - 1
netbox/extras/tests/test_plugins.py

@@ -5,8 +5,9 @@ from django.core.exceptions import ImproperlyConfigured
 from django.test import Client, TestCase, override_settings
 from django.urls import reverse
 
-from extras.plugins import PluginMenu, get_plugin_config
+from extras.plugins import PluginMenu
 from extras.tests.dummy_plugin import config as dummy_config
+from extras.plugins.utils import get_plugin_config
 from netbox.graphql.schema import Query
 from netbox.registry import registry
 

+ 2 - 9
netbox/netbox/api/views.py

@@ -11,6 +11,7 @@ from rest_framework.reverse import reverse
 from rest_framework.views import APIView
 from rq.worker import Worker
 
+from extras.plugins.utils import get_installed_plugins
 from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
 
 
@@ -61,19 +62,11 @@ class StatusView(APIView):
                 installed_apps[app_config.name] = version
         installed_apps = {k: v for k, v in sorted(installed_apps.items())}
 
-        # Gather installed plugins
-        plugins = {}
-        for plugin_name in settings.PLUGINS:
-            plugin_name = plugin_name.rsplit('.', 1)[-1]
-            plugin_config = apps.get_app_config(plugin_name)
-            plugins[plugin_name] = getattr(plugin_config, 'version', None)
-        plugins = {k: v for k, v in sorted(plugins.items())}
-
         return Response({
             'django-version': DJANGO_VERSION,
             'installed-apps': installed_apps,
             'netbox-version': settings.VERSION,
-            'plugins': plugins,
+            'plugins': get_installed_plugins(),
             'python-version': platform.python_version(),
             'rq-workers-running': Worker.count(get_connection('default')),
         })

+ 3 - 0
netbox/netbox/views/errors.py

@@ -11,6 +11,8 @@ from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
 from django.views.generic import View
 from sentry_sdk import capture_message
 
+from extras.plugins.utils import get_installed_plugins
+
 __all__ = (
     'handler_404',
     'handler_500',
@@ -53,4 +55,5 @@ def handler_500(request, template_name=ERROR_500_TEMPLATE_NAME):
         'exception': str(type_),
         'netbox_version': settings.VERSION,
         'python_version': platform.python_version(),
+        'plugins': get_installed_plugins(),
     }))

+ 4 - 1
netbox/templates/500.html

@@ -30,7 +30,10 @@
 {{ error }}
 
 Python version: {{ python_version }}
-NetBox version: {{ netbox_version }}</pre>
+NetBox version: {{ netbox_version }}
+Plugins: {% for plugin, version in plugins.items %}
+  {{ plugin }}: {{ version }}{% empty %}None installed{% endfor %}
+</pre>
                         <p>
                             If further assistance is required, please post to the <a href="https://github.com/netbox-community/netbox/discussions">NetBox discussion forum</a> on GitHub.
                         </p>