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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +14 -1
- data/.travis.yml +2 -6
- data/Gemfile +30 -2
- data/Gemfile.lock +156 -0
- data/README.md +7 -2
- data/Rakefile +4 -3
- data/bin/phut +35 -83
- data/bin/vhost +28 -26
- data/features/{dsl.feature → dsl/error.feature} +8 -6
- data/features/{dsl_link.feature → dsl/link.feature} +11 -14
- data/features/dsl/netns.feature +115 -0
- data/features/dsl/vhost.feature +37 -0
- data/features/{dsl_vswitch.feature → dsl/vswitch.feature} +12 -12
- data/features/phut_run.feature +15 -0
- data/features/shell/vswitch#destroy.feature +10 -0
- data/features/shell/vswitch#ports.feature +36 -0
- data/features/shell/vswitch.all.feature +26 -0
- data/features/shell/vswitch.create.feature +30 -0
- data/features/shell/vswitch.destroy.feature +19 -0
- data/features/shell/vswitch.destroy_all.feature +18 -0
- data/features/step_definitions/link_steps.rb +5 -0
- data/features/step_definitions/netns_steps.rb +31 -0
- data/features/step_definitions/phut_steps.rb +5 -34
- data/features/step_definitions/vhost_steps.rb +5 -0
- data/features/step_definitions/vswitch_steps.rb +17 -0
- data/features/support/env.rb +3 -3
- data/features/support/hooks.rb +23 -15
- data/lib/phut.rb +3 -0
- data/lib/phut/finder.rb +19 -0
- data/lib/phut/link.rb +84 -0
- data/lib/phut/netns.rb +111 -22
- data/lib/phut/open_vswitch.rb +98 -96
- data/lib/phut/parser.rb +39 -8
- data/lib/phut/raw_socket.rb +4 -0
- data/lib/phut/route.rb +34 -0
- data/lib/phut/setting.rb +21 -4
- data/lib/phut/shell_runner.rb +13 -2
- data/lib/phut/syntax.rb +31 -14
- data/lib/phut/syntax/directive.rb +9 -1
- data/lib/phut/syntax/netns_directive.rb +13 -2
- data/lib/phut/syntax/vhost_directive.rb +2 -0
- data/lib/phut/syntax/vswitch_directive.rb +3 -1
- data/lib/phut/version.rb +3 -1
- data/lib/phut/veth.rb +68 -0
- data/lib/phut/vhost.rb +99 -58
- data/lib/phut/vhost_daemon.rb +53 -11
- data/lib/phut/vsctl.rb +125 -0
- data/lib/phut/vswitch.rb +10 -0
- data/phut.gemspec +9 -31
- data/tasks/cucumber.rake +5 -1
- data/tasks/flay.rake +2 -0
- data/tasks/flog.rake +3 -1
- data/tasks/gem.rake +2 -0
- data/tasks/minitest.rake +7 -0
- data/tasks/reek.rake +2 -0
- data/tasks/rubocop.rake +2 -0
- data/tasks/yard.rake +2 -0
- data/test/phut/link_test.rb +85 -0
- data/test/phut/netns_test.rb +58 -0
- data/test/phut/open_vswitch_test.rb +125 -0
- data/test/phut/veth_test.rb +48 -0
- data/test/phut/vhost_test.rb +56 -0
- metadata +41 -287
- data/.rspec +0 -3
- data/Guardfile +0 -29
- data/features/dsl_vhost.feature +0 -37
- data/features/phut_kill.feature +0 -27
- data/features/shell.feature +0 -39
- data/lib/phut/configuration.rb +0 -92
- data/lib/phut/null_logger.rb +0 -14
- data/lib/phut/virtual_link.rb +0 -109
- data/spec/phut/parser_spec.rb +0 -66
- data/spec/phut_spec.rb +0 -45
- data/spec/spec_helper.rb +0 -14
- data/tasks/LICENSE +0 -675
- data/tasks/relish.rake +0 -8
- data/tasks/rspec.rake +0 -8
data/lib/phut/open_vswitch.rb
CHANGED
@@ -1,148 +1,150 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
require 'phut/finder'
|
6
|
+
require 'phut/link'
|
5
7
|
require 'phut/shell_runner'
|
8
|
+
require 'phut/vsctl'
|
9
|
+
require 'pio'
|
6
10
|
|
7
11
|
module Phut
|
8
|
-
# Open vSwitch controller
|
12
|
+
# Open vSwitch controller
|
9
13
|
# rubocop:disable ClassLength
|
10
14
|
class OpenVswitch
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
conflict = find_by(name: vswitch.name)
|
17
|
-
fail "The name #{vswitch.name} conflicts with #{conflict}." if conflict
|
18
|
-
all << vswitch
|
19
|
-
end
|
20
|
-
end
|
15
|
+
extend ShellRunner
|
16
|
+
extend Finder
|
17
|
+
|
18
|
+
class_attribute :bridge_prefix
|
19
|
+
self.bridge_prefix = ''
|
21
20
|
|
22
|
-
def self.
|
23
|
-
|
24
|
-
|
21
|
+
def self.create(args)
|
22
|
+
found = find_by(name: args[:name]) || find_by(dpid: args[:dpid])
|
23
|
+
raise "a Vswitch #{found.inspect} already exists" if found
|
24
|
+
new(args).__send__ :start
|
25
25
|
end
|
26
26
|
|
27
|
-
def self.
|
28
|
-
|
29
|
-
OpenVswitch.new(dpid, port_number, name, logger).stop!
|
27
|
+
def self.destroy(name)
|
28
|
+
find_by!(name: name).destroy
|
30
29
|
end
|
31
30
|
|
32
|
-
def self.
|
33
|
-
|
34
|
-
memo.find_all { |vswitch| vswitch.__send__(attr) == value }
|
35
|
-
end.first
|
31
|
+
def self.destroy_all
|
32
|
+
all.each(&:destroy)
|
36
33
|
end
|
37
34
|
|
38
|
-
def self.
|
39
|
-
|
35
|
+
def self.all
|
36
|
+
Vsctl.list_br(bridge_prefix).map do |bridge_attrs|
|
37
|
+
new(bridge_attrs)
|
38
|
+
end.sort_by(&:dpid)
|
40
39
|
end
|
41
40
|
|
42
41
|
def self.select(&block)
|
43
42
|
all.select(&block)
|
44
43
|
end
|
45
44
|
|
46
|
-
|
45
|
+
def self.dump_flows(name)
|
46
|
+
find_by!(name: name).dump_flows
|
47
|
+
end
|
47
48
|
|
48
49
|
include ShellRunner
|
49
50
|
|
50
|
-
|
51
|
-
alias_method :datapath_id, :dpid
|
52
|
-
attr_reader :network_devices
|
51
|
+
private_class_method :new
|
53
52
|
|
54
|
-
def initialize(dpid
|
53
|
+
def initialize(dpid:, openflow_version: 1.0, name: nil, tcp_port: 6653)
|
55
54
|
@dpid = dpid
|
56
|
-
@port_number = port_number
|
57
55
|
@name = name
|
58
|
-
@
|
59
|
-
@
|
60
|
-
|
61
|
-
|
62
|
-
def name
|
63
|
-
@name || format('%#x', @dpid)
|
56
|
+
@openflow_version = openflow_version
|
57
|
+
@tcp_port = tcp_port
|
58
|
+
@vsctl = Vsctl.new(name: default_name, bridge: default_bridge)
|
64
59
|
end
|
65
60
|
|
66
61
|
def to_s
|
67
62
|
"vswitch (name = #{name}, dpid = #{format('%#x', @dpid)})"
|
68
63
|
end
|
69
64
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@network_devices.each do |each|
|
76
|
-
sh "sudo ovs-vsctl add-port #{bridge_name} #{each}"
|
77
|
-
end
|
78
|
-
sh "sudo ovs-vsctl set bridge #{bridge_name}" \
|
79
|
-
" protocols=#{Pio::OpenFlow.version}" \
|
80
|
-
" other-config:datapath-id=#{dpid_zero_filled}"
|
81
|
-
sh "sudo ovs-vsctl set-controller #{bridge_name} "\
|
82
|
-
"tcp:127.0.0.1:#{@port_number} "\
|
83
|
-
"-- set controller #{bridge_name} connection-mode=out-of-band"
|
84
|
-
sh "sudo ovs-vsctl set-fail-mode #{bridge_name} secure"
|
85
|
-
rescue
|
86
|
-
raise AlreadyRunning, "Open vSwitch (dpid = #{@dpid}) is already running!"
|
87
|
-
end
|
88
|
-
alias_method :start, :run
|
89
|
-
# rubocop:enable MethodLength
|
90
|
-
# rubocop:enable AbcSize
|
91
|
-
|
92
|
-
def stop
|
93
|
-
return unless running?
|
94
|
-
stop!
|
65
|
+
def inspect
|
66
|
+
"#<Vswitch name: \"#{name}\", "\
|
67
|
+
"dpid: #{@dpid.to_hex}, "\
|
68
|
+
"openflow_version: #{openflow_version}, "\
|
69
|
+
"tcp_port: #{tcp_port}>"
|
95
70
|
end
|
96
71
|
|
97
|
-
def
|
98
|
-
|
99
|
-
|
72
|
+
def name
|
73
|
+
/^#{bridge_prefix}(\S+)$/ =~ bridge
|
74
|
+
Regexp.last_match(1)
|
100
75
|
end
|
101
|
-
alias_method :shutdown, :stop!
|
102
76
|
|
103
|
-
|
104
|
-
|
105
|
-
|
77
|
+
delegate :bridge_prefix, to: 'self.class'
|
78
|
+
delegate :bridge, to: :@vsctl
|
79
|
+
delegate :dpid, to: :@vsctl
|
80
|
+
alias datapath_id dpid
|
81
|
+
delegate :openflow_version, to: :@vsctl
|
82
|
+
delegate :tcp_port, to: :@vsctl
|
83
|
+
|
84
|
+
delegate :add_numbered_port, to: :@vsctl
|
85
|
+
delegate :add_port, to: :@vsctl
|
86
|
+
delegate :bring_port_down, to: :@vsctl
|
87
|
+
delegate :bring_port_up, to: :@vsctl
|
88
|
+
delegate :ports, to: :@vsctl
|
89
|
+
delegate :delete_bridge, to: :@vsctl
|
90
|
+
alias destroy delete_bridge
|
91
|
+
delegate :delete_controller, to: :@vsctl
|
92
|
+
alias stop delete_controller
|
106
93
|
|
107
|
-
def
|
108
|
-
|
94
|
+
def run(port)
|
95
|
+
raise "An Open vSwitch #{inspect} is already running" if @vsctl.tcp_port
|
96
|
+
@vsctl.tcp_port = port
|
109
97
|
end
|
110
98
|
|
99
|
+
# rubocop:disable MethodLength
|
100
|
+
# rubocop:disable LineLength
|
111
101
|
def dump_flows
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
102
|
+
openflow_version = case @vsctl.openflow_version
|
103
|
+
when 1.0
|
104
|
+
:OpenFlow10
|
105
|
+
when 1.3
|
106
|
+
:OpenFlow13
|
107
|
+
else
|
108
|
+
raise "Unknown OpenFlow version: #{@vsctl.openflow_version}"
|
109
|
+
end
|
110
|
+
sudo("ovs-ofctl dump-flows #{bridge} -O #{openflow_version}").
|
111
|
+
split("\n").inject('') do |memo, each|
|
112
|
+
memo + (/^(NXST|OFPST)_FLOW reply/ =~ each ? '' : each.lstrip + "\n")
|
116
113
|
end
|
117
114
|
end
|
115
|
+
# rubocop:enable MethodLength
|
116
|
+
# rubocop:enable LineLength
|
118
117
|
|
119
|
-
def
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
def add_network_device(network_device)
|
124
|
-
network_device.port_number = @network_devices.size + 1
|
125
|
-
@network_devices << network_device
|
118
|
+
def <=>(other)
|
119
|
+
dpid <=> other.dpid
|
126
120
|
end
|
127
121
|
|
128
122
|
private
|
129
123
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
124
|
+
# rubocop:disable LineLength
|
125
|
+
def default_name
|
126
|
+
if @name
|
127
|
+
if (bridge_prefix + @name).length > Vsctl::MAX_BRIDGE_NAME_LENGTH
|
128
|
+
raise "Name '#{@name}' is too long (should be <= #{Vsctl::MAX_BRIDGE_NAME_LENGTH - bridge_prefix.length} chars)"
|
129
|
+
end
|
130
|
+
return @name
|
131
|
+
end
|
132
|
+
raise 'DPID is not set' unless @dpid
|
133
|
+
format('%#x', @dpid)
|
137
134
|
end
|
135
|
+
# rubocop:enable LineLength
|
138
136
|
|
139
|
-
def
|
140
|
-
|
137
|
+
def default_bridge
|
138
|
+
bridge_prefix + default_name
|
141
139
|
end
|
142
140
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
141
|
+
def start
|
142
|
+
@vsctl.add_bridge
|
143
|
+
@vsctl.openflow_version = @openflow_version
|
144
|
+
@vsctl.dpid = @dpid
|
145
|
+
@vsctl.tcp_port = @tcp_port
|
146
|
+
@vsctl.set_fail_mode_secure
|
147
|
+
self
|
146
148
|
end
|
147
149
|
end
|
148
150
|
# rubocop:enable ClassLength
|
data/lib/phut/parser.rb
CHANGED
@@ -1,19 +1,50 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'phut/link'
|
3
4
|
require 'phut/syntax'
|
5
|
+
require 'phut/vhost'
|
6
|
+
require 'phut/vswitch'
|
4
7
|
|
5
8
|
module Phut
|
6
9
|
# Configuration DSL parser.
|
7
10
|
class Parser
|
8
|
-
def initialize(
|
9
|
-
@
|
11
|
+
def initialize(file)
|
12
|
+
@file = file
|
13
|
+
@netns = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
Syntax.new(@netns).instance_eval IO.read(@file), @file
|
18
|
+
Link.all.each do |link|
|
19
|
+
Vswitch.all.each do |vswitch|
|
20
|
+
device = link.device(vswitch.name)
|
21
|
+
vswitch.add_port device if device
|
22
|
+
end
|
23
|
+
end
|
24
|
+
Vhost.all.each(&:set_default_arp_table)
|
25
|
+
Vhost.connect_link
|
26
|
+
update_netns_interfaces
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def update_netns_interfaces
|
32
|
+
@netns.each do |each|
|
33
|
+
netns =
|
34
|
+
Netns.create(name: each[:name],
|
35
|
+
ip_address: each[:ip], netmask: each[:netmask],
|
36
|
+
route: { net: each[:net], gateway: each[:gateway] },
|
37
|
+
mac_address: each[:mac_address], vlan: each[:vlan])
|
38
|
+
netns.device = find_network_device(each.name)
|
39
|
+
end
|
10
40
|
end
|
11
41
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
42
|
+
def find_network_device(name)
|
43
|
+
Link.all.each do |each|
|
44
|
+
device = each.device(name)
|
45
|
+
return device if device
|
16
46
|
end
|
47
|
+
nil
|
17
48
|
end
|
18
49
|
end
|
19
50
|
end
|
data/lib/phut/raw_socket.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'socket'
|
2
4
|
|
3
5
|
module Phut
|
@@ -19,8 +21,10 @@ module Phut
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
24
|
+
# rubocop:disable MethodMissing
|
22
25
|
def method_missing(method, *args)
|
23
26
|
@socket.__send__ method, *args
|
24
27
|
end
|
28
|
+
# rubocop:enable MethodMissing
|
25
29
|
end
|
26
30
|
end
|
data/lib/phut/route.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'phut/shell_runner'
|
4
|
+
|
5
|
+
module Phut
|
6
|
+
# routing table entry
|
7
|
+
class Route
|
8
|
+
extend ShellRunner
|
9
|
+
|
10
|
+
def self.read(netns)
|
11
|
+
sudo("ip netns exec #{netns} route -n").split("\n").each do |each|
|
12
|
+
match = /^(\S+)\s+(\S+)\s+\S+\s+UG\s+/.match(each)
|
13
|
+
next unless match
|
14
|
+
return new(net: match[1], gateway: match[2])
|
15
|
+
end
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :net
|
20
|
+
attr_reader :gateway
|
21
|
+
|
22
|
+
def initialize(net:, gateway:)
|
23
|
+
@net = net
|
24
|
+
@gateway = gateway
|
25
|
+
end
|
26
|
+
|
27
|
+
include ShellRunner
|
28
|
+
|
29
|
+
def add(netns)
|
30
|
+
return unless @net && @gateway
|
31
|
+
sudo "ip netns exec #{netns} route add -net #{@net} gw #{@gateway}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/phut/setting.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
1
4
|
require 'tmpdir'
|
2
5
|
|
3
6
|
# Base module.
|
@@ -6,10 +9,14 @@ module Phut
|
|
6
9
|
class Setting
|
7
10
|
DEFAULTS = {
|
8
11
|
root: File.expand_path(File.join(File.dirname(__FILE__), '..', '..')),
|
12
|
+
logger: Logger.new($stderr).tap do |logger|
|
13
|
+
logger.formatter = proc { |_sev, _dtm, _name, msg| msg + "\n" }
|
14
|
+
logger.level = Logger::INFO
|
15
|
+
end,
|
9
16
|
pid_dir: Dir.tmpdir,
|
10
17
|
log_dir: Dir.tmpdir,
|
11
18
|
socket_dir: Dir.tmpdir
|
12
|
-
}
|
19
|
+
}.freeze
|
13
20
|
|
14
21
|
def initialize
|
15
22
|
@options = DEFAULTS.dup
|
@@ -19,12 +26,20 @@ module Phut
|
|
19
26
|
@options.fetch :root
|
20
27
|
end
|
21
28
|
|
29
|
+
def logger
|
30
|
+
@options.fetch :logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def logger=(logger)
|
34
|
+
@options[:logger] = logger
|
35
|
+
end
|
36
|
+
|
22
37
|
def pid_dir
|
23
38
|
@options.fetch :pid_dir
|
24
39
|
end
|
25
40
|
|
26
41
|
def pid_dir=(path)
|
27
|
-
|
42
|
+
raise "No such directory: #{path}" unless FileTest.directory?(path)
|
28
43
|
@options[:pid_dir] = File.expand_path(path)
|
29
44
|
end
|
30
45
|
|
@@ -33,7 +48,7 @@ module Phut
|
|
33
48
|
end
|
34
49
|
|
35
50
|
def log_dir=(path)
|
36
|
-
|
51
|
+
raise "No such directory: #{path}" unless FileTest.directory?(path)
|
37
52
|
@options[:log_dir] = File.expand_path(path)
|
38
53
|
end
|
39
54
|
|
@@ -42,7 +57,7 @@ module Phut
|
|
42
57
|
end
|
43
58
|
|
44
59
|
def socket_dir=(path)
|
45
|
-
|
60
|
+
raise "No such directory: #{path}" unless FileTest.directory?(path)
|
46
61
|
@options[:socket_dir] = File.expand_path(path)
|
47
62
|
end
|
48
63
|
end
|
@@ -50,8 +65,10 @@ module Phut
|
|
50
65
|
SettingSingleton = Setting.new
|
51
66
|
|
52
67
|
class << self
|
68
|
+
# rubocop:disable MethodMissing
|
53
69
|
def method_missing(method, *args)
|
54
70
|
SettingSingleton.__send__ method, *args
|
55
71
|
end
|
72
|
+
# rubocop:enable MethodMissing
|
56
73
|
end
|
57
74
|
end
|
data/lib/phut/shell_runner.rb
CHANGED
@@ -1,9 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'phut/setting'
|
5
|
+
|
1
6
|
module Phut
|
2
7
|
# Provides sh method.
|
3
8
|
module ShellRunner
|
9
|
+
def sudo(command)
|
10
|
+
sh "sudo #{command}"
|
11
|
+
end
|
12
|
+
|
4
13
|
def sh(command)
|
5
|
-
|
6
|
-
|
14
|
+
Phut.logger.debug(command)
|
15
|
+
stdout, stderr, status = Open3.capture3(command)
|
16
|
+
raise %(Command '#{command}' failed: #{stderr}) unless status.success?
|
17
|
+
stdout
|
7
18
|
end
|
8
19
|
end
|
9
20
|
end
|