1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge pull request from netbox-community/plugins-list

Change PLUGINS_ENABLED to a list of specific plugins (PLUGINS)
This commit is contained in:
Jeremy Stretch
2020-04-01 11:44:41 -04:00
committed by GitHub
4 changed files with 83 additions and 71 deletions
docs
netbox/netbox

@ -299,6 +299,17 @@ Determine how many objects to display per page within each list of objects.
---
## PLUGINS
Default: Empty
A list of installed [NetBox plugins](../../plugins/) to enable. Plugins will not take effect unless they are listed here.
!!! warning
Plugins extend NetBox by allowing external code to run with the same access and privileges as NetBox itself. Only install plugins from trusted sources. The NetBox maintainers make absolutely no guarantees about the integrity or security of your installation with plugins enabled.
---
## PLUGINS_CONFIG
Default: Empty
@ -317,18 +328,7 @@ PLUGINS_CONFIG = {
}
```
Note that `PLUGINS_ENABLED` must be set to True for this to take effect.
---
## PLUGINS_ENABLED
Default: `False`
Enable [NetBox plugins](../../plugins/).
!!! warning
Plugins extend NetBox by allowing external code to run with the same access and privileges as NetBox itself. Only install plugins from trusted sources. The NetBox maintainers make absolutely no guarantees about the integrity or security of your installation with plugins enabled.
Note that a plugin must be listed in `PLUGINS` for its configuration to take effect.
---

@ -61,13 +61,10 @@ setup(
install_requires=[],
packages=find_packages(),
include_package_data=True,
entry_points={
'netbox_plugins': 'netbox_animal_sounds=netbox_animal_sounds:AnimalSoundsConfig'
}
)
```
Many of these are self-explanatory, but for more information, see the [setuptools documentation](https://setuptools.readthedocs.io/en/latest/setuptools.html). The key requirement for a NetBox plugin is the presence of an entry point for `netbox_plugins` pointing to the `PluginConfig` subclass, which we'll create next.
Many of these are self-explanatory, but for more information, see the [setuptools documentation](https://setuptools.readthedocs.io/en/latest/setuptools.html).
### Define a PluginConfig
@ -87,8 +84,12 @@ class AnimalSoundsConfig(PluginConfig):
default_settings = {
'loud': False
}
config = AnimalSoundsConfig
```
NetBox looks for the `config` variable within a plugin's `__init__.py` to load its configuration. Typically, this will be set to the PluginConfig subclass, but you may wish to dynamically generate a PluginConfig class based on environment variables or other factors.
#### PluginConfig Attributes
| Name | Description |

@ -41,12 +41,14 @@ $ source /opt/netbox/venv/bin/activate
Alternatively, you may wish to install the plugin manually by running `python setup.py install`. If you are developing a plugin and want to install it only temporarily, run `python setup.py develop` instead.
### Enable Plugins
### Enable the Plugin
In `configuration.py`, ensure the `PLUGINS_ENABLED` parameter is set to True:
In `configuration.py`, add the plugin's name to the `PLUGINS` list:
```python
PLUGINS_ENABLED = True
PLUGINS = [
'plugin_name',
]
```
### Configure Plugin

@ -10,7 +10,7 @@ from urllib.parse import urlsplit
from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import URLValidator
from pkg_resources import iter_entry_points, parse_version
from pkg_resources import parse_version
#
@ -92,8 +92,8 @@ NAPALM_PASSWORD = getattr(configuration, 'NAPALM_PASSWORD', '')
NAPALM_TIMEOUT = getattr(configuration, 'NAPALM_TIMEOUT', 30)
NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
PLUGINS = getattr(configuration, 'PLUGINS', [])
PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {})
PLUGINS_ENABLED = getattr(configuration, 'PLUGINS_ENABLED', False)
PREFER_IPV4 = getattr(configuration, 'PREFER_IPV4', False)
REMOTE_AUTH_AUTO_CREATE_USER = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_USER', False)
REMOTE_AUTH_BACKEND = getattr(configuration, 'REMOTE_AUTH_BACKEND', 'utilities.auth_backends.RemoteUserBackend')
@ -637,59 +637,68 @@ if PAGINATE_COUNT not in PER_PAGE_DEFAULTS:
# Plugins
#
PLUGINS = set()
if PLUGINS_ENABLED:
for entry_point in iter_entry_points(group='netbox_plugins', name=None):
# Append plugin name to PLUGINS
plugin = entry_point.module_name
PLUGINS.add(plugin)
for plugin_name in PLUGINS:
# Append plugin to INSTALLED_APPS. Specify the path to the PluginConfig so that we don't
# have to define default_app_config.
app_config = entry_point.load()
INSTALLED_APPS.append(f"{app_config.__module__}.{app_config.__name__}")
# Import plugin module
try:
plugin = importlib.import_module(plugin_name)
except ImportError:
raise ImproperlyConfigured(
f"Unable to import plugin {plugin_name}: Module not found. Check that the plugin module has been "
f"installed within the correct Python environment."
)
# 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}!")
# Determine plugin config and add to INSTALLED_APPS.
try:
plugin_config = plugin.config
INSTALLED_APPS.append(f"{plugin_config.__module__}.{plugin_config.__name__}")
except AttributeError:
raise ImproperlyConfigured(
f"Plugin {plugin_name} does not provide a 'config' variable. This should be defined in the plugin's "
f"__init__.py file and point to the PluginConfig subclass."
)
# Add middleware
plugin_middleware = app_config.middleware
if plugin_middleware and type(plugin_middleware) in (list, tuple):
MIDDLEWARE.extend(plugin_middleware)
# Check version constraints
parsed_min_version = parse_version(plugin_config.min_version or VERSION)
parsed_max_version = parse_version(plugin_config.max_version or VERSION)
if plugin_config.min_version and plugin_config.max_version and parsed_min_version > parsed_max_version:
raise ImproperlyConfigured(f"Plugin {plugin_name} specifies invalid version constraints!")
if plugin_config.min_version and parsed_min_version > parse_version(VERSION):
raise ImproperlyConfigured(f"Plugin {plugin_name} requires NetBox minimum version {plugin_config.min_version}!")
if plugin_config.max_version and parsed_max_version < parse_version(VERSION):
raise ImproperlyConfigured(f"Plugin {plugin_name} requires NetBox maximum version {plugin_config.max_version}!")
# Verify required configuration settings
if plugin not in PLUGINS_CONFIG:
PLUGINS_CONFIG[plugin] = {}
for setting in app_config.required_settings:
if setting not in PLUGINS_CONFIG[plugin]:
raise ImproperlyConfigured(
f"Plugin {plugin} requires '{setting}' to be present in the PLUGINS_CONFIG section of "
f"configuration.py."
)
# Add middleware
plugin_middleware = plugin_config.middleware
if plugin_middleware and type(plugin_middleware) in (list, tuple):
MIDDLEWARE.extend(plugin_middleware)
# Apply default configuration values
for setting, value in app_config.default_settings.items():
if setting not in PLUGINS_CONFIG[plugin]:
PLUGINS_CONFIG[plugin][setting] = value
# Verify required configuration settings
if plugin_name not in PLUGINS_CONFIG:
PLUGINS_CONFIG[plugin_name] = {}
for setting in plugin_config.required_settings:
if setting not in PLUGINS_CONFIG[plugin_name]:
raise ImproperlyConfigured(
f"Plugin {plugin_name} requires '{setting}' to be present in the PLUGINS_CONFIG section of "
f"configuration.py."
)
# Apply cacheops config
plugin_cacheops = app_config.caching_config
if plugin_cacheops:
if type(plugin_cacheops) is not dict:
raise ImproperlyConfigured(f"Plugin {plugin} caching_config must be a dictionary.")
for key in plugin_cacheops.keys():
# Validate config is only being set for the given plugin
app = key.split('.')[0]
if app != plugin:
raise ImproperlyConfigured(f"Plugin {plugin} may not modify caching config for another app: {app}")
else:
# Apply the default config like all other core apps
plugin_cacheops = {f"{plugin}.*": {'ops': 'all'}}
CACHEOPS.update(plugin_cacheops)
# Apply default configuration values
for setting, value in plugin_config.default_settings.items():
if setting not in PLUGINS_CONFIG[plugin_name]:
PLUGINS_CONFIG[plugin_name][setting] = value
# Apply cacheops config
plugin_cacheops = plugin_config.caching_config
if plugin_cacheops:
if type(plugin_cacheops) is not dict:
raise ImproperlyConfigured(f"Plugin {plugin_name} caching_config must be a dictionary.")
for key in plugin_cacheops.keys():
# Validate config is only being set for the given plugin
app = key.split('.')[0]
if app != plugin_name:
raise ImproperlyConfigured(f"Plugin {plugin_name} may not modify caching config for another app: {app}")
else:
# Apply the default config like all other core apps
plugin_cacheops = {f"{plugin_name}.*": {'ops': 'all'}}
CACHEOPS.update(plugin_cacheops)