first commit
This commit is contained in:
10
docs/development/README.md
Normal file
10
docs/development/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Development Guide: Navigating the Codebase
|
||||
|
||||
This guide is intended for developers and contributors working on the NetBox LibreNMS Plugin. It provides a broad overview of the codebase structure and key elements. For detailed NetBox plugin development documentation, see the [official NetBox documentation](https://docs.netbox.dev/en/stable/plugins/development/).
|
||||
|
||||
## Contents
|
||||
|
||||
- [Project Structure](./structure.md): Overview of the main folders and files in the plugin.
|
||||
- [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.
|
||||
44
docs/development/mixins.md
Normal file
44
docs/development/mixins.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Mixins
|
||||
|
||||
Mixins in `views/mixins.py` provide reusable logic to keep views clean and DRY (Don't Repeat Yourself). They are designed to be combined with Django or NetBox views to add specific behaviors or shared functionality. When adding new views, consider using or extending these mixins to maintain consistency and reduce code duplication.
|
||||
|
||||
### Key Mixins
|
||||
|
||||
**LibreNMSAPIMixin**
|
||||
|
||||
- Provides a `librenms_api` property for accessing the LibreNMS API from any view.
|
||||
- Ensures a single instance of the API client is reused per view instance.
|
||||
- Example usage: Add to views that need to fetch or sync data with LibreNMS.
|
||||
|
||||
**CacheMixin**
|
||||
|
||||
- Supplies helper methods for generating cache keys related to objects and data types (e.g., ports, links, vlans).
|
||||
- Useful for views that cache data fetched from LibreNMS to improve performance.
|
||||
- Methods:
|
||||
- `get_cache_key(obj, data_type="ports")`: Returns a unique cache key for the object and data type.
|
||||
- `get_last_fetched_key(obj, data_type="ports")`: Returns a cache key for tracking when data was last fetched.
|
||||
- `get_vlan_overrides_key(obj)`: Returns a cache key for storing user VLAN group override selections.
|
||||
|
||||
**VlanAssignmentMixin**
|
||||
|
||||
- Provides VLAN group resolution and assignment logic used by both the Interfaces tab (per-interface VLAN assignments) and the VLANs tab (VLAN object sync).
|
||||
- Resolves which VLAN groups are relevant to a device based on a scope hierarchy: Rack → Location → Site → SiteGroup → Region → Global.
|
||||
- Methods:
|
||||
- `get_vlan_groups_for_device(device)`: Returns all VLAN groups relevant to the device based on scope hierarchy.
|
||||
- `_build_vlan_lookup_maps(vlan_groups)`: Builds lookup dictionaries mapping VIDs to groups, VLANs, and names.
|
||||
- `_select_most_specific_group(groups, device)`: Resolves ambiguity when a VID exists in multiple groups by selecting the most specific scope.
|
||||
- `_find_vlan_in_group(vid, vlan_group_id, lookup_maps)`: Finds a VLAN by VID, preferring the specified group.
|
||||
- `_update_interface_vlan_assignment(interface, vlan_data, vlan_group_map, lookup_maps)`: Updates interface mode, untagged VLAN, and tagged VLANs in NetBox.
|
||||
|
||||
### How to Use Mixins
|
||||
|
||||
To use a mixin, simply add it to the inheritance list of your view class. For example:
|
||||
|
||||
```python
|
||||
from .mixins import LibreNMSAPIMixin, CacheMixin
|
||||
|
||||
class MyCustomView(LibreNMSAPIMixin, CacheMixin, SomeBaseView):
|
||||
# ... your view logic ...
|
||||
```
|
||||
|
||||
Mixins can be combined as needed. Place mixins before the main base view to ensure their methods and properties are available.
|
||||
35
docs/development/structure.md
Normal file
35
docs/development/structure.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Project Structure
|
||||
|
||||
This document provides an overview of the NetBox LibreNMS Plugin's codebase organization.
|
||||
|
||||
## Main Directories
|
||||
|
||||
- `netbox_librenms_plugin/` — Main plugin code
|
||||
- `views/` — Custom views for devices, mappings, VMs, etc.
|
||||
- `base/` — Abstract base views for shared logic (interfaces, cables, IP addresses, VLANs)
|
||||
- `object_sync/` — Per-model sync views registered as tabs on Device/VM detail pages
|
||||
- `sync/` — POST-only views that apply sync changes (interfaces, cables, IP addresses, VLANs, devices)
|
||||
- `models.py` — Database models
|
||||
- `forms.py` — Custom forms
|
||||
- `tables/` — Table definitions for UI
|
||||
- `templates/` — Custom templates
|
||||
- `netbox_librenms_plugin/` — Main template directory
|
||||
- `inc/` — Shared template fragments (e.g., paginator)
|
||||
- `api/` — API serializers, views, and URLs
|
||||
- `import_utils/` — Import pipeline logic, split into focused modules
|
||||
- `device_operations.py` — Device validation, single-device import, filtered fetch
|
||||
- `vm_operations.py` — VM creation and import logic
|
||||
- `bulk_import.py` — Multi-device / bulk import orchestration
|
||||
- `filters.py` — LibreNMS device filtering and retrieval
|
||||
- `permissions.py` — User permission checking helpers
|
||||
- `cache.py` — Cache key generation
|
||||
- `virtual_chassis.py` — Virtual chassis data helpers
|
||||
- `import_validation_helpers.py` — Validation state mutation during import (role/cluster/rack assignment, issue removal, status recalculation)
|
||||
- `migrations/` — Django migrations
|
||||
- `utils.py` — Utility functions
|
||||
- `navigation.py` — Menu/navigation integration
|
||||
- `static/` — Static assets (JS, CSS)
|
||||
- `netbox_librenms_plugin/` — Plugin-specific static files
|
||||
- `js/` — JavaScript files
|
||||
- `tests/` — Test suite
|
||||
- `docs/` — Documentation
|
||||
28
docs/development/templates.md
Normal file
28
docs/development/templates.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Templates
|
||||
|
||||
Templates are located in `templates/netbox_librenms_plugin/` and follow NetBox's conventions, using Django's template language. The plugin uses a combination of base templates, partials, and includes to keep the UI modular and maintainable.
|
||||
|
||||
### Structure and Conventions
|
||||
- **Base templates** (e.g., `librenms_sync_base.html`, `interfacetypemapping.html`) typically extend NetBox's generic templates (like `generic/object.html` or `generic/object_list.html`).
|
||||
- **Partials and includes** (e.g., `_interface_sync.html`, `_interface_sync_content.html`, `_cable_sync.html`) are used for reusable UI components and AJAX/HTMX content updates.
|
||||
- **The `inc/` directory** contains shared fragments, such as pagination controls (`paginator.html`).
|
||||
|
||||
### Customization and Inheritance
|
||||
- Use the Django template tag `extends` to build on top of NetBox or plugin base templates, and the `block` tag to override or inject content.
|
||||
- Use the Django template tag `include` for reusable sections (e.g., tables, forms, or modal dialogs).
|
||||
- Static assets (JS/CSS) are loaded with the Django template tag `load static` and referenced using the `static` tag.
|
||||
- Context variables and template tags (e.g., `helpers`, `plugins`, `render_table`) are used to render dynamic content and integrate with NetBox features.
|
||||
|
||||
### Examples
|
||||
**Sync Views:**
|
||||
|
||||
- `librenms_sync_base.html` provides the main layout for device/VM sync pages, extending NetBox's object template and including custom blocks for status, actions, and content.
|
||||
- `_interface_sync.html` and `_interface_sync_content.html` are used for the interface sync tab, supporting dynamic updates and user actions (like syncing selected interfaces).
|
||||
- `_vlan_sync.html` and `_vlan_sync_content.html` provide the VLAN sync tab (Devices only), with per-VLAN group selection dropdowns, color-coded status indicators (green/yellow/red), and a cache countdown timer. The VLANs tab is conditionally rendered only for devices in `librenms_sync_base.html`.
|
||||
|
||||
**Mapping Views:**
|
||||
|
||||
- `interfacetypemapping.html` and `interfacetypemapping_list.html` display and manage interface type mappings, using table layouts and info alerts.
|
||||
|
||||
|
||||
For more on NetBox's template system, see the [NetBox documentation](https://netbox.readthedocs.io/en/stable/plugins/development/#templates).
|
||||
247
docs/development/testing.md
Normal file
247
docs/development/testing.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# Testing Guide
|
||||
|
||||
This guide explains how to run the test suite, write new tests, and debug failures.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run all tests with a single command:
|
||||
|
||||
```bash
|
||||
make unittest
|
||||
```
|
||||
|
||||
Or run pytest directly:
|
||||
|
||||
```bash
|
||||
pytest netbox_librenms_plugin/tests/ -v
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
The test suite covers all major plugin functionality. Tests are organized by the module they verify:
|
||||
|
||||
| Test File | What It Tests |
|
||||
|-----------|---------------|
|
||||
| [test_librenms_api.py](../../netbox_librenms_plugin/tests/test_librenms_api.py) | LibreNMS API client—connections, device operations, locations, ports, and error handling |
|
||||
| [test_import_utils.py](../../netbox_librenms_plugin/tests/test_import_utils.py) | Device import logic—filtering, validation, and data transformation |
|
||||
| [test_import_validation_helpers.py](../../netbox_librenms_plugin/tests/test_import_validation_helpers.py) | Field validation for sites, roles, platforms, and device types |
|
||||
| [test_utils.py](../../netbox_librenms_plugin/tests/test_utils.py) | General utilities—name matching, speed conversion, and data formatting |
|
||||
| [test_background_jobs.py](../../netbox_librenms_plugin/tests/test_background_jobs.py) | Background job execution and view decision logic |
|
||||
| [test_vlan_sync.py](../../netbox_librenms_plugin/tests/test_vlan_sync.py) | VLAN sync—API fetching, comparison logic, CSS class utilities, and sync actions |
|
||||
| [test_interface_vlan_sync.py](../../netbox_librenms_plugin/tests/test_interface_vlan_sync.py) | Interface VLAN assignments—group resolution, mode detection, and per-interface VLAN assignment |
|
||||
| [test_librenms_id.py](../../netbox_librenms_plugin/tests/test_librenms_id.py) | Multi-server librenms_id helpers—get/set/find/migrate and boolean rejection |
|
||||
| [test_mixins.py](../../netbox_librenms_plugin/tests/test_mixins.py) | View mixins—CacheMixin key generation, LibreNMSAPIMixin lazy init |
|
||||
| [test_sync_devices.py](../../netbox_librenms_plugin/tests/test_sync_devices.py) | Device sync views—field updates, platform creation |
|
||||
| [test_sync_interfaces.py](../../netbox_librenms_plugin/tests/test_sync_interfaces.py) | Interface sync—port matching, attribute updates, MAC handling, librenms_id assignment |
|
||||
| [test_virtual_chassis.py](../../netbox_librenms_plugin/tests/test_virtual_chassis.py) | Virtual chassis detection—VC member naming patterns and name generation |
|
||||
| [test_sync_view_mismatch.py](../../netbox_librenms_plugin/tests/test_sync_view_mismatch.py) | Sync page context—device type mismatch detection and badge rendering |
|
||||
| [test_coverage_device_fields.py](../../netbox_librenms_plugin/tests/test_coverage_device_fields.py) | Device field sync view—field update logic and device field mapping |
|
||||
| [test_coverage_list.py](../../netbox_librenms_plugin/tests/test_coverage_list.py) | Import list view—background job decision, job result loading, and GET handler |
|
||||
| [test_coverage_api.py](../../netbox_librenms_plugin/tests/test_coverage_api.py) | LibreNMS API client—malformed payload guards, error paths, and edge cases |
|
||||
| [test_coverage_api2.py](../../netbox_librenms_plugin/tests/test_coverage_api2.py) | API views—device status, background job management, VM status endpoints |
|
||||
| [test_coverage_base_views.py](../../netbox_librenms_plugin/tests/test_coverage_base_views.py) | Base view coverage tests—sync table views, context data, and data pipeline |
|
||||
| [test_coverage_base_views2.py](../../netbox_librenms_plugin/tests/test_coverage_base_views2.py) | Additional base view coverage—IP address sync, cable matching, edge cases |
|
||||
| [test_coverage_cache.py](../../netbox_librenms_plugin/tests/test_coverage_cache.py) | Import cache helpers—cache key generation, active search tracking, metadata |
|
||||
| [test_coverage_device_operations.py](../../netbox_librenms_plugin/tests/test_coverage_device_operations.py) | Device validation—type matching, serial handling, VC detection, role lookup |
|
||||
| [test_coverage_forms.py](../../netbox_librenms_plugin/tests/test_coverage_forms.py) | Import forms—filter form choices, background-job option guards, field validation |
|
||||
| [test_coverage_mixins.py](../../netbox_librenms_plugin/tests/test_coverage_mixins.py) | View mixins—VLAN group scope resolution, VlanAssignmentMixin, scope priority |
|
||||
| [test_coverage_sync_interfaces.py](../../netbox_librenms_plugin/tests/test_coverage_sync_interfaces.py) | Interface sync view—port caching, attribute updates, MAC handling, VC member routing |
|
||||
| [test_coverage_sync_view.py](../../netbox_librenms_plugin/tests/test_coverage_sync_view.py) | Sync view base class—context preparation and tab rendering |
|
||||
| [test_coverage_sync_views.py](../../netbox_librenms_plugin/tests/test_coverage_sync_views.py) | Sync action views—cables, IP addresses, VLAN sync action handlers |
|
||||
| [test_coverage_sync_views2.py](../../netbox_librenms_plugin/tests/test_coverage_sync_views2.py) | Additional sync action view coverage—device fields, device name/type sync |
|
||||
| [test_coverage_sync_views3.py](../../netbox_librenms_plugin/tests/test_coverage_sync_views3.py) | Further sync action view coverage—location sync, VLAN assignment edge cases |
|
||||
| [test_coverage_actions.py](../../netbox_librenms_plugin/tests/test_coverage_actions.py) | Import action views—bulk import, device role/cluster/rack update, validation details |
|
||||
| [test_coverage_filters.py](../../netbox_librenms_plugin/tests/test_coverage_filters.py) | Import filter logic—filter form processing and device count helpers |
|
||||
| [test_init.py](../../netbox_librenms_plugin/tests/test_init.py) | Plugin startup—`_ensure_librenms_id_custom_field` creation, type migration, and multi-DB alias handling |
|
||||
| [test_coverage_tables.py](../../netbox_librenms_plugin/tests/test_coverage_tables.py) | Sync tables—column rendering, row data, interface and cable table helpers |
|
||||
| [test_coverage_utils.py](../../netbox_librenms_plugin/tests/test_coverage_utils.py) | Utility function coverage—name matching, speed conversion, site/platform lookup |
|
||||
| [test_coverage_virtual_chassis.py](../../netbox_librenms_plugin/tests/test_coverage_virtual_chassis.py) | Virtual chassis coverage—VC creation, position conflict handling, member naming |
|
||||
| [test_coverage_vlans_table.py](../../netbox_librenms_plugin/tests/test_coverage_vlans_table.py) | VLAN sync table—column rendering, group assignment, VLAN comparison rows |
|
||||
| [test_sync_modules.py](../../netbox_librenms_plugin/tests/test_sync_modules.py) | Module sync—inventory matching, module type resolution, and normalization rules |
|
||||
| [test_modules_view.py](../../netbox_librenms_plugin/tests/test_modules_view.py) | Module sync view—context preparation, table rendering, and module bay mapping |
|
||||
| [test_tables_modules.py](../../netbox_librenms_plugin/tests/test_tables_modules.py) | Module tables—column rendering, row formatting, and action buttons |
|
||||
| [test_permissions.py](../../netbox_librenms_plugin/tests/test_permissions.py) | Permission enforcement—mixin contracts, object-level permissions, and write guards |
|
||||
| [test_vm_operations.py](../../netbox_librenms_plugin/tests/test_vm_operations.py) | VM operations—virtual machine sync, interface handling, and VM-specific views |
|
||||
| [test_integration_sync.py](../../netbox_librenms_plugin/tests/test_integration_sync.py) | Integration tests—API client against local mock HTTP server |
|
||||
| [test_integration_virtual_chassis.py](../../netbox_librenms_plugin/tests/test_integration_virtual_chassis.py) | Integration tests—VC detection, negative cache, multi-server cache isolation |
|
||||
| [test_view_wiring.py](../../netbox_librenms_plugin/tests/test_view_wiring.py) | Smoke tests—view class MRO, mixin wiring, permission contracts, and template syntax |
|
||||
|
||||
Supporting files:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| [conftest.py](../../netbox_librenms_plugin/tests/conftest.py) | Shared pytest fixtures |
|
||||
| [test_librenms_api_helpers.py](../../netbox_librenms_plugin/tests/test_librenms_api_helpers.py) | Auto-use fixture for API configuration mocking |
|
||||
| [mock_librenms_server.py](../../netbox_librenms_plugin/tests/mock_librenms_server.py) | Minimal HTTP mock server for integration tests |
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Running Specific Tests
|
||||
|
||||
```bash
|
||||
# Run a specific test file
|
||||
pytest netbox_librenms_plugin/tests/test_librenms_api.py -v
|
||||
|
||||
# Run a specific test class
|
||||
pytest netbox_librenms_plugin/tests/test_librenms_api.py::TestLibreNMSAPIConnection -v
|
||||
|
||||
# Run a specific test method
|
||||
pytest netbox_librenms_plugin/tests/test_librenms_api.py::TestLibreNMSAPIConnection::test_connection_success -v
|
||||
```
|
||||
|
||||
### Running Tests by Area
|
||||
|
||||
```bash
|
||||
# API client tests
|
||||
pytest netbox_librenms_plugin/tests/test_librenms_api.py netbox_librenms_plugin/tests/test_coverage_api.py netbox_librenms_plugin/tests/test_coverage_api2.py -v
|
||||
|
||||
# Import and validation tests
|
||||
pytest netbox_librenms_plugin/tests/test_import_utils.py netbox_librenms_plugin/tests/test_import_validation_helpers.py netbox_librenms_plugin/tests/test_utils.py -v
|
||||
|
||||
# Background job tests
|
||||
pytest netbox_librenms_plugin/tests/test_background_jobs.py -v
|
||||
|
||||
# Multi-server librenms_id tests
|
||||
pytest netbox_librenms_plugin/tests/test_librenms_id.py -v
|
||||
|
||||
# Sync view tests (devices, interfaces, modules)
|
||||
pytest netbox_librenms_plugin/tests/test_sync_devices.py netbox_librenms_plugin/tests/test_sync_interfaces.py netbox_librenms_plugin/tests/test_sync_modules.py -v
|
||||
|
||||
# Integration tests (API client against mock HTTP server)
|
||||
pytest netbox_librenms_plugin/tests/test_integration_*.py -v
|
||||
|
||||
# Sync view mismatch detection and permission enforcement
|
||||
pytest netbox_librenms_plugin/tests/test_sync_view_mismatch.py netbox_librenms_plugin/tests/test_permissions.py -v
|
||||
|
||||
# View wiring and template syntax smoke tests
|
||||
pytest netbox_librenms_plugin/tests/test_view_wiring.py -v
|
||||
```
|
||||
|
||||
### Debugging Failed Tests
|
||||
|
||||
```bash
|
||||
# Show full traceback
|
||||
pytest netbox_librenms_plugin/tests/ -v --tb=long
|
||||
|
||||
# Show print statements during tests
|
||||
pytest netbox_librenms_plugin/tests/ -v -s
|
||||
|
||||
# Stop on first failure
|
||||
pytest netbox_librenms_plugin/tests/ -v -x
|
||||
|
||||
# Re-run only failed tests from last run
|
||||
pytest netbox_librenms_plugin/tests/ -v --lf
|
||||
```
|
||||
|
||||
## Testing Philosophy
|
||||
|
||||
The test suite prioritizes speed and isolation so you can run tests frequently during development:
|
||||
|
||||
- **Mock-based**: Unit tests use `MagicMock` instead of real database objects. No Django database setup required.
|
||||
- **Fast execution**: The full suite runs in approximately 15-20 seconds (varies by environment).
|
||||
- **Isolated**: Each test is independent with no shared state between tests.
|
||||
- **No external network access**: Tests never call external services. Integration tests use a local loopback HTTP server (`mock_librenms_server.py`) to exercise the real API client against realistic HTTP responses without requiring a running LibreNMS instance.
|
||||
- **Coverage exclusions**: Test files themselves are excluded from coverage reports (see `[tool.coverage.run]` omit list in `pyproject.toml`).
|
||||
|
||||
This approach means tests work identically in your local development environment, in the devcontainer, and in CI pipelines.
|
||||
|
||||
## Writing New Tests
|
||||
|
||||
### Basic Test Template
|
||||
|
||||
New tests should follow this structure:
|
||||
|
||||
```python
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
class TestFeatureName:
|
||||
"""Tests for [feature description]."""
|
||||
|
||||
pytest_plugins = ["tests.test_librenms_api_helpers"]
|
||||
|
||||
@patch("netbox_librenms_plugin.module_name.external_dependency")
|
||||
def test_specific_behavior(self, mock_dependency, mock_librenms_config):
|
||||
"""Describe what this test verifies."""
|
||||
# Arrange - set up test data and mocks
|
||||
mock_dependency.return_value = {"expected": "response"}
|
||||
|
||||
# Act - call the code being tested
|
||||
from netbox_librenms_plugin.module_name import function_to_test
|
||||
result = function_to_test(input_data)
|
||||
|
||||
# Assert - verify the results
|
||||
assert result == expected_value
|
||||
mock_dependency.assert_called_once_with(expected_args)
|
||||
```
|
||||
|
||||
### Key Testing Conventions
|
||||
|
||||
**Use inline imports** inside test methods to avoid Django initialization at module load time:
|
||||
|
||||
```python
|
||||
def test_something(self):
|
||||
from netbox_librenms_plugin.librenms_api import LibreNMSAPI
|
||||
api = LibreNMSAPI(server_key="default")
|
||||
```
|
||||
|
||||
**Mock NetBox models** with `MagicMock()` instead of creating real database objects:
|
||||
|
||||
```python
|
||||
device = MagicMock()
|
||||
device.name = "test-device"
|
||||
device.primary_ip.address.ip = "192.168.1.1"
|
||||
```
|
||||
|
||||
**Patch at the source module**, not where the function is imported:
|
||||
|
||||
```python
|
||||
# Correct - patch where the function is defined
|
||||
@patch("netbox_librenms_plugin.import_utils.process_device_filters")
|
||||
|
||||
# Incorrect - patching the import location
|
||||
@patch("netbox_librenms_plugin.views.imports.list.process_device_filters")
|
||||
```
|
||||
|
||||
### Available Fixtures
|
||||
|
||||
These fixtures are defined in [conftest.py](../../netbox_librenms_plugin/tests/conftest.py):
|
||||
|
||||
- `mock_librenms_config` — Automatically mocks plugin configuration for all tests
|
||||
- `mock_response_factory` — Factory for creating mock HTTP responses
|
||||
- `mock_netbox_device` — Pre-configured mock NetBox Device object
|
||||
- `mock_netbox_vm` — Pre-configured mock NetBox VM object
|
||||
|
||||
### Common Assertion Patterns
|
||||
|
||||
```python
|
||||
# Methods returning (success, data) tuples
|
||||
success, data = api.get_device_info(123)
|
||||
assert success is True
|
||||
assert data["hostname"] == "expected-hostname"
|
||||
|
||||
# Methods returning dicts with error flags
|
||||
result = api.test_connection()
|
||||
assert "error" not in result
|
||||
|
||||
# Verifying exceptions are raised
|
||||
with pytest.raises(ValueError, match="Invalid configuration"):
|
||||
api.method_that_should_fail()
|
||||
|
||||
# Verifying mock calls
|
||||
mock_get.assert_called_once()
|
||||
mock_post.assert_called_with(expected_url, headers=expected_headers, json=expected_data)
|
||||
mock_delete.assert_not_called()
|
||||
```
|
||||
|
||||
## CI/CD Compatibility
|
||||
|
||||
The tests run in any environment without external dependencies:
|
||||
|
||||
- No database connection required
|
||||
- No external network access needed (integration tests use local loopback only)
|
||||
- Fast execution suitable for pre-commit hooks
|
||||
- Clear failure messages for debugging
|
||||
- Works in containerized environments
|
||||
|
||||
This makes the test suite suitable for GitHub Actions, pre-commit hooks, or any CI pipeline you choose to implement.
|
||||
74
docs/development/views.md
Normal file
74
docs/development/views.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Views & Inheritance
|
||||
|
||||
Views are organized by resource type (e.g., devices, mappings, VMs) in the `views/` directory. The codebase uses a layered approach to views, leveraging inheritance and mixins to maximize code reuse and maintainability.
|
||||
|
||||
### View Organization
|
||||
|
||||
**Resource-specific views:**
|
||||
|
||||
- Device and VM sync tabs live under `object_sync/` (see `object_sync/devices.py` and `object_sync/vms.py`), while mappings/settings/status views remain as individual modules alongside the package.
|
||||
- The LibreNMS import workflow is grouped under `views/imports/`: `list.py` renders the main table view and `actions.py` contains the HTMX endpoints (preview, validation, bulk execute). All legacy handlers formerly in `librenms_import_views.py` and `device_import_views.py` were folded into this package.
|
||||
|
||||
**Base views:**
|
||||
|
||||
- The `base/` subdirectory contains abstract base views (e.g., `BaseLibreNMSSyncView`, `BaseInterfaceTableView`, `BaseCableTableView`, `BaseIPAddressTableView`, `BaseVLANTableView`) that encapsulate shared logic for related resources.
|
||||
|
||||
**Mixins:**
|
||||
|
||||
- Shared behaviors (e.g., API access, caching) are factored into mixins in `mixins.py` and combined with base or resource-specific views as needed.
|
||||
|
||||
### Inheritance Patterns
|
||||
|
||||
- Most resource-specific views inherit from a base view in `base/` and one or more mixins.
|
||||
- Base views themselves often inherit from NetBox or Django generic views (e.g., `generic.ObjectListView`, `django.views.View`).
|
||||
- This allows resource-specific views to override or extend only the methods they need, while inheriting default behaviors from base classes and mixins.
|
||||
|
||||
#### Example: Device Sync View
|
||||
|
||||
```python
|
||||
from .base.librenms_sync_view import BaseLibreNMSSyncView
|
||||
from .mixins import LibreNMSAPIMixin
|
||||
|
||||
class DeviceLibreNMSSyncView(BaseLibreNMSSyncView):
|
||||
# Inherits API access and sync logic from base/mixins
|
||||
# Only device-specific logic needs to be implemented here
|
||||
...
|
||||
```
|
||||
|
||||
#### Example: Interface Table View
|
||||
|
||||
```python
|
||||
from .base.interfaces_view import BaseInterfaceTableView
|
||||
from .mixins import CacheMixin, LibreNMSAPIMixin
|
||||
|
||||
class DeviceInterfaceTableView(BaseInterfaceTableView):
|
||||
model = Device
|
||||
# Implements get_interfaces and get_redirect_url for devices
|
||||
...
|
||||
```
|
||||
|
||||
#### Example: VLAN Table View
|
||||
|
||||
```python
|
||||
from .base.vlan_table_view import BaseVLANTableView
|
||||
|
||||
class DeviceVLANTableView(BaseVLANTableView):
|
||||
model = Device
|
||||
# Inherits VLAN comparison, group resolution, and caching from base view
|
||||
# Only the model attribute needs to be set
|
||||
...
|
||||
```
|
||||
|
||||
`BaseVLANTableView` additionally inherits `VlanAssignmentMixin` for VLAN group scope resolution. It fetches device VLANs from LibreNMS, compares them against NetBox VLAN objects across relevant VLAN groups, and renders a color-coded table with per-VLAN group dropdowns.
|
||||
|
||||
### Customizing or Adding Views
|
||||
|
||||
- To add a new view for a resource, inherit from the relevant base view and mixins, then override or extend methods as needed.
|
||||
- Use the base views as templates for structure and required methods.
|
||||
- Register new views in `urls.py` and add templates if needed.
|
||||
|
||||
### Tips
|
||||
|
||||
- Check the `base/` directory for reusable logic before writing new view code.
|
||||
- Use mixins for cross-cutting concerns (API, caching, permissions).
|
||||
- Keep resource-specific views focused on their unique logic; delegate shared logic to base classes and mixins.
|
||||
Reference in New Issue
Block a user