openflow-controller 0.1.8 → 0.1.9

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: 99acd8cee866cea479b4d67a120f30ff34a8d37c
4
- data.tar.gz: 235d1054c24087989ab7e8376cd87ce2888ab358
3
+ metadata.gz: 21f97d89fd5cca91d616a17cc08cb2923bc04752
4
+ data.tar.gz: f1025ee07213791f7d3472d8b76026a0ac9ba00f
5
5
  SHA512:
6
- metadata.gz: 64908187a2d746e6847e8f3c0d0fe1c0da44631f419573dee622822a08474a053c5ba2ca2a01b1b4384d11ad05a499f4bd3b3fb82bfad2c440fed3a25a5d5e9b
7
- data.tar.gz: 85f1531795e99ef97a2e38e92c0c63d2163243e9011a4afb91e9eeedddb69688b8566dd985de656faab6058a94e65d731328f8dc3e9618e1dce31cf04c0ba9f2
6
+ metadata.gz: 38f589db1750a5395e5b32efa14a249d06b0530c178fcf649a9c5933ae63cba3f1ce9f3b482f996fc979b6e38e017d4c75d5aa551f82aee3ef201c270fa795c0
7
+ data.tar.gz: 6ed5c77459538dd5a16fabe2689886a1a6360b41512998f62d284fe2b19f43ea9b843649ea4d3ea2544c2c3a97954be8be898562d85d680692678ae51fe39545
data/bin/ofctl CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'cri'
3
3
  require 'readline'
4
- require_relative '../lib/controller'
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'openflow-controller'
5
6
 
6
7
  PROMPT = '> '
7
8
  BYE_MSG = 'Bye!'
@@ -27,7 +28,7 @@ command = Cri::Command.define do
27
28
 
28
29
  debug = opts[:debug] || false
29
30
 
30
- ctl = OFController.create(debug)
31
+ ctl = OpenFlow::Controller::Controller.create(debug)
31
32
 
32
33
  init_form = ctl.logger.formatter
33
34
  ctl.logger.formatter = proc do |severity, datetime, progname, msg|
@@ -37,8 +38,8 @@ command = Cri::Command.define do
37
38
  buf
38
39
  end
39
40
 
40
- ip = opts[:ip] || OFController::DEFAULT_IP_ADDRESS
41
- port = opts[:port] || OFController::DEFAULT_TCP_PORT
41
+ ip = opts[:ip] || OpenFlow::Controller::Controller::DEFAULT_IP_ADDRESS
42
+ port = opts[:port] || OpenFlow::Controller::Controller::DEFAULT_TCP_PORT
42
43
 
43
44
  Thread.abort_on_exception = true
44
45
  Thread.new do
@@ -1 +1,2 @@
1
- require_relative 'controller'
1
+ require 'openflow-controller/version'
2
+ require 'openflow-controller/controller'
@@ -0,0 +1,195 @@
1
+ require 'socket'
2
+ require 'logger'
3
+ require 'openflow-controller/switch'
4
+
5
+ class Numeric
6
+ def sec; self end
7
+ end
8
+
9
+ module OpenFlow
10
+ module Controller
11
+ class Controller
12
+ include Protocol
13
+
14
+ DEFAULT_IP_ADDRESS = '0.0.0.0'
15
+ DEFAULT_TCP_PORT = 6633
16
+
17
+ def self.inherited(subclass)
18
+ @controller_class = subclass
19
+ end
20
+
21
+ def self.create(*args)
22
+ (@controller_class || self).new(*args)
23
+ end
24
+
25
+ def self.timer_event(handler, options)
26
+ @timer_handlers ||= {}
27
+ @timer_handlers[handler] = options.fetch(:interval)
28
+ end
29
+
30
+ def self.timer_handlers
31
+ @timer_handlers || {}
32
+ end
33
+
34
+ attr_reader :logger
35
+
36
+ def initialize(debug = false)
37
+ @switches = {}
38
+ @messages = {}
39
+ @logger = Logger.new($stdout).tap do |logger|
40
+ logger.formatter = proc do |severity, datetime, _progname, msg|
41
+ "#{datetime} (#{severity}) -- #{msg}\n"
42
+ end
43
+ logger.level = debug ? Logger::DEBUG : Logger::INFO
44
+ end
45
+ end
46
+
47
+ def eval(input)
48
+ binding.eval(input)
49
+ end
50
+
51
+ def run(ip = DEFAULT_IP_ADDRESS, port = DEFAULT_TCP_PORT, *args)
52
+ maybe_send_handler :start, *args
53
+ socket = TCPServer.open(ip, port)
54
+ socket.setsockopt(:SOCKET, :REUSEADDR, true)
55
+ logger.info "Controller running on #{ip}:#{port}."
56
+ start_timers
57
+ loop { start_switch_thread socket.accept }
58
+ end
59
+
60
+ def datapath_ids
61
+ @switches.keys.map(&:to_i)
62
+ end
63
+
64
+ def switches
65
+ @switches.values
66
+ end
67
+
68
+ def send_message(datapath_id, msg = nil)
69
+ if msg.nil?
70
+ msg = datapath_id
71
+ datapath_id = datapath_ids.first
72
+ end
73
+ @switches.fetch(datapath_id.to_s).send(msg)
74
+ end
75
+
76
+ def broadcast(msg)
77
+ datapath_ids.each { |did| send_message did, msg }
78
+ end
79
+
80
+ def messages_for(datapath_id)
81
+ @messages.fetch(datapath_id.to_s)
82
+ end
83
+
84
+ def last_message_for(datapath_id)
85
+ messages_for(datapath_id).last
86
+ end
87
+
88
+ def messages
89
+ messages_for datapath_ids.first
90
+ end
91
+
92
+ def last_message
93
+ messages.last
94
+ end
95
+
96
+ def start(*_args) end
97
+ def switch_ready(_datapath_id) end
98
+ def message_received(_datapath_id, _msg) end
99
+ def error(_datapath_id, _msg) end
100
+ def echo_request(datapath_id, msg)
101
+ send_message datapath_id, EchoReply.new(xid: msg.xid)
102
+ end
103
+ def packet_in(_datapath_id, _msg) end
104
+ def port_add(_datapath_id, _msg) end
105
+ def port_delete(_datapath_id, _msg) end
106
+ def port_modify(_datapath_id, _msg) end
107
+ def flow_removed(_datapath_id, _msg) end
108
+
109
+ private
110
+
111
+ def maybe_send_handler(handler, *args)
112
+ @handler_mutex ||= Mutex.new
113
+ @handler_mutex.synchronize do
114
+ __send__(handler, *args) if respond_to?(handler)
115
+ end
116
+ end
117
+
118
+ def start_timers
119
+ self.class.timer_handlers.each do |handler, interval|
120
+ Thread.new do
121
+ loop do
122
+ maybe_send_handler handler
123
+ sleep interval
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def start_switch_thread(socket)
130
+ logger.debug 'Socket accepted.'
131
+ Thread.new do
132
+ switch = create_and_register_new_switch(socket)
133
+ start_switch_main(switch.datapath_id)
134
+ end
135
+ end
136
+
137
+ def create_and_register_new_switch(socket)
138
+ switch = Switch.new(self, socket)
139
+ @messages[switch.datapath_id.to_s] = []
140
+ @switches[switch.datapath_id.to_s] = switch
141
+ end
142
+
143
+ def start_switch_main(datapath_id)
144
+ logger.info "Switch #{datapath_id} is ready."
145
+ maybe_send_handler :switch_ready, datapath_id
146
+ loop { handle_openflow_message(datapath_id) }
147
+ rescue => exception
148
+ logger.debug "Switch #{datapath_id} error: #{exception}."
149
+ logger.debug exception.backtrace
150
+ unregister_switch(datapath_id)
151
+ end
152
+
153
+ def unregister_switch(datapath_id)
154
+ @messages.delete(datapath_id.to_s)
155
+ @switches.delete(datapath_id.to_s)
156
+ logger.info "Switch #{datapath_id} is disconnected."
157
+ maybe_send_handler :switch_disconnected, datapath_id
158
+ end
159
+
160
+ def handle_openflow_message(datapath_id)
161
+ msg = @switches.fetch(datapath_id.to_s).receive
162
+
163
+ unless msg.class == EchoRequest
164
+ logger.debug "Switch #{datapath_id} received #{msg.type} message."
165
+ @messages[datapath_id.to_s] << msg
166
+ maybe_send_handler :message_received, datapath_id, msg
167
+ end
168
+
169
+ case msg
170
+ when Error
171
+ maybe_send_handler :error, datapath_id, msg
172
+ when EchoRequest
173
+ maybe_send_handler :echo_request, datapath_id, msg
174
+ when FeaturesReply
175
+ maybe_send_handler :features_reply, datapath_id, msg
176
+ when PacketIn
177
+ maybe_send_handler :packet_in, datapath_id, msg
178
+ when PortStatus
179
+ case msg.reason
180
+ when :add
181
+ maybe_send_handler :port_add, datapath_id, msg
182
+ when :delete
183
+ maybe_send_handler :port_delete, datapath_id, msg
184
+ when :modify
185
+ maybe_send_handler :port_modify, datapath_id, msg
186
+ # else
187
+ end
188
+ when FlowRemoved
189
+ maybe_send_handler :flow_removed, datapath_id, msg
190
+ # else
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,58 @@
1
+ require 'openflow-protocol'
2
+
3
+ module OpenFlow
4
+ module Controller
5
+ class Switch
6
+ include Protocol
7
+
8
+ attr_reader :controller, :features_reply
9
+
10
+ def initialize(controller, socket)
11
+ @controller = controller
12
+ @socket = socket
13
+ begin
14
+ exchange_hello_messages
15
+ exchange_echo_messages
16
+ exchange_features_messages
17
+ rescue => exception
18
+ controller.logger.debug "Switch error: #{exception}."
19
+ raise exception
20
+ end
21
+ end
22
+
23
+ def send(msg)
24
+ @socket.write msg.to_binary_s
25
+ end
26
+
27
+ def receive
28
+ Parser.read @socket
29
+ end
30
+
31
+ def datapath_id
32
+ @features_reply.datapath_id
33
+ end
34
+
35
+ private
36
+
37
+ def exchange_hello_messages
38
+ controller.logger.debug 'Wait OFPT_HELLO.'
39
+ fail unless receive.is_a?(Hello)
40
+ send Hello.new
41
+ end
42
+
43
+ def exchange_echo_messages
44
+ send EchoRequest.new
45
+ controller.logger.debug 'Wait OFPT_ECHO_REPLY.'
46
+ fail unless receive.is_a?(EchoReply)
47
+ end
48
+
49
+ def exchange_features_messages
50
+ send FeaturesRequest.new
51
+ controller.logger.debug 'Wait OFPT_FEATURES_REPLY.'
52
+ @features_reply = receive
53
+ controller.logger.debug "OFPT_FEATURES_REPLY.datapath_id: #{datapath_id}."
54
+ fail unless @features_reply.is_a?(FeaturesReply)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  module OpenFlow
2
2
  module Controller
3
- VERSION = '0.1.8'
3
+ VERSION = '0.1.9'
4
4
  end
5
5
  end
@@ -1,8 +1,6 @@
1
- require 'spec_helper'
2
-
3
- describe OFController do
1
+ describe Controller do
4
2
  before(:all) do
5
- class MyCtl < OFController
3
+ class MyCtl < Controller
6
4
  attr_reader :start_args
7
5
 
8
6
  def start(*args)
@@ -10,7 +8,7 @@ describe OFController do
10
8
  end
11
9
  end
12
10
 
13
- @ctl = OFController.create
11
+ @ctl = Controller.create
14
12
  Thread.new { @ctl.run('127.0.0.1', 4242, 'Hello World!', 42) }
15
13
  end
16
14
 
@@ -27,20 +25,20 @@ describe OFController do
27
25
  socket = TCPSocket.new '127.0.0.1', 4242
28
26
 
29
27
  # Exchange Hello messages
30
- socket.write OFHello.new.to_binary_s
31
- msg = OFParser.read socket
32
- expect(msg.class).to be(OFHello)
28
+ socket.write Hello.new.to_binary_s
29
+ sleep(0.001)
30
+ msg = Parser.read socket
31
+ expect(msg.class).to be(Hello)
33
32
 
34
33
  # Exchange Echo messages
35
- msg = OFParser.read socket
36
- expect(msg.class).to be(OFEchoRequest)
37
- # socket.write msg.to_reply.to_binary_s
38
- socket.write OFEchoReply.new.to_binary_s
34
+ msg = Parser.read socket
35
+ expect(msg.class).to be(EchoRequest)
36
+ socket.write msg.to_reply.to_binary_s
39
37
 
40
38
  # Exchange Features messages
41
- msg = OFParser.read socket
42
- expect(msg.class).to be(OFFeaturesRequest)
43
- socket.write OFFeaturesReply.new(datapath_id: 1).to_binary_s
39
+ msg = Parser.read socket
40
+ expect(msg.class).to be(FeaturesRequest)
41
+ socket.write FeaturesReply.new(datapath_id: 1).to_binary_s
44
42
 
45
43
  sleep(0.001)
46
44
  expect(@ctl.switches.length).to eq(1)
@@ -1 +1,3 @@
1
- require_relative '../lib/openflow-controller'
1
+ require 'openflow-controller'
2
+ include OpenFlow::Controller
3
+ include OpenFlow::Protocol
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openflow-controller
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jérémy Pagé
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.7
19
+ version: 0.1.8
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.7
26
+ version: 0.1.8
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: colored
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.4'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -75,10 +89,10 @@ extensions: []
75
89
  extra_rdoc_files: []
76
90
  files:
77
91
  - bin/ofctl
78
- - lib/controller.rb
79
92
  - lib/openflow-controller.rb
93
+ - lib/openflow-controller/controller.rb
94
+ - lib/openflow-controller/switch.rb
80
95
  - lib/openflow-controller/version.rb
81
- - lib/switch.rb
82
96
  - spec/controller_spec.rb
83
97
  - spec/spec_helper.rb
84
98
  homepage: https://github.com/jejepage/openflow-controller
@@ -1,189 +0,0 @@
1
- require 'socket'
2
- require 'logger'
3
- require_relative 'switch'
4
-
5
- class Numeric
6
- def sec; self end
7
- end
8
-
9
- class OFController
10
- DEFAULT_IP_ADDRESS = '0.0.0.0'
11
- DEFAULT_TCP_PORT = 6633
12
-
13
- def self.inherited(subclass)
14
- @controller_class = subclass
15
- end
16
-
17
- def self.create(*args)
18
- (@controller_class || self).new(*args)
19
- end
20
-
21
- def self.timer_event(handler, options)
22
- @timer_handlers ||= {}
23
- @timer_handlers[handler] = options.fetch(:interval)
24
- end
25
-
26
- def self.timer_handlers
27
- @timer_handlers || {}
28
- end
29
-
30
- attr_reader :logger
31
-
32
- def initialize(debug = false)
33
- @switches = {}
34
- @messages = {}
35
- @logger = Logger.new($stdout).tap do |logger|
36
- logger.formatter = proc do |severity, datetime, _progname, msg|
37
- "#{datetime} (#{severity}) -- #{msg}\n"
38
- end
39
- logger.level = debug ? Logger::DEBUG : Logger::INFO
40
- end
41
- end
42
-
43
- def eval(input)
44
- binding.eval(input)
45
- end
46
-
47
- def run(ip = DEFAULT_IP_ADDRESS, port = DEFAULT_TCP_PORT, *args)
48
- maybe_send_handler :start, *args
49
- socket = TCPServer.open(ip, port)
50
- socket.setsockopt(:SOCKET, :REUSEADDR, true)
51
- logger.info "Controller running on #{ip}:#{port}."
52
- start_timers
53
- loop { start_switch_thread socket.accept }
54
- end
55
-
56
- def datapath_ids
57
- @switches.keys.map(&:to_i)
58
- end
59
-
60
- def switches
61
- @switches.values
62
- end
63
-
64
- def send_message(datapath_id, msg = nil)
65
- if msg.nil?
66
- msg = datapath_id
67
- datapath_id = datapath_ids.first
68
- end
69
- @switches.fetch(datapath_id.to_s).send(msg)
70
- end
71
-
72
- def broadcast(msg)
73
- datapath_ids.each { |did| send_message did, msg }
74
- end
75
-
76
- def messages_for(datapath_id)
77
- @messages.fetch(datapath_id.to_s)
78
- end
79
-
80
- def last_message_for(datapath_id)
81
- messages_for(datapath_id).last
82
- end
83
-
84
- def messages
85
- messages_for datapath_ids.first
86
- end
87
-
88
- def last_message
89
- messages.last
90
- end
91
-
92
- def start(*_args) end
93
- def switch_ready(_datapath_id) end
94
- def message_received(_datapath_id, _msg) end
95
- def error(_datapath_id, _msg) end
96
- def echo_request(datapath_id, msg)
97
- send_message datapath_id, OFEchoReply.new(xid: msg.xid)
98
- end
99
- def packet_in(_datapath_id, _msg) end
100
- def port_add(_datapath_id, _msg) end
101
- def port_delete(_datapath_id, _msg) end
102
- def port_modify(_datapath_id, _msg) end
103
- def flow_removed(_datapath_id, _msg) end
104
-
105
- private
106
-
107
- def maybe_send_handler(handler, *args)
108
- @handler_mutex ||= Mutex.new
109
- @handler_mutex.synchronize do
110
- __send__(handler, *args) if respond_to?(handler)
111
- end
112
- end
113
-
114
- def start_timers
115
- self.class.timer_handlers.each do |handler, interval|
116
- Thread.new do
117
- loop do
118
- maybe_send_handler handler
119
- sleep interval
120
- end
121
- end
122
- end
123
- end
124
-
125
- def start_switch_thread(socket)
126
- logger.debug 'Socket accepted.'
127
- Thread.new do
128
- switch = create_and_register_new_switch(socket)
129
- start_switch_main(switch.datapath_id)
130
- end
131
- end
132
-
133
- def create_and_register_new_switch(socket)
134
- switch = OFSwitch.new(self, socket)
135
- @messages[switch.datapath_id.to_s] = []
136
- @switches[switch.datapath_id.to_s] = switch
137
- end
138
-
139
- def start_switch_main(datapath_id)
140
- logger.info "Switch #{datapath_id} is ready."
141
- maybe_send_handler :switch_ready, datapath_id
142
- loop { handle_openflow_message(datapath_id) }
143
- rescue => exception
144
- logger.debug "Switch #{datapath_id} error: #{exception}."
145
- logger.debug exception.backtrace
146
- unregister_switch(datapath_id)
147
- end
148
-
149
- def unregister_switch(datapath_id)
150
- @messages.delete(datapath_id.to_s)
151
- @switches.delete(datapath_id.to_s)
152
- logger.info "Switch #{datapath_id} is disconnected."
153
- maybe_send_handler :switch_disconnected, datapath_id
154
- end
155
-
156
- def handle_openflow_message(datapath_id)
157
- msg = @switches.fetch(datapath_id.to_s).receive
158
-
159
- unless msg.class == OFEchoRequest
160
- logger.debug "Switch #{datapath_id} received #{msg.type} message."
161
- @messages[datapath_id.to_s] << msg
162
- maybe_send_handler :message_received, datapath_id, msg
163
- end
164
-
165
- case msg
166
- when OFError
167
- maybe_send_handler :error, datapath_id, msg
168
- when OFEchoRequest
169
- maybe_send_handler :echo_request, datapath_id, msg
170
- when OFFeaturesReply
171
- maybe_send_handler :features_reply, datapath_id, msg
172
- when OFPacketIn
173
- maybe_send_handler :packet_in, datapath_id, msg
174
- when OFPortStatus
175
- case msg.reason
176
- when :add
177
- maybe_send_handler :port_add, datapath_id, msg
178
- when :delete
179
- maybe_send_handler :port_delete, datapath_id, msg
180
- when :modify
181
- maybe_send_handler :port_modify, datapath_id, msg
182
- # else
183
- end
184
- when OFFlowRemoved
185
- maybe_send_handler :flow_removed, datapath_id, msg
186
- # else
187
- end
188
- end
189
- end
@@ -1,52 +0,0 @@
1
- require 'openflow-protocol'
2
-
3
- class OFSwitch
4
- attr_reader :controller, :features_reply
5
-
6
- def initialize(controller, socket)
7
- @controller = controller
8
- @socket = socket
9
- begin
10
- exchange_hello_messages
11
- exchange_echo_messages
12
- exchange_features_messages
13
- rescue => exception
14
- controller.logger.debug "Switch error: #{exception}."
15
- raise exception
16
- end
17
- end
18
-
19
- def send(msg)
20
- @socket.write msg.to_binary_s
21
- end
22
-
23
- def receive
24
- OFParser.read @socket
25
- end
26
-
27
- def datapath_id
28
- @features_reply.datapath_id
29
- end
30
-
31
- private
32
-
33
- def exchange_hello_messages
34
- controller.logger.debug 'Wait OFPT_HELLO.'
35
- fail unless receive.is_a?(OFHello)
36
- send OFHello.new
37
- end
38
-
39
- def exchange_echo_messages
40
- send OFEchoRequest.new
41
- controller.logger.debug 'Wait OFPT_ECHO_REPLY.'
42
- fail unless receive.is_a?(OFEchoReply)
43
- end
44
-
45
- def exchange_features_messages
46
- send OFFeaturesRequest.new
47
- controller.logger.debug 'Wait OFPT_FEATURES_REPLY.'
48
- @features_reply = receive
49
- controller.logger.debug "OFPT_FEATURES_REPLY.datapath_id: #{datapath_id}."
50
- fail unless @features_reply.is_a?(OFFeaturesReply)
51
- end
52
- end