phut 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
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