Skip to content

feat: LibreNMS location mapping and location string parsing#302

Open
bonzo81 wants to merge 11 commits into
developfrom
feat/location-mapping-and-parsing
Open

feat: LibreNMS location mapping and location string parsing#302
bonzo81 wants to merge 11 commits into
developfrom
feat/location-mapping-and-parsing

Conversation

@bonzo81

@bonzo81 bonzo81 commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

Adds LibreNMS location handling to the device import flow in two complementary parts:

  1. LocationMapping model — maps a LibreNMS location string to a NetBox Region/Site/Location/Rack/Tenant via a GenericForeignKey, with full CRUD views, REST API, table, filterset, navigation menu item, and a contrib seed YAML.
  2. Location parse settings — two new LibreNMSSettings fields (location_parse_pattern, location_parse_is_regex) that parse the LibreNMS location string into tokens (e.g. site/location/rack) for import. Parsing is best-effort: when no pattern is configured, or a pattern doesn't resolve a token, the import falls back to whole-string matching so baseline behaviour is preserved.

It also fixes a LibreNMS 26.5.0 compatibility issue where the device location field is now a relationship object rather than a flat string.

Motivation / Problem

  • Feature: Users with structured LibreNMS location strings (or location names that don't exactly match NetBox sites) had no way to map them to the correct NetBox objects during import.
  • Bug: On LibreNMS 26.5.0, list_devices returned location as a dict ({id, location, lat, lng, ...}), which broke exact site matching and surfaced as "No matching site" on the import validation modal.

Scope of Change

  • Sync/Import logic
  • NetBox models / ORM
  • LibreNMS API interaction
  • Config / settings
  • Web UI / templates
  • Database migrations
  • Tests

How Was This Tested?

  • Unit tests: yes — new test_location_mapping.py and test_location_parse.py cover the model, parser, settings-aware wrapper (including non-destructive fallback and dict-location normalization), and form validation; test_utils.py updated for the find_matching_site LocationMapping fallback. Full plugin suite passes (3100 tests).
  • Manual testing: yes — verified live that the import validation modal resolves matching sites correctly (including the previously-failing dict-location and malformed-pattern cases), and that the matched site links through to its NetBox object.

Manual Test Steps

  1. Configure a Location Parsing pattern under Plugin Settings (optional) and/or add a LocationMapping entry.
  2. Run a LibreNMS device import and open the validation modal for a device whose location does/doesn't match a NetBox site.
  3. Confirm the matched site resolves (via exact match, mapping, or whole-string fallback) and links to the NetBox site object.

Risk Assessment

  • Affects site/location resolution during device import only.
  • The parse pattern is non-destructive and best-effort: a missing/non-matching pattern falls back to whole-string matching, so it cannot break baseline site matching. Form validation now rejects malformed patterns (e.g. unbalanced braces).

Backwards Compatibility

  • No breaking changes. With no parse pattern and no mappings configured, behaviour is identical to before (exact whole-string site matching). New settings fields default to empty/false.

Other Notes

  • Includes migrations 0011_locationmapping, 0012_librenmssettings_location_parse_is_regex_and_more, and 0013_locationmapping_uniq_locationmapping_unscoped_ci (case-insensitive partial unique constraint for unscoped region/site/tenant mappings, added in response to review).

bonzo81 added 5 commits May 29, 2026 15:34
Add a LocationMapping model that maps a LibreNMS location string to a
NetBox Region/Site/Location/Rack/Tenant via a GenericForeignKey, plus two
LibreNMSSettings fields (location_parse_pattern, location_parse_is_regex)
for parsing the LibreNMS location string into tokens.

Includes the model/form definitions, parser and mapping-resolver helpers
in utils.py, and the supporting migrations.
Wire the LocationMapping model into the UI and API: list/detail/edit views,
django-tables2 table, filterset, REST serializer/endpoints, URL routes, and a
navigation menu item. Add a contrib seed YAML and document it in the contrib
README.
Use the configured parse pattern (with non-destructive fallback to whole-string
matching) and LocationMapping aliases when resolving the site and location during
device import validation and creation. Add the Location Parsing settings card and
link the matched site to its NetBox object in the import validation modal.
LibreNMS 26.5.0 returns each device's location as a relationship object
(id, location, lat, lng, ...) instead of a flat name string. Flatten it to
the location name in list_devices so the import flow matches sites correctly,
mirroring the existing get_device_info handling.
Add test_location_mapping.py and test_location_parse.py covering the model,
parser, settings-aware wrapper (including non-destructive fallback and dict
location normalization), and form validation. Update test_utils.py for the
find_matching_site LocationMapping fallback.
Comment thread netbox_librenms_plugin/utils.py Dismissed
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request adds LocationMapping support: a new LocationMapping model and migrations, parsing settings on LibreNMSSettings, parsing/resolver utilities, integration into device import (exact-name lookup with mapping fallback), full CRUD views/URLs/navigation, REST API serializer/viewset, forms with validation and import helpers, table/templates including bulk YAML export, and comprehensive tests. Example configuration template and README entry are included.

Sequence Diagram(s)

sequenceDiagram
  participant DeviceImport
  participant ParseService
  participant Settings
  participant LocationMapping
  participant NetBoxDB
  DeviceImport->>ParseService: parse_location_for_import(librenms_location)
  ParseService->>Settings: get_location_parse_settings()
  Settings-->>ParseService: pattern, is_regex
  ParseService->>ParseService: parse_librenms_location(...)
  alt parsed site found
    ParseService-->>DeviceImport: site token
  else no parsed site
    DeviceImport->>NetBoxDB: exact Site.name lookup
    alt exact match
      NetBoxDB-->>DeviceImport: Site (match_type='exact', confidence=1.0)
    else no exact match
      DeviceImport->>LocationMapping: resolve_location_mapping('site', value)
      LocationMapping->>NetBoxDB: return mapped Site or None
      NetBoxDB-->>DeviceImport: Site (match_type='mapping') or None
    end
  end
Loading

Possibly related PRs

  • bonzo81/netbox-librenms-plugin#301: Normalises LibreNMS device["location"] from dict format to plain string, which this PR also depends on for consistent location handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 49.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature addition: LibreNMS location mapping and location string parsing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description comprehensively covers all required sections: summary, motivation/problem, scope of change, testing approach, risk assessment, backwards compatibility, and additional notes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 38d57b1e-ce12-40a0-85d9-06335b565d9c

📥 Commits

Reviewing files that changed from the base of the PR and between 29b3e2d and 71cc2e1.

📒 Files selected for processing (25)
  • contrib/README.md
  • contrib/location_mappings.yaml
  • netbox_librenms_plugin/api/serializers.py
  • netbox_librenms_plugin/api/urls.py
  • netbox_librenms_plugin/api/views.py
  • netbox_librenms_plugin/filters.py
  • netbox_librenms_plugin/forms.py
  • netbox_librenms_plugin/import_utils/device_operations.py
  • netbox_librenms_plugin/librenms_api.py
  • netbox_librenms_plugin/migrations/0011_locationmapping.py
  • netbox_librenms_plugin/migrations/0012_librenmssettings_location_parse_is_regex_and_more.py
  • netbox_librenms_plugin/models.py
  • netbox_librenms_plugin/navigation.py
  • netbox_librenms_plugin/tables/mappings.py
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/locationmapping.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/locationmapping_list.html
  • netbox_librenms_plugin/templates/netbox_librenms_plugin/settings.html
  • netbox_librenms_plugin/tests/test_location_mapping.py
  • netbox_librenms_plugin/tests/test_location_parse.py
  • netbox_librenms_plugin/tests/test_utils.py
  • netbox_librenms_plugin/urls.py
  • netbox_librenms_plugin/utils.py
  • netbox_librenms_plugin/views/__init__.py
  • netbox_librenms_plugin/views/mapping_views.py

Comment thread netbox_librenms_plugin/forms.py
Comment thread netbox_librenms_plugin/models.py
Comment thread netbox_librenms_plugin/utils.py
Comment thread netbox_librenms_plugin/utils.py Outdated
bonzo81 added 2 commits June 1, 2026 08:54
- Cap location string length (512 chars) before regex matching to mitigate
  polynomial-regex DoS on user-provided parse patterns (CodeQL).
- Read LibreNMSSettings via order_by('pk').first() instead of hard-coded pk=1
  so a settings row with any PK is honoured.
- Strip the netbox_object value in LocationMappingImportForm.clean() before
  lookup and persist the normalised value.
Add a case-insensitive partial UniqueConstraint on (field_type,
Lower(librenms_value)) for region/site/tenant mappings so concurrent writes
cannot create ambiguous duplicates that make resolution non-deterministic.
location/rack remain excluded (scoped to a parent site). The constraint's
backing partial index also serves the unscoped resolution lookups.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds LibreNMS “location” support to the device import workflow by (1) introducing a LocationMapping model (CRUD UI + API + import/export seed YAML) for explicit aliasing of LibreNMS location tokens to NetBox org objects, and (2) adding settings-driven parsing of the LibreNMS location string into structured tokens with best-effort fallback to preserve existing behavior. Also normalizes LibreNMS 26.5.0+ location relationship objects back to a string value for compatibility.

Changes:

  • Add LocationMapping (model, migrations, tables, filters, forms, views, navigation, API endpoints, templates, contrib seed YAML).
  • Add location parse settings (location_parse_pattern, location_parse_is_regex) + parsing utilities and integrate into device import site/location resolution.
  • Normalize LibreNMS device location payload shape changes (dict → name string) in the API client and downstream parsing.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
netbox_librenms_plugin/views/mapping_views.py Adds CRUD + bulk import/export/changelog views for LocationMapping.
netbox_librenms_plugin/views/init.py Re-exports newly added LocationMapping views.
netbox_librenms_plugin/utils.py Adds location normalization, parsing, settings lookup, and mapping resolution utilities; updates site matching fallback.
netbox_librenms_plugin/urls.py Adds URL routes for LocationMapping UI endpoints.
netbox_librenms_plugin/tests/test_utils.py Updates site-matching tests to cover mapping fallback behavior.
netbox_librenms_plugin/tests/test_location_parse.py Adds unit tests for parsing logic and ImportSettingsForm validation.
netbox_librenms_plugin/tests/test_location_mapping.py Adds unit tests for LocationMapping model behavior and resolver logic.
netbox_librenms_plugin/templates/netbox_librenms_plugin/settings.html Adds settings UI for location parsing + JS change-detection for save button enablement.
netbox_librenms_plugin/templates/netbox_librenms_plugin/locationmapping.html Adds LocationMapping object detail template.
netbox_librenms_plugin/templates/netbox_librenms_plugin/locationmapping_list.html Adds LocationMapping list template text + bulk YAML export button.
netbox_librenms_plugin/templates/netbox_librenms_plugin/htmx/device_validation_details.html Makes matched site in validation details link to the NetBox site object.
netbox_librenms_plugin/tables/mappings.py Adds LocationMappingTable for UI list/bulk operations.
netbox_librenms_plugin/navigation.py Adds “Location Mappings” to the plugin navigation menu.
netbox_librenms_plugin/models.py Adds LocationMapping model and location-parse settings fields on LibreNMSSettings.
netbox_librenms_plugin/migrations/0011_locationmapping.py Creates initial LocationMapping database table.
netbox_librenms_plugin/migrations/0012_librenmssettings_location_parse_is_regex_and_more.py Adds the two new location parse fields to LibreNMSSettings.
netbox_librenms_plugin/migrations/0013_locationmapping_uniq_locationmapping_unscoped_ci.py Adds conditional case-insensitive uniqueness constraint for unscoped mapping types.
netbox_librenms_plugin/librenms_api.py Normalizes LibreNMS 26.5.0+ device location relationship objects to a string.
netbox_librenms_plugin/import_utils/device_operations.py Integrates parsing + mapping resolution into import validation and device creation (site/location).
netbox_librenms_plugin/forms.py Adds settings form validation for parse pattern + LocationMapping create/import/filter forms.
netbox_librenms_plugin/filters.py Adds LocationMappingFilterSet for UI/API filtering.
netbox_librenms_plugin/api/views.py Adds REST API viewset for LocationMapping.
netbox_librenms_plugin/api/urls.py Registers location-mappings API routes.
netbox_librenms_plugin/api/serializers.py Adds LocationMappingSerializer.
contrib/README.md Documents new location_mappings.yaml seed file.
contrib/location_mappings.yaml Provides example seed mappings for region/site/location/rack/tenant.

Comment thread netbox_librenms_plugin/models.py
Comment thread netbox_librenms_plugin/views/mapping_views.py Outdated
bonzo81 added 4 commits June 1, 2026 10:26
- Use models.F("field_type") in the UniqueConstraint to match the generated
  migration and Django's expression-constraint API.
- Add select_related("content_type") to the LocationMapping list and YAML
  export querysets to avoid per-row content type queries, matching the other
  mapping list views.
select_related('content_type') only joins the ContentType row, not the GFK
target object. Add prefetch_related('netbox_object') on the list and YAML
export querysets so rendering netbox_object (table column + to_yaml) issues
one query per content type instead of one per row.
@bonzo81 bonzo81 linked an issue Jun 2, 2026 that may be closed by this pull request
@bonzo81 bonzo81 added the enhancement New feature or request label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No location showing on LibreNMS Sync page

3 participants