فهرست منبع

Closes #18811: Match full-form IPv6 addresses in global search (#19873)

* Closes #18811: Match full-form IPv6 addresses in global search

* Fix typo
Jeremy Stretch 7 ماه پیش
والد
کامیت
f05897d61a
3فایلهای تغییر یافته به همراه35 افزوده شده و 4 حذف شده
  1. 15 1
      netbox/extras/lookups.py
  2. 15 0
      netbox/ipam/models/ip.py
  3. 5 3
      netbox/netbox/search/backends.py

+ 15 - 1
netbox/extras/lookups.py

@@ -18,9 +18,22 @@ class Empty(Lookup):
             return f"CAST(LENGTH({sql}) AS BOOLEAN) IS TRUE", params
 
 
+class NetHost(Lookup):
+    """
+    Similar to ipam.lookups.NetHost, but casts the field to INET.
+    """
+    lookup_name = 'net_host'
+
+    def as_sql(self, qn, connection):
+        lhs, lhs_params = self.process_lhs(qn, connection)
+        rhs, rhs_params = self.process_rhs(qn, connection)
+        params = lhs_params + rhs_params
+        return 'HOST(CAST(%s AS INET)) = HOST(%s)' % (lhs, rhs), params
+
+
 class NetContainsOrEquals(Lookup):
     """
-    This lookup has the same functionality as the one from the ipam app except lhs is cast to inet
+    Similar to ipam.lookups.NetContainsOrEquals, but casts the field to INET.
     """
     lookup_name = 'net_contains_or_equals'
 
@@ -32,4 +45,5 @@ class NetContainsOrEquals(Lookup):
 
 
 CharField.register_lookup(Empty)
+CachedValueField.register_lookup(NetHost)
 CachedValueField.register_lookup(NetContainsOrEquals)

+ 15 - 0
netbox/ipam/models/ip.py

@@ -162,6 +162,11 @@ class Aggregate(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
             return self.prefix.version
         return None
 
+    @property
+    def ipv6_full(self):
+        if self.prefix and self.prefix.version == 6:
+            return netaddr.IPAddress(self.prefix).format(netaddr.ipv6_full)
+
     def get_child_prefixes(self):
         """
         Return all Prefixes within this Aggregate
@@ -330,6 +335,11 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
     def mask_length(self):
         return self.prefix.prefixlen if self.prefix else None
 
+    @property
+    def ipv6_full(self):
+        if self.prefix and self.prefix.version == 6:
+            return netaddr.IPAddress(self.prefix).format(netaddr.ipv6_full)
+
     @property
     def depth(self):
         return self._depth
@@ -808,6 +818,11 @@ class IPAddress(ContactsMixin, PrimaryModel):
         self._original_assigned_object_id = self.__dict__.get('assigned_object_id')
         self._original_assigned_object_type_id = self.__dict__.get('assigned_object_type_id')
 
+    @property
+    def ipv6_full(self):
+        if self.address and self.address.version == 6:
+            return netaddr.IPAddress(self.address).format(netaddr.ipv6_full)
+
     def get_duplicates(self):
         return IPAddress.objects.filter(
             vrf=self.vrf,

+ 5 - 3
netbox/netbox/search/backends.py

@@ -115,11 +115,13 @@ class CachedValueSearchBackend(SearchBackend):
         if lookup in (LookupTypes.STARTSWITH, LookupTypes.ENDSWITH):
             # "Starts/ends with" matches are valid only on string values
             query_filter &= Q(type=FieldTypes.STRING)
-        elif lookup == LookupTypes.PARTIAL:
+        elif lookup in (LookupTypes.PARTIAL, LookupTypes.EXACT):
             try:
-                # If the value looks like an IP address, add an extra match for CIDR values
+                # If the value looks like an IP address, add extra filters for CIDR/INET values
                 address = str(netaddr.IPNetwork(value.strip()).cidr)
-                query_filter |= Q(type=FieldTypes.CIDR) & Q(value__net_contains_or_equals=address)
+                query_filter |= Q(type=FieldTypes.INET) & Q(value__net_host=address)
+                if lookup == LookupTypes.PARTIAL:
+                    query_filter |= Q(type=FieldTypes.CIDR) & Q(value__net_contains_or_equals=address)
             except (AddrFormatError, ValueError):
                 pass