pio 0.25.0 → 0.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +4 -0
  4. data/Rakefile +1 -1
  5. data/bin/terminal-notifier +16 -0
  6. data/features/open_flow10/bad_request.feature +35 -0
  7. data/features/open_flow10/bad_request.raw +0 -0
  8. data/features/open_flow10/barrier_reply.feature +4 -4
  9. data/features/open_flow10/barrier_request.feature +4 -4
  10. data/features/open_flow10/echo_reply.feature +13 -13
  11. data/features/open_flow10/echo_request.feature +6 -6
  12. data/features/open_flow10/enqueue.feature +17 -0
  13. data/features/open_flow10/features_reply.feature +2 -2
  14. data/features/open_flow10/features_request.feature +28 -28
  15. data/features/open_flow10/flow_mod.feature +5 -5
  16. data/features/open_flow10/flow_stats_reply.feature +12 -12
  17. data/features/open_flow10/flow_stats_request.feature +3 -3
  18. data/features/open_flow10/hello.feature +8 -15
  19. data/features/open_flow10/hello_failed.feature +69 -0
  20. data/features/open_flow10/hello_failed.raw +0 -0
  21. data/features/open_flow10/nxast_learn.raw +0 -0
  22. data/features/open_flow10/packet_in.feature +2 -2
  23. data/features/open_flow10/packet_out.feature +13 -13
  24. data/features/open_flow10/port_status.feature +1 -1
  25. data/features/open_flow10/send_out_port.feature +106 -0
  26. data/features/open_flow10/set_ether_destination_address.feature +16 -0
  27. data/features/open_flow10/set_ether_source_address.feature +16 -0
  28. data/features/open_flow10/set_ip_destination_address.feature +16 -0
  29. data/features/open_flow10/set_ip_source_address.feature +16 -0
  30. data/features/open_flow10/set_ip_tos.feature +16 -0
  31. data/features/open_flow10/set_transport_destination_port.feature +16 -0
  32. data/features/open_flow10/set_transport_source_port.feature +16 -0
  33. data/features/open_flow10/set_vlan_priority.feature +16 -0
  34. data/features/open_flow10/set_vlan_vid.feature +16 -0
  35. data/features/open_flow10/strip_vlan_header.feature +15 -0
  36. data/features/open_flow10/vendor_action.feature +14 -0
  37. data/features/open_flow13/bad_request.feature +35 -0
  38. data/features/open_flow13/bad_request.raw +0 -0
  39. data/features/open_flow13/echo_reply.feature +7 -7
  40. data/features/open_flow13/echo_request.feature +7 -7
  41. data/features/open_flow13/features_reply.feature +2 -2
  42. data/features/open_flow13/features_request.feature +28 -28
  43. data/features/open_flow13/flow_mod.feature +24 -24
  44. data/features/open_flow13/hello.feature +5 -5
  45. data/features/open_flow13/hello_failed.feature +69 -0
  46. data/features/open_flow13/hello_failed.raw +0 -0
  47. data/features/open_flow13/packet_in.feature +3 -3
  48. data/features/open_flow13/packet_out.feature +3 -3
  49. data/features/open_flow13/send_out_port.feature +101 -10
  50. data/features/open_flow13/table_stats_reply.raw +0 -0
  51. data/features/open_flow13/table_stats_request.raw +0 -0
  52. data/features/open_flow_read.feature +6 -0
  53. data/features/step_definitions/Gemfile +9 -0
  54. data/features/step_definitions/Gemfile.lock +76 -0
  55. data/features/step_definitions/Guardfile +4 -0
  56. data/features/step_definitions/LICENSE +675 -0
  57. data/features/step_definitions/README.md +7 -0
  58. data/features/step_definitions/Rakefile +10 -0
  59. data/features/step_definitions/dump_flows_steps.rb +13 -0
  60. data/features/step_definitions/packet_data_steps.rb +4 -0
  61. data/features/step_definitions/rest_api_steps.rb +40 -0
  62. data/features/step_definitions/show_stats_steps.rb +83 -0
  63. data/features/step_definitions/trema_steps.rb +33 -0
  64. data/features/step_definitions/virtual_link_steps.rb +8 -0
  65. data/lib/pio/open_flow.rb +5 -5
  66. data/lib/pio/open_flow/echo.rb +1 -1
  67. data/lib/pio/open_flow/error.rb +19 -0
  68. data/lib/pio/open_flow/format.rb +2 -1
  69. data/lib/pio/open_flow/hello_failed_code.rb +21 -0
  70. data/lib/pio/open_flow/open_flow_header.rb +11 -2
  71. data/lib/pio/open_flow/port.rb +69 -0
  72. data/lib/pio/open_flow10.rb +5 -0
  73. data/lib/pio/open_flow10/actions.rb +8 -6
  74. data/lib/pio/open_flow10/enqueue.rb +13 -13
  75. data/lib/pio/open_flow10/error.rb +28 -0
  76. data/lib/pio/open_flow10/error/bad_request.rb +66 -0
  77. data/lib/pio/open_flow10/error/error_type10.rb +26 -0
  78. data/lib/pio/open_flow10/error/hello_failed.rb +41 -0
  79. data/lib/pio/open_flow10/features.rb +6 -10
  80. data/lib/pio/open_flow10/flow_mod.rb +1 -1
  81. data/lib/pio/open_flow10/flow_stats_request.rb +1 -1
  82. data/lib/pio/open_flow10/hello.rb +2 -2
  83. data/lib/pio/open_flow10/match.rb +2 -2
  84. data/lib/pio/{open_flow/phy_port.rb → open_flow10/phy_port16.rb} +4 -4
  85. data/lib/pio/open_flow10/port16.rb +21 -0
  86. data/lib/pio/open_flow10/port_status.rb +1 -1
  87. data/lib/pio/open_flow10/send_out_port.rb +16 -40
  88. data/lib/pio/open_flow10/set_ether_address.rb +9 -8
  89. data/lib/pio/open_flow10/set_ip_address.rb +4 -4
  90. data/lib/pio/open_flow10/set_ip_tos.rb +4 -4
  91. data/lib/pio/open_flow10/set_transport_port.rb +12 -12
  92. data/lib/pio/open_flow10/set_vlan.rb +4 -4
  93. data/lib/pio/open_flow10/set_vlan_vid.rb +0 -12
  94. data/lib/pio/open_flow10/strip_vlan_header.rb +7 -7
  95. data/lib/pio/open_flow10/vendor_action.rb +33 -0
  96. data/lib/pio/open_flow13.rb +3 -0
  97. data/lib/pio/open_flow13/actions.rb +3 -3
  98. data/lib/pio/open_flow13/error.rb +28 -0
  99. data/lib/pio/open_flow13/error/bad_request.rb +66 -0
  100. data/lib/pio/open_flow13/error/error_type13.rb +37 -0
  101. data/lib/pio/open_flow13/error/hello_failed.rb +42 -0
  102. data/lib/pio/open_flow13/features_reply.rb +33 -33
  103. data/lib/pio/open_flow13/features_request.rb +3 -3
  104. data/lib/pio/open_flow13/flow_mod.rb +2 -2
  105. data/lib/pio/open_flow13/goto_table.rb +2 -0
  106. data/lib/pio/open_flow13/packet_out.rb +1 -1
  107. data/lib/pio/open_flow13/port32.rb +21 -0
  108. data/lib/pio/open_flow13/send_out_port.rb +3 -2
  109. data/lib/pio/version.rb +1 -1
  110. data/pio.gemspec +5 -5
  111. data/spec/pio/open_flow10/enqueue_spec.rb +22 -22
  112. data/spec/pio/open_flow10/error/hello_failed_spec.rb +26 -0
  113. data/spec/pio/open_flow10/flow_mod_spec.rb +6 -6
  114. data/spec/pio/open_flow10/flow_stats_request_spec.rb +1 -1
  115. data/spec/pio/open_flow10/hello_spec.rb +3 -3
  116. data/spec/pio/open_flow10/packet_out_spec.rb +33 -33
  117. data/spec/pio/{open_flow/phy_port_spec.rb → open_flow10/phy_port16_spec.rb} +7 -7
  118. data/spec/pio/open_flow10/send_out_port_spec.rb +28 -28
  119. data/spec/pio/open_flow10/set_ether_destination_address_spec.rb +2 -2
  120. data/spec/pio/open_flow10/set_ether_source_address_spec.rb +2 -2
  121. data/spec/pio/open_flow10/set_ip_destination_address_spec.rb +4 -4
  122. data/spec/pio/open_flow10/set_ip_source_address_spec.rb +4 -4
  123. data/spec/pio/open_flow10/set_ip_tos_spec.rb +4 -4
  124. data/spec/pio/open_flow10/set_transport_destination_port_spec.rb +11 -11
  125. data/spec/pio/open_flow10/set_transport_source_port_spec.rb +11 -11
  126. data/spec/pio/open_flow10/set_vlan_priority_spec.rb +4 -4
  127. data/spec/pio/open_flow10/set_vlan_vid_spec.rb +4 -4
  128. data/spec/pio/open_flow10/strip_vlan_header_spec.rb +6 -6
  129. data/spec/pio/open_flow13/error/bad_request_spec.rb +6 -0
  130. data/spec/pio/open_flow13/error/hello_failed_spec.rb +26 -0
  131. data/spec/pio/open_flow13/hello_spec.rb +3 -3
  132. data/spec/support/shared_examples_for_openflow_messages.rb +40 -27
  133. metadata +106 -16
  134. data/lib/pio/open_flow/port_number.rb +0 -39
@@ -6,13 +6,13 @@ module Pio
6
6
  # An action to modify the source/destination Ethernet address of a packet.
7
7
  class SetEtherAddress
8
8
  # rubocop:disable MethodLength
9
- def self.def_format(action_type)
9
+ def self.action_type(action_type)
10
10
  str = %(
11
11
  class Format < BinData::Record
12
12
  endian :big
13
13
 
14
- uint16 :type, value: #{action_type}
15
- uint16 :message_length, value: 16
14
+ uint16 :action_type, value: #{action_type}
15
+ uint16 :action_length, value: 16
16
16
  mac_address :mac_address
17
17
  uint48 :padding
18
18
  hide :padding
@@ -31,7 +31,8 @@ module Pio
31
31
 
32
32
  extend Forwardable
33
33
 
34
- def_delegators :@format, :message_length
34
+ def_delegators :@format, :action_type
35
+ def_delegator :@format, :action_length, :length
35
36
  def_delegators :@format, :mac_address
36
37
  def_delegator :@format, :to_binary_s, :to_binary
37
38
 
@@ -41,12 +42,12 @@ module Pio
41
42
  end
42
43
 
43
44
  # An action to modify the source Ethernet address of a packet.
44
- class SetEtherSourceAddr < SetEtherAddress
45
- def_format 4
45
+ class SetEtherSourceAddress < SetEtherAddress
46
+ action_type 4
46
47
  end
47
48
 
48
49
  # An action to modify the destination Ethernet address of a packet.
49
- class SetEtherDestinationAddr < SetEtherAddress
50
- def_format 5
50
+ class SetEtherDestinationAddress < SetEtherAddress
51
+ action_type 5
51
52
  end
52
53
  end
@@ -10,8 +10,8 @@ module Pio
10
10
  class Format < BinData::Record
11
11
  endian :big
12
12
 
13
- uint16 :type, value: #{action_type}
14
- uint16 :message_length, value: 8
13
+ uint16 :action_type, value: #{action_type}
14
+ uint16 :action_length, value: 8
15
15
  ip_address :ip_address
16
16
  end
17
17
  )
@@ -27,8 +27,8 @@ module Pio
27
27
 
28
28
  extend Forwardable
29
29
 
30
- def_delegators :@format, :type
31
- def_delegators :@format, :message_length
30
+ def_delegators :@format, :action_type
31
+ def_delegator :@format, :action_length, :length
32
32
  def_delegators :@format, :ip_address
33
33
  def_delegator :@format, :to_binary_s, :to_binary
34
34
 
@@ -9,8 +9,8 @@ module Pio
9
9
  class Format < BinData::Record
10
10
  endian :big
11
11
 
12
- uint16 :type, value: 8
13
- uint16 :message_length, value: 8
12
+ uint16 :action_type, value: 8
13
+ uint16 :action_length, value: 8
14
14
  uint8 :type_of_service
15
15
  uint24 :padding
16
16
  hide :padding
@@ -24,8 +24,8 @@ module Pio
24
24
 
25
25
  extend Forwardable
26
26
 
27
- def_delegators :@format, :type
28
- def_delegators :@format, :message_length
27
+ def_delegators :@format, :action_type
28
+ def_delegator :@format, :action_length, :length
29
29
  def_delegators :@format, :type_of_service
30
30
  def_delegator :@format, :to_binary_s, :to_binary
31
31
 
@@ -6,14 +6,14 @@ module Pio
6
6
  # An action to modify the source/destination TCP/UDP port of a packet.
7
7
  class SetTransportPort
8
8
  # rubocop:disable MethodLength
9
- def self.def_format(action_type)
9
+ def self.action_type(action_type)
10
10
  str = %(
11
11
  class Format < BinData::Record
12
12
  endian :big
13
13
 
14
- uint16 :type, value: #{action_type}
15
- uint16 :message_length, value: 8
16
- uint16 :port_number
14
+ uint16 :action_type, value: #{action_type}
15
+ uint16 :action_length, value: 8
16
+ uint16 :port
17
17
  uint16 :padding
18
18
  hide :padding
19
19
  end
@@ -30,17 +30,17 @@ module Pio
30
30
 
31
31
  extend Forwardable
32
32
 
33
- def_delegators :@format, :type
34
- def_delegators :@format, :message_length
35
- def_delegators :@format, :port_number
33
+ def_delegators :@format, :action_type
34
+ def_delegator :@format, :action_length, :length
35
+ def_delegators :@format, :port
36
36
  def_delegator :@format, :to_binary_s, :to_binary
37
37
 
38
38
  def initialize(number)
39
- port_number = number.to_i
40
- unless port_number.unsigned_16bit?
39
+ port = number.to_i
40
+ unless port.unsigned_16bit?
41
41
  fail ArgumentError, 'TCP/UDP port must be an unsigned 16-bit integer.'
42
42
  end
43
- @format = self.class.const_get(:Format).new(port_number: port_number)
43
+ @format = self.class.const_get(:Format).new(port: port)
44
44
  rescue NoMethodError
45
45
  raise TypeError, 'TCP/UDP port must be an unsigned 16-bit integer.'
46
46
  end
@@ -48,11 +48,11 @@ module Pio
48
48
 
49
49
  # An action to modify the source TCP/UDP port of a packet.
50
50
  class SetTransportSourcePort < SetTransportPort
51
- def_format 9
51
+ action_type 9
52
52
  end
53
53
 
54
54
  # An action to modify the source TCP/UDP port of a packet.
55
55
  class SetTransportDestinationPort < SetTransportPort
56
- def_format 10
56
+ action_type 10
57
57
  end
58
58
  end
@@ -12,8 +12,8 @@ module Pio
12
12
  class Format < BinData::Record
13
13
  endian :big
14
14
 
15
- uint16 :type, value: #{action_type}
16
- uint16 :message_length, value: 8
15
+ uint16 :action_type, value: #{action_type}
16
+ uint16 :action_length, value: 8
17
17
  uint16 :#{field_name}
18
18
  uint16 :padding
19
19
  hide :padding
@@ -30,8 +30,8 @@ module Pio
30
30
  set_vlan
31
31
  end
32
32
 
33
- def_delegators :@format, :type
34
- def_delegators :@format, :message_length
33
+ def_delegators :@format, :action_type
34
+ def_delegator :@format, :action_length, :length
35
35
  def_delegator :@format, :to_binary_s, :to_binary
36
36
  end
37
37
  end
@@ -5,18 +5,6 @@ module Pio
5
5
  class SetVlanVid < SetVlan
6
6
  def_format :vlan_id, 1
7
7
 
8
- # Creates an action to modify the VLAN ID of a packet. The VLAN ID
9
- # is 16-bits long but the actual VID (VLAN Identifier) of the IEEE
10
- # 802.1Q frame is 12-bits.
11
- #
12
- # @example
13
- # ActionSetVlanVid.new(number)
14
- #
15
- # @param [Integer] number
16
- # the VLAN ID to set to. Only the lower 12-bits are used.
17
- #
18
- # @raise [ArgumentError] if vlan_id not within 1 and 4095 inclusive.
19
- # @raise [TypeError] if vlan_id is not an Integer.
20
8
  def initialize(number)
21
9
  vlan_id = number.to_i
22
10
  unless vlan_id >= 1 && vlan_id <= 4095
@@ -8,22 +8,22 @@ module Pio
8
8
  class Format < BinData::Record
9
9
  endian :big
10
10
 
11
- uint16 :type, value: 3
12
- uint16 :message_length, value: 8
11
+ uint16 :action_type, value: 3
12
+ uint16 :action_length, value: 8
13
13
  uint32 :padding
14
14
  hide :padding
15
15
  end
16
16
 
17
17
  def self.read(raw_data)
18
- strip_vlan = allocate
19
- strip_vlan.instance_variable_set :@format, Format.read(raw_data)
20
- strip_vlan
18
+ allocate.tap do |strip_vlan|
19
+ strip_vlan.instance_variable_set :@format, Format.read(raw_data)
20
+ end
21
21
  end
22
22
 
23
23
  extend Forwardable
24
24
 
25
- def_delegators :@format, :type
26
- def_delegators :@format, :message_length
25
+ def_delegators :@format, :action_type
26
+ def_delegator :@format, :action_length, :length
27
27
  def_delegator :@format, :to_binary_s, :to_binary
28
28
 
29
29
  def initialize
@@ -0,0 +1,33 @@
1
+ require 'bindata'
2
+ require 'forwardable'
3
+
4
+ module Pio
5
+ # Vendor defined action
6
+ class VendorAction
7
+ # OpenFlow 1.0 OFPAT_VENDOR action format.
8
+ class Format < BinData::Record
9
+ endian :big
10
+
11
+ uint16 :action_type, value: 0xffff
12
+ uint16 :action_length, value: 8
13
+ uint32 :vendor
14
+ end
15
+
16
+ def self.read(raw_data)
17
+ allocate.tap do |strip_vlan|
18
+ strip_vlan.instance_variable_set :@format, Format.read(raw_data)
19
+ end
20
+ end
21
+
22
+ extend Forwardable
23
+
24
+ def_delegators :@format, :action_type
25
+ def_delegator :@format, :action_length, :length
26
+ def_delegators :@format, :vendor
27
+ def_delegator :@format, :to_binary_s, :to_binary
28
+
29
+ def initialize(vendor)
30
+ @format = Format.new(vendor: vendor)
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,8 @@
1
1
  require 'pio/open_flow13/apply'
2
2
  require 'pio/open_flow13/echo'
3
+ require 'pio/open_flow13/error'
4
+ require 'pio/open_flow13/error/bad_request'
5
+ require 'pio/open_flow13/error/hello_failed'
3
6
  require 'pio/open_flow13/features_reply'
4
7
  require 'pio/open_flow13/features_request'
5
8
  require 'pio/open_flow13/flow_mod'
@@ -17,8 +17,8 @@ module Pio
17
17
 
18
18
  string :binary, read_length: :length
19
19
 
20
- def set(value)
21
- self.binary = [value].flatten.map(&:to_binary).join
20
+ def set(actions)
21
+ self.binary = Array(actions).map(&:to_binary).join
22
22
  end
23
23
 
24
24
  # rubocop:disable MethodLength
@@ -32,7 +32,7 @@ module Pio
32
32
  else
33
33
  UnsupportedAction.read(tmp)
34
34
  end
35
- tmp = tmp[action.action_length..-1]
35
+ tmp = tmp[action.length..-1]
36
36
  actions << action
37
37
  end
38
38
  actions
@@ -0,0 +1,28 @@
1
+ require 'pio/open_flow13/error/error_type13'
2
+
3
+ module Pio
4
+ module OpenFlow13
5
+ # Error message parser
6
+ module Error
7
+ # Error message body parser.
8
+ class BodyParser < BinData::Record
9
+ endian :big
10
+ error_type13 :error_type
11
+ uint16 :error_code
12
+ end
13
+
14
+ def self.read(binary)
15
+ body = OpenFlowHeaderParser.read(binary).body
16
+ klass = case BodyParser.read(body).snapshot.error_type
17
+ when :hello_failed
18
+ HelloFailed
19
+ when :bad_request
20
+ BadRequest
21
+ else
22
+ fail 'Unknown error message.'
23
+ end
24
+ klass.read binary
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ require 'pio/open_flow/format'
2
+ require 'pio/open_flow/message'
3
+ require 'pio/open_flow13/error/error_type13'
4
+
5
+ module Pio
6
+ module OpenFlow13
7
+ module Error
8
+ # Bad request error.
9
+ class BadRequest < OpenFlow::Message
10
+ # Bad request error format.
11
+ class Format < BinData::Record
12
+ # enum ofp_bad_request_code
13
+ class BadRequestCode < BinData::Primitive
14
+ ERROR_CODES = {
15
+ bad_version: 0,
16
+ bad_type: 1,
17
+ bad_multipart: 2,
18
+ bad_experimenter: 3,
19
+ bad_experimenter_type: 4,
20
+ permissions_error: 5,
21
+ bad_length: 6,
22
+ buffer_empty: 7,
23
+ buffer_unknown: 8,
24
+ bad_table_id: 9,
25
+ controller_is_slave: 10,
26
+ bad_port: 11,
27
+ bad_packet: 12,
28
+ multipart_buffer_overflow: 13
29
+ }
30
+
31
+ endian :big
32
+ uint16 :error_code
33
+
34
+ def get
35
+ ERROR_CODES.invert.fetch(error_code)
36
+ end
37
+
38
+ def set(value)
39
+ self.error_code = ERROR_CODES.fetch(value)
40
+ end
41
+ end
42
+
43
+ # Bad request error body.
44
+ class Body < BinData::Record
45
+ endian :big
46
+
47
+ error_type13 :error_type, value: -> { :bad_request }
48
+ bad_request_code :error_code
49
+ rest :raw_data
50
+
51
+ def length
52
+ 4 + raw_data.length
53
+ end
54
+ end
55
+
56
+ extend OpenFlow::Format
57
+
58
+ header version: 4, message_type: 1
59
+ body :body
60
+ end
61
+
62
+ body_option :raw_data
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ module Pio
2
+ module OpenFlow13
3
+ module Error
4
+ # enum ofp_error_type
5
+ class ErrorType13 < BinData::Primitive
6
+ ERROR_TYPES = {
7
+ hello_failed: 0,
8
+ bad_request: 1,
9
+ bad_action: 2,
10
+ bad_instruction: 3,
11
+ bad_match: 4,
12
+ flow_mod_failed: 5,
13
+ group_mod_failed: 6,
14
+ port_mod_failed: 7,
15
+ table_mod_failed: 8,
16
+ queue_operation_failed: 9,
17
+ switch_config_failed: 10,
18
+ role_request_failed: 11,
19
+ meter_mod_failed: 12,
20
+ table_features_failed: 13,
21
+ experimenter: 0xffff
22
+ }
23
+
24
+ endian :big
25
+ uint16 :error_type
26
+
27
+ def get
28
+ ERROR_TYPES.invert.fetch(error_type)
29
+ end
30
+
31
+ def set(value)
32
+ self.error_type = ERROR_TYPES.fetch(value)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,42 @@
1
+ require 'pio/open_flow/hello_failed_code'
2
+ require 'pio/open_flow/format'
3
+ require 'pio/open_flow/message'
4
+
5
+ # Base module.
6
+ module Pio
7
+ # OpenFlow 1.3 messages
8
+ module OpenFlow13
9
+ module Error
10
+ # Hello Failed error message
11
+ class HelloFailed < OpenFlow::Message
12
+ # Hello Failed error format.
13
+ class Format < BinData::Record
14
+ # Hello Failed error body.
15
+ class Body < BinData::Record
16
+ endian :big
17
+
18
+ error_type13 :error_type
19
+ hello_failed_code :error_code
20
+ rest :description
21
+
22
+ def length
23
+ 4 + description.length
24
+ end
25
+ end
26
+
27
+ extend OpenFlow::Format
28
+
29
+ header version: 4, message_type: 1
30
+ body :body
31
+
32
+ def length
33
+ 8 + body.length
34
+ end
35
+ end
36
+
37
+ body_option :error_code
38
+ body_option :description
39
+ end
40
+ end
41
+ end
42
+ end