Skip to content

Commit f05897d

Browse files
Closes #18811: Match full-form IPv6 addresses in global search (#19873)
* Closes #18811: Match full-form IPv6 addresses in global search * Fix typo
1 parent b5421f1 commit f05897d

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

netbox/extras/lookups.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,22 @@ def as_sql(self, compiler, connection):
1818
return f"CAST(LENGTH({sql}) AS BOOLEAN) IS TRUE", params
1919

2020

21+
class NetHost(Lookup):
22+
"""
23+
Similar to ipam.lookups.NetHost, but casts the field to INET.
24+
"""
25+
lookup_name = 'net_host'
26+
27+
def as_sql(self, qn, connection):
28+
lhs, lhs_params = self.process_lhs(qn, connection)
29+
rhs, rhs_params = self.process_rhs(qn, connection)
30+
params = lhs_params + rhs_params
31+
return 'HOST(CAST(%s AS INET)) = HOST(%s)' % (lhs, rhs), params
32+
33+
2134
class NetContainsOrEquals(Lookup):
2235
"""
23-
This lookup has the same functionality as the one from the ipam app except lhs is cast to inet
36+
Similar to ipam.lookups.NetContainsOrEquals, but casts the field to INET.
2437
"""
2538
lookup_name = 'net_contains_or_equals'
2639

@@ -32,4 +45,5 @@ def as_sql(self, qn, connection):
3245

3346

3447
CharField.register_lookup(Empty)
48+
CachedValueField.register_lookup(NetHost)
3549
CachedValueField.register_lookup(NetContainsOrEquals)

netbox/ipam/models/ip.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ def family(self):
162162
return self.prefix.version
163163
return None
164164

165+
@property
166+
def ipv6_full(self):
167+
if self.prefix and self.prefix.version == 6:
168+
return netaddr.IPAddress(self.prefix).format(netaddr.ipv6_full)
169+
165170
def get_child_prefixes(self):
166171
"""
167172
Return all Prefixes within this Aggregate
@@ -330,6 +335,11 @@ def family(self):
330335
def mask_length(self):
331336
return self.prefix.prefixlen if self.prefix else None
332337

338+
@property
339+
def ipv6_full(self):
340+
if self.prefix and self.prefix.version == 6:
341+
return netaddr.IPAddress(self.prefix).format(netaddr.ipv6_full)
342+
333343
@property
334344
def depth(self):
335345
return self._depth
@@ -808,6 +818,11 @@ def __init__(self, *args, **kwargs):
808818
self._original_assigned_object_id = self.__dict__.get('assigned_object_id')
809819
self._original_assigned_object_type_id = self.__dict__.get('assigned_object_type_id')
810820

821+
@property
822+
def ipv6_full(self):
823+
if self.address and self.address.version == 6:
824+
return netaddr.IPAddress(self.address).format(netaddr.ipv6_full)
825+
811826
def get_duplicates(self):
812827
return IPAddress.objects.filter(
813828
vrf=self.vrf,

netbox/netbox/search/backends.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ def search(self, value, user=None, object_types=None, lookup=DEFAULT_LOOKUP_TYPE
115115
if lookup in (LookupTypes.STARTSWITH, LookupTypes.ENDSWITH):
116116
# "Starts/ends with" matches are valid only on string values
117117
query_filter &= Q(type=FieldTypes.STRING)
118-
elif lookup == LookupTypes.PARTIAL:
118+
elif lookup in (LookupTypes.PARTIAL, LookupTypes.EXACT):
119119
try:
120-
# If the value looks like an IP address, add an extra match for CIDR values
120+
# If the value looks like an IP address, add extra filters for CIDR/INET values
121121
address = str(netaddr.IPNetwork(value.strip()).cidr)
122-
query_filter |= Q(type=FieldTypes.CIDR) & Q(value__net_contains_or_equals=address)
122+
query_filter |= Q(type=FieldTypes.INET) & Q(value__net_host=address)
123+
if lookup == LookupTypes.PARTIAL:
124+
query_filter |= Q(type=FieldTypes.CIDR) & Q(value__net_contains_or_equals=address)
123125
except (AddrFormatError, ValueError):
124126
pass
125127

0 commit comments

Comments
 (0)