pio 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -0
  4. data/examples/packet_out_new.rb +18 -0
  5. data/examples/packet_out_read.rb +6 -0
  6. data/features/packet_out_read.feature +5 -0
  7. data/lib/pio.rb +1 -0
  8. data/lib/pio/echo.rb +5 -6
  9. data/lib/pio/echo/message.rb +39 -13
  10. data/lib/pio/echo/reply.rb +1 -6
  11. data/lib/pio/echo/request.rb +3 -5
  12. data/lib/pio/enqueue.rb +62 -0
  13. data/lib/pio/features.rb +5 -15
  14. data/lib/pio/features/reply.rb +28 -48
  15. data/lib/pio/features/request.rb +11 -8
  16. data/lib/pio/hello.rb +24 -2
  17. data/lib/pio/monkey_patch/integer.rb +6 -0
  18. data/lib/pio/monkey_patch/integer/ranges.rb +22 -0
  19. data/lib/pio/open_flow.rb +1 -0
  20. data/lib/pio/open_flow/flags.rb +40 -26
  21. data/lib/pio/open_flow/message.rb +28 -0
  22. data/lib/pio/open_flow/parser.rb +22 -0
  23. data/lib/pio/open_flow/phy_port.rb +34 -50
  24. data/lib/pio/open_flow/port_number.rb +39 -0
  25. data/lib/pio/open_flow/type.rb +1 -0
  26. data/lib/pio/packet_in.rb +42 -9
  27. data/lib/pio/packet_out.rb +102 -0
  28. data/lib/pio/send_out_port.rb +74 -0
  29. data/lib/pio/set_eth_addr.rb +52 -0
  30. data/lib/pio/set_ip_addr.rb +49 -0
  31. data/lib/pio/set_ip_tos.rb +42 -0
  32. data/lib/pio/set_transport_port.rb +58 -0
  33. data/lib/pio/set_vlan.rb +37 -0
  34. data/lib/pio/set_vlan_priority.rb +18 -0
  35. data/lib/pio/set_vlan_vid.rb +30 -0
  36. data/lib/pio/strip_vlan_header.rb +19 -0
  37. data/lib/pio/type/ip_address.rb +11 -2
  38. data/lib/pio/type/ipv4_header.rb +1 -0
  39. data/lib/pio/type/mac_address.rb +2 -0
  40. data/lib/pio/version.rb +1 -1
  41. data/pio.gemspec +4 -4
  42. data/spec/pio/echo/reply_spec.rb +1 -4
  43. data/spec/pio/echo/request_spec.rb +5 -8
  44. data/spec/pio/enqueue_spec.rb +67 -0
  45. data/spec/pio/features/request_spec.rb +4 -4
  46. data/spec/pio/features_spec.rb +1 -2
  47. data/spec/pio/hello_spec.rb +1 -4
  48. data/spec/pio/packet_out_spec.rb +290 -0
  49. data/spec/pio/send_out_port_spec.rb +121 -0
  50. data/spec/pio/set_eth_dst_addr_spec.rb +28 -0
  51. data/spec/pio/set_eth_src_addr_spec.rb +28 -0
  52. data/spec/pio/set_ip_dst_addr_spec.rb +25 -0
  53. data/spec/pio/set_ip_src_addr_spec.rb +25 -0
  54. data/spec/pio/set_ip_tos_spec.rb +30 -0
  55. data/spec/pio/set_transport_dst_port_spec.rb +42 -0
  56. data/spec/pio/set_transport_src_port_spec.rb +42 -0
  57. data/spec/pio/set_vlan_priority_spec.rb +42 -0
  58. data/spec/pio/set_vlan_vid_spec.rb +42 -0
  59. data/spec/pio/strip_vlan_header_spec.rb +20 -0
  60. metadata +55 -13
  61. data/lib/pio/echo/format.rb +0 -22
  62. data/lib/pio/hello/format.rb +0 -18
  63. data/lib/pio/packet_in/format.rb +0 -55
@@ -29,10 +29,7 @@ describe Pio::Echo::Reply do
29
29
  context 'with 2**32' do
30
30
  When(:result) { Pio::Echo::Reply.new(2**32) }
31
31
 
32
- Then do
33
- pending 'check if xid is within 32bit range.'
34
- result == Failure(ArgumentError)
35
- end
32
+ Then { result == Failure(ArgumentError) }
36
33
  end
37
34
 
38
35
  context 'with transaction_id: 123' do
@@ -10,7 +10,7 @@ describe Pio::Echo::Request do
10
10
  Then { echo_request.message_length == 8 }
11
11
  Then { echo_request.transaction_id == 0 }
12
12
  Then { echo_request.xid == 0 }
13
- Then { echo_request.user_data == '' }
13
+ Then { echo_request.user_data.empty? }
14
14
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 0].pack('C*') }
15
15
  end
16
16
 
@@ -22,17 +22,14 @@ describe Pio::Echo::Request do
22
22
  Then { echo_request.message_length == 8 }
23
23
  Then { echo_request.transaction_id == 123 }
24
24
  Then { echo_request.xid == 123 }
25
- Then { echo_request.user_data == '' }
25
+ Then { echo_request.user_data.empty? }
26
26
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
27
27
  end
28
28
 
29
29
  context 'with 2**32' do
30
30
  When(:result) { Pio::Echo::Request.new(2**32) }
31
31
 
32
- Then do
33
- pending 'check if xid is within 32bit range.'
34
- result == Failure(ArgumentError)
35
- end
32
+ Then { result == Failure(ArgumentError) }
36
33
  end
37
34
 
38
35
  context 'with transaction_id: 123' do
@@ -43,7 +40,7 @@ describe Pio::Echo::Request do
43
40
  Then { echo_request.message_length == 8 }
44
41
  Then { echo_request.transaction_id == 123 }
45
42
  Then { echo_request.xid == 123 }
46
- Then { echo_request.user_data == '' }
43
+ Then { echo_request.user_data.empty? }
47
44
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
48
45
  end
49
46
 
@@ -55,7 +52,7 @@ describe Pio::Echo::Request do
55
52
  Then { echo_request.message_length == 8 }
56
53
  Then { echo_request.transaction_id == 123 }
57
54
  Then { echo_request.xid == 123 }
58
- Then { echo_request.user_data == '' }
55
+ Then { echo_request.user_data.empty? }
59
56
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
60
57
  end
61
58
 
@@ -0,0 +1,67 @@
1
+ require 'pio/enqueue'
2
+
3
+ describe Pio::Enqueue do
4
+ describe '.new' do
5
+ context 'with port_number: 1, queue_id: 2' do
6
+ When(:enqueue) { Pio::Enqueue.new(port_number: 1, queue_id: 2) }
7
+
8
+ describe '#port_number' do
9
+ Then { enqueue.port_number == 1 }
10
+ end
11
+
12
+ describe '#queue_id' do
13
+ Then { enqueue.queue_id == 2 }
14
+ end
15
+
16
+ describe '#type' do
17
+ Then { enqueue.type == 11 }
18
+ end
19
+
20
+ describe '#message_length' do
21
+ Then { enqueue.message_length == 16 }
22
+ end
23
+
24
+ describe '#to_binary' do
25
+ Then { enqueue.to_binary.length == 16 }
26
+ end
27
+ end
28
+
29
+ context 'with port_number: :in_port, queue_id: 2' do
30
+ When(:enqueue) { Pio::Enqueue.new(port_number: :in_port, queue_id: 2) }
31
+
32
+ describe '#port_number' do
33
+ Then { enqueue.port_number == :in_port }
34
+ end
35
+ end
36
+
37
+ context 'with port_number: :local, queue_id: 2' do
38
+ When(:enqueue) { Pio::Enqueue.new(port_number: :local, queue_id: 2) }
39
+ Then { enqueue == Failure(ArgumentError) }
40
+ end
41
+
42
+ context 'with port_number: -1, queue_id: 2' do
43
+ When(:enqueue) { Pio::Enqueue.new(port_number: -1, queue_id: 2) }
44
+ Then { enqueue == Failure(ArgumentError) }
45
+ end
46
+
47
+ context 'with port_number: 0xff00, queue_id: 2' do
48
+ When(:enqueue) { Pio::Enqueue.new(port_number: 0xff00, queue_id: 2) }
49
+ Then { enqueue == Failure(ArgumentError) }
50
+ end
51
+
52
+ context 'with port_number: 1, queue_id: -2' do
53
+ When(:enqueue) { Pio::Enqueue.new(port_number: 1, queue_id: -2) }
54
+ Then { enqueue == Failure(ArgumentError) }
55
+ end
56
+
57
+ context 'with port_number: 1' do
58
+ When(:enqueue) { Pio::Enqueue.new(port_number: 1) }
59
+ Then { enqueue == Failure(ArgumentError) }
60
+ end
61
+
62
+ context 'with queue_id: 2' do
63
+ When(:enqueue) { Pio::Enqueue.new(queue_id: 2) }
64
+ Then { enqueue == Failure(ArgumentError) }
65
+ end
66
+ end
67
+ end
@@ -10,7 +10,7 @@ describe Pio::Features::Request do
10
10
  Then { request.message_length == 8 }
11
11
  Then { request.transaction_id == 0 }
12
12
  Then { request.xid == 0 }
13
- Then { request.body == '' }
13
+ Then { request.body.empty? }
14
14
  Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
15
15
  end
16
16
 
@@ -22,7 +22,7 @@ describe Pio::Features::Request do
22
22
  Then { request.message_length == 8 }
23
23
  Then { request.transaction_id == 123 }
24
24
  Then { request.xid == 123 }
25
- Then { request.body == '' }
25
+ Then { request.body.empty? }
26
26
  Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
27
27
  end
28
28
 
@@ -40,7 +40,7 @@ describe Pio::Features::Request do
40
40
  Then { request.message_length == 8 }
41
41
  Then { request.transaction_id == 123 }
42
42
  Then { request.xid == 123 }
43
- Then { request.body == '' }
43
+ Then { request.body.empty? }
44
44
  Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
45
45
  end
46
46
 
@@ -52,7 +52,7 @@ describe Pio::Features::Request do
52
52
  Then { request.message_length == 8 }
53
53
  Then { request.transaction_id == 123 }
54
54
  Then { request.xid == 123 }
55
- Then { request.body == '' }
55
+ Then { request.body.empty? }
56
56
  Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
57
57
  end
58
58
 
@@ -89,8 +89,7 @@ describe Pio::Features do
89
89
  When(:result) { Pio::Features.read(hello_dump) }
90
90
 
91
91
  Then do
92
- result == Failure(Pio::ParseError,
93
- 'Invalid features request/reply message.')
92
+ result == Failure(Pio::ParseError, 'Invalid OpenFlow message.')
94
93
  end
95
94
  end
96
95
  end
@@ -53,10 +53,7 @@ describe Pio::Hello do
53
53
  context 'with 2**32' do
54
54
  When(:result) { Pio::Hello.new(2**32) }
55
55
 
56
- Then do
57
- pending 'check if xid is within 32bit range.'
58
- result == Failure(ArgumentError)
59
- end
56
+ Then { result == Failure(ArgumentError) }
60
57
  end
61
58
 
62
59
  context 'with transaction_id: 123' do
@@ -0,0 +1,290 @@
1
+ require 'pio/packet_out'
2
+
3
+ describe Pio::PacketOut do
4
+ Given(:header_dump) do
5
+ [
6
+ 0x01,
7
+ 0x0d,
8
+ 0x00, 0x58,
9
+ 0x00, 0x00, 0x00, 0x16
10
+ ].pack('C*')
11
+ end
12
+
13
+ Given(:data_dump) do
14
+ [
15
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
16
+ 0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
17
+ 0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
18
+ 0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
21
+ 0x00, 0x00, 0x00, 0x00
22
+ ].pack('C*')
23
+ end
24
+
25
+ Given(:body_dump) do
26
+ [
27
+ 0xff, 0xff, 0xff, 0xff,
28
+ 0xff, 0xff,
29
+ 0x00, 0x08,
30
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0xff, 0xff
31
+ ].pack('C*') + data_dump
32
+ end
33
+
34
+ Given(:packet_out_dump) { header_dump + body_dump }
35
+
36
+ describe '.read' do
37
+ context 'with a Packet-Out message' do
38
+ When(:packet_out) do
39
+ Pio::PacketOut.read(packet_out_dump)
40
+ end
41
+
42
+ Then { packet_out.class == Pio::PacketOut }
43
+ Then { packet_out.ofp_version == 0x1 }
44
+ Then { packet_out.message_type == 0xd }
45
+ Then { packet_out.message_length == 0x58 }
46
+ Then { packet_out.transaction_id == 0x16 }
47
+ Then { packet_out.xid == 0x16 }
48
+
49
+ Then { !packet_out.body.empty? }
50
+ Then { packet_out.buffer_id == 0xffffffff }
51
+ Then { packet_out.in_port == 0xffff }
52
+ Then { packet_out.actions_len == 0x8 }
53
+ Then { packet_out.actions.length == 1 }
54
+ Then { packet_out.actions[0].is_a? Pio::SendOutPort }
55
+ Then { packet_out.actions[0].port_number == 2 }
56
+ Then { packet_out.actions[0].max_len == 2**16 - 1 }
57
+ Then { packet_out.data.length == 64 }
58
+ end
59
+ end
60
+
61
+ describe '.new' do
62
+ context 'with a SendOutPort action' do
63
+ Given(:packet_out) do
64
+ Pio::PacketOut.new(transaction_id: 0x16,
65
+ buffer_id: 0xffffffff,
66
+ in_port: 0xffff,
67
+ actions: Pio::SendOutPort.new(2),
68
+ data: data_dump)
69
+ end
70
+
71
+ Then { packet_out.ofp_version == 0x1 }
72
+ Then { packet_out.message_type == 0xd }
73
+ Then { packet_out.message_length == 0x58 }
74
+ Then { packet_out.transaction_id == 0x16 }
75
+ Then { packet_out.xid == 0x16 }
76
+
77
+ Then { !packet_out.body.empty? }
78
+ Then { packet_out.buffer_id == 0xffffffff }
79
+ Then { packet_out.in_port == 0xffff }
80
+ Then { packet_out.actions_len == 0x8 }
81
+ Then { packet_out.actions.length == 1 }
82
+ Then { packet_out.actions[0].is_a? Pio::SendOutPort }
83
+ Then { packet_out.actions[0].port_number == 2 }
84
+ Then { packet_out.actions[0].max_len == 2**16 - 1 }
85
+ Then { packet_out.data.length == 64 }
86
+
87
+ context '#to_binary' do
88
+ When(:binary) { packet_out.to_binary }
89
+ Then { binary == packet_out_dump }
90
+ end
91
+ end
92
+
93
+ context 'with a SetVlanVid action' do
94
+ Given(:packet_out) do
95
+ Pio::PacketOut.new(transaction_id: 0x16,
96
+ buffer_id: 0xffffffff,
97
+ in_port: 0xffff,
98
+ actions: Pio::SetVlanVid.new(10),
99
+ data: data_dump)
100
+ end
101
+
102
+ Then { packet_out.message_length == 0x58 }
103
+ Then { packet_out.actions_len == 0x8 }
104
+ Then { packet_out.actions.length == 1 }
105
+ Then { packet_out.actions[0].is_a? Pio::SetVlanVid }
106
+ Then { packet_out.actions[0].vlan_id == 10 }
107
+ end
108
+
109
+ context 'with a SetVlanPriority action' do
110
+ Given(:packet_out) do
111
+ Pio::PacketOut.new(transaction_id: 0x16,
112
+ buffer_id: 0xffffffff,
113
+ in_port: 0xffff,
114
+ actions: Pio::SetVlanPriority.new(3),
115
+ data: data_dump)
116
+ end
117
+
118
+ Then { packet_out.message_length == 0x58 }
119
+ Then { packet_out.actions_len == 0x8 }
120
+ Then { packet_out.actions.length == 1 }
121
+ Then { packet_out.actions[0].is_a? Pio::SetVlanPriority }
122
+ Then { packet_out.actions[0].vlan_priority == 3 }
123
+ end
124
+
125
+ context 'with a StripVlanHeader action' do
126
+ Given(:packet_out) do
127
+ Pio::PacketOut.new(transaction_id: 0x16,
128
+ buffer_id: 0xffffffff,
129
+ in_port: 0xffff,
130
+ actions: Pio::StripVlanHeader.new,
131
+ data: data_dump)
132
+ end
133
+
134
+ Then { packet_out.message_length == 0x58 }
135
+ Then { packet_out.actions_len == 0x8 }
136
+ Then { packet_out.actions.length == 1 }
137
+ Then { packet_out.actions[0].is_a? Pio::StripVlanHeader }
138
+ end
139
+
140
+ context 'with a SetEthSrcAddr action' do
141
+ Given(:packet_out) do
142
+ Pio::PacketOut.new(transaction_id: 0x16,
143
+ buffer_id: 0xffffffff,
144
+ in_port: 0xffff,
145
+ actions: Pio::SetEthSrcAddr.new('11:22:33:44:55:66'),
146
+ data: data_dump)
147
+ end
148
+
149
+ Then { packet_out.message_length == 0x60 }
150
+ Then { packet_out.actions_len == 0x10 }
151
+ Then { packet_out.actions.length == 1 }
152
+ Then { packet_out.actions[0].is_a? Pio::SetEthSrcAddr }
153
+ Then { packet_out.actions[0].mac_address == '11:22:33:44:55:66' }
154
+ end
155
+
156
+ context 'with a SetEthDstAddr action' do
157
+ Given(:packet_out) do
158
+ Pio::PacketOut.new(transaction_id: 0x16,
159
+ buffer_id: 0xffffffff,
160
+ in_port: 0xffff,
161
+ actions: Pio::SetEthDstAddr.new('11:22:33:44:55:66'),
162
+ data: data_dump)
163
+ end
164
+
165
+ Then { packet_out.message_length == 0x60 }
166
+ Then { packet_out.actions_len == 0x10 }
167
+ Then { packet_out.actions.length == 1 }
168
+ Then { packet_out.actions[0].is_a? Pio::SetEthDstAddr }
169
+ Then { packet_out.actions[0].mac_address == '11:22:33:44:55:66' }
170
+ end
171
+
172
+ context 'with a SetIpSrcAddr action' do
173
+ Given(:packet_out) do
174
+ Pio::PacketOut.new(transaction_id: 0x16,
175
+ buffer_id: 0xffffffff,
176
+ in_port: 0xffff,
177
+ actions: Pio::SetIpSrcAddr.new('1.2.3.4'),
178
+ data: data_dump)
179
+ end
180
+
181
+ Then { packet_out.message_length == 0x58 }
182
+ Then { packet_out.actions_len == 0x8 }
183
+ Then { packet_out.actions.length == 1 }
184
+ Then { packet_out.actions[0].is_a? Pio::SetIpSrcAddr }
185
+ Then { packet_out.actions[0].ip_address == '1.2.3.4' }
186
+ end
187
+
188
+ context 'with a SetIpDstAddr action' do
189
+ Given(:packet_out) do
190
+ Pio::PacketOut.new(transaction_id: 0x16,
191
+ buffer_id: 0xffffffff,
192
+ in_port: 0xffff,
193
+ actions: Pio::SetIpDstAddr.new('1.2.3.4'),
194
+ data: data_dump)
195
+ end
196
+
197
+ Then { packet_out.message_length == 0x58 }
198
+ Then { packet_out.actions_len == 0x8 }
199
+ Then { packet_out.actions.length == 1 }
200
+ Then { packet_out.actions[0].is_a? Pio::SetIpDstAddr }
201
+ Then { packet_out.actions[0].ip_address == '1.2.3.4' }
202
+ end
203
+
204
+ context 'with a SetIpTos action' do
205
+ Given(:packet_out) do
206
+ Pio::PacketOut.new(transaction_id: 0x16,
207
+ buffer_id: 0xffffffff,
208
+ in_port: 0xffff,
209
+ actions: Pio::SetIpTos.new(32),
210
+ data: data_dump)
211
+ end
212
+
213
+ Then { packet_out.message_length == 0x58 }
214
+ Then { packet_out.actions_len == 0x8 }
215
+ Then { packet_out.actions.length == 1 }
216
+ Then { packet_out.actions[0].is_a? Pio::SetIpTos }
217
+ Then { packet_out.actions[0].type_of_service == 32 }
218
+ end
219
+
220
+ context 'with a SetTransportSrcPort action' do
221
+ Given(:packet_out) do
222
+ Pio::PacketOut.new(transaction_id: 0x16,
223
+ buffer_id: 0xffffffff,
224
+ in_port: 0xffff,
225
+ actions: Pio::SetTransportSrcPort.new(100),
226
+ data: data_dump)
227
+ end
228
+
229
+ Then { packet_out.message_length == 0x58 }
230
+ Then { packet_out.actions_len == 0x8 }
231
+ Then { packet_out.actions.length == 1 }
232
+ Then { packet_out.actions[0].is_a? Pio::SetTransportSrcPort }
233
+ Then { packet_out.actions[0].port_number == 100 }
234
+ end
235
+
236
+ context 'with a SetTransportDstPort action' do
237
+ Given(:packet_out) do
238
+ Pio::PacketOut.new(transaction_id: 0x16,
239
+ buffer_id: 0xffffffff,
240
+ in_port: 0xffff,
241
+ actions: Pio::SetTransportDstPort.new(100),
242
+ data: data_dump)
243
+ end
244
+
245
+ Then { packet_out.message_length == 0x58 }
246
+ Then { packet_out.actions_len == 0x8 }
247
+ Then { packet_out.actions.length == 1 }
248
+ Then { packet_out.actions[0].is_a? Pio::SetTransportDstPort }
249
+ Then { packet_out.actions[0].port_number == 100 }
250
+ end
251
+
252
+ context 'with a Enqueue action' do
253
+ Given(:packet_out) do
254
+ Pio::PacketOut.new(transaction_id: 0x16,
255
+ buffer_id: 0xffffffff,
256
+ in_port: 0xffff,
257
+ actions: Pio::Enqueue.new(port_number: 1,
258
+ queue_id: 2),
259
+ data: data_dump)
260
+ end
261
+
262
+ Then { packet_out.message_length == 0x60 }
263
+ Then { packet_out.actions_len == 0x10 }
264
+ Then { packet_out.actions.length == 1 }
265
+ Then { packet_out.actions[0].is_a? Pio::Enqueue }
266
+ Then { packet_out.actions[0].port_number == 1 }
267
+ Then { packet_out.actions[0].queue_id == 2 }
268
+ end
269
+
270
+ context 'with SendOutPort and SetVlanVid action' do
271
+ Given(:packet_out) do
272
+ Pio::PacketOut.new(transaction_id: 0x16,
273
+ buffer_id: 0xffffffff,
274
+ in_port: 0xffff,
275
+ actions: [Pio::SendOutPort.new(2),
276
+ Pio::SetVlanVid.new(10)],
277
+ data: data_dump)
278
+ end
279
+
280
+ Then { packet_out.message_length == 0x60 }
281
+ Then { packet_out.actions_len == 0x10 }
282
+ Then { packet_out.actions.length == 2 }
283
+ Then { packet_out.actions[0].is_a? Pio::SendOutPort }
284
+ Then { packet_out.actions[0].port_number == 2 }
285
+ Then { packet_out.actions[0].max_len == 2**16 - 1 }
286
+ Then { packet_out.actions[1].is_a? Pio::SetVlanVid }
287
+ Then { packet_out.actions[1].vlan_id == 10 }
288
+ end
289
+ end
290
+ end