diff --git a/features/dsl/netns.feature b/features/dsl/netns.feature index 649090f..deb64db 100644 --- a/features/dsl/netns.feature +++ b/features/dsl/netns.feature @@ -77,3 +77,21 @@ Feature: The netns DSL directive. And the netns "host2" have the following route: | net | gateway | | 0.0.0.0 | 192.168.1.254 | + + @sudo + Scenario: vlan option + Given a file named "phut.conf" with: + """ + netns('host1') { + ip '192.168.0.1' + vlan 10 + } + netns('host2') { + ip '192.168.1.2' + vlan 20 + } + link 'host1', 'host2' + """ + When I do phut run "phut.conf" + Then the VLAN of the netns "host1" should be "10" + And the VLAN of the netns "host2" should be "20" diff --git a/features/step_definitions/netns_steps.rb b/features/step_definitions/netns_steps.rb index 0201753..2406ce7 100644 --- a/features/step_definitions/netns_steps.rb +++ b/features/step_definitions/netns_steps.rb @@ -20,3 +20,7 @@ expect(netns.route.net).to eq table.hashes.first['net'] expect(netns.route.gateway).to eq table.hashes.first['gateway'] end + +Then(/^the VLAN of the netns "([^"]*)" should be "([^"]*)"$/) do |name, vlan| + expect(Phut::Netns.find_by!(name: name).vlan).to eq vlan +end diff --git a/lib/phut/netns.rb b/lib/phut/netns.rb index 23d6e7f..6794934 100644 --- a/lib/phut/netns.rb +++ b/lib/phut/netns.rb @@ -2,6 +2,7 @@ require 'phut/finder' require 'phut/route' require 'phut/shell_runner' +require 'phut/veth' module Phut # `ip netns ...` command runner @@ -14,7 +15,7 @@ class Netns def self.all sh('ip netns list').split("\n").map do |each| name = each.split.first - addr_list = sudo("ip -netns #{name} -4 -o addr list").split("\n") + addr_list = sudo("ip netns exec #{name} ip -4 -o addr list").split("\n") if addr_list.size > 1 %r{inet ([^/]+)/(\d+)} =~ addr_list[1] new(name: name, @@ -43,45 +44,70 @@ def self.destroy_all attr_reader :ip_address def initialize(name:, - ip_address: nil, netmask: '255.255.255.255', route: {}) + ip_address: nil, netmask: '255.255.255.255', + route: {}, vlan: nil) @name = name @ip_address = ip_address @netmask = netmask @route = Route.new(net: route[:net], gateway: route[:gateway]) + @vlan = vlan end def run - sudo "ip netns add #{@name}" - sudo "ip netns exec #{@name} ifconfig lo 127.0.0.1" + sudo "ip netns add #{name}" + sudo "ip netns exec #{name} ifconfig lo 127.0.0.1" end - def netmask - if %r{inet [^/]+/(\d+) } =~ - sudo("ip -netns #{@name} -o -4 address show dev #{device}") - IPAddr.new('255.255.255.255').mask(Regexp.last_match(1).to_i).to_s - end + def stop + sudo "ip netns delete #{name}" end def device - if /^\d+: ([^@]+)@/ =~ sudo("ip -netns #{@name} -o link show type veth") + if /^\d+: (#{Phut::Veth::PREFIX}[^:\.]*?)[:@]/ =~ + sudo("ip netns exec #{name} ip -o link show") Regexp.last_match(1) end end + # rubocop:disable MethodLength def device=(device_name) return unless device_name - sudo "ip link set dev #{device_name} netns #{@name}" - sudo "ip netns exec #{@name} "\ - "ifconfig #{device_name} #{@ip_address} netmask #{@netmask}" - @route.add @name + sudo "ip link set dev #{device_name} netns #{name}" + + vlan_suffix = @vlan ? ".#{@vlan}" : '' + if @vlan + sudo "ip netns exec #{name} ip link set #{device_name} up" + sudo "ip netns exec #{name} "\ + "ip link add link #{device_name} name "\ + "#{device_name}#{vlan_suffix} type vlan id #{@vlan}" + end + sudo "ip netns exec #{name} ip link set #{device_name}#{vlan_suffix} up" + sudo "ip netns exec #{name} "\ + "ip addr replace #{@ip_address}/#{@netmask} "\ + "dev #{device_name}#{vlan_suffix}" + + sudo "ip netns exec #{name} ip link set #{device_name}#{vlan_suffix} up" + + @route.add name end + # rubocop:enable MethodLength - def stop - sudo "ip netns delete #{@name}" + def netmask + if %r{inet [^/]+/(\d+) } =~ + sudo("ip netns exec #{name} ip -o -4 address show dev #{device}") + IPAddr.new('255.255.255.255').mask(Regexp.last_match(1).to_i).to_s + end end def route - Route.read @name + Route.read name + end + + def vlan + if /^\d+: #{device}\.(\d+)@/ =~ + sudo("ip netns exec #{name} ip -o link show") + Regexp.last_match(1) + end end end end diff --git a/lib/phut/parser.rb b/lib/phut/parser.rb index cec17b2..2a666a1 100644 --- a/lib/phut/parser.rb +++ b/lib/phut/parser.rb @@ -30,7 +30,8 @@ def update_netns_interfaces netns = Netns.create(name: each[:name], ip_address: each[:ip], netmask: each[:netmask], - route: { net: each[:net], gateway: each[:gateway] }) + route: { net: each[:net], gateway: each[:gateway] }, + vlan: each[:vlan]) netns.device = find_network_device(each.name) end end diff --git a/lib/phut/syntax/netns_directive.rb b/lib/phut/syntax/netns_directive.rb index 3df4df0..4bbb514 100644 --- a/lib/phut/syntax/netns_directive.rb +++ b/lib/phut/syntax/netns_directive.rb @@ -22,6 +22,10 @@ def route(options) @attributes[:net] = options.fetch(:net) @attributes[:gateway] = options.fetch(:gateway) end + + def vlan(value) + @attributes[:vlan] = value + end end end end diff --git a/lib/phut/veth.rb b/lib/phut/veth.rb index 4dd1de6..068e6fe 100644 --- a/lib/phut/veth.rb +++ b/lib/phut/veth.rb @@ -11,8 +11,8 @@ class Veth def self.all Netns.all.map(&:device).compact + - sh('ip -o link show type veth').split("\n").map do |each| - /^\d+: (#{PREFIX}\d+_[^@]+)@/ =~ each ? Regexp.last_match(1) : nil + sh('ip -o link show').split("\n").map do |each| + /^\d+: (#{PREFIX}\d+_[^:]*?)[@:]/ =~ each ? Regexp.last_match(1) : nil end.compact end