From 2ff8d30aaf109ec74f44c84ed8d6181167748156 Mon Sep 17 00:00:00 2001 From: Nick Carboni Date: Fri, 2 Oct 2015 14:31:29 -0400 Subject: [PATCH 1/3] Added MAC address get method to IpAddress class --- lib/linux_admin/ip_address.rb | 9 ++++++ spec/ip_address_spec.rb | 55 ++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/linux_admin/ip_address.rb b/lib/linux_admin/ip_address.rb index 5653bb3..d9eaab4 100644 --- a/lib/linux_admin/ip_address.rb +++ b/lib/linux_admin/ip_address.rb @@ -10,6 +10,15 @@ def address6 address_list.detect { |ip| IPAddr.new(ip).ipv6? } end + def mac_address(interface) + result = run(cmd("ip"), :params => ["addr", "show", interface]) + return nil if result.failure? + + lines = result.output.split("\n") + link_line = lines.detect { |l| l =~ %r{link/ether} } + link_line.nil? ? nil : link_line.strip.split(' ')[1] + end + private def address_list diff --git a/spec/ip_address_spec.rb b/spec/ip_address_spec.rb index 7069f1a..1dea419 100644 --- a/spec/ip_address_spec.rb +++ b/spec/ip_address_spec.rb @@ -1,11 +1,35 @@ describe LinuxAdmin::IpAddress do let(:ip) { described_class.new } - SPAWN_ARGS = [ + ADDR_SPAWN_ARGS = [ described_class.new.cmd("hostname"), :params => ["-I"] ] + MAC_SPAWN_ARGS = [ + described_class.new.cmd("ip"), + :params => %w(addr show eth0) + ] + + IP_ADDR_SHOW_ETH0 = <<-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 + inet 192.168.1.9/24 brd 192.168.1.255 scope global dynamic eth0 + valid_lft 1297sec preferred_lft 1297sec + inet6 fe80::20c:29ff:feed:e8b/64 scope link + valid_lft forever preferred_lft forever + +IP_OUT + + IP_ADDR_ERR = <<-IP_OUT +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + inet 192.168.1.9/24 brd 192.168.1.255 scope global dynamic eth0 + valid_lft 1297sec preferred_lft 1297sec + inet6 fe80::20c:29ff:feed:e8b/64 scope link + valid_lft forever preferred_lft forever + +IP_OUT + def result(output, exit_status) AwesomeSpawn::CommandResult.new("", output, "", exit_status) end @@ -13,19 +37,19 @@ def result(output, exit_status) describe "#address" do it "returns an address" do ip_addr = "192.168.1.2" - expect(AwesomeSpawn).to receive(:run).with(*SPAWN_ARGS).and_return(result(ip_addr, 0)) + expect(AwesomeSpawn).to receive(:run).with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 0)) expect(ip.address).to eq(ip_addr) end it "returns nil when no address is found" do ip_addr = "" - expect(AwesomeSpawn).to receive(:run).at_least(5).times.with(*SPAWN_ARGS).and_return(result(ip_addr, 1)) + expect(AwesomeSpawn).to receive(:run).at_least(5).times.with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 1)) expect(ip.address).to be_nil end it "returns only IPv4 addresses" do ip_addr = "fd12:3456:789a:1::1 192.168.1.2" - expect(AwesomeSpawn).to receive(:run).with(*SPAWN_ARGS).and_return(result(ip_addr, 0)) + expect(AwesomeSpawn).to receive(:run).with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 0)) expect(ip.address).to eq("192.168.1.2") end end @@ -33,20 +57,37 @@ def result(output, exit_status) describe "#address6" do it "returns an address" do ip_addr = "fd12:3456:789a:1::1" - expect(AwesomeSpawn).to receive(:run).with(*SPAWN_ARGS).and_return(result(ip_addr, 0)) + expect(AwesomeSpawn).to receive(:run).with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 0)) expect(ip.address6).to eq(ip_addr) end it "returns nil when no address is found" do ip_addr = "" - expect(AwesomeSpawn).to receive(:run).at_least(5).times.with(*SPAWN_ARGS).and_return(result(ip_addr, 1)) + expect(AwesomeSpawn).to receive(:run).at_least(5).times.with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 1)) expect(ip.address6).to be_nil end it "returns only IPv6 addresses" do ip_addr = "192.168.1.2 fd12:3456:789a:1::1" - expect(AwesomeSpawn).to receive(:run).with(*SPAWN_ARGS).and_return(result(ip_addr, 0)) + expect(AwesomeSpawn).to receive(:run).with(*ADDR_SPAWN_ARGS).and_return(result(ip_addr, 0)) expect(ip.address6).to eq("fd12:3456:789a:1::1") end end + + describe "#mac_address" do + it "returns the correct MAC address" do + expect(AwesomeSpawn).to receive(:run).with(*MAC_SPAWN_ARGS).and_return(result(IP_ADDR_SHOW_ETH0, 0)) + expect(ip.mac_address("eth0")).to eq("00:0c:29:ed:0e:8b") + end + + it "returns nil when the command fails" do + expect(AwesomeSpawn).to receive(:run).with(*MAC_SPAWN_ARGS).and_return(result("", 1)) + expect(ip.mac_address("eth0")).to be_nil + end + + it "returns nil if the link/ether line is not present" do + expect(AwesomeSpawn).to receive(:run).with(*MAC_SPAWN_ARGS).and_return(result(IP_ADDR_ERR, 0)) + expect(ip.mac_address("eth0")).to be_nil + end + end end From cbd8f2abeff0a48a209bd3ecf6658dc000f8efba Mon Sep 17 00:00:00 2001 From: Nick Carboni Date: Fri, 2 Oct 2015 16:18:46 -0400 Subject: [PATCH 2/3] Added netmask method to IpAddress class --- lib/linux_admin/ip_address.rb | 16 +++++++++++--- spec/ip_address_spec.rb | 41 +++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/lib/linux_admin/ip_address.rb b/lib/linux_admin/ip_address.rb index d9eaab4..d94df43 100644 --- a/lib/linux_admin/ip_address.rb +++ b/lib/linux_admin/ip_address.rb @@ -14,13 +14,23 @@ def mac_address(interface) result = run(cmd("ip"), :params => ["addr", "show", interface]) return nil if result.failure? - lines = result.output.split("\n") - link_line = lines.detect { |l| l =~ %r{link/ether} } - link_line.nil? ? nil : link_line.strip.split(' ')[1] + parse_output(result.output, %r{link/ether}, 1) + end + + def netmask(interface) + result = run(cmd("ifconfig"), :params => [interface]) + return nil if result.failure? + + parse_output(result.output, /netmask/, 3) end private + def parse_output(output, regex, col) + the_line = output.split("\n").detect { |l| l =~ regex } + the_line.nil? ? nil : the_line.strip.split(' ')[col] + end + def address_list result = nil # Added retry to account for slow DHCP not assigning an IP quickly at boot; specifically: diff --git a/spec/ip_address_spec.rb b/spec/ip_address_spec.rb index 1dea419..358bf44 100644 --- a/spec/ip_address_spec.rb +++ b/spec/ip_address_spec.rb @@ -11,6 +11,11 @@ :params => %w(addr show eth0) ] + MASK_SPAWN_ARGS = [ + described_class.new.cmd("ifconfig"), + :params => %w(eth0) + ] + IP_ADDR_SHOW_ETH0 = <<-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 @@ -21,12 +26,15 @@ IP_OUT - IP_ADDR_ERR = <<-IP_OUT -2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 - inet 192.168.1.9/24 brd 192.168.1.255 scope global dynamic eth0 - valid_lft 1297sec preferred_lft 1297sec - inet6 fe80::20c:29ff:feed:e8b/64 scope link - valid_lft forever preferred_lft forever + IFCFG = <<-IP_OUT +eth0: flags=4163 mtu 1500 + inet 192.168.1.9 netmask 255.255.255.0 broadcast 192.168.1.255 + inet6 fe80::20c:29ff:feed:e8b prefixlen 64 scopeid 0x20 + ether 00:0c:29:ed:0e:8b txqueuelen 1000 (Ethernet) + RX packets 10171 bytes 8163955 (7.7 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 2871 bytes 321915 (314.3 KiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 IP_OUT @@ -86,8 +94,27 @@ def result(output, exit_status) end it "returns nil if the link/ether line is not present" do - expect(AwesomeSpawn).to receive(:run).with(*MAC_SPAWN_ARGS).and_return(result(IP_ADDR_ERR, 0)) + bad_output = IP_ADDR_SHOW_ETH0.gsub(%r{link/ether}, "") + expect(AwesomeSpawn).to receive(:run).with(*MAC_SPAWN_ARGS).and_return(result(bad_output, 0)) expect(ip.mac_address("eth0")).to be_nil end end + + describe "#netmask" do + it "returns the correct netmask" do + expect(AwesomeSpawn).to receive(:run).with(*MASK_SPAWN_ARGS).and_return(result(IFCFG, 0)) + expect(ip.netmask("eth0")).to eq("255.255.255.0") + end + + it "returns nil when the command fails" do + expect(AwesomeSpawn).to receive(:run).with(*MASK_SPAWN_ARGS).and_return(result("", 1)) + expect(ip.netmask("eth0")).to be_nil + end + + it "returns nil if the netmask line is not present" do + bad_output = IFCFG.gsub(/netmask/, "") + expect(AwesomeSpawn).to receive(:run).with(*MASK_SPAWN_ARGS).and_return(result(bad_output, 0)) + expect(ip.netmask("eth0")).to be_nil + end + end end From b8d6fc71f417bd2112a84a78781b17877f80faef Mon Sep 17 00:00:00 2001 From: Nick Carboni Date: Fri, 2 Oct 2015 16:35:49 -0400 Subject: [PATCH 3/3] Added gateway method to IpAddress class --- lib/linux_admin/ip_address.rb | 7 +++++++ spec/ip_address_spec.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/linux_admin/ip_address.rb b/lib/linux_admin/ip_address.rb index d94df43..9cfefb1 100644 --- a/lib/linux_admin/ip_address.rb +++ b/lib/linux_admin/ip_address.rb @@ -24,6 +24,13 @@ def netmask(interface) parse_output(result.output, /netmask/, 3) end + def gateway + result = run(cmd("ip"), :params => ["route"]) + return nil if result.failure? + + parse_output(result.output, /^default/, 2) + end + private def parse_output(output, regex, col) diff --git a/spec/ip_address_spec.rb b/spec/ip_address_spec.rb index 358bf44..698420b 100644 --- a/spec/ip_address_spec.rb +++ b/spec/ip_address_spec.rb @@ -16,6 +16,11 @@ :params => %w(eth0) ] + GW_SPAWN_ARGS = [ + described_class.new.cmd("ip"), + :params => %w(route) + ] + IP_ADDR_SHOW_ETH0 = <<-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 @@ -36,6 +41,11 @@ TX packets 2871 bytes 321915 (314.3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 +IP_OUT + + IP_ROUTE = <<-IP_OUT +default via 192.168.1.1 dev eth0 proto static metric 100 +192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.9 metric 100 IP_OUT def result(output, exit_status) @@ -117,4 +127,22 @@ def result(output, exit_status) expect(ip.netmask("eth0")).to be_nil end end + + describe "#gateway" do + it "returns the correct gateway address" do + expect(AwesomeSpawn).to receive(:run).with(*GW_SPAWN_ARGS).and_return(result(IP_ROUTE, 0)) + expect(ip.gateway).to eq("192.168.1.1") + end + + it "returns nil when the command fails" do + expect(AwesomeSpawn).to receive(:run).with(*GW_SPAWN_ARGS).and_return(result("", 1)) + expect(ip.gateway).to be_nil + end + + it "returns nil if the default line is not present" do + bad_output = IP_ROUTE.gsub(/default/, "") + expect(AwesomeSpawn).to receive(:run).with(*GW_SPAWN_ARGS).and_return(result(bad_output, 0)) + expect(ip.gateway).to be_nil + end + end end