Explorar el Código

Merge pull request #4392 from netbox-community/refactor-plugins-import

Refactor plugins import
John Anderson hace 5 años
padre
commit
e220c38b97
Se han modificado 3 ficheros con 58 adiciones y 43 borrados
  1. 34 1
      netbox/extras/plugins/__init__.py
  2. 20 39
      netbox/netbox/settings.py
  3. 4 3
      netbox/netbox/urls.py

+ 34 - 1
netbox/extras/plugins/__init__.py

@@ -2,13 +2,46 @@ import collections
 import importlib
 import importlib
 import inspect
 import inspect
 
 
-from django.core.exceptions import ImproperlyConfigured
+from django.apps import AppConfig
 from django.template.loader import get_template
 from django.template.loader import get_template
 
 
 from extras.registry import registry
 from extras.registry import registry
 from .signals import register_detail_page_content_classes, register_nav_menu_link_classes
 from .signals import register_detail_page_content_classes, register_nav_menu_link_classes
 
 
 
 
+#
+# Plugin AppConfig class
+#
+
+class PluginConfig(AppConfig):
+    """
+    Subclass of Django's built-in AppConfig class, to be used for NetBox plugins.
+    """
+    # Plugin metadata
+    author = ''
+    description = ''
+    version = ''
+
+    # Root URL path under /plugins. If not set, the plugin's label will be used.
+    url_slug = None
+
+    # Minimum/maximum compatible versions of NetBox
+    min_version = None
+    max_version = None
+
+    # Default configuration parameters
+    default_settings = {}
+
+    # Mandatory configuration parameters
+    required_settings = []
+
+    # Middleware classes provided by the plugin
+    middleware = []
+
+    # Caching configuration
+    caching_config = {}
+
+
 #
 #
 # Template content injection
 # Template content injection
 #
 #

+ 20 - 39
netbox/netbox/settings.py

@@ -640,71 +640,52 @@ if PAGINATE_COUNT not in PER_PAGE_DEFAULTS:
 
 
 PLUGINS = []
 PLUGINS = []
 if PLUGINS_ENABLED:
 if PLUGINS_ENABLED:
-    for entry_point in iter_entry_points(group='netbox.plugin', name=None):
+    for entry_point in iter_entry_points(group='netbox_plugins', name=None):
         plugin = entry_point.module_name
         plugin = entry_point.module_name
+        app_config = entry_point.load()
+
         PLUGINS.append(plugin)
         PLUGINS.append(plugin)
         INSTALLED_APPS.append(plugin)
         INSTALLED_APPS.append(plugin)
 
 
-        # Import the app config and locate the inner meta class
-        try:
-            module = importlib.import_module(plugin)
-            default_app_config = getattr(module, 'default_app_config')
-            module, app_config = default_app_config.rsplit('.', 1)
-            app_config = getattr(importlib.import_module(module), app_config)
-        except ImportError:
-            raise ImproperlyConfigured('Plugin config for {} could not be imported!'.format(plugin))
-
-        app_config_meta = getattr(app_config, 'NetBoxPluginMeta', None)
-        if not app_config_meta:
-            raise ImproperlyConfigured(
-                'The app config for plugin {} does not contain an inner meta class'.format(plugin)
-            )
-
-        # Check version contraints
-        min_version = getattr(app_config_meta, 'min_version', None)
-        max_version = getattr(app_config_meta, 'max_version', None)
-        parsed_min_version = parse_version(min_version or VERSION)
-        parsed_max_version = parse_version(max_version or VERSION)
-        if min_version and max_version and parsed_min_version > parsed_max_version:
-            raise ImproperlyConfigured('Plugin {} specifies invalid version contraints!'.format(plugin))
-        if min_version and parsed_min_version > parse_version(VERSION):
-            raise ImproperlyConfigured('Plugin {} requires NetBox minimum version {}!'.format(plugin, min_version))
-        if max_version and parsed_max_version < parse_version(VERSION):
-            raise ImproperlyConfigured('Plugin {} requires NetBox maximum version {}!'.format(plugin, max_version))
+        # Check version constraints
+        parsed_min_version = parse_version(app_config.min_version or VERSION)
+        parsed_max_version = parse_version(app_config.max_version or VERSION)
+        if app_config.min_version and app_config.max_version and parsed_min_version > parsed_max_version:
+            raise ImproperlyConfigured(f"Plugin {plugin} specifies invalid version constraints!")
+        if app_config.min_version and parsed_min_version > parse_version(VERSION):
+            raise ImproperlyConfigured(f"Plugin {plugin} requires NetBox minimum version {app_config.min_version}!")
+        if app_config.max_version and parsed_max_version < parse_version(VERSION):
+            raise ImproperlyConfigured(f"Plugin {plugin} requires NetBox maximum version {app_config.max_version}!")
 
 
         # Add middleware
         # Add middleware
-        plugin_middleware = getattr(app_config_meta, 'middleware', [])
+        plugin_middleware = app_config.middleware
         if plugin_middleware and isinstance(plugin_middleware, list):
         if plugin_middleware and isinstance(plugin_middleware, list):
             MIDDLEWARE.extend(plugin_middleware)
             MIDDLEWARE.extend(plugin_middleware)
 
 
         # Verify required configuration settings
         # Verify required configuration settings
         if plugin not in PLUGINS_CONFIG:
         if plugin not in PLUGINS_CONFIG:
             PLUGINS_CONFIG[plugin] = {}
             PLUGINS_CONFIG[plugin] = {}
-        for setting in getattr(app_config_meta, 'required_settings', []):
+        for setting in app_config.required_settings:
             if setting not in PLUGINS_CONFIG[plugin]:
             if setting not in PLUGINS_CONFIG[plugin]:
                 raise ImproperlyConfigured(
                 raise ImproperlyConfigured(
-                    "Plugin {} requires '{}' to be present in the PLUGINS_CONFIG section of configuration.py.".format(
-                        plugin,
-                        setting
-                    )
+                    f"Plugin {plugin} requires '{setting}' to be present in the PLUGINS_CONFIG section of "
+                    f"configuration.py."
                 )
                 )
 
 
         # Set defined default setting values
         # Set defined default setting values
-        for setting, value in getattr(app_config_meta, 'default_settings', {}).items():
+        for setting, value in app_config.default_settings.items():
             if setting not in PLUGINS_CONFIG[plugin]:
             if setting not in PLUGINS_CONFIG[plugin]:
                 PLUGINS_CONFIG[plugin][setting] = value
                 PLUGINS_CONFIG[plugin][setting] = value
 
 
         # Apply cacheops config
         # Apply cacheops config
-        plugin_cacheops = getattr(app_config_meta, 'caching_config', {})
+        plugin_cacheops = app_config.caching_config
         if plugin_cacheops and isinstance(plugin_cacheops, dict):
         if plugin_cacheops and isinstance(plugin_cacheops, dict):
             for key in plugin_cacheops.keys():
             for key in plugin_cacheops.keys():
                 # Validate config is only being set for the given plugin
                 # Validate config is only being set for the given plugin
                 try:
                 try:
                     app = key.split('.')[0]
                     app = key.split('.')[0]
                 except IndexError:
                 except IndexError:
-                    raise ImproperlyConfigured('Plugin {} caching_config is invalid!'.format(plugin))
+                    raise ImproperlyConfigured(f"Plugin {plugin} caching_config is invalid!")
                 if app != plugin:
                 if app != plugin:
-                    raise ImproperlyConfigured(
-                        'Plugin {} may not modify caching config for another app!'.format(plugin)
-                    )
+                    raise ImproperlyConfigured(f"Plugin {plugin} may not modify caching config for another app!")
         CACHEOPS.update(plugin_cacheops)
         CACHEOPS.update(plugin_cacheops)

+ 4 - 3
netbox/netbox/urls.py

@@ -8,6 +8,7 @@ from django.views.static import serve
 from drf_yasg import openapi
 from drf_yasg import openapi
 from drf_yasg.views import get_schema_view
 from drf_yasg.views import get_schema_view
 
 
+from extras.plugins import PluginConfig
 from netbox.views import APIRootView, HomeView, StaticMediaFailureView, SearchView
 from netbox.views import APIRootView, HomeView, StaticMediaFailureView, SearchView
 from users.views import LoginView, LogoutView
 from users.views import LoginView, LogoutView
 from .admin import admin_site
 from .admin import admin_site
@@ -76,11 +77,11 @@ plugin_patterns = []
 plugin_api_patterns = []
 plugin_api_patterns = []
 for app in apps.get_app_configs():
 for app in apps.get_app_configs():
     # Loop over all apps look for installed plugins
     # Loop over all apps look for installed plugins
-    if hasattr(app, 'NetBoxPluginMeta'):
+    if isinstance(app, PluginConfig):
         # Check if the plugin specifies any URLs
         # Check if the plugin specifies any URLs
         if importlib.util.find_spec('{}.urls'.format(app.name)):
         if importlib.util.find_spec('{}.urls'.format(app.name)):
             urls = importlib.import_module('{}.urls'.format(app.name))
             urls = importlib.import_module('{}.urls'.format(app.name))
-            url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label)
+            url_slug = getattr(app, 'url_slug') or app.label
             if hasattr(urls, 'urlpatterns'):
             if hasattr(urls, 'urlpatterns'):
                 # Mount URLs at `<url_slug>/<path>`
                 # Mount URLs at `<url_slug>/<path>`
                 plugin_patterns.append(
                 plugin_patterns.append(
@@ -91,7 +92,7 @@ for app in apps.get_app_configs():
             if importlib.util.find_spec('{}.api.urls'.format(app.name)):
             if importlib.util.find_spec('{}.api.urls'.format(app.name)):
                 urls = importlib.import_module('{}.api.urls'.format(app.name))
                 urls = importlib.import_module('{}.api.urls'.format(app.name))
                 if hasattr(urls, 'urlpatterns'):
                 if hasattr(urls, 'urlpatterns'):
-                    url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label)
+                    url_slug = getattr(app, 'url_slug') or app.label
                     # Mount URLs at `<url_slug>/<path>`
                     # Mount URLs at `<url_slug>/<path>`
                     plugin_api_patterns.append(
                     plugin_api_patterns.append(
                         path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))
                         path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))