| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- 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
- from netbox.registry import registry
- from netbox.search import register_search
- from .navigation import *
- from .registration import *
- from .templates import *
- # Initialize plugin registry
- registry['plugins'] = {
- 'graphql_schemas': [],
- 'menus': [],
- 'menu_items': {},
- 'preferences': {},
- 'template_extensions': collections.defaultdict(list),
- }
- DEFAULT_RESOURCE_PATHS = {
- 'search_indexes': 'search.indexes',
- 'graphql_schema': 'graphql.schema',
- 'menu': 'navigation.menu',
- 'menu_items': 'navigation.menu_items',
- 'template_extensions': 'template_content.template_extensions',
- 'user_preferences': 'preferences.preferences',
- }
- #
- # Plugin AppConfig class
- #
- class PluginConfig(AppConfig):
- """
- Subclass of Django's built-in AppConfig class, to be used for NetBox plugins.
- """
- # Plugin metadata
- author = ''
- author_email = ''
- description = ''
- version = ''
- # Root URL path under /plugins. If not set, the plugin's label will be used.
- base_url = 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 = []
- # Django-rq queues dedicated to the plugin
- queues = []
- # Django apps to append to INSTALLED_APPS when plugin requires them.
- django_apps = []
- # Optional plugin resources
- search_indexes = None
- graphql_schema = None
- menu = None
- menu_items = None
- template_extensions = None
- user_preferences = None
- def _load_resource(self, name):
- # Import from the configured path, if defined.
- if path := getattr(self, name, None):
- return import_string(f"{self.__module__}.{path}")
- # Fall back to the resource's default path. Return None if the module has not been provided.
- default_path = f'{self.__module__}.{DEFAULT_RESOURCE_PATHS[name]}'
- default_module, resource_name = default_path.rsplit('.', 1)
- try:
- module = import_module(default_module)
- return getattr(module, resource_name, None)
- except ModuleNotFoundError:
- pass
- def ready(self):
- plugin_name = self.name.rsplit('.', 1)[-1]
- # Register search extensions (if defined)
- search_indexes = self._load_resource('search_indexes') or []
- for idx in search_indexes:
- register_search(idx)
- # Register template content (if defined)
- if template_extensions := self._load_resource('template_extensions'):
- register_template_extensions(template_extensions)
- # Register navigation menu and/or menu items (if defined)
- if menu := self._load_resource('menu'):
- register_menu(menu)
- if menu_items := self._load_resource('menu_items'):
- register_menu_items(self.verbose_name, menu_items)
- # Register GraphQL schema (if defined)
- if graphql_schema := self._load_resource('graphql_schema'):
- register_graphql_schema(graphql_schema)
- # Register user preferences (if defined)
- if user_preferences := self._load_resource('user_preferences'):
- register_user_preferences(plugin_name, user_preferences)
- @classmethod
- def validate(cls, user_config, netbox_version):
- # Enforce version constraints
- current_version = version.parse(netbox_version)
- if cls.min_version is not None:
- min_version = version.parse(cls.min_version)
- if current_version < min_version:
- raise ImproperlyConfigured(
- f"Plugin {cls.__module__} requires NetBox minimum version {cls.min_version}."
- )
- if cls.max_version is not None:
- max_version = version.parse(cls.max_version)
- if current_version > max_version:
- raise ImproperlyConfigured(
- f"Plugin {cls.__module__} requires NetBox maximum version {cls.max_version}."
- )
- # Verify required configuration settings
- for setting in cls.required_settings:
- if setting not in user_config:
- raise ImproperlyConfigured(
- f"Plugin {cls.__module__} requires '{setting}' to be present in the PLUGINS_CONFIG section of "
- f"configuration.py."
- )
- # Apply default configuration values
- 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.")
|