From afe96f5a8b07af983d0ed4b3cc08a9a69ecb5e41 Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Fri, 23 Jan 2026 12:29:05 +0000 Subject: [PATCH 01/25] Test: Update CI workflow to align with NetBox plugin testing patterns --- .github/workflows/test.yaml | 53 +++++++++++++++------------------- media/configuration.testing.py | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 media/configuration.testing.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index aa82b8bb26..1cb307091e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,21 +1,16 @@ -name: Tests +name: Test with all supported NetBox versions on: - push: - branches: - - master - - develop - pull_request: - branches: - - master - - develop + - push + - pull_request jobs: - test: + test-netbox: runs-on: ubuntu-latest + strategy: matrix: - python-version: ['3.12', '3.13', '3.14'] + python-version: ["3.12", "3.13", "3.14"] services: redis: @@ -37,41 +32,41 @@ jobs: - 5432:5432 steps: - - name: Checkout plugin code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@main with: path: netbox-librenms-plugin - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@main with: python-version: ${{ matrix.python-version }} - name: Checkout NetBox - uses: actions/checkout@v4 + uses: actions/checkout@main with: - repository: netbox-community/netbox + repository: "netbox-community/netbox" path: netbox ref: main - - name: Install NetBox dependencies - working-directory: netbox - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - - name: Install plugin + - name: Install NetBox LibreNMS Plugin working-directory: netbox-librenms-plugin run: | pip install . - pip install -r requirements_dev.txt + pip install pytest pytest-django - - name: Configure NetBox - working-directory: netbox/netbox + - name: Set up configuration + working-directory: netbox run: | - cp netbox/configuration_testing.py netbox/configuration.py + ln -s $(pwd)/../netbox-librenms-plugin/media/configuration.testing.py netbox/netbox/configuration.py + + python -m pip install --upgrade pip + python -m pip install tblib + pip install -r requirements.txt -U - name: Run tests - working-directory: netbox + working-directory: netbox/netbox + env: + NETBOX_CONFIGURATION: netbox.configuration run: | - python -m pytest ../netbox-librenms-plugin/netbox_librenms_plugin/tests/ -v + python -m pytest ../../netbox-librenms-plugin/netbox_librenms_plugin/tests/ -v diff --git a/media/configuration.testing.py b/media/configuration.testing.py new file mode 100644 index 0000000000..aa58b59b1e --- /dev/null +++ b/media/configuration.testing.py @@ -0,0 +1,52 @@ +################################################################### +# This file serves as a base configuration for testing purposes # +# only. It is not intended for production use. # +################################################################### + +ALLOWED_HOSTS = ["*"] + +DATABASE = { + "NAME": "netbox", + "USER": "netbox", + "PASSWORD": "netbox", + "HOST": "localhost", + "PORT": "", + "CONN_MAX_AGE": 300, +} + +PLUGINS = [ + "netbox_librenms_plugin", +] + +PLUGINS_CONFIG = { + "netbox_librenms_plugin": { + "servers": { + "default": { + "librenms_url": "https://librenms.example.com", + "api_token": "test-token-for-testing", + } + } + } +} + +REDIS = { + "tasks": { + "HOST": "localhost", + "PORT": 6379, + "PASSWORD": "", + "DATABASE": 0, + "SSL": False, + }, + "caching": { + "HOST": "localhost", + "PORT": 6379, + "PASSWORD": "", + "DATABASE": 1, + "SSL": False, + }, +} + +SECRET_KEY = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +API_TOKEN_PEPPERS = { + 1: "TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE", +} From 6920679abc825bffade0ed15bacf25244570b590 Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Fri, 23 Jan 2026 12:33:11 +0000 Subject: [PATCH 02/25] Fix: Exclude tests from package, use editable install in CI --- .github/workflows/test.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1cb307091e..d6c2956ba3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -52,7 +52,7 @@ jobs: - name: Install NetBox LibreNMS Plugin working-directory: netbox-librenms-plugin run: | - pip install . + pip install -e . pip install pytest pytest-django - name: Set up configuration diff --git a/pyproject.toml b/pyproject.toml index 596ae21b5a..83c70fc848 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ include-package-data = true [tool.setuptools.packages.find] include = ["netbox_librenms_plugin*"] -exclude = ["site*"] +exclude = ["site*", "netbox_librenms_plugin.tests*"] [tool.setuptools.package-data] netbox_librenms_plugin = ["templates/**"] From ed5d47f484aacd1f105f85f1ed37218220cbed4c Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Fri, 23 Jan 2026 13:01:38 +0000 Subject: [PATCH 03/25] CI: Strict lint/format on all pushes, tests only on develop/master --- .github/workflows/lint-format.yaml | 19 +++---------------- .github/workflows/test.yaml | 10 ++++++++-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/lint-format.yaml b/.github/workflows/lint-format.yaml index 5829a14ce2..afd4dd86e8 100644 --- a/.github/workflows/lint-format.yaml +++ b/.github/workflows/lint-format.yaml @@ -2,25 +2,19 @@ name: Lint and Format on: push: - branches: - - master - - develop pull_request: - branches: - - master - - develop jobs: format-and-lint: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.12' - name: Install dependencies run: | @@ -29,13 +23,6 @@ jobs: - name: Run Ruff linting run: ruff check . - continue-on-error: true - name: Run Ruff formatting check run: ruff format --check . - continue-on-error: true - - - name: Report formatting issues - if: always() - run: | - echo "If there are any formatting issues, run 'ruff check --fix .' and 'ruff format .' locally and push the changes." diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d6c2956ba3..26afa7d787 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,8 +1,14 @@ name: Test with all supported NetBox versions on: - - push - - pull_request + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop jobs: test-netbox: From ba8682700c252a3122abbab9cdfe10a5cfb2c955 Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Mon, 26 Jan 2026 12:01:58 +0000 Subject: [PATCH 04/25] Add SNMPv1 support to LibreNMS add device form --- netbox_librenms_plugin/forms.py | 6 +- netbox_librenms_plugin/librenms_api.py | 6 +- .../js/librenms_sync.js | 18 ++--- .../librenms_sync_base.html | 55 ++++++++------- .../tests/test_librenms_api.py | 68 +++++++++++++++++++ .../views/base/librenms_sync_view.py | 4 +- netbox_librenms_plugin/views/sync/devices.py | 29 +++++--- 7 files changed, 136 insertions(+), 50 deletions(-) diff --git a/netbox_librenms_plugin/forms.py b/netbox_librenms_plugin/forms.py index 3f1bb8aef6..e22cf32222 100644 --- a/netbox_librenms_plugin/forms.py +++ b/netbox_librenms_plugin/forms.py @@ -230,10 +230,11 @@ class InterfaceTypeMappingFilterForm(NetBoxModelFilterSetForm): model = InterfaceTypeMapping -class AddToLIbreSNMPV2(forms.Form): +class AddToLIbreSNMPV1V2(forms.Form): """ - Form for adding devices to LibreNMS using SNMPv2 authentication. + Form for adding devices to LibreNMS using SNMPv1 or SNMPv2c authentication. Collects hostname/IP and SNMP community string information. + The SNMP version (v1 or v2c) is selected via a toggle button in the template. """ hostname = forms.CharField( @@ -241,7 +242,6 @@ class AddToLIbreSNMPV2(forms.Form): max_length=255, required=True, ) - snmp_version = forms.CharField(widget=forms.HiddenInput(), initial="v2c") community = forms.CharField(label="SNMP Community", max_length=255, required=True) port = forms.IntegerField( label="SNMP Port", diff --git a/netbox_librenms_plugin/librenms_api.py b/netbox_librenms_plugin/librenms_api.py index de4192f065..e58cf26153 100644 --- a/netbox_librenms_plugin/librenms_api.py +++ b/netbox_librenms_plugin/librenms_api.py @@ -347,13 +347,13 @@ def add_device(self, data): Args: Dictionary containing device data including: - hostname: Device hostname or IP - - snmp_version: SNMP version (v2c or v3) + - snmp_version: SNMP version (v1, v2c, or v3) - force_add: Skip checks for duplicate device and SNMP reachability (optional, default False) - port: SNMP port (optional, defaults to config value) - transport: SNMP transport protocol (optional: udp, tcp, udp6, tcp6) - port_association_mode: Port identification method (optional: ifIndex, ifName, ifDescr, ifAlias) - poller_group: Poller group ID (optional, defaults to 0) - - community: SNMP community string (for v2c) + - community: SNMP community string (for v1 or v2c) - authlevel, authname, authpass, authalgo, cryptopass, cryptoalgo: SNMP v3 parameters Returns: @@ -375,7 +375,7 @@ def add_device(self, data): if data.get("poller_group") is not None: payload["poller_group"] = data["poller_group"] - if data["snmp_version"] == "v2c": + if data["snmp_version"] in ("v1", "v2c"): payload["community"] = data["community"] elif data["snmp_version"] == "v3": payload.update( diff --git a/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_sync.js b/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_sync.js index 392e84ead4..98357e9d76 100644 --- a/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_sync.js +++ b/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_sync.js @@ -640,21 +640,23 @@ function initializeTabs() { /** * Toggle SNMP form visibility based on selected version. - * Shows either SNMPv2c or SNMPv3 configuration form. + * Shows either SNMPv1/v2c or SNMPv3 configuration form. */ function toggleSNMPForms() { - const snmpSelect = document.querySelector('#add-device-modal select.form-select'); + const snmpSelect = document.getElementById('snmp-version-select'); if (!snmpSelect) return; const version = snmpSelect.value; - const v2Form = document.getElementById('snmpv2-form'); + const v1v2Form = document.getElementById('snmpv1v2-form'); const v3Form = document.getElementById('snmpv3-form'); - if (version === 'v2c') { - v2Form.style.display = 'block'; + if (!v1v2Form || !v3Form) return; + + if (version === 'v1v2c') { + v1v2Form.style.display = 'block'; v3Form.style.display = 'none'; - } else { - v2Form.style.display = 'none'; + } else if (version === 'v3') { + v1v2Form.style.display = 'none'; v3Form.style.display = 'block'; } } @@ -664,7 +666,7 @@ function toggleSNMPForms() { * Sets up version toggle and displays correct form. */ function initializeSNMPModalScripts() { - const snmpSelect = document.querySelector('#add-device-modal select.form-select'); + const snmpSelect = document.getElementById('snmp-version-select'); if (snmpSelect) { snmpSelect.addEventListener('change', toggleSNMPForms); // Initial call to set the correct form visibility diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html index 8d9a553e9a..88357ee734 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html @@ -565,48 +565,57 @@
- +
- +
+ id="snmpv1v2-form" name="snmpv1v2-form" style="display: block;"> {% csrf_token %} - {{ v2form.snmp_version }} +
- {{ v2form.hostname.label_tag }} - {{ v2form.hostname }} + +
+ + + + +
+
+
+ {{ v1v2form.hostname.label_tag }} + {{ v1v2form.hostname }}
- {{ v2form.community.label_tag }} - {{ v2form.community }} + {{ v1v2form.community.label_tag }} + {{ v1v2form.community }}
- {{ v2form.port.label_tag }} - {{ v2form.port }} -
{{ v2form.port.help_text }}
+ {{ v1v2form.port.label_tag }} + {{ v1v2form.port }} +
{{ v1v2form.port.help_text }}
- {{ v2form.transport.label_tag }} - {{ v2form.transport }} + {{ v1v2form.transport.label_tag }} + {{ v1v2form.transport }}
- {{ v2form.port_association_mode.label_tag }} - {{ v2form.port_association_mode }} -
{{ v2form.port_association_mode.help_text }}
+ {{ v1v2form.port_association_mode.label_tag }} + {{ v1v2form.port_association_mode }} +
{{ v1v2form.port_association_mode.help_text }}
- {{ v2form.poller_group.label_tag }} - {{ v2form.poller_group }} -
{{ v2form.poller_group.help_text }}
+ {{ v1v2form.poller_group.label_tag }} + {{ v1v2form.poller_group }} +
{{ v1v2form.poller_group.help_text }}
- {{ v2form.force_add }} - {{ v2form.force_add.label_tag }} -
{{ v2form.force_add.help_text }}
+ {{ v1v2form.force_add }} + {{ v1v2form.force_add.label_tag }} +
{{ v1v2form.force_add.help_text }}
diff --git a/netbox_librenms_plugin/tests/test_librenms_api.py b/netbox_librenms_plugin/tests/test_librenms_api.py index d48cfb7571..a01a1b3cd5 100644 --- a/netbox_librenms_plugin/tests/test_librenms_api.py +++ b/netbox_librenms_plugin/tests/test_librenms_api.py @@ -589,6 +589,34 @@ def test_add_device_success(self, mock_post, mock_librenms_config): assert result[0] is True assert result[1] == "Device added successfully." + @patch("netbox_librenms_plugin.librenms_api.requests.post") + def test_add_device_snmpv1_success(self, mock_post, mock_librenms_config): + """Verify successful device addition using SNMPv1.""" + mock_post.return_value.status_code = 200 + mock_post.return_value.json.return_value = { + "status": "ok", + "message": "Device added successfully", + } + + from netbox_librenms_plugin.librenms_api import LibreNMSAPI + + api = LibreNMSAPI(server_key="default") + result = api.add_device( + data={ + "hostname": "legacy-device.example.com", + "snmp_version": "v1", + "community": "public", + } + ) + + assert result[0] is True + assert result[1] == "Device added successfully." + # Verify the payload includes correct snmpver and community + call_args = mock_post.call_args + payload = call_args.kwargs.get("json") or call_args[1].get("json") + assert payload["snmpver"] == "v1" + assert payload["community"] == "public" + @patch("netbox_librenms_plugin.librenms_api.requests.post") def test_add_device_duplicate_error(self, mock_post, mock_librenms_config): """Verify duplicate device handling.""" @@ -612,6 +640,46 @@ def test_add_device_duplicate_error(self, mock_post, mock_librenms_config): assert result[0] is False assert "Device already exists" in result[1] + @patch("netbox_librenms_plugin.librenms_api.requests.post") + def test_add_device_snmpv3_success(self, mock_post, mock_librenms_config): + """Verify successful device addition using SNMPv3 with all required fields.""" + mock_post.return_value.status_code = 200 + mock_post.return_value.json.return_value = { + "status": "ok", + "message": "Device added successfully", + } + + from netbox_librenms_plugin.librenms_api import LibreNMSAPI + + api = LibreNMSAPI(server_key="default") + result = api.add_device( + data={ + "hostname": "secure-device.example.com", + "snmp_version": "v3", + "authlevel": "authPriv", + "authname": "snmpuser", + "authpass": "authpassword123", + "authalgo": "SHA", + "cryptopass": "cryptopassword456", + "cryptoalgo": "AES", + } + ) + + assert result[0] is True + assert result[1] == "Device added successfully." + # Verify the payload includes correct snmpver and all v3 fields + call_args = mock_post.call_args + payload = call_args.kwargs.get("json") or call_args[1].get("json") + assert payload["snmpver"] == "v3" + assert payload["authlevel"] == "authPriv" + assert payload["authname"] == "snmpuser" + assert payload["authpass"] == "authpassword123" + assert payload["authalgo"] == "SHA" + assert payload["cryptopass"] == "cryptopassword456" + assert payload["cryptoalgo"] == "AES" + # Ensure community is NOT included for v3 + assert "community" not in payload + @patch("netbox_librenms_plugin.librenms_api.requests.patch") def test_update_device_field_success(self, mock_patch, mock_librenms_config): """Verify successful device field update.""" diff --git a/netbox_librenms_plugin/views/base/librenms_sync_view.py b/netbox_librenms_plugin/views/base/librenms_sync_view.py index 1049d6fc18..4477fd261f 100644 --- a/netbox_librenms_plugin/views/base/librenms_sync_view.py +++ b/netbox_librenms_plugin/views/base/librenms_sync_view.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404, render from netbox.views import generic -from netbox_librenms_plugin.forms import AddToLIbreSNMPV2, AddToLIbreSNMPV3 +from netbox_librenms_plugin.forms import AddToLIbreSNMPV1V2, AddToLIbreSNMPV3 from netbox_librenms_plugin.utils import ( get_interface_name_field, get_librenms_sync_device, @@ -101,7 +101,7 @@ def get_context_data(self, request, obj): "interface_sync": interface_context, "cable_sync": cable_context, "ip_sync": ip_context, - "v2form": AddToLIbreSNMPV2(prefix="v2"), + "v1v2form": AddToLIbreSNMPV1V2(prefix="v1v2"), "v3form": AddToLIbreSNMPV3(prefix="v3"), "librenms_device_id": self.librenms_id, "found_in_librenms": librenms_info.get("found_in_librenms"), diff --git a/netbox_librenms_plugin/views/sync/devices.py b/netbox_librenms_plugin/views/sync/devices.py index fcd199e5c2..638574d719 100644 --- a/netbox_librenms_plugin/views/sync/devices.py +++ b/netbox_librenms_plugin/views/sync/devices.py @@ -4,7 +4,7 @@ from django.views import View from virtualization.models import VirtualMachine -from netbox_librenms_plugin.forms import AddToLIbreSNMPV2, AddToLIbreSNMPV3 +from netbox_librenms_plugin.forms import AddToLIbreSNMPV1V2, AddToLIbreSNMPV3 from netbox_librenms_plugin.views.mixins import LibreNMSAPIMixin @@ -14,10 +14,13 @@ class AddDeviceToLibreNMSView(LibreNMSAPIMixin, View): def get_form_class(self): snmp_version = self.request.POST.get("snmp_version") if not snmp_version: - snmp_version = self.request.POST.get("v2-snmp_version") or self.request.POST.get("v3-snmp_version") + snmp_version = ( + self.request.POST.get("v1v2-snmp_version") + or self.request.POST.get("v3-snmp_version") + ) - if snmp_version == "v2c": - return AddToLIbreSNMPV2 + if snmp_version in ("v1", "v2c"): + return AddToLIbreSNMPV1V2 return AddToLIbreSNMPV3 def get_object(self, object_id): @@ -31,26 +34,30 @@ def post(self, request, object_id): form_class = self.get_form_class() snmp_version = ( - request.POST.get("snmp_version") - or request.POST.get("v2-snmp_version") + request.POST.get("v1v2-snmp_version") or request.POST.get("v3-snmp_version") ) - prefix = "v2" if snmp_version == "v2c" else "v3" + prefix = "v1v2" if snmp_version in ("v1", "v2c") else "v3" form = form_class(request.POST, prefix=prefix) if form.is_valid(): - return self.form_valid(form) + # 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): + def form_valid(self, form, snmp_version=None): 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": data.get("snmp_version"), + "snmp_version": version, "force_add": data.get("force_add", False), } @@ -66,7 +73,7 @@ def form_valid(self, form): except (ValueError, TypeError): pass - if device_data["snmp_version"] == "v2c": + if device_data["snmp_version"] in ("v1", "v2c"): device_data["community"] = data.get("community") elif device_data["snmp_version"] == "v3": device_data.update( From 1480d3aad749c035ab9ac2a90522a8224fea944c Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Mon, 26 Jan 2026 12:04:59 +0000 Subject: [PATCH 05/25] refactor(devices): simplify SNMP version retrieval logic --- netbox_librenms_plugin/views/sync/devices.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/netbox_librenms_plugin/views/sync/devices.py b/netbox_librenms_plugin/views/sync/devices.py index 638574d719..7c28698b38 100644 --- a/netbox_librenms_plugin/views/sync/devices.py +++ b/netbox_librenms_plugin/views/sync/devices.py @@ -14,10 +14,7 @@ class AddDeviceToLibreNMSView(LibreNMSAPIMixin, View): def get_form_class(self): 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") - ) + 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 @@ -33,10 +30,7 @@ def post(self, request, object_id): self.object = self.get_object(object_id) form_class = self.get_form_class() - snmp_version = ( - request.POST.get("v1v2-snmp_version") - or request.POST.get("v3-snmp_version") - ) + 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) From afcdd0e8d285f235ae04b8c6c29d8131d59c2bc4 Mon Sep 17 00:00:00 2001 From: Andy Norwood Date: Mon, 26 Jan 2026 12:21:33 +0000 Subject: [PATCH 06/25] Format: Fix trailing whitespace and end-of-file issues - Remove trailing whitespace from templates, JS, docs, and config files - Add missing newlines at end of files - Applied via pre-commit hooks (first run after installation) --- .devcontainer/config/plugin-config.py.example | 2 +- .devcontainer/devcontainer.json | 2 +- .devcontainer/scripts/start-netbox.sh | 2 +- .devcontainer/scripts/welcome.sh | 2 +- .github/copilot-instructions.md | 2 +- .gitignore | 2 +- .pre-commit-config.yaml | 2 +- LICENSE | 2 +- docs/development/README.md | 4 ---- docs/development/mixins.md | 1 - docs/development/structure.md | 2 -- docs/feature_list.md | 1 - docs/librenms_import/import_process.md | 1 - docs/librenms_import/import_settings.md | 1 - docs/usage_tips/custom_field.md | 2 +- .../static/netbox_librenms_plugin/js/librenms_import.js | 4 ++-- .../netbox_librenms_plugin/_cable_sync_content.html | 2 +- .../templates/netbox_librenms_plugin/_interface_sync.html | 2 +- .../netbox_librenms_plugin/_interface_sync_content.html | 2 +- .../templates/netbox_librenms_plugin/_ipaddress_sync.html | 2 +- .../netbox_librenms_plugin/_ipaddress_sync_content.html | 6 +++--- .../netbox_librenms_plugin/htmx/device_import_row.html | 2 +- .../netbox_librenms_plugin/interfacetypemapping.html | 2 +- .../netbox_librenms_plugin/interfacetypemapping_list.html | 8 ++++---- .../templates/netbox_librenms_plugin/librenms_import.html | 1 - .../netbox_librenms_plugin/librenms_sync_base.html | 2 +- 26 files changed, 25 insertions(+), 36 deletions(-) diff --git a/.devcontainer/config/plugin-config.py.example b/.devcontainer/config/plugin-config.py.example index 71da51d4d9..c9648174cc 100644 --- a/.devcontainer/config/plugin-config.py.example +++ b/.devcontainer/config/plugin-config.py.example @@ -5,7 +5,7 @@ Default plugin configuration for the NetBox LibreNMS Plugin in the dev container - Copy this file to .devcontainer/plugin-config.py - Edit values as needed. -- Add config for all other plugins here if any. +- Add config for all other plugins here if any. """ # Ensure our plugin is enabled in dev (the loader sets this as a default too) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 61548824e2..77c4c8cccc 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -72,4 +72,4 @@ "postCreateCommand": "bash .devcontainer/scripts/setup.sh", "postAttachCommand": "bash .devcontainer/scripts/welcome.sh", "remoteUser": "root" -} \ No newline at end of file +} diff --git a/.devcontainer/scripts/start-netbox.sh b/.devcontainer/scripts/start-netbox.sh index 2a02a336ed..0ab8acaf46 100755 --- a/.devcontainer/scripts/start-netbox.sh +++ b/.devcontainer/scripts/start-netbox.sh @@ -95,4 +95,4 @@ else echo "💡 If clicking the URL opens 0.0.0.0:8000, manually type: localhost:8000" echo "" python manage.py runserver 0.0.0.0:8000 -fi \ No newline at end of file +fi diff --git a/.devcontainer/scripts/welcome.sh b/.devcontainer/scripts/welcome.sh index c2776a398d..c9b811e62c 100755 --- a/.devcontainer/scripts/welcome.sh +++ b/.devcontainer/scripts/welcome.sh @@ -48,4 +48,4 @@ echo "🚀 Quick start:" echo " • Type 'netbox-run' to start the development server" echo " • Type 'dev-help' to see all available commands" echo " • Edit code in the workspace - auto-reload is enabled" -echo "" \ No newline at end of file +echo "" diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f1d53cbd84..93b0bd8788 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -29,4 +29,4 @@ ## When in Doubt - Check docs in `docs/development/` for structure, view inheritance, mixins, and template conventions before introducing new patterns. - Review the existing sync views (e.g., `views/sync/interfaces.py`) as reference implementations for data flow and caching patterns. -- Coordinate any schema changes through Django migrations in `migrations/` and update `models.py` + admin/pydantic representations accordingly. \ No newline at end of file +- Coordinate any schema changes through Django migrations in `migrations/` and update `models.py` + admin/pydantic representations accordingly. diff --git a/.gitignore b/.gitignore index 525045b674..dd2caaa5f7 100644 --- a/.gitignore +++ b/.gitignore @@ -284,4 +284,4 @@ cython_debug/ .devcontainer/extra-requirements.txt .devcontainer/config/plugin-config.py .devcontainer/config/extra-configuration.py -.devcontainer/config/extra-plugins.py \ No newline at end of file +.devcontainer/config/extra-plugins.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ea62321cc..7d553d0af5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,4 +15,4 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - - id: check-merge-conflict \ No newline at end of file + - id: check-merge-conflict diff --git a/LICENSE b/LICENSE index f49a4e16e6..261eeb9e9f 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/docs/development/README.md b/docs/development/README.md index d4faac47df..de81c7d02b 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -8,7 +8,3 @@ This guide is intended for developers and contributors working on the NetBox Lib - [Views & Inheritance](./views.md): How views are organized, inheritance patterns, and extension tips. - [Mixins](./mixins.md): Reusable logic for views, including API access and caching. - [Templates](./templates.md): Template structure, conventions, and customization tips. - - - - diff --git a/docs/development/mixins.md b/docs/development/mixins.md index 7762a5fd4c..c6e6cb3337 100644 --- a/docs/development/mixins.md +++ b/docs/development/mixins.md @@ -30,4 +30,3 @@ class MyCustomView(LibreNMSAPIMixin, CacheMixin, SomeBaseView): ``` Mixins can be combined as needed. Place mixins before the main base view to ensure their methods and properties are available. - diff --git a/docs/development/structure.md b/docs/development/structure.md index f8538b1502..daa1b0bb50 100644 --- a/docs/development/structure.md +++ b/docs/development/structure.md @@ -23,5 +23,3 @@ This document provides an overview of the NetBox LibreNMS Plugin's codebase orga - `js/` — JavaScript files - `tests/` — Test suite - `docs/` — Documentation - - diff --git a/docs/feature_list.md b/docs/feature_list.md index fd741c4afe..4ddc982ebc 100644 --- a/docs/feature_list.md +++ b/docs/feature_list.md @@ -63,4 +63,3 @@ * Customizable LibreNMS to NetBox interface type mappings * Interface Speed-based mapping rules * Bulk import support - diff --git a/docs/librenms_import/import_process.md b/docs/librenms_import/import_process.md index e0dc4d92a2..e30e7acbbb 100644 --- a/docs/librenms_import/import_process.md +++ b/docs/librenms_import/import_process.md @@ -93,4 +93,3 @@ After importing devices, typical next steps include: 5. **Sync IP addresses** - Pull IP address assignments from LibreNMS to NetBox These sync operations use the `librenms_id` custom field that was automatically set during import. - diff --git a/docs/librenms_import/import_settings.md b/docs/librenms_import/import_settings.md index a70684081c..5082b0a268 100644 --- a/docs/librenms_import/import_settings.md +++ b/docs/librenms_import/import_settings.md @@ -56,4 +56,3 @@ When using bulk import, you can override the default settings in the confirmatio - Test different naming conventions before changing global defaults The override only affects the current import operation and doesn't change your saved defaults. - diff --git a/docs/usage_tips/custom_field.md b/docs/usage_tips/custom_field.md index e0d8e72d72..9934fbd232 100644 --- a/docs/usage_tips/custom_field.md +++ b/docs/usage_tips/custom_field.md @@ -30,7 +30,7 @@ Follow these steps to create the `librenms_id` custom field in NetBox: 3. **Configure the Custom Field:** - - **Object Types:** + - **Object Types:** - Check **dcim > device** - Check **virtualization > virtual machine** - Check **dcim > interface** diff --git a/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_import.js b/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_import.js index bc8c84bd1b..5cd4eb069c 100644 --- a/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_import.js +++ b/netbox_librenms_plugin/static/netbox_librenms_plugin/js/librenms_import.js @@ -252,7 +252,7 @@ const manager = new ModalManager(modalElement); manager.show(); - + // Store backdrop reference for legacy compatibility if (fallbackBackdropRef && manager.backdropElement) { fallbackBackdropRef.element = manager.backdropElement; @@ -667,7 +667,7 @@ }); if (deviceCount) deviceCount.style.display = 'none'; - + if (cancelBtn) { cancelBtn.innerHTML = ' Close'; cancelBtn.onclick = function () { diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync_content.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync_content.html index f12904ff40..9c4fee6b64 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync_content.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_cable_sync_content.html @@ -107,4 +107,4 @@
Device Association
- \ No newline at end of file + diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync.html index d0241d53fb..fa1d3c0d73 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync.html @@ -34,4 +34,4 @@

Interface Sync

{% include 'netbox_librenms_plugin/_interface_sync_content.html' %} -
\ No newline at end of file + diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html index c2b62d2d6b..2ce1cb8d76 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_interface_sync_content.html @@ -294,4 +294,4 @@

IP Address Sync

{% include 'netbox_librenms_plugin/_ipaddress_sync_content.html' %} -
\ No newline at end of file + diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync_content.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync_content.html index 755d56e4b7..bced7b42e1 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync_content.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/_ipaddress_sync_content.html @@ -48,7 +48,7 @@ flex-wrap: wrap; align-items: center; } - + /* VRF dropdown styles */ td[data-col="vrf"] { width: 250px; @@ -58,7 +58,7 @@ width: 100%; max-width: 100%; } - + /* Ensure select elements in VRF column maintain consistent width */ td[data-col="vrf"] select.form-select { width: 100%; @@ -73,4 +73,4 @@ -{% endif %} \ No newline at end of file +{% endif %} diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_import_row.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_import_row.html index 611efd5042..76aac95546 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_import_row.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_import_row.html @@ -19,4 +19,4 @@ {# Consume and clear any pending Django messages to prevent reappearing toasts #}
{% for _ in messages %}{% endfor %} -
\ No newline at end of file + diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping.html index af754a0b36..119e4e1180 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping.html @@ -27,4 +27,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html index 0fbedc2182..3044c4dda5 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/interfacetypemapping_list.html @@ -3,10 +3,10 @@ {% block content %}

Interface Type Mapping

-

This section allows you to map LibreNMS interface types to NetBox interface types. - When synchronizing interfaces from LibreNMS, these mappings will be used to ensure +

This section allows you to map LibreNMS interface types to NetBox interface types. + When synchronizing interfaces from LibreNMS, these mappings will be used to ensure correct interface type assignment in NetBox.

Example: Map LibreNMS type "ethernetCsmacd" to NetBox type "1000base-t"

- {{ block.super }} -{% endblock %} \ No newline at end of file + {{ block.super }} +{% endblock %} diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_import.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_import.html index e2009dea59..76217b7522 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_import.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_import.html @@ -422,4 +422,3 @@
Applying Filters
{% endblock %} - diff --git a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html index 88357ee734..8674de7557 100644 --- a/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html +++ b/netbox_librenms_plugin/templates/netbox_librenms_plugin/librenms_sync_base.html @@ -805,4 +805,4 @@