first commit
This commit is contained in:
142
netbox_librenms_plugin/__init__.py
Normal file
142
netbox_librenms_plugin/__init__.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from netbox.plugins import PluginConfig
|
||||
|
||||
__author__ = "Andy Norwood"
|
||||
__version__ = "0.4.6"
|
||||
|
||||
|
||||
class LibreNMSSyncConfig(PluginConfig):
|
||||
name = "netbox_librenms_plugin"
|
||||
verbose_name = "NetBox Librenms Plugin"
|
||||
description = "Netbox plugin to sync data between LibreNMS and Netbox."
|
||||
author = __author__
|
||||
version = __version__
|
||||
base_url = "librenms_plugin"
|
||||
min_version = "4.2.0"
|
||||
required_settings = [] # Custom validation in ready() method
|
||||
default_settings = {
|
||||
"enable_caching": True,
|
||||
"verify_ssl": True,
|
||||
"interface_name_field": "ifName",
|
||||
}
|
||||
|
||||
def ready(self):
|
||||
"""
|
||||
Perform custom validation for plugin configuration.
|
||||
Supports both legacy single-server and new multi-server configurations.
|
||||
"""
|
||||
super().ready()
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
plugin_config = getattr(settings, "PLUGINS_CONFIG", {}).get(self.name, {})
|
||||
|
||||
# Check if using new multi-server configuration
|
||||
if "servers" in plugin_config:
|
||||
self._validate_multi_server_config(plugin_config["servers"])
|
||||
else:
|
||||
self._validate_legacy_config(plugin_config)
|
||||
|
||||
# Auto-create the librenms_id custom field after migrations complete
|
||||
post_migrate.connect(
|
||||
_ensure_librenms_id_custom_field,
|
||||
dispatch_uid="netbox_librenms_plugin_ensure_cf",
|
||||
)
|
||||
|
||||
def _validate_multi_server_config(self, servers_config):
|
||||
"""Validate multi-server configuration."""
|
||||
if not servers_config or not isinstance(servers_config, dict):
|
||||
raise ImproperlyConfigured(
|
||||
f"Plugin {self.name} requires at least one server configuration in the 'servers' section."
|
||||
)
|
||||
|
||||
for server_key, server_config in servers_config.items():
|
||||
if not isinstance(server_config, dict):
|
||||
raise ImproperlyConfigured(f"Plugin {self.name} server '{server_key}' must be a dictionary.")
|
||||
|
||||
for setting in ["librenms_url", "api_token"]:
|
||||
if setting not in server_config:
|
||||
raise ImproperlyConfigured(f"Plugin {self.name} server '{server_key}' requires '{setting}'.")
|
||||
|
||||
def _validate_legacy_config(self, plugin_config):
|
||||
"""Validate legacy single-server configuration."""
|
||||
for setting in ["librenms_url", "api_token"]:
|
||||
if setting not in plugin_config:
|
||||
raise ImproperlyConfigured(
|
||||
f"Plugin {self.name} requires either 'servers' configuration or legacy '{setting}' setting."
|
||||
)
|
||||
|
||||
|
||||
def _ensure_librenms_id_custom_field(sender, **kwargs):
|
||||
"""
|
||||
Auto-create (or migrate) the 'librenms_id' custom field.
|
||||
Runs after migrations via post_migrate signal to ensure tables exist.
|
||||
Uses dispatch_uid to avoid duplicate connections.
|
||||
|
||||
librenms_id stores a per-server JSON mapping {"server_key": device_id}.
|
||||
Legacy installations may have this field typed as 'integer'; we upgrade it
|
||||
to 'json' automatically so the UI and API accept the dict format.
|
||||
"""
|
||||
# Track per-alias execution so each database alias is bootstrapped exactly once.
|
||||
db_alias = kwargs.get("using") or "default"
|
||||
executed_aliases = getattr(_ensure_librenms_id_custom_field, "_executed_aliases", set())
|
||||
if db_alias in executed_aliases:
|
||||
return
|
||||
|
||||
import logging
|
||||
|
||||
try:
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from extras.models import CustomField
|
||||
|
||||
cf, created = CustomField.objects.using(db_alias).get_or_create(
|
||||
name="librenms_id",
|
||||
defaults={
|
||||
"type": "json",
|
||||
"label": "LibreNMS ID",
|
||||
"description": "LibreNMS Device ID for synchronization (auto-created by plugin)",
|
||||
"required": False,
|
||||
"ui_visible": "if-set",
|
||||
"ui_editable": "yes",
|
||||
"is_cloneable": False,
|
||||
},
|
||||
)
|
||||
|
||||
# Migrate legacy integer-typed field to JSON so the multi-server
|
||||
# dict format {"server_key": device_id} is accepted by the UI/API.
|
||||
if not created and cf.type == "integer":
|
||||
cf.type = "json"
|
||||
cf.save(using=db_alias, update_fields=["type"])
|
||||
logging.getLogger("netbox_librenms_plugin").info(
|
||||
"Migrated 'librenms_id' custom field type from integer to json"
|
||||
)
|
||||
|
||||
# Ensure the field is assigned to the required object types
|
||||
from dcim.models import Device, Interface
|
||||
from virtualization.models import VirtualMachine, VMInterface
|
||||
|
||||
required_models = [Device, VirtualMachine, Interface, VMInterface]
|
||||
current_types = set(cf.object_types.values_list("pk", flat=True))
|
||||
|
||||
for model in required_models:
|
||||
ct = ContentType.objects.db_manager(db_alias).get_for_model(model)
|
||||
if ct.pk not in current_types:
|
||||
cf.object_types.add(ct)
|
||||
|
||||
if created:
|
||||
logging.getLogger("netbox_librenms_plugin").info(
|
||||
"Auto-created 'librenms_id' custom field for Device, VirtualMachine, Interface, VMInterface"
|
||||
)
|
||||
|
||||
# Mark this alias as executed after successful completion to allow retry on failure.
|
||||
executed_aliases.add(db_alias)
|
||||
_ensure_librenms_id_custom_field._executed_aliases = executed_aliases
|
||||
except Exception as e:
|
||||
# Don't break startup if custom field creation fails (e.g., during initial migration),
|
||||
# but log the error so it's not silently swallowed.
|
||||
logging.getLogger("netbox_librenms_plugin").exception("Failed to auto-create 'librenms_id' custom field: %s", e)
|
||||
|
||||
|
||||
config = LibreNMSSyncConfig
|
||||
Reference in New Issue
Block a user