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.
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