openflow-protocol 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/actions/actions.rb +1 -24
  3. data/lib/helpers/array_of_subclasses.rb +38 -0
  4. data/lib/helpers/superclass_base.rb +2 -0
  5. data/lib/messages/features_reply.rb +2 -2
  6. data/lib/messages/message.rb +1 -1
  7. data/lib/messages/port_mod.rb +2 -0
  8. data/lib/messages/queue_get_config_reply.rb +14 -0
  9. data/lib/messages/queue_get_config_request.rb +11 -0
  10. data/lib/{statistics → messages/statistics}/aggregate_statistics_reply.rb +0 -0
  11. data/lib/{statistics → messages/statistics}/aggregate_statistics_request.rb +0 -0
  12. data/lib/{statistics → messages/statistics}/description_statistics.rb +0 -0
  13. data/lib/{statistics → messages/statistics}/flow_statistics_reply.rb +0 -0
  14. data/lib/{statistics → messages/statistics}/flow_statistics_request.rb +0 -0
  15. data/lib/{statistics → messages/statistics}/port_statistics_reply.rb +1 -1
  16. data/lib/{statistics → messages/statistics}/port_statistics_request.rb +1 -1
  17. data/lib/{statistics → messages/statistics}/queue_statistics_reply.rb +1 -1
  18. data/lib/{statistics → messages/statistics}/queue_statistics_request.rb +1 -1
  19. data/lib/{statistics → messages/statistics}/table_statistics.rb +1 -1
  20. data/lib/{statistics → messages/statistics}/vendor_statistics.rb +0 -0
  21. data/lib/openflow-protocol/version.rb +1 -1
  22. data/lib/structs/packet_queue.rb +10 -0
  23. data/lib/structs/port_number.rb +14 -0
  24. data/lib/structs/queue_properties/queue_properties.rb +5 -0
  25. data/lib/structs/queue_properties/queue_property.rb +16 -0
  26. data/lib/structs/queue_properties/queue_property_min_rate.rb +26 -0
  27. data/lib/structs/queue_properties/queue_property_none.rb +3 -0
  28. data/spec/actions/actions_spec.rb +1 -1
  29. data/spec/messages/barrier_reply_spec.rb +7 -1
  30. data/spec/messages/barrier_request_spec.rb +7 -1
  31. data/spec/messages/echo_reply_spec.rb +7 -1
  32. data/spec/messages/echo_request_spec.rb +7 -1
  33. data/spec/messages/error_spec.rb +10 -2
  34. data/spec/messages/features_reply_spec.rb +50 -39
  35. data/spec/messages/features_request_spec.rb +7 -1
  36. data/spec/messages/flow_mod_spec.rb +10 -2
  37. data/spec/messages/flow_removed_spec.rb +10 -2
  38. data/spec/messages/get_config_reply_spec.rb +10 -2
  39. data/spec/messages/get_config_request_spec.rb +7 -1
  40. data/spec/messages/hello_spec.rb +7 -1
  41. data/spec/messages/packet_in_spec.rb +10 -2
  42. data/spec/messages/packet_out_spec.rb +10 -2
  43. data/spec/messages/port_mod_spec.rb +10 -2
  44. data/spec/messages/port_status_spec.rb +10 -2
  45. data/spec/messages/queue_get_config_reply_spec.rb +68 -0
  46. data/spec/messages/queue_get_config_request_spec.rb +40 -0
  47. data/spec/messages/set_config_spec.rb +10 -2
  48. data/spec/{statistics → messages/statistics}/aggregate_statistics_reply_spec.rb +0 -0
  49. data/spec/{statistics → messages/statistics}/aggregate_statistics_request_spec.rb +0 -0
  50. data/spec/{statistics → messages/statistics}/description_statistics_spec.rb +0 -0
  51. data/spec/{statistics → messages/statistics}/flow_statistics_reply_spec.rb +0 -0
  52. data/spec/{statistics → messages/statistics}/flow_statistics_request_spec.rb +0 -0
  53. data/spec/{statistics → messages/statistics}/port_statistics_reply_spec.rb +0 -0
  54. data/spec/{statistics → messages/statistics}/port_statistics_request_spec.rb +0 -0
  55. data/spec/{statistics → messages/statistics}/queue_statistics_reply_spec.rb +0 -0
  56. data/spec/{statistics → messages/statistics}/queue_statistics_request_spec.rb +0 -0
  57. data/spec/{statistics → messages/statistics}/table_statistics_spec.rb +0 -0
  58. data/spec/{statistics → messages/statistics}/vendor_statistics_spec.rb +0 -0
  59. data/spec/messages/statistics_reply_spec.rb +77 -20
  60. data/spec/messages/statistics_request_spec.rb +13 -5
  61. data/spec/messages/vendor_spec.rb +10 -2
  62. data/spec/structs/packet_queue_spec.rb +37 -0
  63. data/spec/structs/port_number_spec.rb +19 -0
  64. data/spec/structs/queue_properties/queue_properties_spec.rb +29 -0
  65. data/spec/structs/queue_properties/queue_property_min_rate_spec.rb +32 -0
  66. data/spec/structs/queue_properties/queue_property_none_spec.rb +14 -0
  67. metadata +55 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0efcfc19372c5ff81c76eef6c74912c9ddfccaf
4
- data.tar.gz: 02a45e643b2ea6c53f877dfac8cffcdc680c1711
3
+ metadata.gz: b787a021c7be6ba96d78b7f71f9d93676c709bd7
4
+ data.tar.gz: 80ea8bef3b0e8fbd6783d8fdb35fb278032c9d0e
5
5
  SHA512:
6
- metadata.gz: 77b3a36050c5e976899c07f2a9a8ff4917f8b8e4c5cbce3070ea02b262990f9ba67b2324095edba3b83e160fdcb4bfa7c0c6d171abc0d8cff852f4accbacc1cb
7
- data.tar.gz: de3deb39ea4a6e6ca09c0fd3a5eeca3769ab1c62d97945990cca0131f6c9db2c0f27eca5f98da8678f2f78bc4f75a1331077a259c50c6f53a09719f33f89168d
6
+ metadata.gz: 842cbdc2c9b75e80d1dbb53d853fa6c578bbc8ec05d3db6bb8450fd7be2017fb8586c1ba435410b717979e6fe95171fb8d776677c9b9a8c4a59e65e4f79ec0dc
7
+ data.tar.gz: b1139123757191b1c60e00866e8543c423e324f70f49ea23e4ee8ee02fbc2ed72b13c1ae891255e6487e4c9c8cf8137028235956fed6baaf3f8dd76a5c44e47d
@@ -2,27 +2,4 @@ Dir[File.expand_path 'action_*.rb', __dir__].each do |file|
2
2
  require file
3
3
  end
4
4
 
5
- class OFActions < BinData::Primitive
6
- default_parameter length: 0
7
- endian :big
8
- string :binary, read_length: :length
9
-
10
- def get
11
- actions = []
12
- tmp = binary
13
- until tmp.empty?
14
- type_index = BinData::Uint16be.read(tmp)
15
- type_str = OFAction::TYPES.fetch(type_index)
16
- class_name = 'OFAction' + type_str.to_s.split('_').map(&:capitalize).join
17
- klass = Object.const_get class_name
18
- action = klass.read(tmp)
19
- actions << action
20
- tmp = tmp[action.len..-1]
21
- end
22
- actions
23
- end
24
-
25
- def set(value)
26
- self.binary = value.map(&:to_binary_s).join
27
- end
28
- end
5
+ class OFActions < ArrayOfSubclasses; end
@@ -0,0 +1,38 @@
1
+ require 'bindata'
2
+
3
+ class ArrayOfSubclasses < BinData::Primitive
4
+ default_parameter length: 0
5
+ endian :big
6
+ string :binary, read_length: :length
7
+
8
+ def get
9
+ items = []
10
+ tmp = binary
11
+ until tmp.empty?
12
+ type_index = BinData::Uint16be.read(tmp)
13
+ type_str = types.fetch(type_index)
14
+ class_name = name + type_str.to_s.split('_').map(&:capitalize).join
15
+ klass = Object.const_get class_name
16
+ item = klass.read(tmp)
17
+ items << item
18
+ tmp = tmp[item.len..-1]
19
+ end
20
+ items
21
+ end
22
+
23
+ def set(value)
24
+ self.binary = value.map(&:to_binary_s).join
25
+ end
26
+
27
+ private
28
+
29
+ def name
30
+ plural = self.class.to_s
31
+ return plural[0..-4] + 'y' if plural[-3..-1] == 'ies'
32
+ plural[0..-2]
33
+ end
34
+
35
+ def types
36
+ Object.const_get(name + '::TYPES')
37
+ end
38
+ end
@@ -10,6 +10,8 @@ class SuperclassBase < BinData::Record
10
10
  super
11
11
  if self.class.name[2..7] == 'Action'
12
12
  name = self.class.name[8..-1]
13
+ elsif self.class.name[2..14] == 'QueueProperty'
14
+ name = self.class.name[15..-1]
13
15
  else
14
16
  name = self.class.name[2..-1]
15
17
  end
@@ -31,8 +31,8 @@ class OFFeaturesReply < OFMessage
31
31
  :enqueue
32
32
  ]
33
33
  array :ports,
34
- type: :of_physical_port,
35
- initial_length: -> { (len - ports.rel_offset) / 48 }
34
+ type: :of_physical_port,
35
+ initial_length: -> { (len - ports.rel_offset) / 48 }
36
36
 
37
37
  def body_length
38
38
  24 + ports.to_binary_s.length
@@ -1,7 +1,7 @@
1
1
  (Dir[File.expand_path '../helpers/**/*.rb', __dir__] +
2
2
  Dir[File.expand_path '../structs/**/*.rb', __dir__] +
3
3
  Dir[File.expand_path '../actions/**/*.rb', __dir__] +
4
- Dir[File.expand_path '../statistics/**/*.rb', __dir__]).each do |file|
4
+ Dir[File.expand_path 'statistics/**/*.rb', __dir__]).each do |file|
5
5
  require file
6
6
  end
7
7
 
@@ -6,6 +6,8 @@ class OFPortMod < OFMessage
6
6
  flags32 :config, list: OFPhysicalPort::CONFIG
7
7
  flags32 :mask, list: OFPhysicalPort::CONFIG
8
8
  flags32 :advertise, list: OFPhysicalPort::FEATURES
9
+ uint32 :padding
10
+ hide :padding
9
11
 
10
12
  def body_length
11
13
  24
@@ -0,0 +1,14 @@
1
+ require_relative 'message'
2
+
3
+ class OFQueueGetConfigReply < OFMessage
4
+ of_port_number_strict :port
5
+ uint48 :padding
6
+ hide :padding
7
+ array :queues,
8
+ type: :of_packet_queue,
9
+ read_until: :eof
10
+
11
+ def body_length
12
+ 8 + queues.to_binary_s.length
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'message'
2
+
3
+ class OFQueueGetConfigRequest < OFMessage
4
+ of_port_number_strict :port
5
+ uint16 :padding
6
+ hide :padding
7
+
8
+ def body_length
9
+ 4
10
+ end
11
+ end
@@ -1,4 +1,4 @@
1
- require_relative '../structs/port_number'
1
+ require_relative '../../structs/port_number'
2
2
 
3
3
  class OFPortStatisticsReply < BinData::Record
4
4
  endian :big
@@ -1,4 +1,4 @@
1
- require_relative '../structs/port_number'
1
+ require_relative '../../structs/port_number'
2
2
 
3
3
  class OFPortStatisticsRequest < BinData::Record
4
4
  endian :big
@@ -1,4 +1,4 @@
1
- require_relative '../structs/port_number'
1
+ require_relative '../../structs/port_number'
2
2
 
3
3
  class OFQueueStatisticsReply < BinData::Record
4
4
  endian :big
@@ -1,4 +1,4 @@
1
- require_relative '../structs/port_number'
1
+ require_relative '../../structs/port_number'
2
2
 
3
3
  class OFQueueStatisticsRequest < BinData::Record
4
4
  endian :big
@@ -1,4 +1,4 @@
1
- require_relative '../structs/match'
1
+ require_relative '../../structs/match'
2
2
 
3
3
  class OFTableStatistics < BinData::Record
4
4
  TABLE_IDS = {emergency: 0xfe, all: 0xff}
@@ -1,5 +1,5 @@
1
1
  module OpenFlow
2
2
  module Protocol
3
- VERSION = '0.1.6'
3
+ VERSION = '0.1.7'
4
4
  end
5
5
  end
@@ -0,0 +1,10 @@
1
+ require_relative 'queue_properties/queue_properties'
2
+
3
+ class OFPacketQueue < BinData::Record
4
+ endian :big
5
+ uint32 :queue_id, initial_value: 0
6
+ uint16 :len, value: -> { 8 + properties.to_binary_s.length }
7
+ uint16 :padding
8
+ hide :padding
9
+ of_queue_properties :properties, length: -> { len - 8 }
10
+ end
@@ -1,5 +1,19 @@
1
1
  require 'bindata'
2
2
 
3
+ class OFPortNumberStrict < BinData::Primitive
4
+ endian :big
5
+ uint16 :port_number, initial_value: 0
6
+
7
+ def get
8
+ port_number
9
+ end
10
+
11
+ def set(value)
12
+ raise ArgumentError, 'Invalid port number.' unless (0..OFPortNumber::MAX).include? value
13
+ self.port_number = value
14
+ end
15
+ end
16
+
3
17
  class OFPortNumber < BinData::Primitive
4
18
  NUMBERS = {
5
19
  in_port: 0xfff8,
@@ -0,0 +1,5 @@
1
+ Dir[File.expand_path 'queue_property_*.rb', __dir__].each do |file|
2
+ require file
3
+ end
4
+
5
+ class OFQueueProperties < ArrayOfSubclasses; end
@@ -0,0 +1,16 @@
1
+ Dir[File.expand_path '../../helpers/**/*.rb', __dir__].each do |file|
2
+ require file
3
+ end
4
+
5
+ class OFQueueProperty < SuperclassBase
6
+ HEADER_LENGTH = 8
7
+ TYPES = [
8
+ :none,
9
+ :min_rate
10
+ ]
11
+
12
+ enum16 :type, list: TYPES, asserted_value: -> { type_str.to_sym }
13
+ uint16 :len, value: -> { HEADER_LENGTH + body_length }
14
+ uint32 :padding
15
+ hide :padding
16
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'queue_property'
2
+
3
+ class OFQueuePropertyMinRate < OFQueueProperty
4
+ class Rate < BinData::Primitive
5
+ endian :big
6
+ uint16 :rate, initial_value: 0
7
+
8
+ def get
9
+ return :disabled if rate > 1000
10
+ rate
11
+ end
12
+
13
+ def set(value)
14
+ value = 0xffff if value == :disabled
15
+ self.rate = value
16
+ end
17
+ end
18
+
19
+ rate :rate
20
+ uint48 :padding2
21
+ hide :padding2
22
+
23
+ def body_length
24
+ 8
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'queue_property'
2
+
3
+ class OFQueuePropertyNone < OFQueueProperty; end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe OFActionOutput do
3
+ describe OFActions do
4
4
  it 'should read binary' do
5
5
  actions = OFActions.read [
6
6
  # output
@@ -1,13 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFBarrierReply do
4
+ let(:data) { [1, 19, 0, 8, 0, 0, 0, 1].pack('C*') }
5
+
4
6
  it 'should read binary' do
5
- msg = OFBarrierReply.read [1, 19, 0, 8, 0, 0, 0, 1].pack('C*')
7
+ msg = OFBarrierReply.read(data)
6
8
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
7
9
  expect(msg.type).to eq(:barrier_reply)
8
10
  expect(msg.len).to eq(8)
9
11
  expect(msg.xid).to eq(1)
10
12
  end
13
+ it 'should be parsable' do
14
+ msg = OFParser.read(data)
15
+ expect(msg.class).to eq(OFBarrierReply)
16
+ end
11
17
  it 'should initialize with default values' do
12
18
  msg = OFBarrierReply.new
13
19
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
@@ -1,13 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFBarrierRequest do
4
+ let(:data) { [1, 18, 0, 8, 0, 0, 0, 1].pack('C*') }
5
+
4
6
  it 'should read binary' do
5
- msg = OFBarrierRequest.read [1, 18, 0, 8, 0, 0, 0, 1].pack('C*')
7
+ msg = OFBarrierRequest.read(data)
6
8
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
7
9
  expect(msg.type).to eq(:barrier_request)
8
10
  expect(msg.len).to eq(8)
9
11
  expect(msg.xid).to eq(1)
10
12
  end
13
+ it 'should be parsable' do
14
+ msg = OFParser.read(data)
15
+ expect(msg.class).to eq(OFBarrierRequest)
16
+ end
11
17
  it 'should initialize with default values' do
12
18
  msg = OFBarrierRequest.new
13
19
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
@@ -1,14 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFEchoReply do
4
+ let(:data) { [1, 3, 0, 8, 0, 0, 0, 1].pack('C*') }
5
+
4
6
  it 'should read binary' do
5
- msg = OFEchoReply.read [1, 3, 0, 8, 0, 0, 0, 1].pack('C*')
7
+ msg = OFEchoReply.read(data)
6
8
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
7
9
  expect(msg.type).to eq(:echo_reply)
8
10
  expect(msg.len).to eq(8)
9
11
  expect(msg.xid).to eq(1)
10
12
  expect(msg.data).to be_empty
11
13
  end
14
+ it 'should be parsable' do
15
+ msg = OFParser.read(data)
16
+ expect(msg.class).to eq(OFEchoReply)
17
+ end
12
18
  it 'should read binary with data' do
13
19
  msg = OFEchoReply.read [1, 3, 0, 10, 0, 0, 0, 1, 10, 20].pack('C*')
14
20
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
@@ -1,14 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFEchoRequest do
4
+ let(:data) { [1, 2, 0, 8, 0, 0, 0, 1].pack('C*') }
5
+
4
6
  it 'should read binary' do
5
- msg = OFEchoRequest.read [1, 2, 0, 8, 0, 0, 0, 1].pack('C*')
7
+ msg = OFEchoRequest.read(data)
6
8
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
7
9
  expect(msg.type).to eq(:echo_request)
8
10
  expect(msg.len).to eq(8)
9
11
  expect(msg.xid).to eq(1)
10
12
  expect(msg.data).to be_empty
11
13
  end
14
+ it 'should be parsable' do
15
+ msg = OFParser.read(data)
16
+ expect(msg.class).to eq(OFEchoRequest)
17
+ end
12
18
  it 'should read binary with data' do
13
19
  msg = OFEchoRequest.read [1, 2, 0, 10, 0, 0, 0, 1, 10, 20].pack('C*')
14
20
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
@@ -1,13 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFError do
4
- it 'should read binary' do
5
- msg = OFError.read [
4
+ let(:data) {
5
+ [
6
6
  1, 1, 0, 16, 0, 0, 0, 1, # header
7
7
  0, 0, # type
8
8
  0, 0, # code
9
9
  1, 2, 3, 4 # data
10
10
  ].pack('C*')
11
+ }
12
+
13
+ it 'should read binary' do
14
+ msg = OFError.read(data)
11
15
  expect(msg.version).to eq(OFMessage::OFP_VERSION)
12
16
  expect(msg.type).to eq(:error)
13
17
  expect(msg.len).to eq(16)
@@ -16,6 +20,10 @@ describe OFError do
16
20
  expect(msg.error_code).to eq(:incompatible)
17
21
  expect(msg.data).to eq([1, 2, 3, 4].pack('C*'))
18
22
  end
23
+ it 'should be parsable' do
24
+ msg = OFParser.read(data)
25
+ expect(msg.class).to eq(OFError)
26
+ end
19
27
  it 'should read binary' do
20
28
  msg = OFError.read [
21
29
  1, 1, 0, 12, 0, 0, 0, 1, # header
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OFFeaturesReply do
4
- it 'should read binary' do
5
- msg = OFFeaturesReply.read [
4
+ let(:data) {
5
+ [
6
6
  1, 6, 0, 128, 0, 0, 0, 1, # header
7
7
  0, 0, 0, 0, 0, 0, 0, 42, # datapath_id
8
8
  0, 0, 0, 10, # n_buffers
@@ -37,43 +37,9 @@ describe OFFeaturesReply do
37
37
  0, 0, 0x0f, 0xff, # supported_features
38
38
  0, 0, 0x0f, 0xff # peer_features
39
39
  ].pack('C*')
40
- expect(msg.version).to eq(OFMessage::OFP_VERSION)
41
- expect(msg.type).to eq(:features_reply)
42
- expect(msg.len).to eq(128)
43
- expect(msg.xid).to eq(1)
44
- expect(msg.datapath_id).to eq(42)
45
- expect(msg.n_buffers).to eq(10)
46
- expect(msg.n_tables).to eq(5)
47
- expect(msg.capabilities).to eq([
48
- :flow_stats,
49
- :table_stats,
50
- :port_stats,
51
- :stp,
52
- :reserved,
53
- :ip_reasm,
54
- :queue_stats,
55
- :arp_match_ip
56
- ])
57
- expect(msg.actions).to eq([
58
- :output,
59
- :set_vlan_vid,
60
- :set_vlan_pcp,
61
- :strip_vlan,
62
- :set_dl_src,
63
- :set_dl_dst,
64
- :set_nw_src,
65
- :set_nw_dst,
66
- :set_nw_tos,
67
- :set_tp_src,
68
- :set_tp_dst,
69
- :enqueue
70
- ])
71
- expect(msg.ports.length).to eq(2)
72
- expect(msg.ports[0].port_number).to eq(1)
73
- expect(msg.ports[1].port_number).to eq(2)
74
- end
75
- it 'should read a real binary message' do
76
- msg = OFFeaturesReply.read [
40
+ }
41
+ let(:real_data) {
42
+ [
77
43
  1, 6, 0, 176, 0, 0, 0, 0,
78
44
  0, 0, 0, 0, 0, 0, 0, 1,
79
45
  0, 0, 1, 0,
@@ -121,6 +87,51 @@ describe OFFeaturesReply do
121
87
  0, 0, 0, 0,
122
88
  0, 0, 0, 0
123
89
  ].pack('C*')
90
+ }
91
+
92
+ it 'should read binary' do
93
+ msg = OFFeaturesReply.read(data)
94
+ expect(msg.version).to eq(OFMessage::OFP_VERSION)
95
+ expect(msg.type).to eq(:features_reply)
96
+ expect(msg.len).to eq(128)
97
+ expect(msg.xid).to eq(1)
98
+ expect(msg.datapath_id).to eq(42)
99
+ expect(msg.n_buffers).to eq(10)
100
+ expect(msg.n_tables).to eq(5)
101
+ expect(msg.capabilities).to eq([
102
+ :flow_stats,
103
+ :table_stats,
104
+ :port_stats,
105
+ :stp,
106
+ :reserved,
107
+ :ip_reasm,
108
+ :queue_stats,
109
+ :arp_match_ip
110
+ ])
111
+ expect(msg.actions).to eq([
112
+ :output,
113
+ :set_vlan_vid,
114
+ :set_vlan_pcp,
115
+ :strip_vlan,
116
+ :set_dl_src,
117
+ :set_dl_dst,
118
+ :set_nw_src,
119
+ :set_nw_dst,
120
+ :set_nw_tos,
121
+ :set_tp_src,
122
+ :set_tp_dst,
123
+ :enqueue
124
+ ])
125
+ expect(msg.ports.length).to eq(2)
126
+ expect(msg.ports[0].port_number).to eq(1)
127
+ expect(msg.ports[1].port_number).to eq(2)
128
+ end
129
+ it 'should be parsable' do
130
+ msg = OFParser.read(data)
131
+ expect(msg.class).to eq(OFFeaturesReply)
132
+ end
133
+ it 'should read a real binary message' do
134
+ msg = OFFeaturesReply.read(real_data)
124
135
  expect(msg.ports.length).to eq(3)
125
136
  end
126
137
  it 'should initialize with default values' do