pio 0.10.1 → 0.11.0

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