How to query subnets by location inside Foreman?

We use many smart proxies spread across the world with a single foreman master cluster in AWS. Our proxys’ subnets are all identical. So when we discover a host, Foreman just picks the first one that matches the IP of the discovered host, which is almost always in the wrong location/proxy. We’re looking for ways to work around this problem by patching the Foreman code here. We’re defining a custom foreman_location fact, so we already can control the discovered host’s location. But now we also need to filter the subnet association by the location. So I’m looking to change this line:

Subnet.unscoped.all.select {|s| s.family == ip.family && s.contains?(ip)}.max_by(&:cidr)

to also filter the subnets by location_id, something like:

Subnet.unscoped.all.select {|s| s.location_id = location_id && s.family == ip.family && s.contains?(ip)}.max_by(&:cidr)

I know it’s possible, but haven’t been able to figure it out after going through the code base. What’s the right way to do this?

1 Like

Hi,

I do not know how to ease of your problems without patching, which I believe should be possible.

But the code would look like:
Subnet.unscoped.joins(:locations).where(Location.arel_table[:id].eq(location_id)).all.select {|s| s.family == ip.family && s.contains?(ip)}.max_by(&:cidr)
given you know how to propagate the location_id there.

Hope it helps, but maybe someone will advice how to solve you problem without touching the code :smirk:

Thank you!! This should unblock us. But I’d really like to see a better fix for this long term, especially now that locs/orgs are always enabled. It would be great if the discovery plugin could be aware of what proxy is behind the discovered host and filter the subnets by those that include this proxy as a dhcp proxy

That’s definitely possible to implement. Do you mind opening a feature request. Maybe somebody has time to implement this.

If you want to open a PR, you should be able to change this file. The location should be set before the subnet (the order in after_populate needs to be changed). Then you can extend suggested_subnet to find the subnet filtered by the suggested location.
The algorithm that detects the location still needs to be changed to find the location based of the proxy the request came through. I’d need to look into that further to figure out a solution.

1 Like

Thanks Timo! We were able to steal existing logic for determining which proxy a request came from based on the request’s cert or IP. This allows us to pass a populated smart_proxy instance to the discovery code and use it when looking up the subnet to filter out subnets whose discovery.id != smart_proxy. Since we pick the right subnet from the start, we also get the right location without having to change the ordering. Does that sound reasonable?

This is working well in our environment. So we’ll prepare a feature request and attach a pull request for review so we can discuss it with you guys in detail.

1 Like

Please go ahead with a PR, honestly I do not exactly understand the description however when you push a patch that will hopefully make it more clear to us (or just me).

Your approach sounds very similar to how Remote Execution determines the proxy, but I don’t know enough about the implementation to confirm this. Probably worth to have a look if they could use the same logic in some way.

Note: We do have logic in core that detects the proxy doing a request.