phut 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +11 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +34 -0
  7. data/CHANGELOG.md +8 -0
  8. data/CONTRIBUTING.md +46 -0
  9. data/Gemfile +4 -0
  10. data/Guardfile +29 -0
  11. data/LICENSE +339 -0
  12. data/README.md +49 -0
  13. data/Rakefile +10 -0
  14. data/bin/phut +111 -0
  15. data/features/dsl.feature +26 -0
  16. data/features/dsl_link.feature +22 -0
  17. data/features/dsl_vhost.feature +37 -0
  18. data/features/dsl_vswitch.feature +45 -0
  19. data/features/phut.feature +5 -0
  20. data/features/shell.feature +40 -0
  21. data/features/step_definitions/phut_steps.rb +30 -0
  22. data/features/support/env.rb +4 -0
  23. data/features/support/hooks.rb +34 -0
  24. data/lib/phut.rb +5 -0
  25. data/lib/phut/cli.rb +61 -0
  26. data/lib/phut/configuration.rb +64 -0
  27. data/lib/phut/null_logger.rb +14 -0
  28. data/lib/phut/open_vswitch.rb +116 -0
  29. data/lib/phut/parser.rb +51 -0
  30. data/lib/phut/phost.rb +92 -0
  31. data/lib/phut/setting.rb +54 -0
  32. data/lib/phut/shell_runner.rb +9 -0
  33. data/lib/phut/syntax.rb +123 -0
  34. data/lib/phut/version.rb +4 -0
  35. data/lib/phut/virtual_link.rb +56 -0
  36. data/phut.gemspec +55 -0
  37. data/spec/phut/open_vswitch_spec.rb +21 -0
  38. data/spec/phut/parser_spec.rb +69 -0
  39. data/spec/phut_spec.rb +43 -0
  40. data/spec/spec_helper.rb +14 -0
  41. data/tasks/cucumber.rake +18 -0
  42. data/tasks/flog.rake +25 -0
  43. data/tasks/gem.rake +13 -0
  44. data/tasks/openvswitch.rake +26 -0
  45. data/tasks/reek.rake +11 -0
  46. data/tasks/relish.rake +4 -0
  47. data/tasks/rspec.rake +9 -0
  48. data/tasks/rubocop.rake +8 -0
  49. data/tasks/vhost.rake +32 -0
  50. metadata +426 -0
@@ -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
+ ```
@@ -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 }
@@ -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,5 @@
1
+ Feature: phut command
2
+
3
+ Scenario: phut --version
4
+ When I successfully run `phut --version`
5
+ Then the output should match /\d+\.\d+\.\d+/
@@ -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,4 @@
1
+ require 'aruba/cucumber'
2
+
3
+ require 'coveralls'
4
+ Coveralls.wear_merged!
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'phut/cli'
2
+ require 'phut/open_vswitch'
3
+ require 'phut/parser'
4
+ require 'phut/setting'
5
+ require 'phut/version'
@@ -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