phut 0.7.7 → 0.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +14 -1
  4. data/.travis.yml +2 -6
  5. data/Gemfile +30 -2
  6. data/Gemfile.lock +156 -0
  7. data/README.md +7 -2
  8. data/Rakefile +4 -3
  9. data/bin/phut +35 -83
  10. data/bin/vhost +28 -26
  11. data/features/{dsl.feature → dsl/error.feature} +8 -6
  12. data/features/{dsl_link.feature → dsl/link.feature} +11 -14
  13. data/features/dsl/netns.feature +115 -0
  14. data/features/dsl/vhost.feature +37 -0
  15. data/features/{dsl_vswitch.feature → dsl/vswitch.feature} +12 -12
  16. data/features/phut_run.feature +15 -0
  17. data/features/shell/vswitch#destroy.feature +10 -0
  18. data/features/shell/vswitch#ports.feature +36 -0
  19. data/features/shell/vswitch.all.feature +26 -0
  20. data/features/shell/vswitch.create.feature +30 -0
  21. data/features/shell/vswitch.destroy.feature +19 -0
  22. data/features/shell/vswitch.destroy_all.feature +18 -0
  23. data/features/step_definitions/link_steps.rb +5 -0
  24. data/features/step_definitions/netns_steps.rb +31 -0
  25. data/features/step_definitions/phut_steps.rb +5 -34
  26. data/features/step_definitions/vhost_steps.rb +5 -0
  27. data/features/step_definitions/vswitch_steps.rb +17 -0
  28. data/features/support/env.rb +3 -3
  29. data/features/support/hooks.rb +23 -15
  30. data/lib/phut.rb +3 -0
  31. data/lib/phut/finder.rb +19 -0
  32. data/lib/phut/link.rb +84 -0
  33. data/lib/phut/netns.rb +111 -22
  34. data/lib/phut/open_vswitch.rb +98 -96
  35. data/lib/phut/parser.rb +39 -8
  36. data/lib/phut/raw_socket.rb +4 -0
  37. data/lib/phut/route.rb +34 -0
  38. data/lib/phut/setting.rb +21 -4
  39. data/lib/phut/shell_runner.rb +13 -2
  40. data/lib/phut/syntax.rb +31 -14
  41. data/lib/phut/syntax/directive.rb +9 -1
  42. data/lib/phut/syntax/netns_directive.rb +13 -2
  43. data/lib/phut/syntax/vhost_directive.rb +2 -0
  44. data/lib/phut/syntax/vswitch_directive.rb +3 -1
  45. data/lib/phut/version.rb +3 -1
  46. data/lib/phut/veth.rb +68 -0
  47. data/lib/phut/vhost.rb +99 -58
  48. data/lib/phut/vhost_daemon.rb +53 -11
  49. data/lib/phut/vsctl.rb +125 -0
  50. data/lib/phut/vswitch.rb +10 -0
  51. data/phut.gemspec +9 -31
  52. data/tasks/cucumber.rake +5 -1
  53. data/tasks/flay.rake +2 -0
  54. data/tasks/flog.rake +3 -1
  55. data/tasks/gem.rake +2 -0
  56. data/tasks/minitest.rake +7 -0
  57. data/tasks/reek.rake +2 -0
  58. data/tasks/rubocop.rake +2 -0
  59. data/tasks/yard.rake +2 -0
  60. data/test/phut/link_test.rb +85 -0
  61. data/test/phut/netns_test.rb +58 -0
  62. data/test/phut/open_vswitch_test.rb +125 -0
  63. data/test/phut/veth_test.rb +48 -0
  64. data/test/phut/vhost_test.rb +56 -0
  65. metadata +41 -287
  66. data/.rspec +0 -3
  67. data/Guardfile +0 -29
  68. data/features/dsl_vhost.feature +0 -37
  69. data/features/phut_kill.feature +0 -27
  70. data/features/shell.feature +0 -39
  71. data/lib/phut/configuration.rb +0 -92
  72. data/lib/phut/null_logger.rb +0 -14
  73. data/lib/phut/virtual_link.rb +0 -109
  74. data/spec/phut/parser_spec.rb +0 -66
  75. data/spec/phut_spec.rb +0 -45
  76. data/spec/spec_helper.rb +0 -14
  77. data/tasks/LICENSE +0 -675
  78. data/tasks/relish.rake +0 -8
  79. data/tasks/rspec.rake +0 -8
@@ -0,0 +1,26 @@
1
+ Feature: Vswitch.all
2
+ Background:
3
+ Given I run `phut -v` interactively
4
+
5
+ @sudo
6
+ Scenario: Vswitch.all #=> []
7
+ When I type "Vswitch.all"
8
+ Then the output should contain "[]"
9
+
10
+ @sudo
11
+ Scenario: Vswitch.all #=> [aVswitch]
12
+ Given I type "Vswitch.create(name: 'firewall', dpid: 0xabc)"
13
+ When I type "Vswitch.all"
14
+ Then the output should contain:
15
+ """
16
+ [#<Vswitch name: "firewall", dpid: 0xabc, openflow_version: 1.0, tcp_port: 6653>]
17
+ """
18
+
19
+ @sudo
20
+ Scenario: Vswitch.all #=> [aVswitch]
21
+ Given I type "Vswitch.create(dpid: 0xabc)"
22
+ When I type "Vswitch.all"
23
+ Then the output should contain:
24
+ """
25
+ [#<Vswitch name: "0xabc", dpid: 0xabc, openflow_version: 1.0, tcp_port: 6653>]
26
+ """
@@ -0,0 +1,30 @@
1
+ Feature: Vswitch.create
2
+ Background:
3
+ Given I run `phut -v` interactively
4
+
5
+ @sudo
6
+ Scenario: Vswitch.create(dpid: ...)
7
+ When I type "Vswitch.create(dpid: 0xabc)"
8
+ And sleep 5
9
+ Then a vswitch named "0xabc" should be running
10
+
11
+ @sudo
12
+ Scenario: Vswitch.create(name: ..., dpid: ...)
13
+ When I type "Vswitch.create(name: 'firewall', dpid: 0xabc)"
14
+ And sleep 5
15
+ Then a vswitch named "firewall" should be running
16
+
17
+ @sudo
18
+ Scenario: Vswitch.create(name: ..., dpid: ..., tcp_port:)
19
+ When I type "vswitch = Vswitch.create(name: 'firewall', dpid: 0xabc, tcp_port: 99999)"
20
+ And sleep 5
21
+ Then a vswitch named "firewall" should be running on port "99999"
22
+
23
+ @sudo
24
+ Scenario: Vswitch.create twice and fail
25
+ Given I type "Vswitch.create(name: 'firewall', dpid: 0xabc)"
26
+ When I type "Vswitch.create(dpid: 0xabc)"
27
+ Then the output should contain:
28
+ """
29
+ a Vswitch #<Vswitch name: "firewall", dpid: 0xabc, openflow_version: 1.0, tcp_port: 6653> already exists
30
+ """
@@ -0,0 +1,19 @@
1
+ Feature: Vswitch.destroy
2
+ Background:
3
+ Given I run `phut -v` interactively
4
+
5
+ @sudo
6
+ Scenario: Vswitch.destroy
7
+ Given I type "Vswitch.create(dpid: 0xabc)"
8
+ When I type "Vswitch.destroy('0xabc')"
9
+ And sleep 5
10
+ Then a vswitch named "0xabc" should not be running
11
+
12
+ @sudo
13
+ Scenario: Vswitch.destroy #=> error
14
+ When I type "Vswitch.destroy('no_such_switch')"
15
+ Then the output should contain:
16
+ """
17
+ Vswitch {:name=>"no_such_switch"} not found
18
+ """
19
+
@@ -0,0 +1,18 @@
1
+ Feature: Vswitch.destroy_all
2
+ Background:
3
+ Given I run `phut -v` interactively
4
+
5
+ @sudo
6
+ Scenario: Vswitch.destroy_all
7
+ When I type "Vswitch.destroy_all"
8
+ And I type "Vswitch.all"
9
+ And sleep 5
10
+ Then the output should contain "[]"
11
+
12
+ @sudo
13
+ Scenario: Vswitch.destroy_all
14
+ Given I type "Vswitch.create(dpid: 0xabc)"
15
+ When I type "Vswitch.destroy_all"
16
+ And I type "Vswitch.all"
17
+ And sleep 5
18
+ Then the output should contain "[]"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Then(/^a link between "(.*?)" and "(.*?)" should be created$/) do |namea, nameb|
4
+ expect(Phut::Link.find(namea, nameb)).not_to be_nil
5
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ Then(/^a netns named "(.*?)" should be started$/) do |name|
4
+ expect(Phut::Netns.find_by!(name: name)).not_to be_nil
5
+ end
6
+
7
+ Then(/^the IP address of the netns "([^"]*)" should not be set$/) do |name|
8
+ expect(Phut::Netns.find_by!(name: name).ip_address).to be_nil
9
+ end
10
+
11
+ Then(/^the IP address of the netns "([^"]*)" should be "([^"]*)"$/) do |name, ip|
12
+ expect(Phut::Netns.find_by!(name: name).ip_address).to eq ip
13
+ end
14
+
15
+ Then(/^the MAC address of the netns "([^"]*)" should be "([^"]*)"$/) do |name, mac|
16
+ expect(Phut::Netns.find_by!(name: name).mac_address).to eq mac
17
+ end
18
+
19
+ Then(/^the netmask of the netns "([^"]*)" should be "([^"]*)"$/) do |name, netmask|
20
+ expect(Phut::Netns.find_by!(name: name).netmask).to eq netmask
21
+ end
22
+
23
+ Then(/^the netns "([^"]*)" have the following route:$/) do |name, table|
24
+ netns = Phut::Netns.find_by!(name: name)
25
+ expect(netns.route.net).to eq table.hashes.first['net']
26
+ expect(netns.route.gateway).to eq table.hashes.first['gateway']
27
+ end
28
+
29
+ Then(/^the VLAN of the netns "([^"]*)" should be "([^"]*)"$/) do |name, vlan|
30
+ expect(Phut::Netns.find_by!(name: name).vlan).to eq vlan
31
+ end
@@ -1,11 +1,8 @@
1
- require 'phut'
1
+ # frozen_string_literal: true
2
2
 
3
3
  When(/^I do phut run "(.*?)"$/) do |config_file|
4
- @config_file = config_file
5
- cd('.') do
6
- run_opts = "-P #{@pid_dir} -L #{@log_dir} -S #{@socket_dir}"
7
- step %(I run `phut -v run #{run_opts} #{@config_file}`)
8
- end
4
+ run_opts = "-P #{@pid_dir} -L #{@log_dir} -S #{@socket_dir}"
5
+ step %(I run `phut -v run #{run_opts} #{config_file}`)
9
6
  end
10
7
 
11
8
  When(/^I do phut kill "(.*?)"$/) do |name|
@@ -13,32 +10,6 @@ When(/^I do phut kill "(.*?)"$/) do |name|
13
10
  step %(I successfully run `phut -v kill #{run_opts} #{name}`)
14
11
  end
15
12
 
16
- When(/^sleep (\d+)$/) do |time|
17
- sleep time.to_i
18
- end
19
-
20
- Then(/^a vswitch named "(.*?)" should be running$/) do |name|
21
- expect(system("sudo ovs-vsctl br-exists br#{name}")).to be_truthy
22
- end
23
-
24
- # rubocop:disable LineLength
25
- Then(/^a vswitch named "([^"]*)" \(controller port = (\d+)\) should be running$/) do |name, port_number|
26
- step %(a vswitch named "#{name}" should be running)
27
- step %(the output should contain "ovs-vsctl set-controller br#{name} tcp:127.0.0.1:#{port_number}")
28
- end
29
- # rubocop:enable LineLength
30
-
31
- Then(/^a vswitch named "(.*?)" should not be running$/) do |name|
32
- expect(system("sudo ovs-vsctl br-exists br#{name}")).to be_falsey
33
- end
34
-
35
- Then(/^a vhost named "(.*?)" launches$/) do |name|
36
- step %(a file named "vhost.#{name}.pid" should exist)
37
- end
38
-
39
- Then(/^a link is created between "(.*?)" and "(.*?)"$/) do |name_a, name_b|
40
- cd('.') do
41
- link = Phut::Parser.new.parse(@config_file).fetch([name_a, name_b].sort)
42
- expect(link).to be_up
43
- end
13
+ When(/^sleep (\d+)$/) do |second|
14
+ step "I successfully run `sleep #{second}`"
44
15
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Then(/^a vhost named "(.*?)" should be running$/) do |name|
4
+ expect(Phut::Vhost.find_by!(name: name)).to be_running
5
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ Then(/^a vswitch named "([^"]*)" should be running$/) do |name|
4
+ expect(Phut::Vswitch.find_by(name: name)).not_to be_nil
5
+ end
6
+
7
+ Then(/^a vswitch named "([^"]*)" should be running on port "([^"]*)"$/) do |name, tcp_port|
8
+ expect(Phut::Vswitch.find_by!(name: name).tcp_port).to eq tcp_port.to_i
9
+ end
10
+
11
+ Then(/^a vswitch named "([^"]*)" should not be running$/) do |name|
12
+ expect(Phut::Vswitch.find_by(name: name)).to be_nil
13
+ end
14
+
15
+ Then(/^a vswitch named "([^"]*)" \(controller port = (\d+)\) should be running$/) do |name, port_number|
16
+ expect(Phut::Vswitch.find_by!(name: name).tcp_port).to eq port_number.to_i
17
+ end
@@ -1,4 +1,4 @@
1
- require 'aruba/cucumber'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'coveralls'
4
- Coveralls.wear_merged!
3
+ require 'aruba/cucumber'
4
+ require 'phut'
@@ -1,13 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'phut'
2
4
 
3
5
  Before do
4
- @pid_dir = '.'
5
- @log_dir = '.'
6
- @socket_dir = '.'
6
+ Aruba.configure do |config|
7
+ Dir.chdir(config.working_directory) do
8
+ @log_dir = './log'
9
+ @pid_dir = './tmp/pids'
10
+ @socket_dir = './tmp/sockets'
11
+
12
+ FileUtils.mkdir_p(@log_dir) unless File.exist?(@log_dir)
13
+ FileUtils.mkdir_p(@pid_dir) unless File.exist?(@pid_dir)
14
+ FileUtils.mkdir_p(@socket_dir) unless File.exist?(@socket_dir)
15
+
16
+ Phut.pid_dir = @pid_dir
17
+ Phut.log_dir = @log_dir
18
+ Phut.socket_dir = @socket_dir
19
+ end
20
+ end
7
21
  end
8
22
 
9
23
  Before('@sudo') do
10
- fail 'sudo authentication failed' unless system 'sudo -v'
24
+ raise 'sudo authentication failed' unless system 'sudo -v'
11
25
  @aruba_timeout_seconds = 10
12
26
  end
13
27
 
@@ -17,17 +31,11 @@ After('@sudo') do
17
31
  Phut.pid_dir = @pid_dir
18
32
  Phut.log_dir = @log_dir
19
33
  Phut.socket_dir = @socket_dir
20
- Phut::Parser.new.parse(@config_file).stop
21
- end
22
- end
23
- end
24
-
25
- Before('@shell') do
26
- fail 'sudo authentication failed' unless system 'sudo -v'
27
- end
28
34
 
29
- After('@shell') do
30
- `sudo ovs-vsctl list-br`.split("\n").each do |each|
31
- run "sudo ovs-vsctl del-br #{each}"
35
+ Phut::Vswitch.destroy_all
36
+ Phut::Vhost.destroy_all
37
+ Phut::Netns.destroy_all
38
+ Phut::Link.destroy_all
39
+ end
32
40
  end
33
41
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'phut/open_vswitch'
2
4
  require 'phut/parser'
3
5
  require 'phut/setting'
4
6
  require 'phut/version'
5
7
  require 'phut/vhost_daemon'
8
+ require 'phut/vswitch'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module Phut
6
+ # Defines find_by method
7
+ module Finder
8
+ def find_by(queries)
9
+ queries.inject(all) do |memo, (attr, value)|
10
+ memo.find_all { |each| each.__send__(attr) == value }
11
+ end.first
12
+ end
13
+
14
+ def find_by!(queries)
15
+ name = to_s.demodulize
16
+ find_by(queries) || raise("#{name} #{queries.inspect} not found")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'phut/netns'
4
+ require 'phut/shell_runner'
5
+ require 'phut/veth'
6
+
7
+ module Phut
8
+ # Virtual link
9
+ class Link
10
+ def self.all
11
+ link = Hash.new { [] }
12
+ Veth.all.each { |each| link[each.link_id] += [each.name] }
13
+ link.map { |link_id, names| new(names.first, names.second, link_id) }
14
+ end
15
+
16
+ def self.find(end1, end2)
17
+ all.find { |each| each.ends.map(&:name) == [end1, end2].map(&:to_s).sort }
18
+ end
19
+
20
+ def self.create(end1, end2)
21
+ new(end1, end2).start
22
+ end
23
+
24
+ def self.destroy_all
25
+ all.each(&:destroy)
26
+ end
27
+
28
+ include ShellRunner
29
+
30
+ attr_reader :ends
31
+
32
+ def initialize(name1, name2, link_id = Link.all.size)
33
+ raise if name1 == name2
34
+ @ends = [Veth.new(name: name1, link_id: link_id),
35
+ Veth.new(name: name2, link_id: link_id)].sort
36
+ end
37
+
38
+ def start
39
+ return self if up?
40
+ add
41
+ up
42
+ self
43
+ end
44
+
45
+ def destroy
46
+ sudo "ip link delete #{end1} || true"
47
+ sudo "ip link delete #{end2} || true"
48
+ end
49
+ alias stop destroy
50
+
51
+ def device(name)
52
+ ends.find { |each| each.name == name.to_s }
53
+ end
54
+
55
+ def ==(other)
56
+ ends == other.ends
57
+ end
58
+
59
+ private
60
+
61
+ def end1
62
+ ends.first
63
+ end
64
+
65
+ def end2
66
+ ends.second
67
+ end
68
+
69
+ def add
70
+ sudo "ip link add name #{end1.device} type veth peer name #{end2.device}"
71
+ sudo "/sbin/sysctl -q -w net.ipv6.conf.#{end1.device}.disable_ipv6=1"
72
+ sudo "/sbin/sysctl -q -w net.ipv6.conf.#{end2.device}.disable_ipv6=1"
73
+ end
74
+
75
+ def up?
76
+ Link.all.include? self
77
+ end
78
+
79
+ def up
80
+ sudo "/sbin/ifconfig #{end1.device} up"
81
+ sudo "/sbin/ifconfig #{end2.device} up"
82
+ end
83
+ end
84
+ end
@@ -1,48 +1,137 @@
1
- require 'active_support/core_ext/class/attribute_accessors'
2
- require 'phut/null_logger'
1
+ # frozen_string_literal: true
2
+
3
+ require 'phut/finder'
4
+ require 'phut/route'
3
5
  require 'phut/shell_runner'
6
+ require 'phut/veth'
4
7
 
5
8
  module Phut
6
9
  # `ip netns ...` command runner
7
10
  class Netns
8
- cattr_accessor(:all, instance_reader: false) { [] }
11
+ extend ShellRunner
12
+ extend Finder
13
+
14
+ # rubocop:disable MethodLength
15
+ # rubocop:disable AbcSize
16
+ def self.all
17
+ sh('ip netns list').split("\n").map do |each|
18
+ name = each.split.first
19
+ ip_addr_list =
20
+ sudo("ip netns exec #{name} ip -4 -o addr list").split("\n")
21
+ mac_addr_list =
22
+ sudo("ip netns exec #{name} ip -4 -o link list").split("\n")
23
+ if ip_addr_list.size > 1
24
+ %r{inet ([^/]+)/(\d+)} =~ ip_addr_list[1]
25
+ ip_address = Regexp.last_match(1)
26
+ mask_length = Regexp.last_match(2).to_i
27
+ netmask = IPAddr.new('255.255.255.255').mask(mask_length).to_s
28
+ %r{link/ether ((?:[a-f\d]{2}:){5}[a-f\d]{2})}i =~ mac_addr_list[1]
29
+ new(name: name, ip_address: ip_address, netmask: netmask,
30
+ mac_address: Regexp.last_match(1))
31
+ else
32
+ new(name: name)
33
+ end
34
+ end.sort_by(&:name)
35
+ end
36
+ # rubocop:enable MethodLength
37
+ # rubocop:enable AbcSize
9
38
 
10
- def self.create(options, name, logger = NullLogger.new)
11
- new(options, name, logger).tap { |netns| all << netns }
39
+ def self.create(*args)
40
+ new(*args).tap(&:run)
12
41
  end
13
42
 
14
- def self.each(&block)
15
- all.each(&block)
43
+ def self.destroy_all
44
+ all.each(&:stop)
16
45
  end
17
46
 
18
47
  include ShellRunner
19
48
 
20
49
  attr_reader :name
21
- attr_accessor :network_device
50
+ attr_reader :ip_address
51
+ attr_reader :mac_address
22
52
 
23
- def initialize(options, name, logger)
53
+ # rubocop:disable ParameterLists
54
+ def initialize(name:,
55
+ ip_address: nil,
56
+ mac_address: nil,
57
+ netmask: '255.255.255.255',
58
+ route: {},
59
+ vlan: nil)
24
60
  @name = name
25
- @options = options
26
- @logger = logger
61
+ @ip_address = ip_address
62
+ @mac_address = mac_address
63
+ @netmask = netmask
64
+ @route = Route.new(net: route[:net], gateway: route[:gateway])
65
+ @vlan = vlan
27
66
  end
67
+ # rubocop:enable MethodLength
68
+ # rubocop:enable ParameterLists
28
69
 
29
- # rubocop:disable AbcSize
30
70
  def run
31
- sh "sudo ip netns add #{name}"
32
- sh "sudo ip link set dev #{network_device} netns #{name}"
33
- sh "sudo ip netns exec #{name} ifconfig lo 127.0.0.1"
34
- sh "sudo ip netns exec #{name}"\
35
- " ifconfig #{network_device} #{ip} netmask #{netmask}"
36
- sh "sudo ip netns exec #{name} route add -net #{net} gw #{gateway}"
71
+ sudo "ip netns add #{name}"
72
+ sudo "ip netns exec #{name} ifconfig lo 127.0.0.1"
37
73
  end
38
- # rubocop:enable AbcSize
39
74
 
40
75
  def stop
41
- sh "sudo ip netns delete #{name}"
76
+ sudo("ip netns pids #{name}").split("\n").each do |each|
77
+ exec "kill #{each}"
78
+ end
79
+ sudo "ip netns delete #{name}"
80
+ end
81
+
82
+ def exec(command)
83
+ sudo "ip netns exec #{name} #{command}"
84
+ end
85
+
86
+ def device
87
+ return unless /^\d+: #{Veth::PREFIX}(\d+)_([^:\.]*?)[@:]/ =~
88
+ sudo("ip netns exec #{name} ip -o link show")
89
+ Veth.new(name: $LAST_MATCH_INFO[2], link_id: $LAST_MATCH_INFO[1].to_i)
90
+ end
91
+
92
+ # rubocop:disable MethodLength
93
+ # rubocop:disable AbcSize
94
+ def device=(veth)
95
+ sudo "ip link set dev #{veth} netns #{name}"
96
+
97
+ vlan_suffix = @vlan ? ".#{@vlan}" : ''
98
+ if @vlan
99
+ sudo "ip netns exec #{name} ip link set #{veth} up"
100
+ sudo "ip netns exec #{name} "\
101
+ "ip link add link #{veth} name "\
102
+ "#{veth}#{vlan_suffix} type vlan id #{@vlan}"
103
+ end
104
+ if @mac_address
105
+ sudo "ip netns exec #{name} "\
106
+ "ip link set #{veth}#{vlan_suffix} address #{@mac_address}"
107
+ end
108
+ sudo "ip netns exec #{name} ip link set #{veth}#{vlan_suffix} up"
109
+ sudo "ip netns exec #{name} "\
110
+ "ip addr replace #{@ip_address}/#{@netmask} "\
111
+ "dev #{veth}#{vlan_suffix}"
112
+ sudo "ip netns exec #{name} ip link set #{veth}#{vlan_suffix} up"
113
+
114
+ @route.add name
115
+ end
116
+ # rubocop:enable MethodLength
117
+ # rubocop:enable AbcSize
118
+
119
+ def netmask
120
+ if %r{inet [^/]+/(\d+) } =~
121
+ sudo("ip netns exec #{name} ip -o -4 address show dev #{device}")
122
+ IPAddr.new('255.255.255.255').mask(Regexp.last_match(1).to_i).to_s
123
+ end
124
+ end
125
+
126
+ def route
127
+ Route.read name
42
128
  end
43
129
 
44
- def method_missing(message, *_args)
45
- @options.__send__ :[], message
130
+ def vlan
131
+ if /^\d+: #{device.device}\.(\d+)@/ =~
132
+ sudo("ip netns exec #{name} ip -o link show")
133
+ Regexp.last_match(1)
134
+ end
46
135
  end
47
136
  end
48
137
  end