pio 0.9.0 → 0.10.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 (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