Support Primary IP Address Lookup Expression for Device and Virtual Machine #11644

Closed
opened 2025-12-29 21:48:00 +01:00 by adam · 6 comments
Owner

Originally created by @jseifeddine on GitHub (Sep 23, 2025).

NetBox version

v4.4.1

Feature type

Change to existing functionality

Proposed functionality

It would be nice to be able to use lookup expressions with primary IP address on device / vm
As documented: String Lookup Expressions

I see that some work was done for IP address filtering recently...
https://github.com/netbox-community/netbox/issues/19110

But rather than fixing a fix - I would rather mimic the behavior of lookup expressions for the name field, as an IP address is a string also... it makes sense that it should have the same expressions available

And just as a side note: the documentation for String Lookup Expressions that I would expect works with any string field, is a hit and miss .... depending on what field it is - and i guess, who implemented the filterset FR

So there is possibly more in this than just the primary IP address filter
But it would be nice that all fields are consistent in the way that they can be filtered...

I am happy to spend quality time on this and submit a PR, if the issue is accepted and we can get some solid direction to move forward

Maybe I am missing something? If not, I'm happy to get to work on at least the primary IP address fix - and ultimately - if this is a valid issue across the board, apply this fix to any string filters across the board.

(sorry I can't come up with any other examples right now, but I can track that down and post updates)

Use case

Easily search devices / vms based on IP address starting with, ie. primary_ip4__isw

eg. /dcim/devices/?primary_ip4__isw=100.64.

Database changes

Don't think so

External dependencies

None

Originally created by @jseifeddine on GitHub (Sep 23, 2025). ### NetBox version v4.4.1 ### Feature type Change to existing functionality ### Proposed functionality It would be nice to be able to use lookup expressions with primary IP address on device / vm As documented: [`String Lookup Expressions`](https://netboxlabs.com/docs/netbox/reference/filtering/#string-fields) I see that some work was done for IP address filtering recently... https://github.com/netbox-community/netbox/issues/19110 But rather than fixing a fix - I would rather mimic the behavior of lookup expressions for the `name` field, as an IP address is a string also... it makes sense that it should have the same expressions available And just as a side note: the documentation for [`String Lookup Expressions`](https://netboxlabs.com/docs/netbox/reference/filtering/#string-fields) that I would expect works with any string field, is a hit and miss .... depending on what field it is - and i guess, who implemented the filterset `FR` So there is possibly more in this than just the primary IP address filter But it would be nice that all fields are consistent in the way that they can be filtered... I am happy to spend quality time on this and submit a `PR`, if the issue is accepted and we can get some solid direction to move forward Maybe I am missing something? If not, I'm happy to get to work on at least the primary IP address fix - and ultimately - if this is a valid issue across the board, apply this fix to any string filters across the board. (sorry I can't come up with any other examples right now, but I can track that down and post updates) ### Use case Easily search devices / vms based on IP address starting with, ie. `primary_ip4__isw` eg. `/dcim/devices/?primary_ip4__isw=100.64.` ### Database changes Don't think so ### External dependencies None
adam added the type: feature label 2025-12-29 21:48:00 +01:00
adam closed this issue 2025-12-29 21:48:01 +01:00
Author
Owner

@jnovinger commented on GitHub (Oct 2, 2025):

The primary_ip4 and primary_ip6 fields are foreign keys to IPAddress objects, not string fields, so string lookup expressions don't apply. IP addresses are actually numbers under the hood, despite being displayed in dotted notation, therefore we don't support string-based filtering on them.

@jnovinger commented on GitHub (Oct 2, 2025): The `primary_ip4` and `primary_ip6` fields are foreign keys to `IPAddress` objects, not string fields, so string lookup expressions don't apply. IP addresses are actually numbers under the hood, despite being displayed in dotted notation, therefore we don't support string-based filtering on them.
Author
Owner

@jseifeddine commented on GitHub (Oct 3, 2025):

This works... its minimal, I don't believe this would be a breaking change and would be really useful.

f25d5db3da

diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py
index 1d201fc38..66c071c08 100644
--- a/netbox/ipam/filtersets.py
+++ b/netbox/ipam/filtersets.py
@@ -1259,10 +1259,8 @@ class PrimaryIPFilterSet(django_filters.FilterSet):
         queryset=IPAddress.objects.all(),
         label=_('Primary IPv4 (ID)'),
     )
-    primary_ip4 = django_filters.ModelMultipleChoiceFilter(
+    primary_ip4 = MultiValueCharFilter(
         field_name='primary_ip4__address',
-        queryset=IPAddress.objects.all(),
-        to_field_name='address',
         label=_('Primary IPv4 (address)'),
     )
     primary_ip6_id = django_filters.ModelMultipleChoiceFilter(
@@ -1270,9 +1268,7 @@ class PrimaryIPFilterSet(django_filters.FilterSet):
         queryset=IPAddress.objects.all(),
         label=_('Primary IPv6 (ID)'),
     )
-    primary_ip6 = django_filters.ModelMultipleChoiceFilter(
+    primary_ip6 = MultiValueCharFilter(
         field_name='primary_ip6__address',
-        queryset=IPAddress.objects.all(),
-        to_field_name='address',
         label=_('Primary IPv6 (address)'),
     )
Image Image Image
@jseifeddine commented on GitHub (Oct 3, 2025): This works... its minimal, I don't believe this would be a breaking change and would be really useful. https://github.com/jseifeddine/netbox/commit/f25d5db3da558ffa789a6c1033642c91b1fee080 ```diff diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 1d201fc38..66c071c08 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -1259,10 +1259,8 @@ class PrimaryIPFilterSet(django_filters.FilterSet): queryset=IPAddress.objects.all(), label=_('Primary IPv4 (ID)'), ) - primary_ip4 = django_filters.ModelMultipleChoiceFilter( + primary_ip4 = MultiValueCharFilter( field_name='primary_ip4__address', - queryset=IPAddress.objects.all(), - to_field_name='address', label=_('Primary IPv4 (address)'), ) primary_ip6_id = django_filters.ModelMultipleChoiceFilter( @@ -1270,9 +1268,7 @@ class PrimaryIPFilterSet(django_filters.FilterSet): queryset=IPAddress.objects.all(), label=_('Primary IPv6 (ID)'), ) - primary_ip6 = django_filters.ModelMultipleChoiceFilter( + primary_ip6 = MultiValueCharFilter( field_name='primary_ip6__address', - queryset=IPAddress.objects.all(), - to_field_name='address', label=_('Primary IPv6 (address)'), ) ``` <img width="1037" height="546" alt="Image" src="https://github.com/user-attachments/assets/06cc746b-3374-4e75-bf20-9dd01e699d8f" /> <img width="1040" height="519" alt="Image" src="https://github.com/user-attachments/assets/82aebf39-fafb-40b5-90c5-ac9c6925a0fd" /> <img width="1036" height="504" alt="Image" src="https://github.com/user-attachments/assets/46b409fa-3559-479a-862c-7b1b32e1e65b" />
Author
Owner

@jseifeddine commented on GitHub (Oct 6, 2025):

No breaking functionality
The removal of these parameters:
Does NOT break existing exact match filtering
Produces the same query results for valid inputs
⚠️ Removes early validation (but this just means invalid IPs return empty results instead of potentially raising an error)
The PostgreSQL INET field handles the actual IP validation

In practice: Users passing valid IP addresses will see identical behavior. Users passing invalid IP addresses will still get empty results (no matches), just without early validation.

I think this is acceptable and follows NetBox patterns - other char filters don't validate against the database either; they just filter and return results or no results.

@jeremystretch

@jseifeddine commented on GitHub (Oct 6, 2025): ✅ No breaking functionality The removal of these parameters: ✅ Does NOT break existing exact match filtering ✅ Produces the same query results for valid inputs ⚠️ Removes early validation (but this just means invalid IPs return empty results instead of potentially raising an error) ✅ The PostgreSQL INET field handles the actual IP validation In practice: Users passing valid IP addresses will see identical behavior. Users passing invalid IP addresses will still get empty results (no matches), just without early validation. I think this is acceptable and follows NetBox patterns - other char filters don't validate against the database either; they just filter and return results or no results. @jeremystretch
Author
Owner

@InevitableMarble249 commented on GitHub (Oct 8, 2025):

+1 This would be really useful for us too

@InevitableMarble249 commented on GitHub (Oct 8, 2025): +1 This would be really useful for us too
Author
Owner

@jnovinger commented on GitHub (Oct 9, 2025):

Thank you for the idea and working implementation. The community interest suggests this might be worth exploring further.

However, I have concerns about relying on PostgreSQL's implicit INET -> text coercion for string pattern matching. Before accepting this approach, we'd need to investigate several technical questions that would be better addressed in a GitHub Discussion with community input.

Key Concerns

  1. PostgreSQL Behavior

    • Is LIKE/ILIKE on INET fields documented behavior or implementation detail?
    • Is this behavior intentional or not? If not, would the PostgreSQL team consider it a bug and fix it?
    • What SQL does Django actually generate? Something like CAST(address AS TEXT) LIKE '10.%' or native INET support?)
    • Is this guaranteed across PostgreSQL 12-16 (NetBox's supported range)?
  2. PostgreSQL normalizes IPv6 addresses (2001:db8::1 vs 2001:0db8:0000:0000:0000:0000:0000:0001). String matching might bypass this:

    • Does ?primary_ip6__istartswith=2001:0db8 match addresses stored as 2001:db8::1?
    • How does __icontains handle compressed notation?
  3. Dependency Stack

    • Do PostgreSQL versions 12-16 provide consistent behavior?
    • I imagine this implementation was testing against a single combination of NetBox versions, Python dependency versions, and PostgreSQL version. While it might seems unlikely, we need to verify that this behavior will continue for all applicable components.
  4. Performance

    • Does string pattern matching make use of existing INET indexes?
    • At threshold does this cause performance issues on installations with many IP addresses? thousands? millions?
  5. Alternative Approaches?

    • Given the seeming intention of a query like /dcim/devices/?primary_ip4__isw=100.64. is to find devices with primary IPs contained within the 100.64.0.0/16 network, could we find an alternative that implements the semantics of "is contained by network 100.64.0.0/16" as opposed to the inferred meaning?
    • This could look like ?primary_ip4__net_contained=100.64.0.0/16 or similar? This would work with PostgreSQL's INET machinery? The result could be a much more explicit query with very specific semantics rather than trying to infer based on string coercion and intended meaning.
    • Should the existing filters (__isw or __icontains) be implemented for IP addresses in a way that maintains the specific semantic meaning mentioned above? is this even a good idea?
    • Something else entirely?

What We'd Need (at a minimum)

  1. PostgreSQL documentation confirming LIKE/ILIKE on INET is intended behavior
  2. SQL query inspection across supported PostgreSQL versions
  3. IPv6 testing with various address representations
  4. Performance analysis (index usage)
  5. Cross-version testing across PostgreSQL 12-16
  6. Discussion and analysis of alternative approaches

If you would like to pursue this further, then I encourage you to open a GitHub Discussion with the goal of addressing the points above. If the community can arrive at something that makes sense and is deemed maintainable by the maintainers, then we could proceed to a new FR. This will help us evaluate whether string lookups are the right solution or if network-based filtering would better serve the community while working with PostgreSQL's type system.

We appreciate the initiative. This discussion will ensure any solution is robust across NetBox's diverse deployment environments and is maintainable in the long-term.

@jnovinger commented on GitHub (Oct 9, 2025): Thank you for the idea and working implementation. The community interest suggests this might be worth exploring further. However, I have concerns about relying on PostgreSQL's implicit `INET` -> `text` coercion for string pattern matching. Before accepting this approach, we'd need to investigate several technical questions that would be better addressed in a [GitHub Discussion](https://github.com/netbox-community/netbox/discussions/new?category=ideas) with community input. **Key Concerns** 1. PostgreSQL Behavior - Is `LIKE`/`ILIKE` on `INET` fields documented behavior or implementation detail? - Is this behavior intentional or not? If not, would the PostgreSQL team consider it a bug and fix it? - What SQL does Django actually generate? Something like `CAST(address AS TEXT) LIKE '10.%'` or native `INET` support?) - Is this guaranteed across PostgreSQL 12-16 (NetBox's supported range)? 2. PostgreSQL normalizes IPv6 addresses (`2001:db8::1` vs `2001:0db8:0000:0000:0000:0000:0000:0001`). String matching might bypass this: - Does `?primary_ip6__istartswith=2001:0db8` match addresses stored as `2001:db8::1`? - How does `__icontains` handle compressed notation? 3. Dependency Stack - Do PostgreSQL versions 12-16 provide consistent behavior? - I imagine this implementation was testing against a single combination of NetBox versions, Python dependency versions, and PostgreSQL version. While it might seems unlikely, we need to verify that this behavior will continue for all applicable components. 4. Performance - Does string pattern matching make use of existing `INET` indexes? - At threshold does this cause performance issues on installations with many IP addresses? thousands? millions? 5. Alternative Approaches? - Given the seeming intention of a query like `/dcim/devices/?primary_ip4__isw=100.64.` is to find devices with primary IPs contained within the `100.64.0.0/16` network, could we find an alternative that implements the semantics of "is contained by network 100.64.0.0/16" as opposed to the inferred meaning? - This could look like `?primary_ip4__net_contained=100.64.0.0/16` or similar? This would work with PostgreSQL's `INET` machinery? The result could be a much more explicit query with very specific semantics rather than trying to infer based on string coercion and intended meaning. - Should the existing filters (`__isw` or `__icontains`) be implemented for IP addresses in a way that maintains the specific semantic meaning mentioned above? is this even a good idea? - Something else entirely? What We'd Need (at a minimum) 1. PostgreSQL documentation confirming `LIKE`/`ILIKE` on `INET` is intended behavior 2. SQL query inspection across supported PostgreSQL versions 3. IPv6 testing with various address representations 4. Performance analysis (index usage) 5. Cross-version testing across PostgreSQL 12-16 6. Discussion and analysis of alternative approaches If you would like to pursue this further, then I encourage you to open a [GitHub Discussion](https://github.com/netbox-community/netbox/discussions/new?category=ideas) with the goal of addressing the points above. If the community can arrive at something that makes sense and is deemed maintainable by the maintainers, then we could proceed to a new FR. This will help us evaluate whether string lookups are the right solution or if network-based filtering would better serve the community while working with PostgreSQL's type system. We appreciate the initiative. This discussion will ensure any solution is robust across NetBox's diverse deployment environments and is maintainable in the long-term.
Author
Owner

@jseifeddine commented on GitHub (Oct 10, 2025):

Thanks @jnovinger

For most of your concerns - I can do some testing and report back all findings.

In regard to the alternative, I guess you are implying they would be in the NetBox IP prefix etc.

Sure, in the perfect world - but we know that people are messy and don't always rules.

I'll open a discussion too.

Thank you

@jseifeddine commented on GitHub (Oct 10, 2025): Thanks @jnovinger For most of your concerns - I can do some testing and report back all findings. In regard to the alternative, I guess you are implying they would be in the NetBox IP prefix etc. Sure, in the perfect world - but we know that people are messy and don't always rules. I'll open a discussion too. Thank you
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11644