Skip to content

Commit

Permalink
Use --json for ip command output
Browse files Browse the repository at this point in the history
  • Loading branch information
agrare committed Aug 6, 2024
1 parent 1e256b9 commit 5c268b3
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 64 deletions.
58 changes: 25 additions & 33 deletions lib/linux_admin/network_interface.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
require 'ipaddr'

module LinuxAdmin
class NetworkInterface
require "ipaddr"
require "json"

# Cached class instance variable for what distro we are running on
@dist_class = nil

Expand Down Expand Up @@ -60,14 +61,16 @@ def reload
return false
end

parse_ip4(ip_output)
parse_ip6(ip_output, :global)
parse_ip6(ip_output, :link)
addr_info = ip_output["addr_info"]

parse_ip4(addr_info)
parse_ip6(addr_info, "global")
parse_ip6(addr_info, "link")

@network_conf[:mac] = parse_ip_output(ip_output, %r{link/ether}, 1)
@network_conf[:mac] = ip_output["address"]

[4, 6].each do |version|
@network_conf["gateway#{version}".to_sym] = parse_ip_output(ip_route(version), /^default/, 2)
@network_conf["gateway#{version}".to_sym] = ip_route(version, "default")&.dig("gateway")
end
true
end
Expand Down Expand Up @@ -168,23 +171,13 @@ def stop

private

# Parses the output of `ip addr show`
#
# @param output [String] The command output
# @param regex [Regexp] Regular expression to match the desired output line
# @param col [Fixnum] The whitespace delimited column to be returned
# @return [String] The parsed data
def parse_ip_output(output, regex, col)
the_line = output.split("\n").detect { |l| l =~ regex }
the_line.nil? ? nil : the_line.strip.split(' ')[col]
end

# Runs the command `ip addr show <interface>`
#
# @return [String] The command output
# @raise [NetworkInterfaceError] if the command fails
def ip_show
Common.run!(Common.cmd("ip"), :params => ["addr", "show", @interface]).output
output = Common.run!(Common.cmd("ip"), :params => ["--json", "addr", "show", @interface]).output
JSON.parse(output).first
rescue AwesomeSpawn::CommandResultError => e
raise NetworkInterfaceError.new(e.message, e.result)
end
Expand All @@ -194,35 +187,34 @@ def ip_show
# @param version [Fixnum] Version of IP protocol (4 or 6)
# @return [String] The command output
# @raise [NetworkInterfaceError] if the command fails
def ip_route(version)
Common.run!(Common.cmd("ip"), :params => ["-#{version}", 'route']).output
def ip_route(version, route = "default")
output = Common.run!(Common.cmd("ip"), :params => ["--json", "-#{version}", "route", "show", route]).output
JSON.parse(output).first
rescue AwesomeSpawn::CommandResultError => e
raise NetworkInterfaceError.new(e.message, e.result)
end

# Parses the IPv4 information from the output of `ip addr show <device>`
#
# @param ip_output [String] The command output
def parse_ip4(ip_output)
cidr_ip = parse_ip_output(ip_output, /inet /, 1)
return unless cidr_ip
def parse_ip4(addr_info)
inet = addr_info.detect { |addr| addr["family"] == "inet" }
return if inet.nil?

parts = cidr_ip.split('/')
@network_conf[:address] = parts[0]
@network_conf[:prefix] = parts[1].to_i
@network_conf[:address] = inet["local"]
@network_conf[:prefix] = inet["prefixlen"]
end

# Parses the IPv6 information from the output of `ip addr show <device>`
#
# @param ip_output [String] The command output
# @param scope [Symbol] The IPv6 scope (either `:global` or `:local`)
def parse_ip6(ip_output, scope)
cidr_ip = parse_ip_output(ip_output, /inet6 .* scope #{scope}/, 1)
return unless cidr_ip
def parse_ip6(addr_info, scope)
inet6 = addr_info.detect { |addr| addr["family"] == "inet6" && addr["scope"] == scope }
return if inet6.nil?

parts = cidr_ip.split('/')
@network_conf["address6_#{scope}".to_sym] = parts[0]
@network_conf["prefix6_#{scope}".to_sym] = parts[1].to_i
@network_conf["address6_#{scope}".to_sym] = inet6["local"]
@network_conf["prefix6_#{scope}".to_sym] = inet6["prefixlen"]
end
end
end
Expand Down
41 changes: 10 additions & 31 deletions spec/network_interface_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@

context "on all systems" 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(:ip_show_eth0_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[--json addr show eth0]}] }
let(:ip_show_lo_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[--json addr show lo]}] }
let(:ip_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[--json -4 route show default]}] }
let(:ip6_route_args) { [LinuxAdmin::Common.cmd("ip"), {:params => %w[--json -6 route show default]}] }
let(:ifup_args) { [LinuxAdmin::Common.cmd("ifup"), {:params => ["eth0"]}] }
let(:ifdown_args) { [LinuxAdmin::Common.cmd("ifdown"), {:params => ["eth0"]}] }
let(:ip_link_out) do
Expand All @@ -69,53 +69,32 @@
end
let(:ip_addr_eth0_out) do
<<~IP_OUT
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
inet6 fd12:3456:789a:1::1/96 scope global
valid_lft forever preferred_lft forever
[{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"fq_codel","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"00:0c:29:ed:0e:8b","broadcast":"ff:ff:ff:ff:ff:ff","altnames":["enp0s2","ens2"],"addr_info":[{"family":"inet","local":"192.168.1.9","prefixlen":24,"broadcast":"192.168.255","scope":"global","noprefixroute":true,"label":"eth0","valid_life_time":4294967295,"preferred_life_time":4294967295},{"family":"inet6","local":"fe80::20c:29ff:feed:e8b","prefixlen":64,"scope":"link","noprefixroute":true,"valid_life_time":"forever","preferred_life_time":"forever"},{"family":"inet6","local":"fd12:3456:789a:1::1","prefixlen":96,"scope":"global","noprefixroute":true,"valid_life_time":"forever","preferred_life_time":"forever"}]}]
IP_OUT
end
let(:ip_addr_lo_out) do
<<~IP_OUT
1: lo: <LOOPBACK,UP,LOWER_UP> 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
[{"ifindex":1,"ifname":"lo","flags":["LOOPBACK","UP","LOWER_UP"],"mtu":65536,"qdisc":"noqueue","operstate":"UNKNOWN","group":"default","txqlen":1000,"link_type":"loopback","address":"00:00:00:00:00:00","broadcast":"00:00:00:00:00:00","addr_info":[{"family":"inet","local":"127.0.0.1","prefixlen":8,"scope":"host","label":"lo","valid_life_time":4294967295,"preferred_life_time":4294967295},{"family":"inet6","local":"::1","prefixlen":128,"scope":"host","valid_life_time":"forever","preferred_life_time":"forever"}]}]
IP_OUT
end
let(:ip6_addr_out) do
<<~IP_OUT
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:ed:0e:8b brd ff:ff:ff:ff:ff:ff
inet6 fe80::20c:29ff:feed:e8b/64 scope link
valid_lft forever preferred_lft forever
inet6 fd12:3456:789a:1::1/96 scope global
valid_lft forever preferred_lft forever
[{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"fq_codel","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"00:0c:29:ed:0e:8b","broadcast":"ff:ff:ff:ff:ff:ff","altnames":["enp0s2","ens2"],"addr_info":[{"family":"inet6","local":"fe80::20c:29ff:feed:e8b","prefixlen":64,"scope":"link","noprefixroute":true,"valid_life_time":"forever","preferred_life_time":"forever"},{"family":"inet6","local":"fd12:3456:789a:1::1","prefixlen":96,"scope":"global","noprefixroute":true,"valid_life_time":"forever","preferred_life_time":"forever"}]}]
IP_OUT
end
let(:ip_route_out) do
<<~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
[{"dst":"default","gateway":"192.168.1.1","dev":"eth0","protocol":"static","metric":100,"flags":[]}]
IP_OUT
end
let(:ip6_route_out) do
<<~IP_OUT
default via d:e:a:d:b:e:e:f dev eth0 proto static metric 100
fc00:dead:beef:a::/64 dev virbr1 proto kernel metric 256 linkdown pref medium
fe80::/64 dev eth0 proto kernel scope link metric 100
[{"dst":"default","gateway":"d:e:a:d:b:e:e:f","dev":"eth0","protocol":"static","metric":100,"flags":[]}]
IP_OUT
end
let(:ip_none_addr_out) do
<<~IP_OUT
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr1 state DOWN group default qlen 1000
link/ether 52:54:00:ce:b4:f4 brd ff:ff:ff:ff:ff:ff
[{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"fq_codel","operstate":"UP","group":"default","txqlen":1000,"link_type":"ether","address":"00:0c:29:ed:0e:8b","broadcast":"ff:ff:ff:ff:ff:ff","altnames":["enp0s2","ens2"],"addr_info":[]}]
IP_OUT
end

Expand Down

0 comments on commit 5c268b3

Please sign in to comment.