pio 0.10.1 → 0.11.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +33 -2
  4. data/examples/echo_read.rb +1 -1
  5. data/examples/features_read.rb +1 -1
  6. data/examples/flow_mod_new.rb +13 -0
  7. data/examples/flow_mod_read.rb +6 -0
  8. data/features/echo_read.feature +27 -3
  9. data/features/features_read.feature +46 -2
  10. data/features/flow_mod_read.feature +186 -0
  11. data/features/hello_read.feature +9 -0
  12. data/features/packet_data/flow_mod_add.raw +0 -0
  13. data/features/packet_data/flow_mod_delete.raw +0 -0
  14. data/features/packet_data/flow_mod_delete_strict.raw +0 -0
  15. data/features/packet_data/flow_mod_modify.raw +0 -0
  16. data/features/packet_data/flow_mod_modify_strict.raw +0 -0
  17. data/features/packet_in_read.feature +13 -0
  18. data/features/packet_out_read.feature +16 -0
  19. data/features/step_definitions/packet_data_steps.rb +10 -1
  20. data/lib/pio.rb +1 -0
  21. data/lib/pio/echo.rb +10 -8
  22. data/lib/pio/enqueue.rb +1 -1
  23. data/lib/pio/features.rb +64 -7
  24. data/lib/pio/flow_mod.rb +86 -0
  25. data/lib/pio/hello.rb +4 -74
  26. data/lib/pio/ipv4_address.rb +1 -1
  27. data/lib/pio/match.rb +167 -0
  28. data/lib/pio/open_flow.rb +1 -0
  29. data/lib/pio/open_flow/actions.rb +65 -0
  30. data/lib/pio/open_flow/flags.rb +12 -9
  31. data/lib/pio/open_flow/message.rb +105 -21
  32. data/lib/pio/open_flow/phy_port.rb +31 -27
  33. data/lib/pio/open_flow/type.rb +1 -0
  34. data/lib/pio/packet_in.rb +2 -12
  35. data/lib/pio/packet_out.rb +4 -74
  36. data/lib/pio/send_out_port.rb +1 -0
  37. data/lib/pio/type/ip_address.rb +2 -7
  38. data/lib/pio/version.rb +1 -1
  39. data/pio.gemspec +8 -8
  40. data/spec/pio/echo/reply_spec.rb +61 -6
  41. data/spec/pio/echo/request_spec.rb +61 -6
  42. data/spec/pio/features/reply_spec.rb +81 -4
  43. data/spec/pio/features/request_spec.rb +88 -41
  44. data/spec/pio/flow_mod_spec.rb +151 -0
  45. data/spec/pio/hello_spec.rb +73 -56
  46. data/spec/pio/match_spec.rb +194 -0
  47. data/spec/pio/packet_in_spec.rb +35 -38
  48. data/spec/pio/packet_out_spec.rb +243 -182
  49. data/spec/pio/wildcards_spec.rb +115 -0
  50. metadata +37 -30
  51. data/features/packet_data/echo.raw +0 -0
  52. data/lib/pio/echo/message.rb +0 -49
  53. data/lib/pio/echo/reply.rb +0 -41
  54. data/lib/pio/echo/request.rb +0 -43
  55. data/lib/pio/features/reply.rb +0 -88
  56. data/lib/pio/features/request.rb +0 -68
  57. data/lib/pio/open_flow/parser.rb +0 -22
  58. data/spec/pio/echo_spec.rb +0 -49
  59. data/spec/pio/features_spec.rb +0 -96
@@ -1,41 +0,0 @@
1
- require 'pio/echo/message'
2
- require 'pio/open_flow'
3
-
4
- module Pio
5
- class Echo
6
- # OpenFlow 1.0 Echo Reply message generator.
7
- class Reply < Message
8
- def_format Pio::OpenFlow::Type::ECHO_REPLY
9
-
10
- # Creates an EchoReply OpenFlow message. This message can be
11
- # used to measure the bandwidth of a controller/switch
12
- # connection as well as to verify its liveness.
13
- #
14
- # @overload initialize()
15
- # @example
16
- # Pio::Echo::Reply.new
17
- #
18
- # @overload initialize(transaction_id)
19
- # @example
20
- # Pio::Echo::Reply.new(123)
21
- # @param [Number] transaction_id
22
- # An unsigned 32-bit integer number associated with this
23
- # message.
24
- #
25
- # @overload initialize(user_options)
26
- # @example
27
- # Pio::Echo::Reply.new(
28
- # transaction_id: transaction_id,
29
- # user_data: 'Thu Aug 25 13:09:00 +0900 2011'
30
- # )
31
- # @param [Hash] user_options The options to create a message with.
32
- # @option user_options [Number] :transaction_id
33
- # @option user_options [Number] :xid An alias to transaction_id.
34
- # @option user_options [String] :user_data
35
- # The user data field specified as a String may be a message
36
- # timestamp to check latency, various lengths to measure
37
- # bandwidth or zero-size(nil) to verify liveness between the
38
- # switch and controller.
39
- end
40
- end
41
- end
@@ -1,43 +0,0 @@
1
- require 'pio/echo/message'
2
- require 'pio/open_flow'
3
-
4
- module Pio
5
- class Echo
6
- # OpenFlow 1.0 Echo Request message.
7
- class Request < Message
8
- def_format Pio::OpenFlow::Type::ECHO_REQUEST
9
-
10
- # Creates an EchoRequest OpenFlow message. This message can be
11
- # used to measure the bandwidth of a controller/switch
12
- # connection as well as to verify its liveness.
13
- #
14
- # @overload initialize()
15
- # @example
16
- # Pio::Echo::Request.new
17
- #
18
- # @overload initialize(transaction_id)
19
- # @example
20
- # Pio::Echo::Request.new(123)
21
- # @param [Number] transaction_id
22
- # An unsigned 32-bit integer number associated with this
23
- # message.
24
- #
25
- # @overload initialize(user_options)
26
- # @example
27
- # Pio::Echo::Request.new(
28
- # transaction_id: transaction_id,
29
- # user_data: 'Thu Aug 25 13:09:00 +0900 2011'
30
- # )
31
- # @param [Hash] user_options The options to create a message with.
32
- # @option user_options [Number] :transaction_id
33
- # @option user_options [Number] :xid An alias to transaction_id.
34
- # @option user_options [String] :user_data
35
- # The user data field specified as a String may be a message
36
- # timestamp to check latency, various lengths to measure
37
- # bandwidth or zero-size(nil) to verify liveness between the
38
- # switch and controller.
39
- #
40
- # rubocop:disable MethodLength
41
- end
42
- end
43
- end
@@ -1,88 +0,0 @@
1
- require 'bindata'
2
- require 'pio/open_flow'
3
-
4
- module Pio
5
- # OpenFlow 1.0 Features messages
6
- class Features
7
- # OpenFlow 1.0 Features Reply message
8
- class Reply < Pio::OpenFlow::Message
9
- # Message body of features reply.
10
- class Body < BinData::Record
11
- extend Flags
12
-
13
- # enum ofp_capabilities
14
- def_flags :capabilities,
15
- [:flow_stats,
16
- :table_stats,
17
- :port_stats,
18
- :stp,
19
- :reserved,
20
- :ip_reasm,
21
- :queue_stats,
22
- :arp_match_ip]
23
-
24
- # enum ofp_action_type
25
- def_flags :actions,
26
- [:output,
27
- :set_vlan_vid,
28
- :set_vlan_pcp,
29
- :strip_vlan,
30
- :set_dl_src,
31
- :set_dl_dst,
32
- :set_nw_src,
33
- :set_nw_dst,
34
- :set_nw_tos,
35
- :set_tp_src,
36
- :set_tp_dst,
37
- :enqueue]
38
-
39
- endian :big
40
-
41
- uint64 :datapath_id
42
- uint32 :n_buffers
43
- uint8 :n_tables
44
- uint24 :padding
45
- hide :padding
46
- capabilities :capabilities
47
- actions :actions
48
- array :ports, type: :phy_port, read_until: :eof
49
-
50
- def empty?
51
- false
52
- end
53
-
54
- def length
55
- 24 + ports.to_binary_s.length
56
- end
57
- end
58
-
59
- def_format Pio::OpenFlow::Type::FEATURES_REPLY
60
-
61
- def datapath_id
62
- @format.body.datapath_id
63
- end
64
-
65
- def_delegators :body, :datapath_id
66
- def_delegator :body, :datapath_id, :dpid
67
- def_delegators :body, :n_buffers
68
- def_delegators :body, :n_tables
69
- def_delegators :body, :capabilities
70
- def_delegators :body, :actions
71
- def_delegators :body, :ports
72
-
73
- # @reek This method smells of :reek:FeatureEnvy
74
- def initialize(user_options)
75
- body_options =
76
- {
77
- datapath_id: user_options[:dpid],
78
- n_buffers: user_options[:n_buffers],
79
- n_tables: user_options[:n_tables],
80
- capabilities: user_options[:capabilities],
81
- actions: user_options[:actions],
82
- ports: user_options[:ports]
83
- }
84
- @format = Format.new(user_options.merge(body: body_options))
85
- end
86
- end
87
- end
88
- end
@@ -1,68 +0,0 @@
1
- require 'forwardable'
2
- require 'pio/open_flow'
3
-
4
- module Pio
5
- # OpenFlow 1.0 Features messages
6
- class Features
7
- # OpenFlow 1.0 Features Request message
8
- class Request < Pio::OpenFlow::Message
9
- # Message body of Features Request
10
- class Body < BinData::Record
11
- endian :big
12
-
13
- string :body # ignored
14
-
15
- def length
16
- 0
17
- end
18
-
19
- def empty?
20
- true
21
- end
22
- end
23
-
24
- def_format Pio::OpenFlow::Type::FEATURES_REQUEST
25
-
26
- # Creates a Features Request OpenFlow message.
27
- #
28
- # @overload initialize()
29
- # @example
30
- # Pio::Features::Request.new
31
- #
32
- # @overload initialize(transaction_id)
33
- # @example
34
- # Pio::Features::Request.new(123)
35
- # @param [Number] transaction_id
36
- # An unsigned 32-bit integer number associated with this
37
- # message.
38
- #
39
- # @overload initialize(user_options)
40
- # @example
41
- # Pio::Features::Request.new(transaction_id: 123)
42
- # Pio::Features::Request.new(xid: 123)
43
- # @param [Hash] user_options
44
- # The options to create a message with.
45
- # @option user_options [Number] :transaction_id
46
- # @option user_options [Number] :xid An alias to transaction_id.
47
- #
48
- # @reek This method smells of :reek:FeatureEnvy
49
- # rubocop:disable MethodLength
50
- def initialize(user_options = {})
51
- options = if user_options.respond_to?(:to_i)
52
- { open_flow_header: { transaction_id: user_options.to_i } }
53
- elsif user_options.respond_to?(:fetch)
54
- transaction_id =
55
- user_options[:transaction_id] || user_options[:xid] || 0
56
- { open_flow_header: { transaction_id: transaction_id } }
57
- else
58
- fail TypeError
59
- end
60
- if options[:open_flow_header][:transaction_id] >= 2**32
61
- fail ArgumentError, 'Transaction ID >= 2**32'
62
- end
63
- @format = Format.new(options)
64
- end
65
- # rubocop:enable MethodLength
66
- end
67
- end
68
- end
@@ -1,22 +0,0 @@
1
- require 'pio/open_flow'
2
- require 'pio/parse_error'
3
-
4
- module Pio
5
- module OpenFlow
6
- # OpenFlow message parser interface.
7
- module Parser
8
- # This method smells of :reek:TooManyStatements
9
- # This method smells of :reek:FeatureEnvy
10
- def read(raw_data)
11
- header = Pio::Type::OpenFlow::OpenFlowHeader.read(raw_data)
12
- klass = const_get(:KLASS).fetch(header.message_type)
13
- format = klass.const_get(:Format).read(raw_data)
14
- message = klass.allocate
15
- message.instance_variable_set :@format, format
16
- message
17
- rescue KeyError
18
- raise Pio::ParseError, 'Invalid OpenFlow message.'
19
- end
20
- end
21
- end
22
- end
@@ -1,49 +0,0 @@
1
- require 'pio/echo'
2
-
3
- describe Pio::Echo do
4
- describe '.read' do
5
- context 'with an Echo Request message' do
6
- Given(:echo_request_dump) do
7
- [1, 2, 0, 8, 0, 0, 0, 0].pack('C*')
8
- end
9
-
10
- When(:echo_request) do
11
- Pio::Echo.read echo_request_dump
12
- end
13
-
14
- Then { echo_request.class == Pio::Echo::Request }
15
- Then { echo_request.ofp_version == 1 }
16
- Then { echo_request.message_type == Pio::OpenFlow::Type::ECHO_REQUEST }
17
- Then { echo_request.message_length == 8 }
18
- Then { echo_request.xid == 0 }
19
- Then { echo_request.user_data == '' }
20
- Then { echo_request.to_binary == echo_request_dump }
21
- end
22
-
23
- context 'with an Echo Reply message' do
24
- Given(:echo_reply_dump) do
25
- [1, 3, 0, 8, 0, 0, 0, 0].pack('C*')
26
- end
27
-
28
- When(:echo_reply) do
29
- Pio::Echo.read echo_reply_dump
30
- end
31
-
32
- Then { echo_reply.class == Pio::Echo::Reply }
33
- Then { echo_reply.ofp_version == 1 }
34
- Then { echo_reply.message_type == Pio::OpenFlow::Type::ECHO_REPLY }
35
- Then { echo_reply.message_length == 8 }
36
- Then { echo_reply.xid == 0 }
37
- Then { echo_reply.user_data == '' }
38
- Then { echo_reply.to_binary == echo_reply_dump }
39
- end
40
-
41
- context 'with a Features Request message' do
42
- Given(:features_request_dump) { [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
43
-
44
- When(:result) { Pio::Echo.read(features_request_dump) }
45
-
46
- Then { result == Failure(Pio::ParseError) }
47
- end
48
- end
49
- end
@@ -1,96 +0,0 @@
1
- require 'pio/features'
2
-
3
- describe Pio::Features do
4
- describe '.read' do
5
- context 'with a Features Request message' do
6
- Given(:request_dump) { [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
7
-
8
- When(:request) { Pio::Features.read(request_dump) }
9
-
10
- Then { request.class == Pio::Features::Request }
11
- Then { request.ofp_version == 1 }
12
- Then do
13
- request.message_type ==
14
- Pio::OpenFlow::Type::FEATURES_REQUEST
15
- end
16
- Then { request.message_length == 8 }
17
- Then { request.transaction_id == 0 }
18
- Then { request.xid == 0 }
19
- Then { request.body.empty? }
20
- end
21
-
22
- context 'with a Features Reply message' do
23
- Given(:reply_dump) do
24
- [0x01, 0x06, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
25
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,
26
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00,
27
- 0x0f, 0xff, 0x00, 0x02, 0x16, 0x7d, 0xa4, 0x37, 0xba, 0x10,
28
- 0x74, 0x72, 0x65, 0x6d, 0x61, 0x30, 0x2d, 0x30, 0x00, 0x00,
29
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
31
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32
- 0xff, 0xfe, 0x2a, 0xb4, 0xd6, 0x3c, 0x66, 0xba, 0x76, 0x73,
33
- 0x77, 0x5f, 0x30, 0x78, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
34
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
35
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00,
36
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
37
- 0x62, 0x94, 0x3a, 0xf6, 0x40, 0xdb, 0x74, 0x72, 0x65, 0x6d,
38
- 0x61, 0x31, 0x2d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40
- 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].pack('C*')
42
- end
43
-
44
- When(:reply) { Pio::Features.read(reply_dump) }
45
-
46
- Then { reply.class == Pio::Features::Reply }
47
- Then { reply.ofp_version == 1 }
48
- Then do
49
- reply.message_type ==
50
- Pio::OpenFlow::Type::FEATURES_REPLY
51
- end
52
- Then { reply.message_length == 176 }
53
- Then { reply.transaction_id == 2 }
54
- Then { reply.xid == 2 }
55
- Then { !reply.body.empty? }
56
- Then { reply.datapath_id == 1 }
57
- Then { reply.n_buffers == 0x100 }
58
- Then { reply.n_tables == 1 }
59
- Then do
60
- reply.capabilities ==
61
- [:flow_stats, :table_stats, :port_stats, :arp_match_ip]
62
- end
63
- Then do
64
- reply.actions ==
65
- [:output, :set_vlan_vid, :set_vlan_pcp, :strip_vlan,
66
- :set_dl_src, :set_dl_dst, :set_nw_src, :set_nw_dst,
67
- :set_nw_tos, :set_tp_src, :set_tp_dst, :enqueue]
68
- end
69
- Then { reply.ports.size == 3 }
70
- Then { reply.ports.all? { |each| each.port_no > 0 } }
71
- Then do
72
- reply.ports.all? do |each|
73
- /^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/i=~
74
- each.hardware_address.to_s
75
- end
76
- end
77
- Then { reply.ports.all? { |each| !each.name.empty? } }
78
- Then { reply.ports.any? { |each| each.config == [:port_down] } }
79
- Then { reply.ports.any? { |each| each.state == [:link_down] } }
80
- Then { reply.ports.all? { |each| each.curr.include? :port_copper } }
81
- Then { reply.ports.all? { |each| each.advertised.empty? } }
82
- Then { reply.ports.all? { |each| each.supported.empty? } }
83
- Then { reply.ports.all? { |each| each.peer.empty? } }
84
- end
85
-
86
- context 'with a Hello message' do
87
- Given(:hello_dump) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
88
-
89
- When(:result) { Pio::Features.read(hello_dump) }
90
-
91
- Then do
92
- result == Failure(Pio::ParseError, 'Invalid OpenFlow message.')
93
- end
94
- end
95
- end
96
- end