Skip to content

Commit dbf3b23

Browse files
author
Bogdan Irimie
authored
Merge pull request #2217 from sebastian-miclea/maint_extract_primary_interface
(maint) Extracted primary interface to util class
2 parents 853f4dd + 5a1ca2d commit dbf3b23

File tree

8 files changed

+167
-12
lines changed

8 files changed

+167
-12
lines changed

lib/facter/resolvers/networking_linux_resolver.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def post_resolve(fact_name)
1919
def retrieve_network_info(fact_name)
2020
retrieve_interface_info
2121
retrieve_interfaces_mac_and_mtu
22-
retrieve_default_interface
22+
retrieve_primary_interface
2323
Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
2424
@fact_list[fact_name]
2525
end
@@ -141,13 +141,13 @@ def fill_io_v6_info!(ip_tokens, network_info)
141141
network_info[interface_name][:bindings6] << binding
142142
end
143143

144-
def retrieve_default_interface
145-
output = Facter::Core::Execution.execute('ip route get 1', logger: log)
144+
def retrieve_primary_interface
145+
primary_helper = Facter::Util::Resolvers::Networking::PrimaryInterface
146+
primary_interface = primary_helper.read_from_proc_route
147+
primary_interface ||= primary_helper.read_from_ip_route
148+
primary_interface ||= primary_helper.find_in_interfaces(@fact_list[:interfaces])
146149

147-
ip_route_tokens = output.each_line.first.strip.split(' ')
148-
default_interface = ip_route_tokens[4]
149-
150-
@fact_list[:primary_interface] = default_interface
150+
@fact_list[:primary_interface] = primary_interface
151151
end
152152

153153
def build_network_info_structure!(network_info, interface_name, binding)

lib/facter/resolvers/solaris/networking.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def read_facts(fact_name)
2626
end
2727

2828
@fact_list = { interfaces: @interfaces } unless @interfaces.empty?
29-
@fact_list[:primary_interface] = Facter::Util::Resolvers::Networking::PrimaryInterface.get(@log)
29+
@fact_list[:primary_interface] = Facter::Util::Resolvers::Networking::PrimaryInterface.read_from_route
3030

3131
Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
3232
rescue StandardError => e

lib/facter/util/resolvers/networking/primary_interface.rb

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,71 @@ module Util
55
module Resolvers
66
module Networking
77
module PrimaryInterface
8+
@log ||= Log.new(self)
9+
810
class << self
9-
def get(log = nil)
10-
result = Facter::Core::Execution.execute('route -n get default', logger: log)
11+
ROUTE_TABLE_MAPPING = {
12+
'Iface' => 0,
13+
'Destination' => 1,
14+
'Gateway' => 2,
15+
'Flags' => 3,
16+
'RefCnt' => 4,
17+
'Use' => 5,
18+
'Metric' => 6,
19+
'Mask' => 7,
20+
'MTU' => 8,
21+
'Window' => 9,
22+
'IRTT' => 10
23+
}.freeze
24+
25+
def read_from_route
26+
return if Facter::Core::Execution.which('route').nil?
27+
28+
result = Facter::Core::Execution.execute('route -n get default', logger: @log)
1129

1230
result.match(/interface: (.+)/)&.captures&.first
1331
end
32+
33+
def read_from_proc_route
34+
content = Facter::Util::FileHelper.safe_read('/proc/net/route', '')
35+
36+
content.each_line.with_index do |line, index|
37+
next if index.zero?
38+
39+
route = line.strip.split("\t")
40+
if route.count > 7 &&
41+
route[ROUTE_TABLE_MAPPING['Destination']] == '00000000' &&
42+
route[ROUTE_TABLE_MAPPING['Mask']] == '00000000'
43+
return route[ROUTE_TABLE_MAPPING['Iface']]
44+
end
45+
end
46+
end
47+
48+
def read_from_ip_route
49+
return if Facter::Core::Execution.which('ip').nil?
50+
51+
output = Facter::Core::Execution.execute('ip route show default', logger: @log)
52+
primary_interface = nil
53+
output.each_line do |line|
54+
primary_interface = line.strip.split(' ')[4] if line.start_with?('default')
55+
end
56+
57+
primary_interface
58+
end
59+
60+
def find_in_interfaces(interfaces)
61+
interfaces.each do |iface_name, interface|
62+
interface[:bindings].each do |binding|
63+
return iface_name unless Facter::Util::Resolvers::Networking.ignored_ip_address(binding[:address])
64+
end
65+
66+
interface[:bindings6].each do |binding|
67+
return iface_name unless Facter::Util::Resolvers::Networking.ignored_ip_address(binding[:address])
68+
end
69+
end
70+
71+
nil
72+
end
1473
end
1574
end
1675
end

spec/facter/resolvers/networking_linux_resolver_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
.with('/run/systemd/netif/leases/1', nil).and_return(nil)
2424
allow(Facter::Util::FileHelper).to receive(:safe_read)
2525
.with('/run/systemd/netif/leases/2', nil).and_return(load_fixture('dhcp_lease').read)
26+
allow(Facter::Util::FileHelper).to receive(:safe_read)
27+
.with('/proc/net/route', '').and_return(load_fixture('proc_net_route').read)
2628

2729
allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(true)
2830
allow(Dir).to receive(:entries).with('/var/lib/dhclient/').and_return(['dhclient.lo.leases', 'dhclient.leases'])
@@ -177,7 +179,7 @@
177179
networking_linux.resolve(:interfaces)
178180

179181
expect(Facter::Core::Execution).to have_received(:execute)
180-
.with('ip route get 1', logger: log_spy).once
182+
.with('ip -o address', logger: log_spy).once
181183
end
182184
end
183185

@@ -188,7 +190,7 @@
188190
networking_linux.resolve(:interfaces)
189191

190192
expect(Facter::Core::Execution).to have_received(:execute)
191-
.with('ip route get 1', logger: log_spy).twice
193+
.with('ip -o address', logger: log_spy).twice
192194
end
193195
end
194196

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
describe Facter::Util::Resolvers::Networking::PrimaryInterface do
4+
subject(:primary_interface) { Facter::Util::Resolvers::Networking::PrimaryInterface }
5+
6+
describe '#read_from_route' do
7+
before do
8+
allow(Facter::Core::Execution).to receive(:execute)
9+
.and_return(load_fixture('route_n_get_default').read)
10+
end
11+
12+
it 'parses output from `route -n get default`' do
13+
allow(Facter::Core::Execution).to receive(:which).with('route').and_return('/some/path')
14+
expect(primary_interface.read_from_route).to eq('net0')
15+
end
16+
17+
it 'returns nil if route command does not exist' do
18+
allow(Facter::Core::Execution).to receive(:which).with('route').and_return(nil)
19+
expect(primary_interface.read_from_route).to be_nil
20+
end
21+
end
22+
23+
describe '#read_from_proc_route' do
24+
before do
25+
allow(Facter::Util::FileHelper).to receive(:safe_read).with('/proc/net/route', '') \
26+
.and_return(load_fixture('proc_net_route'))
27+
end
28+
29+
it 'parses output /proc/net/route file' do
30+
expect(primary_interface.read_from_proc_route).to eq('ens160')
31+
end
32+
end
33+
34+
describe '#read_from_ip_route' do
35+
before do
36+
allow(Facter::Core::Execution).to receive(:execute)
37+
.and_return(load_fixture('ip_route_show_default').read)
38+
end
39+
40+
it 'parses output from `ip route show default`' do
41+
allow(Facter::Core::Execution).to receive(:which).with('ip').and_return('/some/path')
42+
expect(primary_interface.read_from_ip_route).to eq('ens160')
43+
end
44+
45+
it 'returns nil if route command does not exist' do
46+
allow(Facter::Core::Execution).to receive(:which).with('ip').and_return(nil)
47+
expect(primary_interface.read_from_ip_route).to be_nil
48+
end
49+
end
50+
51+
describe '#find_in_interfaces' do
52+
let(:interfaces) do
53+
{ 'lo' =>
54+
{ bindings: [{ address: '127.0.0.1', netmask: '255.0.0.0', network: '127.0.0.0' }],
55+
bindings6: [{
56+
address: '::1',
57+
netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
58+
network: '::1',
59+
scope6: 'host'
60+
}],
61+
scope6: 'host',
62+
mtu: 65_536 },
63+
'ens160' =>
64+
{ bindings: [{ address: '10.16.124.1', netmask: '255.255.240.0', network: '10.16.112.0' }],
65+
dhcp: '10.32.22.9',
66+
bindings6: [{
67+
address: 'fe80::250:56ff:fe9a:41fc',
68+
netmask: 'ffff:ffff:ffff:ffff::',
69+
network: 'fe80::',
70+
scope6: 'link'
71+
}],
72+
scope6: 'link',
73+
mac: '00:50:56:9a:41:fc',
74+
mtu: 1500 } }
75+
end
76+
77+
it 'parses interfaces to find primary interface' do
78+
expect(primary_interface.find_in_interfaces(interfaces)).to eq('ens160')
79+
end
80+
end
81+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
10.16.112.0/20 dev ens160 proto kernel scope link src 10.16.124.1
2+
default via 10.16.112.1 dev ens160

spec/fixtures/proc_net_route

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
2+
ens160 00000000 0170100A 0003 0 0 0 00000000 0 0 0
3+
ens160 0070100A 00000000 0001 0 0 0 00F0FFFF 0 0 0

spec/fixtures/route_n_get_default

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
route to: default
2+
destination: default
3+
mask: default
4+
gateway: 10.16.112.1
5+
interface: net0
6+
flags: <UP,GATEWAY,DONE,STATIC>
7+
recvpipe sendpipe ssthresh rtt,ms rttvar,ms hopcount mtu expire
8+
0 0 0 0 0 0 1500 0

0 commit comments

Comments
 (0)