Repository: netbox-community/netbox

Issue:
Allow searching on device page by IP address
### NetBox version

v3.3.0

### Feature type

Change to existing functionality

### Proposed functionality

We often use the devices page. It has a great layout and shows the info we need most of the time. We also frequently search for a machine by IP address. In order to do this we have to navigate to the IP Addresses page and then search there, get the machine name, then go back to the devices page.

It would be great if the 'search' on the devices pages can also search IP addresses on the same page (or perhaps other info as well).

### Use case

Use case described in the functionality text.

### Database changes

Don't believe db changes are needed.

### External dependencies

None.


Discussion:
This would likely be implemented first : https://github.com/netbox-community/netbox/issues/7016
This doesn't necessarily rely on #7016, however it would be very difficult to implement efficiently without some change to the data model. Searching for devices by IP address requires us to traverse two relations, from device to interface and from interface to IP address.
Is the ask to filter using the Device model using the  primary_ip4 or primary_ip6 fields?  I think I have a working example in the Django shell, something like this:

`Device.objects.filter(primary_ip4__net_host="x.x.x.x")`

I found where the search is being done in netbox/dcim/filtersets at DeviceFilterSet.search().  Another Q query could be appended for primary_ip4__net_host or primary_ip6__net_host, there would just need to be some validation to make sure it is a valid IPv4/IPv6 address then conditionally add the filter.

Thoughts?  I'd like to take a stab at creating a PR with the update and some tests for it if this is a valid solution.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md).
I've added the primary_ip4 and primary_ip6 filters here (https://github.com/netbox-community/netbox/pull/11382), so this could be implemented with those filters?

This would allow to search for a specific device with the mentioned primary IP address from the device filter page, but since the primary_ip4/primary_ip6 is unique, there will only be maximum one result though.

If this is okay, I could take a look at adding these fields. It looks fairly straightforward, by just adding some fields in `netbox/dcim/forms/filtersets.py`, right?
It is actually on the `netbox/dcim/filtersets.py`

I added this, and it works pretty well : 
```
    def search(self, queryset, name, value):
        if not value.strip():
            return queryset
        return queryset.filter(
            Q(name__icontains=value) |
            Q(serial__icontains=value.strip()) |
            Q(inventoryitems__serial__icontains=value.strip()) |
            Q(asset_tag__icontains=value.strip()) |
            Q(comments__icontains=value) | 
            Q(primary_ip4__address__startswith=value) |
            Q(primary_ip6__address__startswith=value)
        ).distinct()
```

I also added the fields to the search index: 
```class DeviceIndex(SearchIndex):
    model = models.Device
    fields = (
        ('asset_tag', 50),
        ('serial', 60),
        ('name', 100),
        ('primary_ip4', 110),
        ('primary_ip6', 110),
        ('description', 500),
        ('comments', 5000),
    )
```



Resolving patch:
diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py
--- a/netbox/dcim/filtersets.py
+++ b/netbox/dcim/filtersets.py
@@ -981,7 +981,9 @@ def search(self, queryset, name, value):
             Q(serial__icontains=value.strip()) |
             Q(inventoryitems__serial__icontains=value.strip()) |
             Q(asset_tag__icontains=value.strip()) |
-            Q(comments__icontains=value)
+            Q(comments__icontains=value) |
+            Q(primary_ip4__address__startswith=value) |
+            Q(primary_ip6__address__startswith=value)
         ).distinct()
 
     def _has_primary_ip(self, queryset, name, value):


Test patch: