phut 0.1.0
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 +7 -0
- data/.gitignore +20 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/.travis.yml +34 -0
- data/CHANGELOG.md +8 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +4 -0
- data/Guardfile +29 -0
- data/LICENSE +339 -0
- data/README.md +49 -0
- data/Rakefile +10 -0
- data/bin/phut +111 -0
- data/features/dsl.feature +26 -0
- data/features/dsl_link.feature +22 -0
- data/features/dsl_vhost.feature +37 -0
- data/features/dsl_vswitch.feature +45 -0
- data/features/phut.feature +5 -0
- data/features/shell.feature +40 -0
- data/features/step_definitions/phut_steps.rb +30 -0
- data/features/support/env.rb +4 -0
- data/features/support/hooks.rb +34 -0
- data/lib/phut.rb +5 -0
- data/lib/phut/cli.rb +61 -0
- data/lib/phut/configuration.rb +64 -0
- data/lib/phut/null_logger.rb +14 -0
- data/lib/phut/open_vswitch.rb +116 -0
- data/lib/phut/parser.rb +51 -0
- data/lib/phut/phost.rb +92 -0
- data/lib/phut/setting.rb +54 -0
- data/lib/phut/shell_runner.rb +9 -0
- data/lib/phut/syntax.rb +123 -0
- data/lib/phut/version.rb +4 -0
- data/lib/phut/virtual_link.rb +56 -0
- data/phut.gemspec +55 -0
- data/spec/phut/open_vswitch_spec.rb +21 -0
- data/spec/phut/parser_spec.rb +69 -0
- data/spec/phut_spec.rb +43 -0
- data/spec/spec_helper.rb +14 -0
- data/tasks/cucumber.rake +18 -0
- data/tasks/flog.rake +25 -0
- data/tasks/gem.rake +13 -0
- data/tasks/openvswitch.rake +26 -0
- data/tasks/reek.rake +11 -0
- data/tasks/relish.rake +4 -0
- data/tasks/rspec.rake +9 -0
- data/tasks/rubocop.rake +8 -0
- data/tasks/vhost.rake +32 -0
- metadata +426 -0
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
phut
|
2
|
+
====
|
3
|
+
|
4
|
+
[][travis]
|
5
|
+
[][codeclimate]
|
6
|
+
[][coveralls]
|
7
|
+
[][gemnasium]
|
8
|
+
[][gitter]
|
9
|
+
|
10
|
+
Virtual network in seconds
|
11
|
+
|
12
|
+
[travis]: http://travis-ci.org/trema/phut
|
13
|
+
[codeclimate]: https://codeclimate.com/github/trema/phut
|
14
|
+
[coveralls]: https://coveralls.io/r/trema/phut
|
15
|
+
[gemnasium]: https://gemnasium.com/trema/phut
|
16
|
+
[gitter]: https://gitter.im/trema/phut
|
17
|
+
|
18
|
+
|
19
|
+
Install
|
20
|
+
-------
|
21
|
+
|
22
|
+
```
|
23
|
+
$ git clone https://github.com/trema/phut.git
|
24
|
+
$ cd phut
|
25
|
+
$ bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
Play
|
30
|
+
----
|
31
|
+
|
32
|
+
With Phut network DSL, you can describe the network topology in which
|
33
|
+
your OpenFlow controller is executed.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
# phut.conf
|
37
|
+
# One virtual switch + two virtual hosts.
|
38
|
+
vswitch { dpid 0xabc }
|
39
|
+
vhost 'host1'
|
40
|
+
vhost 'host2'
|
41
|
+
link '0xabc', 'host1'
|
42
|
+
link '0xabc', 'host2'
|
43
|
+
```
|
44
|
+
|
45
|
+
Then you can pass the network configuration to `phut run`.
|
46
|
+
|
47
|
+
```
|
48
|
+
$ bundle exec phut run phut.conf
|
49
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
|
3
|
+
require 'rake/clean'
|
4
|
+
|
5
|
+
task default: [:openvswitch, :vhost]
|
6
|
+
task test: [:spec, :cucumber, :quality]
|
7
|
+
task quality: [:rubocop, :reek, :flog]
|
8
|
+
task travis: [:spec, 'cucumber:travis', :quality]
|
9
|
+
|
10
|
+
Dir.glob('tasks/*.rake').each { |each| import each }
|
data/bin/phut
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
require 'gli'
|
4
|
+
require 'logger'
|
5
|
+
require 'phut'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
$stdout.sync = true
|
9
|
+
|
10
|
+
module Phut
|
11
|
+
# /bin/phut command
|
12
|
+
module App
|
13
|
+
extend GLI::App
|
14
|
+
|
15
|
+
program_desc 'Virtual network in seconds'
|
16
|
+
|
17
|
+
version Phut::VERSION
|
18
|
+
|
19
|
+
desc 'Be verbose'
|
20
|
+
switch [:v, :verbose], negatable: false
|
21
|
+
|
22
|
+
command :shell do |c|
|
23
|
+
c.action do |_global_options, _options, _args|
|
24
|
+
Pry.prompt =
|
25
|
+
[
|
26
|
+
proc { 'phut> ' },
|
27
|
+
proc { 'phut* ' }
|
28
|
+
]
|
29
|
+
Pry::Commands.block_command 'pid_dir' do |dir|
|
30
|
+
Phut.pid_dir = dir
|
31
|
+
end
|
32
|
+
Pry::Commands.block_command 'log_dir' do |dir|
|
33
|
+
Phut.log_dir = dir
|
34
|
+
end
|
35
|
+
Pry::Commands.block_command 'socket_dir' do |dir|
|
36
|
+
Phut.socket_dir = dir
|
37
|
+
end
|
38
|
+
Pry::Commands.block_command 'vswitch' do |dpid|
|
39
|
+
Phut::OpenVswitch.new(dpid).run
|
40
|
+
end
|
41
|
+
Pry::Commands.block_command 'kill' do |dpid|
|
42
|
+
Phut::OpenVswitch.new(dpid).shutdown
|
43
|
+
end
|
44
|
+
pry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'Starts a virtual network'
|
49
|
+
arg_name 'FILE'
|
50
|
+
command :run do |c|
|
51
|
+
c.desc 'Location to put pid files'
|
52
|
+
c.flag [:p, :pid_dir], default_value: Phut.pid_dir
|
53
|
+
|
54
|
+
c.desc 'Location to put log files'
|
55
|
+
c.flag [:l, :log_dir], default_value: Phut.log_dir
|
56
|
+
|
57
|
+
c.desc 'Location to put socket files'
|
58
|
+
c.flag [:s, :socket_dir], default_value: Phut.socket_dir
|
59
|
+
|
60
|
+
c.action do |global_options, options, args|
|
61
|
+
stdout_logger = Logger.new($stderr).tap do |logger|
|
62
|
+
logger.formatter = proc { |_sev, _dtm, _name, msg| msg + "\n" }
|
63
|
+
logger.level = global_options[:verbose] ? Logger::DEBUG : Logger::INFO
|
64
|
+
end
|
65
|
+
Phut.pid_dir = options[:pid_dir]
|
66
|
+
Phut.log_dir = options[:log_dir]
|
67
|
+
Phut.socket_dir = options[:socket_dir]
|
68
|
+
Phut::Parser.new(stdout_logger).parse(args[0]).run
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'Stops a virtual network'
|
73
|
+
arg_name 'FILE'
|
74
|
+
command :stop do |c|
|
75
|
+
c.desc 'Location to put pid files'
|
76
|
+
c.flag [:p, :pid_dir], default_value: Phut.pid_dir
|
77
|
+
|
78
|
+
c.desc 'Location to put log files'
|
79
|
+
c.flag [:l, :log_dir], default_value: Phut.log_dir
|
80
|
+
|
81
|
+
c.desc 'Location to put socket files'
|
82
|
+
c.flag [:s, :socket_dir], default_value: Phut.socket_dir
|
83
|
+
|
84
|
+
c.action do |global_options, _options, args|
|
85
|
+
stdout_logger = Logger.new($stderr).tap do |logger|
|
86
|
+
logger.formatter = proc { |_sev, _dtm, _name, msg| msg + "\n" }
|
87
|
+
logger.level = global_options[:verbose] ? Logger::DEBUG : Logger::INFO
|
88
|
+
end
|
89
|
+
Phut::Parser.new(stdout_logger).parse(args[0]).stop
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
command :kill do |c|
|
94
|
+
c.action do |_global_options, _options, args|
|
95
|
+
args.each { |each| Phut::OpenVswitch.new(each).shutdown }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
pre do |global, _command, _options, _args|
|
100
|
+
if global[:version]
|
101
|
+
puts "#{exe_name} version #{version_string}"
|
102
|
+
exit_now! nil, 0
|
103
|
+
end
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
default_command :shell
|
108
|
+
|
109
|
+
exit run(ARGV)
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: DSL parser
|
2
|
+
Scenario: name conflict (vsiwtch and vswitch)
|
3
|
+
Given a file named "network.conf" with:
|
4
|
+
"""
|
5
|
+
vswitch { dpid 0xabc }
|
6
|
+
vswitch { dpid 0xabc }
|
7
|
+
"""
|
8
|
+
When I do phut run "network.conf"
|
9
|
+
Then the exit status should not be 0
|
10
|
+
And the stderr should contain:
|
11
|
+
"""
|
12
|
+
The name 0xabc conflicts with vswitch (name = 0xabc, dpid = 0xabc).
|
13
|
+
"""
|
14
|
+
|
15
|
+
Scenario: name conflict (vhost and vhost)
|
16
|
+
Given a file named "network.conf" with:
|
17
|
+
"""
|
18
|
+
vhost { ip '192.168.0.1' }
|
19
|
+
vhost { ip '192.168.0.1' }
|
20
|
+
"""
|
21
|
+
When I do phut run "network.conf"
|
22
|
+
Then the exit status should not be 0
|
23
|
+
And the stderr should contain:
|
24
|
+
"""
|
25
|
+
The name 192.168.0.1 conflicts with vhost (name = 192.168.0.1, ip = 192.168.0.1).
|
26
|
+
"""
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: The link DSL directive.
|
2
|
+
@sudo
|
3
|
+
Scenario: phut run with "link ..."
|
4
|
+
Given a file named "network.conf" with:
|
5
|
+
"""
|
6
|
+
vswitch { dpid '0xabc' }
|
7
|
+
vhost { ip '192.168.0.1' }
|
8
|
+
link '0xabc', '192.168.0.1'
|
9
|
+
"""
|
10
|
+
When I do phut run "network.conf"
|
11
|
+
Then a link is created between "0xabc" and "192.168.0.1"
|
12
|
+
|
13
|
+
@sudo
|
14
|
+
Scenario: phut run with "link alias1, alias2"
|
15
|
+
Given a file named "network.conf" with:
|
16
|
+
"""
|
17
|
+
vswitch('my_switch') { dpid '0xabc' }
|
18
|
+
vhost('host1') { ip '192.168.0.1' }
|
19
|
+
link 'my_switch', 'host1'
|
20
|
+
"""
|
21
|
+
When I do phut run "network.conf"
|
22
|
+
Then a link is created between "my_switch" and "host1"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Feature: The vhost DSL directive.
|
2
|
+
@sudo
|
3
|
+
Scenario: phut run with "vhost { ip ... }"
|
4
|
+
Given a file named "network.conf" with:
|
5
|
+
"""
|
6
|
+
vhost { ip '192.168.0.1' }
|
7
|
+
vhost { ip '192.168.0.2' }
|
8
|
+
link '192.168.0.1', '192.168.0.2'
|
9
|
+
"""
|
10
|
+
When I do phut run "network.conf"
|
11
|
+
Then a vhost named "192.168.0.1" launches
|
12
|
+
And a vhost named "192.168.0.2" launches
|
13
|
+
|
14
|
+
@sudo
|
15
|
+
Scenario: phut run with "vhost(alias) { ... }"
|
16
|
+
Given a file named "network.conf" with:
|
17
|
+
"""
|
18
|
+
vhost('host1') { ip '192.168.0.1' }
|
19
|
+
vhost('host2') { ip '192.168.0.2' }
|
20
|
+
link 'host1', 'host2'
|
21
|
+
"""
|
22
|
+
When I do phut run "network.conf"
|
23
|
+
Then a vhost named "host1" launches
|
24
|
+
And a vhost named "host2" launches
|
25
|
+
|
26
|
+
@sudo
|
27
|
+
Scenario: phut run with "vhost(alias)"
|
28
|
+
Given a file named "network.conf" with:
|
29
|
+
"""
|
30
|
+
vhost('host1')
|
31
|
+
vhost('host2')
|
32
|
+
link 'host1', 'host2'
|
33
|
+
"""
|
34
|
+
When I do phut run "network.conf"
|
35
|
+
Then a vhost named "host1" launches
|
36
|
+
And a vhost named "host2" launches
|
37
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Feature: The vswitch DSL directive.
|
2
|
+
@sudo
|
3
|
+
Scenario: phut run with "vswitch { dpid STRING }"
|
4
|
+
Given a file named "network.conf" with:
|
5
|
+
"""
|
6
|
+
vswitch { dpid '0xabc' }
|
7
|
+
"""
|
8
|
+
When I do phut run "network.conf"
|
9
|
+
Then a vswitch named "0xabc" launches
|
10
|
+
|
11
|
+
@sudo
|
12
|
+
Scenario: phut run with "vswitch { dpid NUMBER }"
|
13
|
+
Given a file named "network.conf" with:
|
14
|
+
"""
|
15
|
+
vswitch { dpid 0xabc }
|
16
|
+
"""
|
17
|
+
When I do phut run "network.conf"
|
18
|
+
Then a vswitch named "0xabc" launches
|
19
|
+
|
20
|
+
@sudo
|
21
|
+
Scenario: phut run with "vswitch { datapath_id STRING }"
|
22
|
+
Given a file named "network.conf" with:
|
23
|
+
"""
|
24
|
+
vswitch { datapath_id '0xabc' }
|
25
|
+
"""
|
26
|
+
When I do phut run "network.conf"
|
27
|
+
Then a vswitch named "0xabc" launches
|
28
|
+
|
29
|
+
@sudo
|
30
|
+
Scenario: phut run with "vswitch { datapath_id NUMBER }"
|
31
|
+
Given a file named "network.conf" with:
|
32
|
+
"""
|
33
|
+
vswitch { datapath_id 0xabc }
|
34
|
+
"""
|
35
|
+
When I do phut run "network.conf"
|
36
|
+
Then a vswitch named "0xabc" launches
|
37
|
+
|
38
|
+
@sudo
|
39
|
+
Scenario: phut run with "vswitch(alias) { ... }"
|
40
|
+
Given a file named "network.conf" with:
|
41
|
+
"""
|
42
|
+
vswitch('my_switch') { datapath_id '0xabc' }
|
43
|
+
"""
|
44
|
+
When I do phut run "network.conf"
|
45
|
+
Then a vswitch named "my_switch" launches
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Shell
|
2
|
+
Background:
|
3
|
+
Given I run `phut -v` interactively
|
4
|
+
And I type "pid_dir '.'"
|
5
|
+
And I type "log_dir '.'"
|
6
|
+
And I type "socket_dir '.'"
|
7
|
+
|
8
|
+
@shell
|
9
|
+
Scenario: vswitch NUMBER
|
10
|
+
When I type "vswitch 0xabc"
|
11
|
+
And I type "quit"
|
12
|
+
Then a file named "open_vswitch.0xabc.pid" should exist
|
13
|
+
And a file named "open_vswitch.0xabc.log" should exist
|
14
|
+
|
15
|
+
@shell
|
16
|
+
Scenario: vswitch STRING
|
17
|
+
When I type "vswitch '0xabc'"
|
18
|
+
And I type "quit"
|
19
|
+
Then a file named "open_vswitch.0xabc.pid" should exist
|
20
|
+
And a file named "open_vswitch.0xabc.log" should exist
|
21
|
+
|
22
|
+
@shell
|
23
|
+
Scenario: vswitch twice and fail
|
24
|
+
And I type "vswitch 0xabc"
|
25
|
+
When I type "vswitch 0xabc"
|
26
|
+
And I type "quit"
|
27
|
+
Then the output should contain "already running!"
|
28
|
+
|
29
|
+
@shell
|
30
|
+
Scenario: Kill and .pid is deleted
|
31
|
+
And I type "vswitch 0xabc"
|
32
|
+
When I type "kill 0xabc"
|
33
|
+
And I type "quit"
|
34
|
+
Then a file named "open_vswitch.0xabc.pid" should not exist
|
35
|
+
|
36
|
+
@shell
|
37
|
+
Scenario: Kill without run and fail
|
38
|
+
When I type "kill 0xabc"
|
39
|
+
And I type "quit"
|
40
|
+
Then the output should contain "Open vSwitch (dpid = 0xabc) is not running!"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'phut'
|
2
|
+
|
3
|
+
When(/^I do phut run "(.*?)"$/) do |file_name|
|
4
|
+
@config_file = file_name
|
5
|
+
run_opts = "-p #{@pid_dir} -l #{@log_dir} -s #{@socket_dir}"
|
6
|
+
step %(I run `phut -v run #{run_opts} #{@config_file}`)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then(/^a vswitch named "(.*?)" launches$/) do |name|
|
10
|
+
in_current_dir do
|
11
|
+
pid_file = File.join(File.expand_path(@pid_dir), "open_vswitch.#{name}.pid")
|
12
|
+
step %(a file named "#{pid_file}" should exist)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Then(/^a vhost named "(.*?)" launches$/) do |name|
|
17
|
+
in_current_dir do
|
18
|
+
pid_file = File.join(File.expand_path(@pid_dir), "phost.#{name}.pid")
|
19
|
+
step %(a file named "#{pid_file}" should exist)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Then(/^a link is created between "(.*?)" and "(.*?)"$/) do |name_a, name_b|
|
24
|
+
in_current_dir do
|
25
|
+
link = Phut::Parser.new.parse(@config_file).links.find do |each|
|
26
|
+
each.name_a == name_a && each.name_b == name_b
|
27
|
+
end
|
28
|
+
expect(link).to be_up
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'phut'
|
2
|
+
|
3
|
+
Before do
|
4
|
+
@pid_dir = '.'
|
5
|
+
@log_dir = '.'
|
6
|
+
@socket_dir = '.'
|
7
|
+
end
|
8
|
+
|
9
|
+
Before('@sudo') do
|
10
|
+
fail 'sudo authentication failed' unless system 'sudo -v'
|
11
|
+
@aruba_timeout_seconds = 10
|
12
|
+
end
|
13
|
+
|
14
|
+
Before('@shell') do
|
15
|
+
fail 'sudo authentication failed' unless system 'sudo -v'
|
16
|
+
end
|
17
|
+
|
18
|
+
After('@sudo') do
|
19
|
+
in_current_dir do
|
20
|
+
Phut.pid_dir = @pid_dir
|
21
|
+
Phut.log_dir = @log_dir
|
22
|
+
Phut.socket_dir = @socket_dir
|
23
|
+
Phut::Parser.new.parse(@config_file).stop
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
After('@shell') do
|
28
|
+
in_current_dir do
|
29
|
+
Dir.glob(File.join(Dir.getwd, '*.pid')).each do |each|
|
30
|
+
pid = IO.read(each).to_i
|
31
|
+
run "sudo kill #{pid}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/phut.rb
ADDED
data/lib/phut/cli.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'phut/null_logger'
|
2
|
+
require 'phut/setting'
|
3
|
+
require 'phut/shell_runner'
|
4
|
+
|
5
|
+
module Phut
|
6
|
+
# cli command wrapper.
|
7
|
+
class Cli
|
8
|
+
include ShellRunner
|
9
|
+
|
10
|
+
def initialize(host, logger = NullLogger.new)
|
11
|
+
@host = host
|
12
|
+
@logger = logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_packets(dest, options = {})
|
16
|
+
n_pkts = options[:n_pkts]
|
17
|
+
sh [executable,
|
18
|
+
"-i #{@host.interface} send_packets",
|
19
|
+
"--ip_dst #{dest.ip}",
|
20
|
+
"--ip_src #{@host.ip}",
|
21
|
+
'--tp_src 1',
|
22
|
+
'--tp_dst 1',
|
23
|
+
'--pps 1',
|
24
|
+
'--length 22',
|
25
|
+
n_pkts ? "--n_pkts=#{n_pkts}" : '--duration 1'].join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def show_tx_stats
|
29
|
+
puts stats(:tx)
|
30
|
+
end
|
31
|
+
|
32
|
+
def show_rx_stats
|
33
|
+
puts stats(:rx)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_arp_entry(other)
|
37
|
+
sh "sudo #{executable} -i #{@host.interface} add_arp_entry " \
|
38
|
+
"--ip_addr #{other.ip} --mac_addr #{other.mac}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_ip_and_mac_address
|
42
|
+
sh "sudo #{executable} -i #{@host.interface} set_host_addr " \
|
43
|
+
"--ip_addr #{@host.ip} --ip_mask #{@host.netmask} " \
|
44
|
+
"--mac_addr #{@host.mac}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def enable_promisc
|
48
|
+
sh "sudo #{executable} -i #{@host.interface} enable_promisc"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def executable
|
54
|
+
"#{Phut.root}/vendor/phost/src/cli"
|
55
|
+
end
|
56
|
+
|
57
|
+
def stats(type)
|
58
|
+
`sudo #{executable} -i #{@host.interface} show_stats --#{type}`
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|