phut 0.7.4 → 0.7.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 703fe2a4bb37ea1ca4cadf9ddc07886897dd085a
4
- data.tar.gz: 808e1dfab4de0c60cfaf70f251278388ea13a7bd
3
+ metadata.gz: 1c3b681d2cbe588388b838a3b6d9d888242a319a
4
+ data.tar.gz: 2b8602c3b6d63299c5f648256cffa40c201684c6
5
5
  SHA512:
6
- metadata.gz: ec653af4dfc965654373cb287cf34d707565df9e5658ef82ef93da10bc72123b16b81b48fff7855c0bb1512831967b27e7193cfe9efc2687c34788f00c63fd3f
7
- data.tar.gz: ff6403182467d4db9be23e1aa94bd3afdabd61bbfb5e96ce4bedd7fc90ad674e1d7434d758937524aa23d2a8c7c7d571d41efc172c80e8dadc1de594aad5dde3
6
+ metadata.gz: 6f5bca013e25718e9e209dd90e9597ea62373e914af63c5a81bd286f8aa917e7f2de6dd8cd755664cc3593545c47fd532d1e8fc844f06ad641cd97167702d8e6
7
+ data.tar.gz: 3b31460f0b73ca4ee302b4664504e78c359549213bc92900dc887e2c93766c73849faa2cccf80e14137cb467fb955191f1f6ed929754fda7faae19b7c48f40dc
@@ -1,6 +1,9 @@
1
+ sudo: false
2
+
1
3
  language: ruby
2
4
 
3
5
  bundler_args: --without development
6
+ cache: bundler
4
7
 
5
8
  script: bundle exec rake travis
6
9
 
@@ -3,6 +3,11 @@
3
3
  ## develop (unreleased)
4
4
 
5
5
 
6
+ ## 0.7.5 (12/17/2015)
7
+ ### Bugs fixed
8
+ * [#33](https://github.com/trema/phut/pull/33): Fix dump_flows in OpenFlow 1.3.
9
+
10
+
6
11
  ## 0.7.4 (11/30/2015)
7
12
  ### Bugs fixed
8
13
  * Fix NoMethodError.
data/bin/phut CHANGED
@@ -41,13 +41,17 @@ module Phut
41
41
  Phut.socket_dir = dir
42
42
  end
43
43
  Pry::Commands.block_command 'vswitch' do |dpid|
44
- Phut::OpenVswitch.new(dpid).run
44
+ if Phut::OpenVswitch.find_by(name: dpid)
45
+ fail(Phut::OpenVswitch::AlreadyRunning,
46
+ "Open vSwitch (dpid = #{dpid}) is already running!")
47
+ end
48
+ Phut::OpenVswitch.create(dpid).run
45
49
  end
46
50
  Pry::Commands.block_command 'dump_flows' do |dpid|
47
- puts Phut::OpenVswitch.new(dpid).dump_flows
51
+ puts Phut::OpenVswitch.dump_flows(dpid)
48
52
  end
49
53
  Pry::Commands.block_command 'kill' do |dpid|
50
- Phut::OpenVswitch.new(dpid).shutdown
54
+ Phut::OpenVswitch.shutdown(dpid)
51
55
  end
52
56
  pry
53
57
  end
@@ -57,11 +61,11 @@ module Phut
57
61
  arg_name 'FILE'
58
62
  command :run do |c|
59
63
  c.desc 'Location to put pid files'
60
- c.flag [:p, :pid_dir], default_value: Phut.pid_dir
64
+ c.flag [:P, :pid_dir], default_value: Phut.pid_dir
61
65
  c.desc 'Location to put log files'
62
- c.flag [:l, :log_dir], default_value: Phut.log_dir
66
+ c.flag [:L, :log_dir], default_value: Phut.log_dir
63
67
  c.desc 'Location to put socket files'
64
- c.flag [:s, :socket_dir], default_value: Phut.socket_dir
68
+ c.flag [:S, :socket_dir], default_value: Phut.socket_dir
65
69
 
66
70
  c.action do |global_options, options, args|
67
71
  stdout_logger = Logger.new($stderr).tap do |logger|
@@ -87,19 +91,20 @@ module Phut
87
91
  arg_name 'FILE'
88
92
  command :stop do |c|
89
93
  c.desc 'Location to put pid files'
90
- c.flag [:p, :pid_dir], default_value: Phut.pid_dir
91
-
94
+ c.flag [:P, :pid_dir], default_value: Phut.pid_dir
92
95
  c.desc 'Location to put log files'
93
- c.flag [:l, :log_dir], default_value: Phut.log_dir
94
-
96
+ c.flag [:L, :log_dir], default_value: Phut.log_dir
95
97
  c.desc 'Location to put socket files'
96
- c.flag [:s, :socket_dir], default_value: Phut.socket_dir
98
+ c.flag [:S, :socket_dir], default_value: Phut.socket_dir
97
99
 
98
- c.action do |global_options, _options, args|
100
+ c.action do |global_options, options, args|
99
101
  stdout_logger = Logger.new($stderr).tap do |logger|
100
102
  logger.formatter = proc { |_sev, _dtm, _name, msg| msg + "\n" }
101
103
  logger.level = global_options[:verbose] ? Logger::DEBUG : Logger::INFO
102
104
  end
105
+ Phut.pid_dir = options.fetch(:pid_dir)
106
+ Phut.log_dir = options.fetch(:log_dir)
107
+ Phut.socket_dir = options.fetch(:socket_dir)
103
108
  Phut::Parser.new(stdout_logger).parse(args[0]).stop
104
109
  end
105
110
  end
@@ -108,14 +113,14 @@ module Phut
108
113
  arg_name 'NAME'
109
114
  command :kill do |c|
110
115
  c.desc 'Location to find socket files'
111
- c.flag [:s, :socket_dir], default_value: Phut.socket_dir
116
+ c.flag [:S, :socket_dir], default_value: Phut.socket_dir
112
117
 
113
118
  c.action do |_global_options, options, args|
114
119
  help_now! if args.size != 1
115
120
  begin
116
121
  Phut::VhostDaemon.process(args[0], options[:socket_dir]).stop
117
122
  rescue
118
- Phut::OpenVswitch.new(args[0]).shutdown
123
+ Phut::OpenVswitch.shutdown(args[0])
119
124
  end
120
125
  end
121
126
  end
data/bin/vhost CHANGED
@@ -56,7 +56,6 @@ module Phut
56
56
  command :stop do |c|
57
57
  desc 'Host name'
58
58
  c.flag [:n, :name]
59
-
60
59
  c.desc 'Location to put socket files'
61
60
  c.flag [:S, :socket_dir], default_value: Phut.socket_dir
62
61
 
@@ -1,26 +1,26 @@
1
1
  Feature: DSL parser
2
2
  Scenario: name conflict (vsiwtch and vswitch)
3
3
  Given a file named "network.conf" with:
4
- """
5
- vswitch { dpid 0xabc }
6
- vswitch { dpid 0xabc }
7
- """
4
+ """ruby
5
+ vswitch { dpid 0xabc }
6
+ vswitch { dpid 0xabc }
7
+ """
8
8
  When I do phut run "network.conf"
9
9
  Then the exit status should not be 0
10
10
  And the stderr should contain:
11
- """
12
- The name 0xabc conflicts with vswitch (name = 0xabc, dpid = 0xabc).
13
- """
11
+ """
12
+ The name 0xabc conflicts with vswitch (name = 0xabc, dpid = 0xabc).
13
+ """
14
14
 
15
15
  Scenario: name conflict (vhost and vhost)
16
16
  Given a file named "network.conf" with:
17
- """
18
- vhost { ip '192.168.0.1' }
19
- vhost { ip '192.168.0.1' }
20
- """
17
+ """ruby
18
+ vhost { ip '192.168.0.1' }
19
+ vhost { ip '192.168.0.1' }
20
+ """
21
21
  When I do phut run "network.conf"
22
22
  Then the exit status should not be 0
23
23
  And the stderr should contain:
24
- """
25
- The name 192.168.0.1 conflicts with vhost (name = 192.168.0.1, IP address = 192.168.0.1).
26
- """
24
+ """
25
+ The name 192.168.0.1 conflicts with vhost (name = 192.168.0.1, IP address = 192.168.0.1).
26
+ """
@@ -1,20 +1,20 @@
1
1
  Feature: Shell
2
2
  Background:
3
- Given I run `phut -v` interactively
4
- And I wait for stdout to contain "phut>"
3
+ Given I wait 5 seconds for a command to start up
4
+ And I run `phut -v` interactively
5
5
 
6
6
  @shell
7
7
  Scenario: vswitch NUMBER
8
8
  When I type "vswitch 0xabc"
9
9
  And I type "quit"
10
- And I run `sleep 1`
10
+ And sleep 1
11
11
  Then a vswitch named "0xabc" should be running
12
12
 
13
13
  @shell
14
14
  Scenario: vswitch STRING
15
15
  When I type "vswitch '0xabc'"
16
16
  And I type "quit"
17
- And I run `sleep 1`
17
+ And sleep 1
18
18
  Then a vswitch named "0xabc" should be running
19
19
 
20
20
  @shell
@@ -22,7 +22,7 @@ Feature: Shell
22
22
  Given I type "vswitch '0xabc'"
23
23
  When I type "dump_flows 0xabc"
24
24
  And I type "quit"
25
- And I run `sleep 1`
25
+ And sleep 1
26
26
  Then the output should contain "NXST_FLOW reply"
27
27
 
28
28
  @shell
@@ -37,7 +37,7 @@ Feature: Shell
37
37
  And I type "vswitch 0xabc"
38
38
  When I type "kill 0xabc"
39
39
  And I type "quit"
40
- And I run `sleep 1`
40
+ And sleep 1
41
41
  Then a vswitch named "0xabc" should not be running
42
42
 
43
43
  @shell
@@ -1,18 +1,22 @@
1
1
  require 'phut'
2
2
 
3
- When(/^I do phut run "(.*?)"$/) do |file_name|
4
- @config_file = file_name
3
+ When(/^I do phut run "(.*?)"$/) do |config_file|
4
+ @config_file = config_file
5
5
  cd('.') do
6
- run_opts = "-p #{@pid_dir} -l #{@log_dir} -s #{@socket_dir}"
6
+ run_opts = "-P #{@pid_dir} -L #{@log_dir} -S #{@socket_dir}"
7
7
  step %(I run `phut -v run #{run_opts} #{@config_file}`)
8
8
  end
9
9
  end
10
10
 
11
11
  When(/^I do phut kill "(.*?)"$/) do |name|
12
- run_opts = "-s #{@socket_dir}"
12
+ run_opts = "-S #{@socket_dir}"
13
13
  step %(I successfully run `phut -v kill #{run_opts} #{name}`)
14
14
  end
15
15
 
16
+ When(/^sleep (\d+)$/) do |time|
17
+ sleep time.to_i
18
+ end
19
+
16
20
  Then(/^a vswitch named "(.*?)" should be running$/) do |name|
17
21
  expect(system("sudo ovs-vsctl br-exists br#{name}")).to be_truthy
18
22
  end
@@ -12,11 +12,13 @@ Before('@sudo') do
12
12
  end
13
13
 
14
14
  After('@sudo') do
15
- cd('.') do
16
- Phut.pid_dir = @pid_dir
17
- Phut.log_dir = @log_dir
18
- Phut.socket_dir = @socket_dir
19
- Phut::Parser.new.parse(@config_file).stop
15
+ Aruba.configure do |config|
16
+ Dir.chdir(config.working_directory) do
17
+ Phut.pid_dir = @pid_dir
18
+ Phut.log_dir = @log_dir
19
+ Phut.socket_dir = @socket_dir
20
+ Phut::Parser.new.parse(@config_file).stop
21
+ end
20
22
  end
21
23
  end
22
24
 
@@ -4,19 +4,25 @@ require 'phut/open_vswitch'
4
4
  module Phut
5
5
  # Parsed DSL data.
6
6
  class Configuration
7
- def initialize(logger = NullLogger.new)
8
- @all = {}
9
- @links = []
10
- @logger = logger
7
+ def initialize(&block)
8
+ OpenVswitch.all.clear
9
+ Vhost.all.clear
10
+ Netns.all.clear
11
+ VirtualLink.all.clear
12
+ block.call self
11
13
  end
12
14
 
13
15
  # rubocop:disable MethodLength
14
16
  def fetch(key)
15
17
  case key
16
18
  when String
17
- @all.fetch(key)
19
+ [OpenVswitch, Vhost, Netns].each do |each|
20
+ found = each.find_by(name: key)
21
+ return found if found
22
+ end
23
+ fail "Invalid key: #{key.inspect}"
18
24
  when Array
19
- @links.each do |each|
25
+ VirtualLink.each do |each|
20
26
  return each if [each.name_a, each.name_b].sort == key.sort
21
27
  end
22
28
  fail "link #{key.join ' '} not found."
@@ -33,62 +39,22 @@ module Phut
33
39
  self
34
40
  end
35
41
 
36
- def vswitches
37
- @all.values.select { |each| each.is_a? OpenVswitch }
38
- end
39
-
40
- def vhosts
41
- @all.values.select { |each| each.is_a? Vhost }
42
- end
43
-
44
- def netnss
45
- @all.values.select { |each| each.is_a? Netns }
46
- end
47
-
48
42
  def run
49
- @links.each(&:run)
50
- vhosts.each { |each| each.run vhosts }
51
- netnss.each(&:run)
52
- vswitches.each(&:run)
43
+ [VirtualLink, Vhost, Netns, OpenVswitch].each do |klass|
44
+ klass.each(&:run)
45
+ end
53
46
  end
54
47
 
55
48
  def stop
56
- vswitches.each(&:maybe_stop)
57
- vhosts.each(&:maybe_stop)
58
- netnss.each(&:maybe_stop)
59
- @links.each(&:maybe_stop)
60
- end
61
-
62
- def add_vswitch(name, attrs)
63
- check_name_conflict name
64
- @all[name] =
65
- OpenVswitch.new(attrs[:dpid], attrs[:port_number], name, @logger)
66
- end
67
-
68
- def add_vhost(name, attrs)
69
- check_name_conflict name
70
- @all[name] =
71
- Vhost.new(attrs[:ip], attrs[:mac], attrs[:promisc], name, @logger)
72
- end
73
-
74
- def add_netns(name, attrs)
75
- check_name_conflict name
76
- @all[name] = Netns.new(attrs, name, @logger)
77
- end
78
-
79
- def add_link(name_a, name_b)
80
- @links << VirtualLink.new(name_a, name_b, @logger)
49
+ [OpenVswitch, Vhost, Netns, VirtualLink].each do |klass|
50
+ klass.each(&:stop)
51
+ end
81
52
  end
82
53
 
83
54
  private
84
55
 
85
- def check_name_conflict(name)
86
- conflict = @all[name]
87
- fail "The name #{name} conflicts with #{conflict}." if conflict
88
- end
89
-
90
56
  def update_vswitch_ports
91
- @links.each do |each|
57
+ VirtualLink.each do |each|
92
58
  maybe_connect_link_to_vswitch each
93
59
  end
94
60
  end
@@ -100,23 +66,23 @@ module Phut
100
66
  end
101
67
 
102
68
  def vswitches_connected_to(link)
103
- vswitches.select { |each| link.connect_to?(each) }
69
+ OpenVswitch.select { |each| link.connect_to?(each) }
104
70
  end
105
71
 
106
72
  def update_vhost_interfaces
107
- vhosts.each do |each|
73
+ Vhost.each do |each|
108
74
  each.network_device = find_network_device(each)
109
75
  end
110
76
  end
111
77
 
112
78
  def update_netns_interfaces
113
- netnss.each do |each|
79
+ Netns.each do |each|
114
80
  each.network_device = find_network_device(each)
115
81
  end
116
82
  end
117
83
 
118
84
  def find_network_device(vhost)
119
- @links.each do |each|
85
+ VirtualLink.each do |each|
120
86
  device = each.find_network_device(vhost)
121
87
  return device if device
122
88
  end
@@ -1,15 +1,26 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
1
2
  require 'phut/null_logger'
2
3
  require 'phut/shell_runner'
3
4
 
4
5
  module Phut
5
6
  # `ip netns ...` command runner
6
7
  class Netns
8
+ cattr_accessor(:all, instance_reader: false) { [] }
9
+
10
+ def self.create(options, name, logger = NullLogger.new)
11
+ new(options, name, logger).tap { |netns| all << netns }
12
+ end
13
+
14
+ def self.each(&block)
15
+ all.each(&block)
16
+ end
17
+
7
18
  include ShellRunner
8
19
 
9
20
  attr_reader :name
10
21
  attr_accessor :network_device
11
22
 
12
- def initialize(options, name, logger = NullLogger.new)
23
+ def initialize(options, name, logger)
13
24
  @name = name
14
25
  @options = options
15
26
  @logger = logger
@@ -26,7 +37,7 @@ module Phut
26
37
  end
27
38
  # rubocop:enable AbcSize
28
39
 
29
- def maybe_stop
40
+ def stop
30
41
  sh "sudo ip netns delete #{name}"
31
42
  end
32
43
 
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
1
2
  require 'pio'
2
3
  require 'phut/null_logger'
3
4
  require 'phut/setting'
@@ -5,7 +6,43 @@ require 'phut/shell_runner'
5
6
 
6
7
  module Phut
7
8
  # Open vSwitch controller.
9
+ # rubocop:disable ClassLength
8
10
  class OpenVswitch
11
+ cattr_accessor(:all, instance_reader: false) { [] }
12
+
13
+ def self.create(dpid, port_number = 6653, name = nil,
14
+ logger = NullLogger.new)
15
+ new(dpid, port_number, name, logger).tap do |vswitch|
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
21
+
22
+ def self.dump_flows(dpid, port_number = 6653, name = nil,
23
+ logger = NullLogger.new)
24
+ OpenVswitch.new(dpid, port_number, name, logger).dump_flows
25
+ end
26
+
27
+ def self.shutdown(dpid, port_number = 6653, name = nil,
28
+ logger = NullLogger.new)
29
+ OpenVswitch.new(dpid, port_number, name, logger).stop!
30
+ end
31
+
32
+ def self.find_by(queries)
33
+ queries.inject(all) do |memo, (attr, value)|
34
+ memo.find_all { |vswitch| vswitch.__send__(attr) == value }
35
+ end.first
36
+ end
37
+
38
+ def self.each(&block)
39
+ all.each(&block)
40
+ end
41
+
42
+ def self.select(&block)
43
+ all.select(&block)
44
+ end
45
+
9
46
  class AlreadyRunning < StandardError; end
10
47
 
11
48
  include ShellRunner
@@ -14,8 +51,7 @@ module Phut
14
51
  alias_method :datapath_id, :dpid
15
52
  attr_reader :network_devices
16
53
 
17
- def initialize(dpid, port_number = 6653,
18
- name = nil, logger = NullLogger.new)
54
+ def initialize(dpid, port_number, name, logger)
19
55
  @dpid = dpid
20
56
  @port_number = port_number
21
57
  @name = name
@@ -54,15 +90,15 @@ module Phut
54
90
  # rubocop:enable AbcSize
55
91
 
56
92
  def stop
57
- fail "Open vSwitch (dpid = #{@dpid}) is not running!" unless running?
58
- sh "sudo ovs-vsctl del-br #{bridge_name}"
93
+ return unless running?
94
+ stop!
59
95
  end
60
- alias_method :shutdown, :stop
61
96
 
62
- def maybe_stop
63
- return unless running?
64
- stop
97
+ def stop!
98
+ fail "Open vSwitch (dpid = #{@dpid}) is not running!" unless running?
99
+ sh "sudo ovs-vsctl del-br #{bridge_name}"
65
100
  end
101
+ alias_method :shutdown, :stop!
66
102
 
67
103
  def bring_port_up(port_number)
68
104
  sh "sudo ovs-ofctl mod-port #{bridge_name} #{port_number} up"
@@ -73,7 +109,7 @@ module Phut
73
109
  end
74
110
 
75
111
  def dump_flows
76
- `sudo ovs-ofctl dump-flows #{bridge_name}`
112
+ `sudo ovs-ofctl dump-flows #{bridge_name} -O #{Pio::OpenFlow.version}`
77
113
  end
78
114
 
79
115
  def running?
@@ -105,4 +141,5 @@ module Phut
105
141
  '0' * (16 - hex.length) + hex
106
142
  end
107
143
  end
144
+ # rubocop:enable ClassLength
108
145
  end
@@ -10,9 +10,9 @@ module Phut
10
10
  end
11
11
 
12
12
  def parse(file)
13
- Configuration.new(@logger).tap do |configuration|
14
- Syntax.new(configuration).instance_eval IO.read(file), file
15
- configuration.update_connections
13
+ Configuration.new do |config|
14
+ Syntax.new(config, @logger).instance_eval IO.read(file), file
15
+ config.update_connections
16
16
  end
17
17
  end
18
18
  end
@@ -1,128 +1,36 @@
1
1
  require 'phut/netns'
2
+ require 'phut/syntax/netns_directive'
3
+ require 'phut/syntax/vhost_directive'
4
+ require 'phut/syntax/vswitch_directive'
2
5
  require 'phut/vhost'
3
6
  require 'phut/virtual_link'
4
7
 
5
8
  module Phut
6
9
  # DSL syntax definitions.
7
10
  class Syntax
8
- # The 'vswitch(name) { ...attributes... }' directive.
9
- class VswitchDirective
10
- def initialize(alias_name, &block)
11
- @attributes = { name: alias_name, port_number: 6653 }
12
- instance_eval(&block)
13
- end
14
-
15
- def dpid(value)
16
- dpid = if value.is_a?(String) && /^0x/=~ value
17
- value.hex
18
- else
19
- value.to_i
20
- end
21
- @attributes[:dpid] = dpid
22
- @attributes[:name] ||= format('%#x', @attributes[:dpid])
23
- end
24
- alias_method :datapath_id, :dpid
25
-
26
- def port(port_no)
27
- @attributes[:port_number] = port_no
28
- end
29
-
30
- def [](key)
31
- @attributes[key]
32
- end
33
- end
34
-
35
- # The 'vhost(name) { ...attributes... }' directive.
36
- class VhostDirective
37
- # Generates an unused IP address
38
- class UnusedIpAddress
39
- def initialize
40
- @index = 0
41
- end
42
-
43
- def generate
44
- @index += 1
45
- "192.168.0.#{@index}"
46
- end
47
- end
48
-
49
- UnusedIpAddressSingleton = UnusedIpAddress.new
50
-
51
- def initialize(alias_name, &block)
52
- @attributes =
53
- { name: alias_name, mac: Pio::Mac.new(rand(0xffffffffffff + 1)) }
54
- if block
55
- instance_eval(&block) if block
56
- else
57
- @attributes[:ip] = UnusedIpAddressSingleton.generate
58
- end
59
- end
60
-
61
- def ip(value)
62
- @attributes[:ip] = value
63
- @attributes[:name] ||= value
64
- end
65
-
66
- def mac(value)
67
- @attributes[:mac] = value
68
- end
69
-
70
- def promisc(on_off)
71
- @attributes[:promisc] = on_off
72
- end
73
-
74
- def [](key)
75
- @attributes[key]
76
- end
77
- end
78
-
79
- # The 'netns(name) { ...attributes... }' directive.
80
- class NetnsDirective
81
- def initialize(alias_name, &block)
82
- @attributes = { name: alias_name }
83
- instance_eval(&block)
84
- end
85
-
86
- def ip(value)
87
- @attributes[:ip] = value
88
- @attributes[:name] ||= value
89
- end
90
-
91
- def netmask(value)
92
- @attributes[:netmask] = value
93
- end
94
-
95
- def route(options)
96
- @attributes[:net] = options.fetch(:net)
97
- @attributes[:gateway] = options.fetch(:gateway)
98
- end
99
-
100
- def [](key)
101
- @attributes[key]
102
- end
103
- end
104
-
105
- def initialize(config)
11
+ def initialize(config, logger)
106
12
  @config = config
13
+ @logger = logger
107
14
  end
108
15
 
109
16
  def vswitch(alias_name = nil, &block)
110
17
  attrs = VswitchDirective.new(alias_name, &block)
111
- @config.add_vswitch attrs[:name], attrs
18
+ OpenVswitch.create(attrs[:dpid], attrs[:port], attrs[:name], @logger)
112
19
  end
113
20
 
114
21
  def vhost(alias_name = nil, &block)
115
22
  attrs = VhostDirective.new(alias_name, &block)
116
- @config.add_vhost attrs[:name], attrs
23
+ Vhost.create(attrs[:ip], attrs[:mac], attrs[:promisc], attrs[:name],
24
+ @logger)
117
25
  end
118
26
 
119
27
  def netns(name, &block)
120
28
  attrs = NetnsDirective.new(name, &block)
121
- @config.add_netns attrs[:name], attrs
29
+ Netns.create(attrs, attrs[:name], @logger)
122
30
  end
123
31
 
124
32
  def link(name_a, name_b)
125
- @config.add_link name_a, name_b
33
+ VirtualLink.create(name_a, name_b, @logger)
126
34
  end
127
35
  end
128
36
  end
@@ -0,0 +1,16 @@
1
+ module Phut
2
+ class Syntax
3
+ # Common DSL directive
4
+ class Directive
5
+ def self.attribute(name)
6
+ define_method(name) do |value|
7
+ @attributes[name] = value
8
+ end
9
+ end
10
+
11
+ def [](key)
12
+ @attributes.fetch(key)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ require 'phut/syntax/directive'
2
+
3
+ module Phut
4
+ class Syntax
5
+ # The 'netns(name) { ...attributes... }' directive.
6
+ class NetnsDirective < Directive
7
+ attribute :netmask
8
+
9
+ def initialize(alias_name, &block)
10
+ @attributes = { name: alias_name }
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def ip(value)
15
+ @attributes[:ip] = value
16
+ @attributes[:name] ||= value
17
+ end
18
+
19
+ def route(options)
20
+ @attributes[:net] = options.fetch(:net)
21
+ @attributes[:gateway] = options.fetch(:gateway)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'phut/syntax/directive'
2
+
3
+ module Phut
4
+ class Syntax
5
+ # The 'vhost(name) { ...attributes... }' directive.
6
+ class VhostDirective < Directive
7
+ # Generates an unused IP address
8
+ class UnusedIpAddress
9
+ def initialize
10
+ @index = 0
11
+ end
12
+
13
+ def generate
14
+ @index += 1
15
+ "192.168.0.#{@index}"
16
+ end
17
+ end
18
+
19
+ UnusedIpAddressSingleton = UnusedIpAddress.new
20
+
21
+ attribute :mac
22
+ attribute :promisc
23
+
24
+ def initialize(alias_name, &block)
25
+ @attributes =
26
+ { name: alias_name,
27
+ mac: Pio::Mac.new(rand(0xffffffffffff + 1)),
28
+ promisc: false }
29
+ if block
30
+ instance_eval(&block)
31
+ else
32
+ @attributes[:ip] = UnusedIpAddressSingleton.generate
33
+ end
34
+ end
35
+
36
+ def ip(value)
37
+ @attributes[:ip] = value
38
+ @attributes[:name] ||= value
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ require 'phut/syntax/directive'
2
+
3
+ module Phut
4
+ class Syntax
5
+ # The 'vswitch(name) { ...attributes... }' directive.
6
+ class VswitchDirective < Directive
7
+ attribute :port
8
+
9
+ def initialize(alias_name, &block)
10
+ @attributes = { name: alias_name, port: 6653 }
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def dpid(value)
15
+ dpid = if value.is_a?(String) && /^0x/=~ value
16
+ value.hex
17
+ else
18
+ value.to_i
19
+ end
20
+ @attributes[:dpid] = dpid
21
+ @attributes[:name] ||= format('%#x', @attributes[:dpid])
22
+ end
23
+ alias_method :datapath_id, :dpid
24
+ end
25
+ end
26
+ end
@@ -1,4 +1,4 @@
1
1
  # Base module.
2
2
  module Phut
3
- VERSION = '0.7.4'
3
+ VERSION = '0.7.5'
4
4
  end
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
1
2
  require 'phut/null_logger'
2
3
  require 'phut/setting'
3
4
  require 'phut/shell_runner'
@@ -6,14 +7,35 @@ require 'pio/mac'
6
7
  module Phut
7
8
  # An interface class to vhost emulation utility program.
8
9
  class Vhost
10
+ cattr_accessor(:all, instance_reader: false) { [] }
11
+
12
+ def self.create(ip_address, mac_address, promisc, name = nil,
13
+ logger = NullLogger.new)
14
+ new(ip_address, mac_address, promisc, name, logger).tap do |vhost|
15
+ conflict = find_by(name: vhost.name)
16
+ fail "The name #{vhost.name} conflicts with #{conflict}." if conflict
17
+ all << vhost
18
+ end
19
+ end
20
+
21
+ # This method smells of :reek:NestedIterators but ignores them
22
+ def self.find_by(queries)
23
+ queries.inject(all) do |memo, (attr, value)|
24
+ memo.find_all { |vhost| vhost.__send__(attr) == value }
25
+ end.first
26
+ end
27
+
28
+ def self.each(&block)
29
+ all.each(&block)
30
+ end
31
+
9
32
  include ShellRunner
10
33
 
11
34
  attr_reader :ip_address
12
35
  attr_reader :mac_address
13
36
  attr_accessor :network_device
14
37
 
15
- def initialize(ip_address, mac_address, promisc,
16
- name = nil, logger = NullLogger.new)
38
+ def initialize(ip_address, mac_address, promisc, name, logger)
17
39
  @ip_address = ip_address
18
40
  @promisc = promisc
19
41
  @name = name
@@ -29,7 +51,7 @@ module Phut
29
51
  "vhost (name = #{name}, IP address = #{@ip_address})"
30
52
  end
31
53
 
32
- def run(all_hosts = [])
54
+ def run(all_hosts = Vhost.all)
33
55
  @all_hosts ||= all_hosts
34
56
  if ENV['rvm_path']
35
57
  sh "rvmsudo vhost run #{run_options}"
@@ -40,13 +62,13 @@ module Phut
40
62
  end
41
63
 
42
64
  def stop
43
- fail "vhost (name = #{name}) is not running!" unless running?
44
- sh "vhost stop -n #{name} -s #{Phut.socket_dir}"
65
+ return unless running?
66
+ stop!
45
67
  end
46
68
 
47
- def maybe_stop
48
- return unless running?
49
- stop
69
+ def stop!
70
+ fail "vhost (name = #{name}) is not running!" unless running?
71
+ sh "vhost stop -n #{name} -S #{Phut.socket_dir}"
50
72
  end
51
73
 
52
74
  def running?
@@ -1,9 +1,20 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
1
2
  require 'phut/null_logger'
2
3
  require 'phut/shell_runner'
3
4
 
4
5
  module Phut
5
6
  # Network virtual link.
6
7
  class VirtualLink
8
+ cattr_accessor(:all, instance_reader: false) { [] }
9
+
10
+ def self.create(name_a, name_b, logger = NullLogger.new)
11
+ new(name_a, name_b, logger).tap { |vlink| all << vlink }
12
+ end
13
+
14
+ def self.each(&block)
15
+ all.each(&block)
16
+ end
17
+
7
18
  # Creates a valid network device name.
8
19
  class NetworkDeviceName
9
20
  attr_reader :name
@@ -35,7 +46,7 @@ module Phut
35
46
  attr_reader :device_a
36
47
  attr_reader :device_b
37
48
 
38
- def initialize(name_a, name_b, logger = NullLogger.new)
49
+ def initialize(name_a, name_b, logger)
39
50
  fail if name_a == name_b
40
51
  @name_a = name_a
41
52
  @name_b = name_b
@@ -44,6 +55,13 @@ module Phut
44
55
  @logger = logger
45
56
  end
46
57
 
58
+ def ==(other)
59
+ @name_a == other.name_a &&
60
+ @name_b == other.name_b &&
61
+ @device_a == other.device_a &&
62
+ @device_b == other.device_b
63
+ end
64
+
47
65
  def run
48
66
  stop if up?
49
67
  add
@@ -51,16 +69,16 @@ module Phut
51
69
  end
52
70
 
53
71
  def stop
72
+ return unless up?
73
+ stop!
74
+ end
75
+
76
+ def stop!
54
77
  sh "sudo ip link delete #{@device_a}"
55
78
  rescue
56
79
  raise "link #{@name_a} #{@name_b} does not exist!"
57
80
  end
58
81
 
59
- def maybe_stop
60
- return unless up?
61
- stop
62
- end
63
-
64
82
  def up?
65
83
  /^#{@device_a}\s+Link encap:Ethernet/ =~ `LANG=C ifconfig -a` || false
66
84
  end
@@ -23,7 +23,8 @@ Gem::Specification.new do |gem|
23
23
 
24
24
  gem.required_ruby_version = '>= 2.0.0'
25
25
 
26
- gem.add_dependency 'gli', '~> 2.13.2'
26
+ gem.add_dependency 'activesupport', '~> 4.2', '>= 4.2.5'
27
+ gem.add_dependency 'gli', '~> 2.13.4'
27
28
  gem.add_dependency 'pio', '~> 0.30.0'
28
29
  gem.add_dependency 'pry', '~> 0.10.3'
29
30
 
@@ -32,23 +33,22 @@ Gem::Specification.new do |gem|
32
33
  gem.add_development_dependency 'yard', '~> 0.8.7.6'
33
34
 
34
35
  # Development
35
- gem.add_development_dependency 'byebug', '~> 7.0.0'
36
36
  gem.add_development_dependency 'guard', '~> 2.13.0'
37
37
  gem.add_development_dependency 'guard-bundler', '~> 2.1.0'
38
- gem.add_development_dependency 'guard-cucumber', '~> 1.6.0'
38
+ gem.add_development_dependency 'guard-cucumber', '~> 2.0.0'
39
39
  gem.add_development_dependency 'guard-rspec', '~> 4.6.4'
40
40
  gem.add_development_dependency 'guard-rubocop', '~> 1.2.0'
41
41
 
42
42
  # Test
43
- gem.add_development_dependency 'aruba', '~> 0.8.1'
43
+ gem.add_development_dependency 'aruba', '~> 0.11.2'
44
44
  gem.add_development_dependency 'codeclimate-test-reporter'
45
- gem.add_development_dependency 'coveralls', '~> 0.8.3'
45
+ gem.add_development_dependency 'coveralls', '~> 0.8.10'
46
46
  gem.add_development_dependency 'cucumber', '~> 2.1.0'
47
47
  gem.add_development_dependency 'flay', '~> 2.6.1'
48
48
  gem.add_development_dependency 'flog', '~> 4.3.2'
49
49
  gem.add_development_dependency 'rake'
50
- gem.add_development_dependency 'reek', '~> 3.6.0'
51
- gem.add_development_dependency 'rspec', '~> 3.3.0'
50
+ gem.add_development_dependency 'reek', '~> 3.7.1'
51
+ gem.add_development_dependency 'rspec', '~> 3.4.0'
52
52
  gem.add_development_dependency 'rspec-given', '~> 3.7.1'
53
- gem.add_development_dependency 'rubocop', '~> 0.34.2'
53
+ gem.add_development_dependency 'rubocop', '~> 0.35.1'
54
54
  end
@@ -12,7 +12,6 @@ describe Phut::Parser do
12
12
  When(:string) { "vswitch { dpid '0xabc' }" }
13
13
 
14
14
  describe '#vswitch' do
15
- Then { configuration.vswitches.size == 1 }
16
15
  Then { configuration.fetch('0xabc').datapath_id == 0xabc }
17
16
  Then { configuration.fetch('0xabc').dpid == 0xabc }
18
17
  end
@@ -22,7 +21,6 @@ describe Phut::Parser do
22
21
  When(:string) { "vswitch { datapath_id '0xabc' }" }
23
22
 
24
23
  describe '#vswitch' do
25
- Then { configuration.vswitches.size == 1 }
26
24
  Then { configuration.fetch('0xabc').dpid == 0xabc }
27
25
  Then { configuration.fetch('0xabc').datapath_id == 0xabc }
28
26
  end
@@ -32,7 +30,6 @@ describe Phut::Parser do
32
30
  When(:string) { "vswitch('my_controller') { dpid '0xabc' }" }
33
31
 
34
32
  describe '#vswitch' do
35
- Then { configuration.vswitches.size == 1 }
36
33
  Then { configuration.fetch('my_controller').dpid == 0xabc }
37
34
  Then { configuration.fetch('my_controller').datapath_id == 0xabc }
38
35
  end
@@ -48,7 +45,6 @@ CONFIGURATION
48
45
  end
49
46
 
50
47
  describe '#vhost' do
51
- Then { configuration.vhosts.size == 2 }
52
48
  Then { configuration.fetch('192.168.0.1').ip_address == '192.168.0.1' }
53
49
  end
54
50
  end
@@ -63,7 +59,6 @@ CONFIGURATION
63
59
  end
64
60
 
65
61
  describe '#vhost' do
66
- Then { configuration.vhosts.size == 2 }
67
62
  Then { configuration.fetch('host1').ip_address == '192.168.0.1' }
68
63
  end
69
64
  end
@@ -8,7 +8,7 @@ if ENV['CODECLIMATE_REPO_TOKEN']
8
8
  formatters << CodeClimate::TestReporter::Formatter
9
9
  end
10
10
 
11
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
11
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
12
12
  SimpleCov.start { add_filter '/vendor/' }
13
13
 
14
14
  require 'rspec/given'
metadata CHANGED
@@ -1,29 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yasuhito Takamiya
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-30 00:00:00.000000000 Z
11
+ date: 2015-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.5
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 4.2.5
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: gli
15
35
  requirement: !ruby/object:Gem::Requirement
16
36
  requirements:
17
37
  - - "~>"
18
38
  - !ruby/object:Gem::Version
19
- version: 2.13.2
39
+ version: 2.13.4
20
40
  type: :runtime
21
41
  prerelease: false
22
42
  version_requirements: !ruby/object:Gem::Requirement
23
43
  requirements:
24
44
  - - "~>"
25
45
  - !ruby/object:Gem::Version
26
- version: 2.13.2
46
+ version: 2.13.4
27
47
  - !ruby/object:Gem::Dependency
28
48
  name: pio
29
49
  requirement: !ruby/object:Gem::Requirement
@@ -80,20 +100,6 @@ dependencies:
80
100
  - - "~>"
81
101
  - !ruby/object:Gem::Version
82
102
  version: 0.8.7.6
83
- - !ruby/object:Gem::Dependency
84
- name: byebug
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 7.0.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 7.0.0
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: guard
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +134,14 @@ dependencies:
128
134
  requirements:
129
135
  - - "~>"
130
136
  - !ruby/object:Gem::Version
131
- version: 1.6.0
137
+ version: 2.0.0
132
138
  type: :development
133
139
  prerelease: false
134
140
  version_requirements: !ruby/object:Gem::Requirement
135
141
  requirements:
136
142
  - - "~>"
137
143
  - !ruby/object:Gem::Version
138
- version: 1.6.0
144
+ version: 2.0.0
139
145
  - !ruby/object:Gem::Dependency
140
146
  name: guard-rspec
141
147
  requirement: !ruby/object:Gem::Requirement
@@ -170,14 +176,14 @@ dependencies:
170
176
  requirements:
171
177
  - - "~>"
172
178
  - !ruby/object:Gem::Version
173
- version: 0.8.1
179
+ version: 0.11.2
174
180
  type: :development
175
181
  prerelease: false
176
182
  version_requirements: !ruby/object:Gem::Requirement
177
183
  requirements:
178
184
  - - "~>"
179
185
  - !ruby/object:Gem::Version
180
- version: 0.8.1
186
+ version: 0.11.2
181
187
  - !ruby/object:Gem::Dependency
182
188
  name: codeclimate-test-reporter
183
189
  requirement: !ruby/object:Gem::Requirement
@@ -198,14 +204,14 @@ dependencies:
198
204
  requirements:
199
205
  - - "~>"
200
206
  - !ruby/object:Gem::Version
201
- version: 0.8.3
207
+ version: 0.8.10
202
208
  type: :development
203
209
  prerelease: false
204
210
  version_requirements: !ruby/object:Gem::Requirement
205
211
  requirements:
206
212
  - - "~>"
207
213
  - !ruby/object:Gem::Version
208
- version: 0.8.3
214
+ version: 0.8.10
209
215
  - !ruby/object:Gem::Dependency
210
216
  name: cucumber
211
217
  requirement: !ruby/object:Gem::Requirement
@@ -268,28 +274,28 @@ dependencies:
268
274
  requirements:
269
275
  - - "~>"
270
276
  - !ruby/object:Gem::Version
271
- version: 3.6.0
277
+ version: 3.7.1
272
278
  type: :development
273
279
  prerelease: false
274
280
  version_requirements: !ruby/object:Gem::Requirement
275
281
  requirements:
276
282
  - - "~>"
277
283
  - !ruby/object:Gem::Version
278
- version: 3.6.0
284
+ version: 3.7.1
279
285
  - !ruby/object:Gem::Dependency
280
286
  name: rspec
281
287
  requirement: !ruby/object:Gem::Requirement
282
288
  requirements:
283
289
  - - "~>"
284
290
  - !ruby/object:Gem::Version
285
- version: 3.3.0
291
+ version: 3.4.0
286
292
  type: :development
287
293
  prerelease: false
288
294
  version_requirements: !ruby/object:Gem::Requirement
289
295
  requirements:
290
296
  - - "~>"
291
297
  - !ruby/object:Gem::Version
292
- version: 3.3.0
298
+ version: 3.4.0
293
299
  - !ruby/object:Gem::Dependency
294
300
  name: rspec-given
295
301
  requirement: !ruby/object:Gem::Requirement
@@ -310,14 +316,14 @@ dependencies:
310
316
  requirements:
311
317
  - - "~>"
312
318
  - !ruby/object:Gem::Version
313
- version: 0.34.2
319
+ version: 0.35.1
314
320
  type: :development
315
321
  prerelease: false
316
322
  version_requirements: !ruby/object:Gem::Requirement
317
323
  requirements:
318
324
  - - "~>"
319
325
  - !ruby/object:Gem::Version
320
- version: 0.34.2
326
+ version: 0.35.1
321
327
  description: A simple network emulator with capabilities similar to mininet.
322
328
  email:
323
329
  - yasuhito@gmail.com
@@ -332,7 +338,6 @@ files:
332
338
  - ".hound.yml"
333
339
  - ".rspec"
334
340
  - ".rubocop.yml"
335
- - ".ruby-version"
336
341
  - ".travis.yml"
337
342
  - CHANGELOG.md
338
343
  - CONTRIBUTING.md
@@ -363,12 +368,15 @@ files:
363
368
  - lib/phut/setting.rb
364
369
  - lib/phut/shell_runner.rb
365
370
  - lib/phut/syntax.rb
371
+ - lib/phut/syntax/directive.rb
372
+ - lib/phut/syntax/netns_directive.rb
373
+ - lib/phut/syntax/vhost_directive.rb
374
+ - lib/phut/syntax/vswitch_directive.rb
366
375
  - lib/phut/version.rb
367
376
  - lib/phut/vhost.rb
368
377
  - lib/phut/vhost_daemon.rb
369
378
  - lib/phut/virtual_link.rb
370
379
  - phut.gemspec
371
- - spec/phut/open_vswitch_spec.rb
372
380
  - spec/phut/parser_spec.rb
373
381
  - spec/phut_spec.rb
374
382
  - spec/spec_helper.rb
@@ -1 +0,0 @@
1
- 2.0.0
@@ -1,22 +0,0 @@
1
- require 'phut/open_vswitch'
2
-
3
- describe Phut::OpenVswitch do
4
- describe '.new' do
5
- context 'with 0xabc' do
6
- Given(:vswitch) do
7
- Phut::OpenVswitch.new(0xabc).tap do |vswitch|
8
- allow(vswitch).to receive(:running?).and_return(false, true)
9
- allow(vswitch).to receive(:sh)
10
- end
11
- end
12
-
13
- describe '#run' do
14
- When { vswitch.run }
15
- Then do
16
- expect(vswitch).to(have_received(:sh).
17
- with('sudo ovs-vsctl add-br br0xabc'))
18
- end
19
- end
20
- end
21
- end
22
- end