phut 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](http://img.shields.io/travis/trema/phut/develop.svg?style=flat)][travis]
|
5
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/trema/phut.svg?style=flat)][codeclimate]
|
6
|
+
[![Coverage Status](http://img.shields.io/codeclimate/coverage/github/trema/phut.svg?style=flat)][coveralls]
|
7
|
+
[![Dependency Status](http://img.shields.io/gemnasium/trema/phut.svg?style=flat)][gemnasium]
|
8
|
+
[![Gitter chat](http://img.shields.io/badge/GITTER-phut-blue.svg?style=flat)][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
|