Skip to content

Commit

Permalink
Add DatapathId primitive.
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuhito committed Jul 2, 2015
1 parent 454193a commit 5937c7c
Show file tree
Hide file tree
Showing 14 changed files with 264 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Rakefile
@@ -1,7 +1,7 @@
require 'bundler/gem_tasks'

RELISH_PROJECT = 'trema/pio'
FLAY_THRESHOLD = 452
FLAY_THRESHOLD = 818

task default: :travis
task test: [:spec, :cucumber]
Expand Down
16 changes: 16 additions & 0 deletions features/open_flow10/hello.feature
Expand Up @@ -61,6 +61,22 @@ Feature: Pio::Hello
"""
Then it should fail with "ArgumentError", "Transaction ID should be an unsigned 32-bit integer."

Scenario: new(body: 'hello')
When I try to create an OpenFlow message with:
"""
Pio::Hello.new(body: 'hello')
"""
Then it should finish successfully
And the message have the following fields and values:
| field | value |
| class | Pio::Hello |
| ofp_version | 1 |
| message_type | 0 |
| message_length | 13 |
| transaction_id | 0 |
| xid | 0 |
| body | hello |

Scenario: read
When I try to parse a file named "open_flow10/hello.raw" with "Hello" class
Then it should finish successfully
Expand Down
4 changes: 4 additions & 0 deletions lib/pio/monkey_patch/integer/ranges.rb
Expand Up @@ -14,6 +14,10 @@ def unsigned_32bit?
_within_range? 32
end

def unsigned_64bit?
_within_range? 64
end

def _within_range?(nbit)
(0 <= self) && (self < 2**nbit)
end
Expand Down
1 change: 1 addition & 0 deletions lib/pio/open_flow.rb
Expand Up @@ -14,6 +14,7 @@ module OpenFlow
end
end

require 'pio/open_flow/datapath_id'
require 'pio/open_flow/flags'
require 'pio/open_flow/open_flow_header'
require 'pio/open_flow/phy_port'
Expand Down
26 changes: 26 additions & 0 deletions lib/pio/open_flow/datapath_id.rb
@@ -0,0 +1,26 @@
require 'bindata'
require 'pio/monkey_patch/integer'

module Pio
module OpenFlow
# Datapath unique ID. The lower 48-bits are for a MAC address,
# while the upper 16-bits are implementer-defined.
class DatapathId < BinData::Primitive
endian :big

uint64 :datapath_id

def set(value)
unless value.unsigned_64bit?
fail(ArgumentError,
'Datapath ID should be an unsigned 64-bit integer.')
end
self.datapath_id = value
end

def get
datapath_id
end
end
end
end
86 changes: 82 additions & 4 deletions lib/pio/open_flow10/echo.rb
Expand Up @@ -6,11 +6,89 @@ module Pio
# OpenFlow 1.0 Echo Request and Reply message.
module Echo
# OpenFlow 1.0 Echo Request message.
class Request; end
OpenFlow::Message.factory(Request, OpenFlow::ECHO_REQUEST)
class Request
# OpenFlow 1.0 Hello message
class Format < BinData::Record
endian :big

open_flow_header :header,
ofp_version_value: 1,
message_type_value: OpenFlow::ECHO_REQUEST
virtual assert: -> { header.message_type == OpenFlow::ECHO_REQUEST }

string :body
end

extend Forwardable

def_delegators :@format, :snapshot
def_delegators :snapshot, :header
def_delegators :header, :ofp_version
def_delegators :header, :message_type
def_delegators :header, :message_length
def_delegators :header, :transaction_id
def_delegator :header, :transaction_id, :xid

def_delegators :snapshot, :body
def_delegator :snapshot, :body, :user_data
def_delegator :@format, :to_binary_s, :to_binary

def self.read(raw_data)
allocate.tap do |message|
message.instance_variable_set(:@format, Format.read(raw_data))
end
rescue BinData::ValidityError
raise Pio::ParseError, 'Invalid Echo Request message.'
end

def initialize(user_options = {})
header_options = OpenFlowHeader::Options.parse(user_options)
body_options = user_options[:body] || user_options[:user_data] || ''
@format = Format.new(header: header_options, body: body_options)
end
end

# OpenFlow 1.0 Echo Reply message.
class Reply; end
OpenFlow::Message.factory(Reply, OpenFlow::ECHO_REPLY)
class Reply
# OpenFlow 1.0 Hello message
class Format < BinData::Record
endian :big

open_flow_header :header,
ofp_version_value: 1,
message_type_value: OpenFlow::ECHO_REPLY
virtual assert: -> { header.message_type == OpenFlow::ECHO_REPLY }

string :body
end

extend Forwardable

def_delegators :@format, :snapshot
def_delegators :snapshot, :header
def_delegators :header, :ofp_version
def_delegators :header, :message_type
def_delegators :header, :message_length
def_delegators :header, :transaction_id
def_delegator :header, :transaction_id, :xid

def_delegators :snapshot, :body
def_delegator :snapshot, :body, :user_data
def_delegator :@format, :to_binary_s, :to_binary

def self.read(raw_data)
allocate.tap do |message|
message.instance_variable_set(:@format, Format.read(raw_data))
end
rescue BinData::ValidityError
raise Pio::ParseError, 'Invalid Echo Reply message.'
end

def initialize(user_options = {})
header_options = OpenFlowHeader::Options.parse(user_options)
body_options = user_options[:body] || user_options[:user_data] || ''
@format = Format.new(header: header_options, body: body_options)
end
end
end
end
46 changes: 43 additions & 3 deletions lib/pio/open_flow10/features.rb
@@ -1,12 +1,52 @@
require 'forwardable'
require 'pio/open_flow'
require 'pio/open_flow10/message'

module Pio
# OpenFlow 1.0 Features Request and Reply message.
class Features
# OpenFlow 1.0 Features Request message.
class Request; end
OpenFlow::Message.factory(Request, OpenFlow::FEATURES_REQUEST)
class Request
# OpenFlow 1.0 Features Request message
class Format < BinData::Record
endian :big

open_flow_header :header,
ofp_version_value: 1,
message_type_value: OpenFlow::FEATURES_REQUEST
virtual assert: -> { header.message_type == OpenFlow::FEATURES_REQUEST }

string :body
end

extend Forwardable

def_delegators :@format, :snapshot
def_delegators :snapshot, :header
def_delegators :header, :ofp_version
def_delegators :header, :message_type
def_delegators :header, :message_length
def_delegators :header, :transaction_id
def_delegator :header, :transaction_id, :xid

def_delegators :snapshot, :body
def_delegator :snapshot, :body, :user_data
def_delegator :@format, :to_binary_s, :to_binary

def self.read(raw_data)
allocate.tap do |message|
message.instance_variable_set(:@format, Format.read(raw_data))
end
rescue BinData::ValidityError
raise Pio::ParseError, 'Invalid Features Request message.'
end

def initialize(user_options = {})
header_options = OpenFlowHeader::Options.parse(user_options)
body_options = user_options[:body] || user_options[:user_data] || ''
@format = Format.new(header: header_options, body: body_options)
end
end

# OpenFlow 1.0 Features Reply message.
class Reply
Expand Down Expand Up @@ -42,7 +82,7 @@ class Body < BinData::Record

endian :big

uint64 :datapath_id
datapath_id :datapath_id
uint32 :n_buffers
uint8 :n_tables
uint24 :padding
Expand Down
44 changes: 42 additions & 2 deletions lib/pio/open_flow10/hello.rb
Expand Up @@ -3,6 +3,46 @@
# Base module.
module Pio
# OpenFlow 1.0 Hello message
class Hello; end
OpenFlow::Message.factory(Hello, OpenFlow::HELLO)
class Hello
# OpenFlow 1.0 Hello message
class Format < BinData::Record
endian :big

open_flow_header :open_flow_header,
ofp_version_value: 1,
message_type_value: OpenFlow::HELLO
virtual assert: -> { open_flow_header.message_type == OpenFlow::HELLO }

string :body
end

extend Forwardable

def_delegators :@format, :snapshot
def_delegators :snapshot, :open_flow_header
def_delegators :open_flow_header, :ofp_version
def_delegators :open_flow_header, :message_type
def_delegators :open_flow_header, :message_length
def_delegators :open_flow_header, :transaction_id
def_delegator :open_flow_header, :transaction_id, :xid

def_delegators :snapshot, :body
def_delegator :snapshot, :body, :user_data
def_delegator :@format, :to_binary_s, :to_binary

def self.read(raw_data)
allocate.tap do |message|
message.instance_variable_set(:@format, Format.read(raw_data))
end
rescue BinData::ValidityError
raise Pio::ParseError, 'Invalid Hello message.'
end

def initialize(user_options = {})
header_options = OpenFlowHeader::Options.parse(user_options)
body_options = user_options[:body] || user_options[:user_data] || ''
@format = Format.new(open_flow_header: header_options,
body: body_options)
end
end
end
6 changes: 1 addition & 5 deletions lib/pio/open_flow10/message.rb
Expand Up @@ -57,11 +57,7 @@ def initialize(user_options = {})
user_options.delete :xid
dpid = user_options[:dpid]
user_options[:datapath_id] = dpid if dpid
if user_options.keys.size > 1
user_options
else
user_options[:user_data] || ''
end
user_options
else
''
end
Expand Down
4 changes: 2 additions & 2 deletions lib/pio/open_flow13/features_reply.rb
Expand Up @@ -5,7 +5,7 @@
module Pio
# OpenFlow 1.3 Features Request and Reply message.
class Features
remove_const :Reply
remove_const :Reply if const_defined?(:Reply)

# OpenFlow 1.3 Features Reply message.
class Reply
Expand All @@ -26,7 +26,7 @@ class Body < BinData::Record

endian :big

uint64 :datapath_id
datapath_id :datapath_id
uint32 :n_buffers
uint8 :n_tables
uint8 :auxiliary_id
Expand Down
8 changes: 8 additions & 0 deletions spec/pio/open_flow10/features_reply_spec.rb
@@ -0,0 +1,8 @@
require 'pio/open_flow10/features'

describe Pio::Features::Reply do
describe '.new' do
it_should_behave_like('an OpenFlow message with Datapath ID',
Pio::Features::Reply)
end
end
8 changes: 8 additions & 0 deletions spec/pio/open_flow13/features_reply_spec.rb
@@ -0,0 +1,8 @@
require 'pio/open_flow13/features_reply'

describe Pio::Features::Reply do
describe '.new' do
it_should_behave_like('an OpenFlow message with Datapath ID',
Pio::Features::Reply)
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Expand Up @@ -12,3 +12,4 @@
SimpleCov.start { add_filter '/vendor/' }

require 'rspec/given'
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
29 changes: 29 additions & 0 deletions spec/support/shared_examples_for_openflow_messages.rb
@@ -0,0 +1,29 @@
shared_examples 'an OpenFlow message with Datapath ID' do |klass|
When(:message) { klass.new(options) }

context 'with { datapath_id: -1 }' do
Given(:options) { { datapath_id: -1 } }
Then do
message == Failure(ArgumentError,
'Datapath ID should be an unsigned 64-bit integer.')
end
end

context 'with { datapath_id: 0 }' do
Given(:options) { { datapath_id: 0 } }
Then { message.datapath_id == 0 }
end

context 'with { datapath_id: 2**64 - 1 }' do
Given(:options) { { datapath_id: 2**64 - 1 } }
Then { message.datapath_id == 2**64 - 1 }
end

context 'with { datapath_id: 2**64 }' do
Given(:options) { { datapath_id: 2**64 } }
Then do
message == Failure(ArgumentError,
'Datapath ID should be an unsigned 64-bit integer.')
end
end
end

0 comments on commit 5937c7c

Please sign in to comment.