hrr_rb_netconf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +32 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +201 -0
  8. data/README.md +220 -0
  9. data/Rakefile +6 -0
  10. data/demo/server.rb +54 -0
  11. data/demo/server_over_ssh.rb +97 -0
  12. data/demo/server_with_session-oriented-database.rb +134 -0
  13. data/demo/server_with_sessionless-database.rb +81 -0
  14. data/hrr_rb_netconf.gemspec +28 -0
  15. data/lib/hrr_rb_netconf.rb +10 -0
  16. data/lib/hrr_rb_netconf/logger.rb +56 -0
  17. data/lib/hrr_rb_netconf/server.rb +109 -0
  18. data/lib/hrr_rb_netconf/server/capabilities.rb +75 -0
  19. data/lib/hrr_rb_netconf/server/capability.rb +206 -0
  20. data/lib/hrr_rb_netconf/server/capability/base_1_0.rb +183 -0
  21. data/lib/hrr_rb_netconf/server/capability/base_1_1.rb +247 -0
  22. data/lib/hrr_rb_netconf/server/capability/candidate_1_0.rb +34 -0
  23. data/lib/hrr_rb_netconf/server/capability/confirmed_commit_1_0.rb +24 -0
  24. data/lib/hrr_rb_netconf/server/capability/confirmed_commit_1_1.rb +24 -0
  25. data/lib/hrr_rb_netconf/server/capability/rollback_on_error_1_0.rb +16 -0
  26. data/lib/hrr_rb_netconf/server/capability/startup_1_0.rb +22 -0
  27. data/lib/hrr_rb_netconf/server/capability/url_1_0.rb +23 -0
  28. data/lib/hrr_rb_netconf/server/capability/validate_1_0.rb +25 -0
  29. data/lib/hrr_rb_netconf/server/capability/validate_1_1.rb +25 -0
  30. data/lib/hrr_rb_netconf/server/capability/writable_running_1_0.rb +17 -0
  31. data/lib/hrr_rb_netconf/server/capability/xpath_1_0.rb +14 -0
  32. data/lib/hrr_rb_netconf/server/datastore.rb +30 -0
  33. data/lib/hrr_rb_netconf/server/datastore/oper_handler.rb +29 -0
  34. data/lib/hrr_rb_netconf/server/datastore/session.rb +52 -0
  35. data/lib/hrr_rb_netconf/server/error.rb +52 -0
  36. data/lib/hrr_rb_netconf/server/error/access_denied.rb +19 -0
  37. data/lib/hrr_rb_netconf/server/error/bad_attribute.rb +20 -0
  38. data/lib/hrr_rb_netconf/server/error/bad_element.rb +20 -0
  39. data/lib/hrr_rb_netconf/server/error/data_exists.rb +19 -0
  40. data/lib/hrr_rb_netconf/server/error/data_missing.rb +19 -0
  41. data/lib/hrr_rb_netconf/server/error/in_use.rb +19 -0
  42. data/lib/hrr_rb_netconf/server/error/invalid_value.rb +19 -0
  43. data/lib/hrr_rb_netconf/server/error/lock_denied.rb +19 -0
  44. data/lib/hrr_rb_netconf/server/error/malformed_message.rb +19 -0
  45. data/lib/hrr_rb_netconf/server/error/missing_attribute.rb +19 -0
  46. data/lib/hrr_rb_netconf/server/error/missing_element.rb +19 -0
  47. data/lib/hrr_rb_netconf/server/error/operation_failed.rb +19 -0
  48. data/lib/hrr_rb_netconf/server/error/operation_not_supported.rb +19 -0
  49. data/lib/hrr_rb_netconf/server/error/partial_operation.rb +19 -0
  50. data/lib/hrr_rb_netconf/server/error/resource_denied.rb +19 -0
  51. data/lib/hrr_rb_netconf/server/error/rollback_failed.rb +19 -0
  52. data/lib/hrr_rb_netconf/server/error/rpc_errorable.rb +138 -0
  53. data/lib/hrr_rb_netconf/server/error/too_big.rb +19 -0
  54. data/lib/hrr_rb_netconf/server/error/unknown_attribute.rb +19 -0
  55. data/lib/hrr_rb_netconf/server/error/unknown_element.rb +19 -0
  56. data/lib/hrr_rb_netconf/server/error/unknown_namespace.rb +19 -0
  57. data/lib/hrr_rb_netconf/server/errors.rb +28 -0
  58. data/lib/hrr_rb_netconf/server/filter.rb +48 -0
  59. data/lib/hrr_rb_netconf/server/filter/subtree.rb +135 -0
  60. data/lib/hrr_rb_netconf/server/filter/xpath.rb +59 -0
  61. data/lib/hrr_rb_netconf/server/model.rb +123 -0
  62. data/lib/hrr_rb_netconf/server/model/node.rb +19 -0
  63. data/lib/hrr_rb_netconf/server/operation.rb +92 -0
  64. data/lib/hrr_rb_netconf/server/session.rb +177 -0
  65. data/lib/hrr_rb_netconf/version.rb +6 -0
  66. metadata +149 -0
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class OperationNotSupported < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'operation-not-supported'
13
+ TYPE = ['protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class PartialOperation < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'partial-operation'
13
+ TYPE = ['application']
14
+ SEVERITY = ['error']
15
+ INFO = ['ok-element', 'err-element', 'noop-element']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class ResourceDenied < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'resource-denied'
13
+ TYPE = ['transport', 'rpc', 'protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class RollbackFailed < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'rollback-failed'
13
+ TYPE = ['protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,138 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'rexml/document'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error < StandardError
9
+ module RpcErrorable
10
+ def initialize type, severity, info: nil, app_tag: nil, path: nil, message: nil
11
+ @logger = Logger.new self.class.name
12
+
13
+ @tag = self.class::TAG
14
+ @type = type
15
+ @severity = severity
16
+ @info = info
17
+ @app_tag = app_tag
18
+ @path = path
19
+ @message = message
20
+
21
+ validate
22
+ end
23
+
24
+ def validate
25
+ validate_type
26
+ validate_severity
27
+ validate_info
28
+ validate_app_tag
29
+ validate_path
30
+ validate_message
31
+ end
32
+
33
+ def validate_type
34
+ unless self.class::TYPE.include? @type
35
+ raise ArgumentError.new "error-type arg must be one of #{self.class::TYPE}, but given #{@type}"
36
+ end
37
+ end
38
+
39
+ def validate_severity
40
+ unless self.class::SEVERITY.include? @severity
41
+ raise ArgumentError.new "error-severity arg must be one of #{self.class::SEVERITY}, but given #{@severity}"
42
+ end
43
+ end
44
+
45
+ def validate_info
46
+ unless self.class::INFO.empty?
47
+ unless @info.kind_of? Hash
48
+ raise ArgumentError.new "error-info arg must be a kind of Hash, but given #{@info.class}"
49
+ end
50
+ unless self.class::INFO.all?{ |e| @info[e] }
51
+ raise ArgumentError.new "error-info arg must contain #{self.class::INFO} as keys, but given #{@info}"
52
+ end
53
+ end
54
+ end
55
+
56
+ def validate_app_tag
57
+ # Pass
58
+ end
59
+
60
+ def validate_path
61
+ if @path
62
+ case @path
63
+ when Hash
64
+ unless @path['value']
65
+ raise ArgumentError.new "error-path arg must contain 'value' key if Hash"
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def validate_message
72
+ if @message
73
+ case @message
74
+ when Hash
75
+ unless @message['value']
76
+ raise ArgumentError.new "error-message arg must contain \"value\" key if Hash"
77
+ end
78
+ unless @message.fetch('attributes', {}).keys.include?('xml:lang')
79
+ @logger.warn { "error-message arg does not contain \"xml:lang\" attribute, so assuming \"en\"" }
80
+ end
81
+ else
82
+ @logger.warn { "error-message arg does not contain \"xml:lang\" attribute, so assuming \"en\"" }
83
+ end
84
+ end
85
+ end
86
+
87
+ def to_rpc_error
88
+ xml_doc = REXML::Document.new
89
+ rpc_error_e = xml_doc.add_element 'rpc-error'
90
+ tag_e = rpc_error_e.add_element 'error-tag'
91
+ tag_e.text = @tag
92
+ type_e = rpc_error_e.add_element 'error-type'
93
+ type_e.text = @type
94
+ severity_e = rpc_error_e.add_element 'error-severity'
95
+ severity_e.text = @severity
96
+ if @info
97
+ info_e = rpc_error_e.add_element 'error-info'
98
+ case @info
99
+ when Hash
100
+ @info.each{ |k, v|
101
+ child_e = info_e.add_element k
102
+ child_e.text = v
103
+ }
104
+ else
105
+ info_e.text = @info
106
+ end
107
+ end
108
+ if @app_tag
109
+ app_tag_e = rpc_error_e.add_element 'error-app-tag'
110
+ app_tag_e.text = @app_tag
111
+ end
112
+ if @path
113
+ path_e = rpc_error_e.add_element 'error-path'
114
+ case @path
115
+ when Hash
116
+ path_e.add_attributes @path['attributes']
117
+ path_e.text = @path['value']
118
+ else
119
+ path_e.text = @path
120
+ end
121
+ end
122
+ if @message
123
+ message_e = rpc_error_e.add_element 'error-message'
124
+ message_e.add_attribute 'xml:lang', 'en'
125
+ case @message
126
+ when Hash
127
+ message_e.add_attributes @message['attributes']
128
+ message_e.text = @message['value']
129
+ else
130
+ message_e.text = @message
131
+ end
132
+ end
133
+ xml_doc.root
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class TooBig < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'too-big'
13
+ TYPE = ['transport', 'rpc', 'protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class UnknownAttribute < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'unknown-attribute'
13
+ TYPE = ['rpc', 'protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = ['bad-attribute', 'bad-element']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class UnknownElement < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'unknown-element'
13
+ TYPE = ['protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = ['bad-element']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error/rpc_errorable'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Error
9
+ class UnknownNamespace < Error
10
+ include RpcErrorable
11
+
12
+ TAG = 'unknown-namespace'
13
+ TYPE = ['protocol', 'application']
14
+ SEVERITY = ['error']
15
+ INFO = ['bad-element', 'bad-namespace']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_netconf/server/error'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Errors < StandardError
9
+ include Enumerable
10
+
11
+ def initialize *errors
12
+ @errors = errors.flatten
13
+ validate_errors
14
+ end
15
+
16
+ def validate_errors
17
+ unless @errors.all?{ |e| e.kind_of? HrrRbNetconf::Server::Error }
18
+ given = @errors.reject{ |e| e.kind_of? HrrRbNetconf::Server::Error }.map{ |e| e.class }
19
+ raise ArgumentError.new "Wrong argument type: given #{given}, expected HrrRbNetconf::Server::Error"
20
+ end
21
+ end
22
+
23
+ def each &blk
24
+ @errors.each &blk
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Filter
7
+ @subclass_list = Array.new
8
+
9
+ class << self
10
+ def inherited klass
11
+ @subclass_list.push klass if @subclass_list
12
+ end
13
+
14
+ def [] key
15
+ __subclass_list__(__method__).find{ |klass| klass::TYPE == key }
16
+ end
17
+
18
+ def list
19
+ __subclass_list__(__method__).map{ |klass| klass::TYPE }
20
+ end
21
+
22
+ def __subclass_list__ method_name
23
+ send(:method_missing, method_name) unless @subclass_list
24
+ @subclass_list
25
+ end
26
+
27
+ def filter raw_output_e, input_e
28
+ filter_e = input_e.elements['filter']
29
+ if filter_e
30
+ filter_type = filter_e.attributes['type']
31
+ if self[filter_type]
32
+ self[filter_type].filter raw_output_e, filter_e
33
+ else
34
+ raise Error['bad-attribute'].new('protocol', 'error', info: {'bad-attribute' => filter_type, 'bad-element' => 'filter'})
35
+ end
36
+ else
37
+ raw_output_e
38
+ end
39
+ end
40
+
41
+ private :__subclass_list__
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ require 'hrr_rb_netconf/server/filter/subtree'
48
+ require 'hrr_rb_netconf/server/filter/xpath'
@@ -0,0 +1,135 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'rexml/document'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Filter
9
+ class Subtree < Filter
10
+ TYPE = 'subtree'
11
+
12
+ class << self
13
+ def filter raw_output_e, filter_e
14
+ selected_element_xpaths = []
15
+ output_xml_doc = REXML::Document.new
16
+ subtree_e = filter_e.elements[1]
17
+ filter_recursively(selected_element_xpaths, output_xml_doc, raw_output_e, subtree_e)
18
+ output_xml_doc.root
19
+ end
20
+
21
+ def content_match_nodes_match_all? content_match_nodes, raw_output_e
22
+ content_match_nodes.all?{ |child_filter_e|
23
+ child_filter_attributes = child_filter_e.attributes.to_a.reject{ |attr| (attr.prefix =='' && attr.name == 'xmlns') || attr.prefix == 'xmlns' }
24
+ raw_output_e.elements.to_a.any?{ |child_raw_output_e|
25
+ (child_filter_e.namespace == "" || child_filter_e.namespace == child_raw_output_e.namespace) && child_filter_e.name == child_raw_output_e.name && child_filter_e.text == child_raw_output_e.text && (
26
+ child_filter_attributes.empty? || child_filter_attributes.reject{ |child_filter_attr|
27
+ child_raw_output_e.attributes.to_a.reject{ |attr| (attr.prefix =='' && attr.name == 'xmlns') || attr.prefix == 'xmlns' }.any?{ |child_raw_output_attr|
28
+ (child_filter_attr.namespace == "" || child_filter_attr.namespace == child_raw_output_attr.namespace) && child_filter_attr.name == child_raw_output_attr.name && child_filter_attr.value == child_raw_output_attr.value
29
+ }
30
+ }
31
+ )
32
+ }
33
+ }
34
+ end
35
+
36
+ def add_elem selected_element_xpaths, output_e, raw_output_e, output_e_duplicated
37
+ if output_e_duplicated
38
+ xpath, elem = selected_element_xpaths.find{ |xpath, elem| xpath == raw_output_e.xpath }
39
+ unless xpath
40
+ child_output_e = output_e.add raw_output_e.clone
41
+ selected_element_xpaths.push [raw_output_e.xpath, child_output_e]
42
+ [child_output_e, false]
43
+ else
44
+ [elem, true]
45
+ end
46
+ else
47
+ child_output_e = output_e.add raw_output_e.clone
48
+ selected_element_xpaths.push [raw_output_e.xpath, child_output_e]
49
+ [child_output_e, false]
50
+ end
51
+ end
52
+
53
+ def filter_recursively selected_element_xpaths, output_e, raw_output_e, filter_e, options={}
54
+ if filter_e
55
+ if raw_output_e.name == filter_e.name
56
+ # Namespace Selection
57
+ filter_namespace = options['namespace'] || filter_e.namespace
58
+ if filter_namespace == nil || filter_namespace == "" || raw_output_e.namespace == filter_namespace
59
+ # Attribute Match Expressions
60
+ filter_attributes = filter_e.attributes.to_a.reject{ |attr| (attr.prefix =='' && attr.name == 'xmlns') || attr.prefix == 'xmlns' }
61
+ if filter_attributes.empty? || filter_attributes.reject{ |filter_attr|
62
+ raw_output_e.attributes.to_a.reject{ |attr| (attr.prefix =='' && attr.name == 'xmlns') || attr.prefix == 'xmlns' }.any?{ |raw_output_attr|
63
+ filter_attr.namespace == raw_output_attr.namespace && filter_attr.name == raw_output_attr.name && filter_attr.value == raw_output_attr.value
64
+ }
65
+ }.empty?
66
+ if filter_e.has_elements?
67
+ # Containment Nodes
68
+ if filter_e.elements.to_a.select{ |c| c.has_text? }.empty?
69
+ child_output_e, child_output_e_duplicated = add_elem selected_element_xpaths, output_e, raw_output_e, options['output_e_duplicated']
70
+ raw_output_e.each_element{ |child_raw_output_e|
71
+ filter_e.elements.to_a.select{ |c| c.name == child_raw_output_e.name && (c.namespace == "" || c.namespace == child_raw_output_e.namespace) }.each{ |child_filter_e|
72
+ unless child_output_e_duplicated
73
+ if selected_element_xpaths.any?{ |xpath, elem| xpath == child_raw_output_e.xpath }
74
+ child_output_e_duplicated = true
75
+ end
76
+ end
77
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, child_filter_e, {'namespace' => filter_namespace, 'output_e_duplicated' => child_output_e_duplicated}
78
+ }
79
+ }
80
+ # Content Match Nodes
81
+ else
82
+ content_match_nodes = filter_e.elements.to_a.select{ |c| c.has_text? }
83
+ not_content_match_nodes = filter_e.elements.to_a.reject{ |c| c.has_text? }
84
+ if content_match_nodes_match_all? content_match_nodes, raw_output_e
85
+ child_output_e, child_output_e_duplicated = add_elem selected_element_xpaths, output_e, raw_output_e, options['output_e_duplicated']
86
+ if not_content_match_nodes.empty?
87
+ raw_output_e.each_element{ |child_raw_output_e|
88
+ child_filter_e = filter_e.elements.to_a.find{ |c| (c.namespace == "" || child_raw_output_e.namespace == c.namespace) && child_raw_output_e.name == c.name }
89
+ if child_filter_e
90
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, child_filter_e, {'namespace' => filter_namespace, 'output_e_duplicated' => child_output_e_duplicated}
91
+ else
92
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, child_filter_e, {'namespace' => filter_namespace, 'in_filter' => true, 'output_e_duplicated' => child_output_e_duplicated}
93
+ end
94
+ }
95
+ else
96
+ raw_output_e.each_element{ |child_raw_output_e|
97
+ child_filter_e = filter_e.elements.to_a.find{ |c| (c.namespace == "" || child_raw_output_e.namespace == c.namespace) && child_raw_output_e.name == c.name }
98
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, child_filter_e, {'namespace' => filter_namespace, 'output_e_duplicated' => child_output_e_duplicated}
99
+ }
100
+ end
101
+ else
102
+ end
103
+ end
104
+ else
105
+ child_output_e, child_output_e_duplicated = add_elem selected_element_xpaths, output_e, raw_output_e, options['output_e_duplicated']
106
+ if raw_output_e.has_text?
107
+ child_output_e.text = raw_output_e.text
108
+ else
109
+ raw_output_e.each_element{ |child_raw_output_e|
110
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, nil, {'namespace' => filter_namespace, 'in_filter' => true, 'output_e_duplicated' => child_output_e_duplicated}
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ elsif options['in_filter']
118
+ # Namespace Selection
119
+ filter_namespace = options['namespace']
120
+ if filter_namespace == nil || filter_namespace == "" || raw_output_e.namespace == filter_namespace
121
+ child_output_e, child_output_e_duplicated = add_elem selected_element_xpaths, output_e, raw_output_e, options['output_e_duplicated']
122
+ if raw_output_e.has_text?
123
+ child_output_e.text = raw_output_e.text
124
+ end
125
+ raw_output_e.each_element{ |child_raw_output_e|
126
+ filter_recursively selected_element_xpaths, child_output_e, child_raw_output_e, filter_e, {'namespace' => filter_namespace, 'in_filter' => true, 'outout_e_duplicated' => child_output_e_duplicated}
127
+ }
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end