Skip to content

Commit

Permalink
Fixes #38046 - Make sure IPv6 interface can be primary
Browse files Browse the repository at this point in the history
  • Loading branch information
ShimShtein authored and stejskalleos committed Jan 9, 2025
1 parent cab93bf commit ab23d6c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
29 changes: 15 additions & 14 deletions app/services/fact_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def interfaces
# tries to detect primary interface among interfaces using host name
def suggested_primary_interface(host)
# we search among interface with ip and mac if we didn't find it by name
potential = interfaces.select { |_, values| values[:ipaddress].present? && values[:macaddress].present? }
potential = interfaces.select { |_, values| (values[:ipaddress].present? || values[:ipaddress6].present?) && values[:macaddress].present? }
find_interface_by_name(host.name) || find_physical_interface(potential) ||
find_virtual_interface(potential) || potential.first || interfaces.first
end
Expand Down Expand Up @@ -139,20 +139,21 @@ def bios
def find_interface_by_name(host_name)
resolver = Resolv::DNS.new
resolver.timeouts = PRIMARY_INTERFACE_RESOLVE_TIMEOUTS
interfaces.detect do |int, values|
if (ip = values[:ipaddress]).present?
begin
logger.debug { "Resolving fact '#{ip}' via DNS to match reported hostname #{host_name}" }
if resolver.getnames(ip).any? { |name| name.to_s == host_name }
logger.debug { "Match: '#{ip}', interface #{int} is selected as primary" }
return [int, values]
end
rescue Resolv::ResolvError => e
logger.debug { "Could not resolv name for #{ip} because of #{e} #{e.message}" }
nil
end
end
interfaces.find do |int, values|
[values[:ipaddress], values[:ipaddress6]].find { |ip| try_resolve(int, ip, host_name, resolver) }
end
end

def try_resolve(int, ip, host_name, resolver)
return nil unless ip
logger.debug { "Resolving fact '#{ip}' via DNS to match reported hostname #{host_name}" }
if resolver.getnames(ip).any? { |name| name.to_s == host_name }
logger.debug { "Match: '#{ip}', interface #{int} is selected as primary" }
true
end
rescue Resolv::ResolvError => e
logger.debug { "Could not resolv name for #{ip} because of #{e} #{e.message}" }
nil
end

def find_physical_interface(interfaces)
Expand Down
32 changes: 32 additions & 0 deletions test/unit/fact_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,22 @@ class FactParserTest < ActiveSupport::TestCase
assert_equal '00:00:00:00:00:12', found.last[:macaddress]
end

test "#suggested_primary_interface detects primary interface using DNS IPv6" do
parser.stubs(:interfaces).returns({
'br0' => {'ipaddress6' => '2001:db8::10', 'macaddress' => '00:00:00:00:00:10'},
'em1' => {'ipaddress6' => '2001:db8::20', 'macaddress' => '00:00:00:00:00:20'},
'em2' => {'ipaddress6' => '2001:db8::30', 'macaddress' => '00:00:00:00:00:30'},
'bond0' => {'ipaddress6' => '2001:db8::40', 'macaddress' => '00:00:00:00:00:40'},
}.with_indifferent_access)

Resolv::DNS.any_instance.stubs(:getnames).returns([])
Resolv::DNS.any_instance.expects(:getnames).with('2001:db8::30').returns([host.name])
found = parser.suggested_primary_interface(host)
assert_equal 'em2', found.first
assert_equal '2001:db8::30', found.last[:ipaddress6]
assert_equal '00:00:00:00:00:30', found.last[:macaddress]
end

test "#suggested_primary_interface primary interface detection falls back to physical with ip and mac" do
parser.stubs(:interfaces).returns({
'br0' => {'ipaddress' => '30.0.0.30', 'macaddress' => '00:00:00:00:00:30'},
Expand All @@ -313,6 +329,22 @@ class FactParserTest < ActiveSupport::TestCase
assert_equal '00:00:00:00:00:10', found.last[:macaddress]
end

test "#suggested_primary_interface primary interface detection falls back to physical with ip and mac" do
parser.stubs(:interfaces).returns({
'br0' => {'ipaddress6' => '2001:db8::10', 'macaddress' => '00:00:00:00:00:30'},
'em0' => {'ipaddress6' => '', 'macaddress' => ''},
'em1' => {'ipaddress6' => '2001:db8::20', 'macaddress' => '00:00:00:00:00:10'},
'em2' => {'ipaddress6' => '2001:db8::30', 'macaddress' => '00:00:00:00:00:12'},
'bond0' => {'ipaddress6' => '2001:db8::40', 'macaddress' => '00:00:00:00:00:15'},
}.with_indifferent_access)

Resolv::DNS.any_instance.stubs(:getnames).returns([])
found = parser.suggested_primary_interface(host)
assert_equal 'em1', found.first
assert_equal '2001:db8::20', found.last[:ipaddress6]
assert_equal '00:00:00:00:00:10', found.last[:macaddress]
end

test "#suggested_primary_interface primary interface detection falls back to first with ip and mac if no physical" do
parser.stubs(:interfaces).returns({
'bond1' => {'ipaddress' => '', 'macaddress' => ''},
Expand Down

0 comments on commit ab23d6c

Please sign in to comment.