Просмотр исходного кода

add version contraints and cacheops config

John Anderson 5 лет назад
Родитель
Сommit
c7fb2ff894
3 измененных файлов с 45 добавлено и 18 удалено
  1. 9 9
      netbox/extras/plugins/__init__.py
  2. 29 9
      netbox/netbox/settings.py
  3. 7 0
      netbox/netbox/urls.py

+ 9 - 9
netbox/extras/plugins/__init__.py

@@ -5,7 +5,7 @@ import inspect
 from django.core.exceptions import ImproperlyConfigured
 from django.core.exceptions import ImproperlyConfigured
 from django.template.loader import get_template
 from django.template.loader import get_template
 
 
-from extras.utils 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
 
 
 
 
@@ -76,7 +76,7 @@ def register_content_classes():
     """
     """
     Helper method that populates the registry with all template content classes that have been registered by plugins
     Helper method that populates the registry with all template content classes that have been registered by plugins
     """
     """
-    registry.plugin_template_content_classes = collections.defaultdict(list)
+    registry['plugin_template_content_classes'] = collections.defaultdict(list)
 
 
     responses = register_detail_page_content_classes.send('registration_event')
     responses = register_detail_page_content_classes.send('registration_event')
     for receiver, response in responses:
     for receiver, response in responses:
@@ -90,7 +90,7 @@ def register_content_classes():
             if template_class.model is None:
             if template_class.model is None:
                 raise TypeError('Plugin content class {} does not define a valid model!'.format(template_class))
                 raise TypeError('Plugin content class {} does not define a valid model!'.format(template_class))
 
 
-            registry.plugin_template_content_classes[template_class.model].append(template_class)
+            registry['plugin_template_content_classes'][template_class.model].append(template_class)
 
 
 
 
 def get_content_classes(model):
 def get_content_classes(model):
@@ -98,10 +98,10 @@ def get_content_classes(model):
     Given a model string, return the list of all registered template content classes.
     Given a model string, return the list of all registered template content classes.
     Populate the registry if it is empty.
     Populate the registry if it is empty.
     """
     """
-    if not hasattr(registry, 'plugin_template_content_classes'):
+    if 'plugin_template_content_classes' not in registry:
         register_content_classes()
         register_content_classes()
 
 
-    return registry.plugin_template_content_classes.get(model, [])
+    return registry['plugin_template_content_classes'].get(model, [])
 
 
 
 
 #
 #
@@ -139,7 +139,7 @@ def register_nav_menu_links():
     """
     """
     Helper method that populates the registry with all nav menu link classes that have been registered by plugins
     Helper method that populates the registry with all nav menu link classes that have been registered by plugins
     """
     """
-    registry.plugin_nav_menu_link_classes = {}
+    registry['plugin_nav_menu_link_classes'] = {}
 
 
     responses = register_nav_menu_link_classes.send('registration_event')
     responses = register_nav_menu_link_classes.send('registration_event')
     for receiver, response in responses:
     for receiver, response in responses:
@@ -165,7 +165,7 @@ def register_nav_menu_links():
                 if not isinstance(button, PluginNavMenuButton):
                 if not isinstance(button, PluginNavMenuButton):
                     raise TypeError('{} must be an instance of PluginNavMenuButton!'.format(button))
                     raise TypeError('{} must be an instance of PluginNavMenuButton!'.format(button))
 
 
-        registry.plugin_nav_menu_link_classes[section_name] = response
+        registry['plugin_nav_menu_link_classes'][section_name] = response
 
 
 
 
 def get_nav_menu_link_classes():
 def get_nav_menu_link_classes():
@@ -173,7 +173,7 @@ def get_nav_menu_link_classes():
     Return the list of all registered nav menu link classes.
     Return the list of all registered nav menu link classes.
     Populate the registry if it is empty.
     Populate the registry if it is empty.
     """
     """
-    if not hasattr(registry, 'plugin_nav_menu_link_classes'):
+    if 'plugin_nav_menu_link_classes' not in registry:
         register_nav_menu_links()
         register_nav_menu_links()
 
 
-    return registry.plugin_nav_menu_link_classes
+    return registry['plugin_nav_menu_link_classes']

+ 29 - 9
netbox/netbox/settings.py

@@ -10,7 +10,7 @@ from urllib.parse import urlsplit
 from django.contrib.messages import constants as messages
 from django.contrib.messages import constants as messages
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.validators import URLValidator
 from django.core.validators import URLValidator
-from pkg_resources import iter_entry_points
+from pkg_resources import iter_entry_points, parse_version
 
 
 
 
 #
 #
@@ -645,6 +645,7 @@ if PLUGINS_ENABLED:
         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:
         try:
             module = importlib.import_module(plugin)
             module = importlib.import_module(plugin)
             default_app_config = getattr(module, 'default_app_config')
             default_app_config = getattr(module, 'default_app_config')
@@ -659,18 +660,22 @@ if PLUGINS_ENABLED:
                 'The app config for plugin {} does not contain an inner meta class'.format(plugin)
                 '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))
+
         # Add middleware
         # Add middleware
         plugin_middleware = getattr(app_config_meta, 'middleware', [])
         plugin_middleware = getattr(app_config_meta, '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)
-        plugin_middleware_prepend = getattr(app_config_meta, 'middleware_prepend', [])
-        if plugin_middleware_prepend and isinstance(plugin_middleware_prepend, list):
-            MIDDLEWARE[:0] = plugin_middleware_prepend
-
-        # Add installed apps
-        plugin_installed_apps = getattr(app_config_meta, 'installed_apps', [])
-        if plugin_installed_apps and isinstance(plugin_installed_apps, list):
-            INSTALLED_APPS.extend(plugin_installed_apps)
 
 
         # Verify required configuration settings
         # Verify required configuration settings
         if plugin not in PLUGINS_CONFIG:
         if plugin not in PLUGINS_CONFIG:
@@ -688,3 +693,18 @@ if PLUGINS_ENABLED:
         for setting, value in getattr(app_config_meta, 'default_settings', {}).items():
         for setting, value in getattr(app_config_meta, '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
+        plugin_cacheops = getattr(app_config_meta, 'caching_config', {})
+        if plugin_cacheops and isinstance(plugin_cacheops, dict):
+            for key in plugin_cacheops.keys():
+                # Validate config is only being set for the given plugin
+                try:
+                    app = key.split('.')[0]
+                except IndexError:
+                    raise ImproperlyConfigured('Plugin {} caching_config is invalid!'.format(plugin))
+                if app != plugin:
+                    raise ImproperlyConfigured(
+                        'Plugin {} may not modify caching config for another app!'.format(plugin)
+                    )
+        CACHEOPS.update(plugin_cacheops)

+ 7 - 0
netbox/netbox/urls.py

@@ -75,26 +75,33 @@ _patterns = [
 plugin_patterns = []
 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
     if hasattr(app, 'NetBoxPluginMeta'):
     if hasattr(app, 'NetBoxPluginMeta'):
+        # 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.NetBoxPluginMeta, 'url_slug', app.label)
             if hasattr(urls, 'urlpatterns'):
             if hasattr(urls, 'urlpatterns'):
+                # Mount URLs at `<url_slug>/<path>`
                 plugin_patterns.append(
                 plugin_patterns.append(
                     path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))
                     path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))
                 )
                 )
+        # Check if the plugin specifies any API URLs
         if importlib.util.find_spec('{}.api'.format(app.name)):
         if importlib.util.find_spec('{}.api'.format(app.name)):
             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.NetBoxPluginMeta, 'url_slug', app.label)
+                    # 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)))
                     )
                     )
 
 
+# Mount all plugin URLs within the `plugins` namespace
 _patterns.append(
 _patterns.append(
     path('plugins/', include((plugin_patterns, 'plugins')))
     path('plugins/', include((plugin_patterns, 'plugins')))
 )
 )
+# Mount all plugin API URLs within the `plugins-api` namespace
 _patterns.append(
 _patterns.append(
     path('api/plugins/', include((plugin_api_patterns, 'plugins-api')))
     path('api/plugins/', include((plugin_api_patterns, 'plugins-api')))
 )
 )