diff --git a/lib/linux_admin/network_interface.rb b/lib/linux_admin/network_interface.rb index 4a5ad78..ece1d5a 100644 --- a/lib/linux_admin/network_interface.rb +++ b/lib/linux_admin/network_interface.rb @@ -20,6 +20,21 @@ def self.dist_class(clear_cache = false) end end + def self.list + ip_link.pluck("ifname").map { |iface| new(iface) } + rescue AwesomeSpawn::CommandResultError => e + raise NetworkInterfaceError.new(e.message, e.result) + end + + private_class_method def self.ip_link + require "json" + + result = Common.run!(Common.cmd("ip"), :params => ["--json", "link"]) + JSON.parse(result.output) + rescue AwesomeSpawn::CommandResultError, JSON::ParserError => e + raise NetworkInterfaceError.new(e.message, e.result) + end + # Creates an instance of the correct NetworkInterface subclass for the local distro def self.new(*args) self == LinuxAdmin::NetworkInterface ? dist_class.new(*args) : super diff --git a/spec/network_interface_spec.rb b/spec/network_interface_spec.rb index e7aaeba..6a50af3 100644 --- a/spec/network_interface_spec.rb +++ b/spec/network_interface_spec.rb @@ -55,12 +55,19 @@ end context "on all systems" do - let(:ip_show_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[addr show eth0]}] } - let(:ip_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => ['-4', 'route']}] } - let(:ip6_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => ['-6', 'route']}] } - let(:ifup_args) { [LinuxAdmin::Common.cmd("ifup"), {:params => ["eth0"]}] } - let(:ifdown_args) { [LinuxAdmin::Common.cmd("ifdown"), {:params => ["eth0"]}] } - let(:ip_addr_out) do + let(:ip_link_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[--json link]}] } + let(:ip_show_eth0_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[addr show eth0]}] } + let(:ip_show_lo_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[addr show lo]}] } + let(:ip_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => ['-4', 'route']}] } + let(:ip6_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => ['-6', 'route']}] } + let(:ifup_args) { [LinuxAdmin::Common.cmd("ifup"), {:params => ["eth0"]}] } + let(:ifdown_args) { [LinuxAdmin::Common.cmd("ifdown"), {:params => ["eth0"]}] } + let(:ip_link_out) do + <<~IP_OUT + [{"ifindex":1,"ifname":"lo","flags":["LOOPBACK","UP","LOWER_UP"],"mtu":65536,"qdisc":"noqueue","operstate":"UNKNOWN","linkmode":"DEFAULT","group":"default","txqlen":1000,"link_type":"loopback","address":"00:00:00:00:00:00","broadcast":"00:00:00:00:00:00"},{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"fq_codel","operstate":"UP","linkmode":"DEFAULT","group":"default","txqlen":1000,"link_type":"ether","address":"52:54:00:e8:67:81","broadcast":"ff:ff:ff:ff:ff:ff","altnames":["enp0s2","ens2"]}] + IP_OUT + end + let(:ip_addr_eth0_out) do <<~IP_OUT 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:0c:29:ed:0e:8b brd ff:ff:ff:ff:ff:ff @@ -72,6 +79,16 @@ valid_lft forever preferred_lft forever IP_OUT end + let(:ip_addr_lo_out) do + <<~IP_OUT + 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever + IP_OUT + end let(:ip6_addr_out) do <<~IP_OUT 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 @@ -102,11 +119,23 @@ IP_OUT end + subject(:subj_list) do + allow(LinuxAdmin::Distros).to receive(:local).and_return(LinuxAdmin::Distros.generic) + described_class.dist_class(true) + + allow(AwesomeSpawn).to receive(:run!).with(*ip_link_args).and_return(result(ip_link_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_lo_args).and_return(result(ip_addr_lo_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip_addr_eth0_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_return(result(ip_route_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_return(result(ip6_route_out, 0)) + described_class.list + end + subject(:subj) do allow(LinuxAdmin::Distros).to receive(:local).and_return(LinuxAdmin::Distros.generic) described_class.dist_class(true) - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_return(result(ip_addr_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip_addr_eth0_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_return(result(ip_route_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_return(result(ip6_route_out, 0)) described_class.new(device_name) @@ -116,7 +145,7 @@ allow(LinuxAdmin::Distros).to receive(:local).and_return(LinuxAdmin::Distros.generic) described_class.dist_class(true) - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_return(result(ip6_addr_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip6_addr_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_return(result(ip_route_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_return(result(ip6_route_out, 0)) described_class.new(device_name) @@ -126,7 +155,7 @@ allow(LinuxAdmin::Distros).to receive(:local).and_return(LinuxAdmin::Distros.generic) described_class.dist_class(true) - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_return(result(ip_none_addr_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip_none_addr_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_return(result(ip_route_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_return(result(ip6_route_out, 0)) described_class.new(device_name) @@ -136,18 +165,26 @@ def result(output, exit_status) AwesomeSpawn::CommandResult.new("", output, "", nil, exit_status) end + describe ".list" do + it "returns a list of NetworkInterface objects" do + interfaces = subj_list + expect(interfaces.count).to eq(2) + expect(interfaces.map(&:interface)).to match_array(["eth0", "lo"]) + end + end + describe "#reload" do it "returns false when ip addr show fails" do subj awesome_error = AwesomeSpawn::CommandResultError.new("", nil) - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_raise(awesome_error) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_raise(awesome_error) expect(subj.reload).to eq(false) end it "raises when ip route fails" do subj awesome_error = AwesomeSpawn::CommandResultError.new("", nil) - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_return(result(ip_addr_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip_addr_eth0_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_raise(awesome_error) allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_raise(awesome_error) expect { subj.reload }.to raise_error(LinuxAdmin::NetworkInterfaceError) @@ -155,7 +192,7 @@ def result(output, exit_status) it "doesn't blow up when given only ipv6 addresses" do subj6 - allow(AwesomeSpawn).to receive(:run!).with(*ip_show_args).and_return(result(ip6_addr_out, 0)) + allow(AwesomeSpawn).to receive(:run!).with(*ip_show_eth0_args).and_return(result(ip6_addr_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip_route_args).and_return(result(ip_route_out, 0)) allow(AwesomeSpawn).to receive(:run!).with(*ip6_route_args).and_return(result(ip6_route_out, 0)) expect { subj.reload }.to_not raise_error