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,247 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'rexml/document'
5
+ require 'hrr_rb_netconf/logger'
6
+
7
+ module HrrRbNetconf
8
+ class Server
9
+ class Capability
10
+ class Base_1_1 < Capability
11
+ ID = 'urn:ietf:params:netconf:base:1.1'
12
+ DEPENDENCIES = []
13
+ IF_FEATURES = []
14
+
15
+ oper_proc('get'){ |session, datastore, input_e|
16
+ datastore.run 'get', input_e
17
+ }
18
+
19
+ oper_proc('get-config'){ |session, datastore, input_e|
20
+ datastore.run 'get-config', input_e
21
+ }
22
+
23
+ oper_proc('edit-config'){ |session, datastore, input_e|
24
+ datastore.run 'edit-config', input_e
25
+ '<ok />'
26
+ }
27
+
28
+ oper_proc('copy-config'){ |session, datastore, input_e|
29
+ datastore.run 'copy-config', input_e
30
+ '<ok />'
31
+ }
32
+
33
+ oper_proc('delete-config'){ |session, datastore, input_e|
34
+ datastore.run 'delete-config', input_e
35
+ '<ok />'
36
+ }
37
+
38
+ oper_proc('lock'){ |session, datastore, input_e|
39
+ target = input_e.elements['target'].elements[1].name
40
+ session.lock target
41
+ begin
42
+ datastore.run 'lock', input_e
43
+ '<ok />'
44
+ rescue
45
+ session.unlock target
46
+ raise
47
+ end
48
+ }
49
+
50
+ oper_proc('unlock'){ |session, datastore, input_e|
51
+ datastore.run 'unlock', input_e
52
+ target = input_e.elements['target'].elements[1].name
53
+ session.unlock target
54
+ '<ok />'
55
+ }
56
+
57
+ oper_proc('close-session'){ |session, datastore, input_e|
58
+ datastore.run 'close-session', input_e
59
+ session.close
60
+ '<ok />'
61
+ }
62
+
63
+ oper_proc('kill-session'){ |session, datastore, input_e|
64
+ session.close_other Integer(input_e.elements['session-id'].text)
65
+ '<ok />'
66
+ }
67
+
68
+ model 'get', ['filter'], 'leaf', 'type' => 'anyxml'
69
+ model 'get-config', ['source'], 'container'
70
+ model 'get-config', ['source', 'config-source'], 'choice', 'mandatory' => true
71
+ model 'get-config', ['source', 'config-source', 'running'], 'leaf', 'type' => 'empty'
72
+ model 'get-config', ['filter'], 'leaf', 'type' => 'anyxml'
73
+ model 'edit-config', ['target'], 'container'
74
+ model 'edit-config', ['target', 'config-target'], 'choice', 'mandatory' => true
75
+ model 'edit-config', ['default-operation'], 'leaf', 'type' => 'enumeration', 'enum' => ['merge', 'replace', 'none'], 'default' => 'merge'
76
+ model 'edit-config', ['error-option'], 'leaf', 'type' => 'enumeration', 'enum' => ['stop-on-error', 'continue-on-error', 'rollback-on-error'], 'default' => 'stop-on-error'
77
+ model 'edit-config', ['edit-content'], 'choice', 'mandatory' => true
78
+ model 'edit-config', ['edit-content', 'config'], 'leaf', 'type' => 'anyxml'
79
+ model 'copy-config', ['target'], 'container'
80
+ model 'copy-config', ['target', 'config-target'], 'choice', 'mandatory' => true
81
+ model 'copy-config', ['source'], 'container'
82
+ model 'copy-config', ['source', 'config-source'], 'choice', 'mandatory' => true
83
+ model 'copy-config', ['source', 'config-source', 'running'], 'leaf', 'type' => 'empty'
84
+ model 'copy-config', ['source', 'config-source', 'config'], 'leaf', 'type' => 'anyxml'
85
+ model 'delete-config', ['target'], 'container'
86
+ model 'delete-config', ['target', 'config-target'], 'choice', 'mandatory' => true
87
+ model 'lock', ['target'], 'container'
88
+ model 'lock', ['target', 'config-target'], 'choice', 'mandatory' => true
89
+ model 'lock', ['target', 'config-target', 'running'], 'leaf', 'type' => 'empty'
90
+ model 'unlock', ['target'], 'container'
91
+ model 'unlock', ['target', 'config-target'], 'choice', 'mandatory' => true
92
+ model 'unlock', ['target', 'config-target', 'running'], 'leaf', 'type' => 'empty'
93
+ model 'close-session', []
94
+ model 'kill-session', ['session-id'], 'leaf', 'type' => 'integer', 'range' => [1, 2**32-1]
95
+
96
+ class Sender
97
+ MAX_CHUNK_SIZE = 2**32 - 1
98
+
99
+ def initialize io_w
100
+ @logger = Logger.new self.class.name
101
+ @io_w = io_w
102
+ @formatter = REXML::Formatters::Pretty.new(2)
103
+ @formatter.compact = true
104
+ end
105
+
106
+ def send_message msg
107
+ raw_msg = StringIO.new
108
+ case msg
109
+ when String
110
+ begin
111
+ @formatter.write(REXML::Document.new(msg, {:ignore_whitespace_nodes => :all}).root, raw_msg)
112
+ rescue => e
113
+ @logger.error { "Invalid sending message: #{msg.inspect}: #{e.message}" }
114
+ raise "Invalid sending message: #{msg.inspect}: #{e.message}"
115
+ end
116
+ when REXML::Document
117
+ @formatter.write(msg.root, raw_msg)
118
+ when REXML::Element
119
+ @formatter.write(msg, raw_msg)
120
+ else
121
+ @logger.error { "Unexpected sending message: #{msg.inspect}" }
122
+ raise ArgumentError, "Unexpected sending message: #{msg.inspect}"
123
+ end
124
+ @logger.debug { "Sending message: #{raw_msg.string.inspect}" }
125
+ raw_msg.rewind
126
+ encoded_msg = StringIO.new
127
+ until raw_msg.eof?
128
+ chunk_size = rand(1..MAX_CHUNK_SIZE)
129
+ chunk_data = raw_msg.read(chunk_size)
130
+ encoded_msg.write "\n##{chunk_data.size}\n#{chunk_data}"
131
+ end
132
+ encoded_msg.write "\n##\n"
133
+ @logger.debug { "Sending encoded message: #{encoded_msg.string.inspect}" }
134
+ begin
135
+ @io_w.write encoded_msg.string
136
+ rescue => e
137
+ @logger.info { "Sender IO closed: #{e.class}: #{e.message}" }
138
+ raise IOError, "Sender IO closed: #{e.class}: #{e.message}"
139
+ end
140
+ end
141
+ end
142
+
143
+ class Receiver
144
+ def initialize io_r
145
+ @logger = Logger.new self.class.name
146
+ @io_r = io_r
147
+ end
148
+
149
+ def receive_message
150
+ chunk_size = StringIO.new
151
+ chunked_msg = StringIO.new
152
+ decoded_msg = StringIO.new
153
+ read_len = 1
154
+ state = :beginning_of_msg
155
+ until state == :end_of_msg
156
+ begin
157
+ buf = @io_r.read(read_len)
158
+ rescue => e
159
+ @logger.info { "Receiver IO closed: #{e.class}: #{e.message}" }
160
+ return nil
161
+ end
162
+ if buf.nil?
163
+ @logger.info { "Receiver IO closed" }
164
+ return nil
165
+ end
166
+ chunked_msg.write buf
167
+ case state
168
+ when :beginning_of_msg
169
+ if buf == "\n"
170
+ state = :before_chunk_size
171
+ else
172
+ info = "In beginning_of_msg: expected #{"\n".inspect}, but got #{buf.inspect}: #{chunked_msg.string.inspect}"
173
+ @logger.info { info }
174
+ raise Error['malformed-message'].new("rpc", "error")
175
+ end
176
+ when :before_chunk_size
177
+ if buf == "#"
178
+ state = :in_chunk_size
179
+ else
180
+ info = "In before_chunk_size: expected #{"#".inspect}, but got #{buf.inspect}: #{chunked_msg.string.inspect}"
181
+ @logger.info { info }
182
+ raise Error['malformed-message'].new("rpc", "error")
183
+ end
184
+ when :in_chunk_size
185
+ if buf =~ /[0-9]/
186
+ chunk_size.write buf
187
+ elsif buf == "\n"
188
+ read_len = chunk_size.string.to_i
189
+ state = :in_chunk_data
190
+ elsif buf == "#"
191
+ state = :ending_msg
192
+ else
193
+ info = "In in_chunk_size: expected #{"/[0-9]/".inspect}, #{"\n".inspect}, or #{"#".inspect}, but got #{buf.inspect}: #{chunked_msg.string.inspect}"
194
+ @logger.info { info }
195
+ raise Error['malformed-message'].new("rpc", "error")
196
+ end
197
+ when :in_chunk_data
198
+ chunk_size = StringIO.new
199
+ decoded_msg.write buf
200
+ read_len = 1
201
+ state = :after_chunk_data
202
+ when :after_chunk_data
203
+ if buf == "\n"
204
+ state = :before_chunk_size
205
+ else
206
+ info = "In after_chunk_data: expected #{"\n".inspect}, but got #{buf.inspect}: #{chunked_msg.string.inspect}"
207
+ @logger.info { info }
208
+ raise Error['malformed-message'].new("rpc", "error")
209
+ end
210
+ when :ending_msg
211
+ if buf == "\n"
212
+ state = :end_of_msg
213
+ else
214
+ info = "In ending_msg: expected #{"\n".inspect}, but got #{buf.inspect}: #{chunked_msg.string.inspect}"
215
+ @logger.info { info }
216
+ raise Error['malformed-message'].new("rpc", "error")
217
+ end
218
+ end
219
+ end
220
+ @logger.debug { "Received message: #{decoded_msg.string.inspect}" }
221
+ begin
222
+ received_msg = REXML::Document.new(decoded_msg.string, {:ignore_whitespace_nodes => :all}).root
223
+ validate_received_msg received_msg
224
+ received_msg
225
+ rescue => e
226
+ info = "Invalid received message: #{e.message.split("\n").first}: #{decoded_msg.string.inspect}"
227
+ @logger.info { info }
228
+ raise Error['malformed-message'].new("rpc", "error")
229
+ end
230
+ end
231
+
232
+ def validate_received_msg received_msg
233
+ unless received_msg
234
+ raise "No valid root tag interpreted"
235
+ end
236
+ unless "rpc" == received_msg.name
237
+ raise "Invalid message: expected #{"rpc".inspect}, but got #{received_msg.name.inspect}"
238
+ end
239
+ unless "urn:ietf:params:xml:ns:netconf:base:1.0" == received_msg.namespace
240
+ raise "Invalid namespace: expected #{"urn:ietf:params:xml:ns:netconf:base:1.0".inspect}, but got #{received_msg.namespace.inspect}"
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class Candidate_1_0 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:candidate:1.0'
9
+ DEPENDENCIES = []
10
+ IF_FEATURES = ['candidate']
11
+
12
+ oper_proc('commit'){ |session, datastore, input_e|
13
+ datastore.run 'commit', input_e
14
+ '<ok />'
15
+ }
16
+
17
+ oper_proc('discard-changes'){ |session, datastore, input_e|
18
+ datastore.run 'discard-changes', input_e
19
+ '<ok />'
20
+ }
21
+
22
+ model 'get-config', ['source', 'config-source', 'candidate'], 'leaf', 'type' => 'empty'
23
+ model 'edit-config', ['target', 'config-target', 'candidate'], 'leaf', 'type' => 'empty'
24
+ model 'copy-config', ['source', 'config-source', 'candidate'], 'leaf', 'type' => 'empty'
25
+ model 'copy-config', ['target', 'config-target', 'candidate'], 'leaf', 'type' => 'empty'
26
+ model 'validate', ['source', 'config-source', 'candidate'], 'leaf', 'type' => 'empty'
27
+ model 'lock', ['target', 'config-target', 'candidate'], 'leaf', 'type' => 'empty'
28
+ model 'unlock', ['target', 'config-target', 'candidate'], 'leaf', 'type' => 'empty'
29
+ model 'commit', []
30
+ model 'discard-changes', []
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class ConfirmedCommit_1_0 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:confirmed-commit:1.0'
9
+ DEPENDENCIES = ['urn:ietf:params:netconf:capability:candidate:1.0']
10
+ IF_FEATURES = ['candidate', 'confirmed-commit']
11
+
12
+ oper_proc('cancel-commit'){ |session, datastore, input_e|
13
+ datastore.run 'cancel-commit', input_e
14
+ '<ok />'
15
+ }
16
+
17
+ model 'commit', ['confirmed'], 'leaf', 'type' => 'empty'
18
+ model 'commit', ['confirm-timeout'], 'leaf', 'type' => 'integer', 'range' => [1, 2**32-1], 'default' => '600'
19
+ model 'commit', ['persist'], 'leaf', 'type' => 'string'
20
+ model 'commit', ['persist-id'], 'leaf', 'type' => 'string'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class ConfirmedCommit_1_1 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:confirmed-commit:1.1'
9
+ DEPENDENCIES = ['urn:ietf:params:netconf:capability:candidate:1.0']
10
+ IF_FEATURES = ['candidate', 'confirmed-commit']
11
+
12
+ oper_proc('cancel-commit'){ |session, datastore, input_e|
13
+ datastore.run 'cancel-commit', input_e
14
+ '<ok />'
15
+ }
16
+
17
+ model 'commit', ['confirmed'], 'leaf', 'type' => 'empty'
18
+ model 'commit', ['confirm-timeout'], 'leaf', 'type' => 'integer', 'range' => [1, 2**32-1], 'default' => '600'
19
+ model 'commit', ['persist'], 'leaf', 'type' => 'string'
20
+ model 'commit', ['persist-id'], 'leaf', 'type' => 'string'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class RollbackOnError_1_0 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:rollback-on-error:1.0'
9
+ DEPENDENCIES = []
10
+ IF_FEATURES = ['rollback-on-error']
11
+
12
+ model 'edit-config', ['error-option'], 'leaf', 'type' => 'enumeration', 'enum' => ['stop-on-error', 'continue-on-error', 'rollback-on-error'], 'default' => 'stop-on-error'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class Startup_1_0 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:startup:1.0'
9
+ DEPENDENCIES = []
10
+ IF_FEATURES = ['startup']
11
+
12
+ model 'get-config', ['source', 'config-source', 'startup'], 'leaf', 'type' => 'empty'
13
+ model 'copy-config', ['source', 'config-source', 'startup'], 'leaf', 'type' => 'empty'
14
+ model 'copy-config', ['target', 'config-target', 'startup'], 'leaf', 'type' => 'empty'
15
+ model 'lock', ['target', 'config-target', 'startup'], 'leaf', 'type' => 'empty'
16
+ model 'unlock', ['target', 'config-target', 'startup'], 'leaf', 'type' => 'empty'
17
+ model 'validate', ['source', 'config-source', 'startup'], 'leaf', 'type' => 'empty'
18
+ model 'delete-config', ['target', 'config-target', 'startup'], 'leaf', 'type' => 'empty'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'uri'
5
+
6
+ module HrrRbNetconf
7
+ class Server
8
+ class Capability
9
+ class Url_1_0 < Capability
10
+ ID = 'urn:ietf:params:netconf:capability:url:1.0'
11
+ QUERIES = {'scheme' => ['http', 'ftp', 'file']}
12
+ DEPENDENCIES = []
13
+ IF_FEATURES = []
14
+
15
+ model 'edit-config', ['edit-content', 'url'], 'leaf', 'type' => 'string', 'validation' => proc { |cap, node| cap.queries['scheme'].any?{|s| s == URI.parse(node.text).scheme} }
16
+ model 'copy-config', ['target', 'config-target', 'url'], 'leaf', 'type' => 'string'
17
+ model 'copy-config', ['source', 'config-source', 'url'], 'leaf', 'type' => 'string'
18
+ model 'delete-config', ['target', 'config-target', 'url'], 'leaf', 'type' => 'string'
19
+ model 'validate', ['source', 'config-source', 'url'], 'leaf', 'type' => 'string'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class Validate_1_0 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:validate:1.0'
9
+ DEPENDENCIES = []
10
+ IF_FEATURES = ['validate']
11
+
12
+ oper_proc('validate'){ |session, datastore, input_e|
13
+ datastore.run 'validate', input_e
14
+ '<ok />'
15
+ }
16
+
17
+ model 'edit-config', ['test-option'], 'leaf', 'type' => 'enumeration', 'enum' => ['test-then-set', 'set', 'test-only'], 'default' => 'test-then-set'
18
+ model 'validate', ['source'], 'container'
19
+ model 'validate', ['source', 'config-source'], 'choice', 'mandatory' => true
20
+ model 'validate', ['source', 'config-source', 'running'], 'leaf', 'type' => 'empty'
21
+ model 'validate', ['source', 'config-source', 'config'], 'leaf', 'type' => 'anyxml'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbNetconf
5
+ class Server
6
+ class Capability
7
+ class Validate_1_1 < Capability
8
+ ID = 'urn:ietf:params:netconf:capability:validate:1.1'
9
+ DEPENDENCIES = []
10
+ IF_FEATURES = ['validate']
11
+
12
+ oper_proc('validate'){ |session, datastore, input_e|
13
+ datastore.run 'validate', input_e
14
+ '<ok />'
15
+ }
16
+
17
+ model 'edit-config', ['test-option'], 'leaf', 'type' => 'enumeration', 'enum' => ['test-then-set', 'set', 'test-only'], 'default' => 'test-then-set'
18
+ model 'validate', ['source'], 'container'
19
+ model 'validate', ['source', 'config-source'], 'choice', 'mandatory' => true
20
+ model 'validate', ['source', 'config-source', 'running'], 'leaf', 'type' => 'empty'
21
+ model 'validate', ['source', 'config-source', 'config'], 'leaf', 'type' => 'anyxml'
22
+ end
23
+ end
24
+ end
25
+ end