phut 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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