Files
netbox-librenms-plugin/netbox_librenms_plugin/views/sync/devices.py
Vlastislav Svatek 673e67106e
Some checks failed
ci / deploy (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
first commit
2026-06-05 10:39:05 +02:00

142 lines
5.8 KiB
Python

from dcim.models import Device
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.views import View
from virtualization.models import VirtualMachine
from netbox_librenms_plugin.forms import AddToLIbreSNMPV1V2, AddToLIbreSNMPV3
from netbox_librenms_plugin.views.mixins import LibreNMSAPIMixin, LibreNMSPermissionMixin
class AddDeviceToLibreNMSView(LibreNMSPermissionMixin, LibreNMSAPIMixin, View):
"""Add a NetBox device or VM to LibreNMS via the API."""
def get_form_class(self):
"""Return the appropriate SNMP form class based on the SNMP version."""
snmp_version = self.request.POST.get("snmp_version")
if not snmp_version:
snmp_version = self.request.POST.get("v1v2-snmp_version") or self.request.POST.get("v3-snmp_version")
if snmp_version in ("v1", "v2c"):
return AddToLIbreSNMPV1V2
return AddToLIbreSNMPV3
def get_object(self, object_id, object_type=None):
"""
Return the Device or VirtualMachine for the given ID.
Uses object_type hint when provided to avoid PK collision ambiguity
(Device and VirtualMachine share independent PK sequences).
"""
if object_type == "virtualmachine":
return get_object_or_404(VirtualMachine, pk=object_id)
try:
return Device.objects.get(pk=object_id)
except Device.DoesNotExist:
return get_object_or_404(VirtualMachine, pk=object_id)
def post(self, request, object_id):
"""Add a device to LibreNMS using the submitted SNMP form."""
# Check write permission before adding device to LibreNMS
if error := self.require_write_permission():
return error
self.object = self.get_object(object_id, request.POST.get("object_type"))
form_class = self.get_form_class()
snmp_version = request.POST.get("v1v2-snmp_version") or request.POST.get("v3-snmp_version")
prefix = "v1v2" if snmp_version in ("v1", "v2c") else "v3"
form = form_class(request.POST, prefix=prefix)
if form.is_valid():
# Inject snmp_version from toggle into cleaned_data for v1/v2c forms
if snmp_version in ("v1", "v2c"):
form.cleaned_data["snmp_version"] = snmp_version
return self.form_valid(form, snmp_version=snmp_version)
for field, errors in form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
return redirect(self.object.get_absolute_url())
def form_valid(self, form, snmp_version=None):
"""Submit the validated SNMP form data to the LibreNMS API."""
data = form.cleaned_data
# Use the snmp_version from toggle/form for v1/v2c, or from form data for v3
version = snmp_version or data.get("snmp_version")
device_data = {
"hostname": data.get("hostname"),
"snmp_version": version,
"force_add": data.get("force_add", False),
}
if data.get("port"):
device_data["port"] = data.get("port")
if data.get("transport"):
device_data["transport"] = data.get("transport")
if data.get("port_association_mode"):
device_data["port_association_mode"] = data.get("port_association_mode")
if data.get("poller_group"):
try:
device_data["poller_group"] = int(data.get("poller_group"))
except (ValueError, TypeError):
pass
if device_data["snmp_version"] in ("v1", "v2c"):
device_data["community"] = data.get("community")
elif device_data["snmp_version"] == "v3":
device_data.update(
{
"authlevel": data.get("authlevel"),
"authname": data.get("authname"),
"authpass": data.get("authpass"),
"authalgo": data.get("authalgo"),
"cryptopass": data.get("cryptopass"),
"cryptoalgo": data.get("cryptoalgo"),
}
)
else:
messages.error(self.request, "Unknown SNMP version.")
return redirect(self.object.get_absolute_url())
success, message = self.librenms_api.add_device(device_data)
if success:
messages.success(self.request, message)
else:
messages.error(self.request, message)
return redirect(self.object.get_absolute_url())
class UpdateDeviceLocationView(LibreNMSPermissionMixin, LibreNMSAPIMixin, View):
"""Update the LibreNMS site/location based on the NetBox site."""
def post(self, request, pk):
"""Sync the device location to LibreNMS from the NetBox site."""
# Check write permission before updating location in LibreNMS
if error := self.require_write_permission():
return error
device = get_object_or_404(Device, pk=pk)
self.librenms_id = self.librenms_api.get_librenms_id(device)
if device.site:
librenms_api = self.librenms_api
field_data = {
"field": ["location", "override_sysLocation"],
"data": [device.site.name, "1"],
}
success, message = librenms_api.update_device_field(self.librenms_id, field_data)
if success:
messages.success(
request,
f"Device location updated in LibreNMS to {device.site.name}",
)
else:
messages.error(request, f"Failed to update device location in LibreNMS: {message}")
else:
messages.warning(request, "Device has no associated site in NetBox")
return redirect("plugins:netbox_librenms_plugin:device_librenms_sync", pk=pk)