hrr_rb_netconf 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.rspec +3 -0
- data/.travis.yml +32 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +201 -0
- data/README.md +220 -0
- data/Rakefile +6 -0
- data/demo/server.rb +54 -0
- data/demo/server_over_ssh.rb +97 -0
- data/demo/server_with_session-oriented-database.rb +134 -0
- data/demo/server_with_sessionless-database.rb +81 -0
- data/hrr_rb_netconf.gemspec +28 -0
- data/lib/hrr_rb_netconf.rb +10 -0
- data/lib/hrr_rb_netconf/logger.rb +56 -0
- data/lib/hrr_rb_netconf/server.rb +109 -0
- data/lib/hrr_rb_netconf/server/capabilities.rb +75 -0
- data/lib/hrr_rb_netconf/server/capability.rb +206 -0
- data/lib/hrr_rb_netconf/server/capability/base_1_0.rb +183 -0
- data/lib/hrr_rb_netconf/server/capability/base_1_1.rb +247 -0
- data/lib/hrr_rb_netconf/server/capability/candidate_1_0.rb +34 -0
- data/lib/hrr_rb_netconf/server/capability/confirmed_commit_1_0.rb +24 -0
- data/lib/hrr_rb_netconf/server/capability/confirmed_commit_1_1.rb +24 -0
- data/lib/hrr_rb_netconf/server/capability/rollback_on_error_1_0.rb +16 -0
- data/lib/hrr_rb_netconf/server/capability/startup_1_0.rb +22 -0
- data/lib/hrr_rb_netconf/server/capability/url_1_0.rb +23 -0
- data/lib/hrr_rb_netconf/server/capability/validate_1_0.rb +25 -0
- data/lib/hrr_rb_netconf/server/capability/validate_1_1.rb +25 -0
- data/lib/hrr_rb_netconf/server/capability/writable_running_1_0.rb +17 -0
- data/lib/hrr_rb_netconf/server/capability/xpath_1_0.rb +14 -0
- data/lib/hrr_rb_netconf/server/datastore.rb +30 -0
- data/lib/hrr_rb_netconf/server/datastore/oper_handler.rb +29 -0
- data/lib/hrr_rb_netconf/server/datastore/session.rb +52 -0
- data/lib/hrr_rb_netconf/server/error.rb +52 -0
- data/lib/hrr_rb_netconf/server/error/access_denied.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/bad_attribute.rb +20 -0
- data/lib/hrr_rb_netconf/server/error/bad_element.rb +20 -0
- data/lib/hrr_rb_netconf/server/error/data_exists.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/data_missing.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/in_use.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/invalid_value.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/lock_denied.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/malformed_message.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/missing_attribute.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/missing_element.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/operation_failed.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/operation_not_supported.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/partial_operation.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/resource_denied.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/rollback_failed.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/rpc_errorable.rb +138 -0
- data/lib/hrr_rb_netconf/server/error/too_big.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/unknown_attribute.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/unknown_element.rb +19 -0
- data/lib/hrr_rb_netconf/server/error/unknown_namespace.rb +19 -0
- data/lib/hrr_rb_netconf/server/errors.rb +28 -0
- data/lib/hrr_rb_netconf/server/filter.rb +48 -0
- data/lib/hrr_rb_netconf/server/filter/subtree.rb +135 -0
- data/lib/hrr_rb_netconf/server/filter/xpath.rb +59 -0
- data/lib/hrr_rb_netconf/server/model.rb +123 -0
- data/lib/hrr_rb_netconf/server/model/node.rb +19 -0
- data/lib/hrr_rb_netconf/server/operation.rb +92 -0
- data/lib/hrr_rb_netconf/server/session.rb +177 -0
- data/lib/hrr_rb_netconf/version.rb +6 -0
- metadata +149 -0
@@ -0,0 +1,59 @@
|
|
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 Xpath < Filter
|
10
|
+
TYPE = 'xpath'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def filter raw_output_e, filter_e
|
14
|
+
xpath = filter_e.attributes['select']
|
15
|
+
unless xpath
|
16
|
+
raise Error['missing-attribute'].new('protocol', 'error', info: {'bad-attribute' => 'select', 'bad-element' => 'filter'})
|
17
|
+
end
|
18
|
+
raw_output_xml_doc = REXML::Document.new
|
19
|
+
raw_output_xml_doc.add raw_output_e.deep_clone
|
20
|
+
selected_element_xpaths = []
|
21
|
+
output_xml_doc = REXML::Document.new
|
22
|
+
raw_output_xml_doc.each_element(xpath){ |e|
|
23
|
+
ctx_output_e = add_ancestors_recursively selected_element_xpaths, output_xml_doc, e
|
24
|
+
e.children.each{ |c|
|
25
|
+
case c
|
26
|
+
when REXML::Parent
|
27
|
+
ctx_output_e.add c.deep_clone
|
28
|
+
else
|
29
|
+
ctx_output_e.add c.clone
|
30
|
+
end
|
31
|
+
}
|
32
|
+
}
|
33
|
+
output_xml_doc.root
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_elem selected_element_xpaths, output_e, raw_output_e
|
37
|
+
xpath, elem = selected_element_xpaths.find{ |xpath, elem| xpath == raw_output_e.xpath }
|
38
|
+
unless xpath
|
39
|
+
child_output_e = output_e.add raw_output_e.clone
|
40
|
+
selected_element_xpaths.push [raw_output_e.xpath, child_output_e]
|
41
|
+
child_output_e
|
42
|
+
else
|
43
|
+
elem
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_ancestors_recursively selected_element_xpaths, output_e, raw_output_e
|
48
|
+
if raw_output_e == raw_output_e.root_node
|
49
|
+
output_e
|
50
|
+
else
|
51
|
+
parent_e = add_ancestors_recursively selected_element_xpaths, output_e, raw_output_e.parent
|
52
|
+
add_elem selected_element_xpaths, parent_e, raw_output_e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'hrr_rb_netconf/server/error'
|
6
|
+
require 'hrr_rb_netconf/server/model/node'
|
7
|
+
|
8
|
+
module HrrRbNetconf
|
9
|
+
class Server
|
10
|
+
class Model
|
11
|
+
def initialize operation
|
12
|
+
@operation = operation
|
13
|
+
@tree = Node.new nil, operation, 'root', {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_recursively capability, node, path, stmt, options
|
17
|
+
name = path.shift
|
18
|
+
case path.size
|
19
|
+
when 0
|
20
|
+
node.children.push Node.new capability, name, stmt, options
|
21
|
+
else
|
22
|
+
child_node = node.children.find{|n| name == n.name}
|
23
|
+
add_recursively capability, child_node, path, stmt, options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add capability, path, stmt, options
|
28
|
+
if path.size > 0
|
29
|
+
add_recursively capability, @tree, path.dup, stmt, options
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_recursively node, xml_e, parent_xml_e: nil, validated: []
|
34
|
+
case node.stmt
|
35
|
+
when 'root', 'container'
|
36
|
+
case xml_e
|
37
|
+
when nil
|
38
|
+
true
|
39
|
+
else
|
40
|
+
node.children.all?{ |c|
|
41
|
+
case c.stmt
|
42
|
+
when 'container'
|
43
|
+
validated.push c.name
|
44
|
+
validate_recursively c, xml_e.elements[c.name]
|
45
|
+
when 'leaf'
|
46
|
+
validated.push c.name
|
47
|
+
if xml_e.elements[c.name].nil? && c.options['default'].nil?
|
48
|
+
if c.options['validation'].nil?
|
49
|
+
true
|
50
|
+
else
|
51
|
+
raise Error['operation-failed'].new('application', 'error', message: 'Not implemented')
|
52
|
+
end
|
53
|
+
else
|
54
|
+
validate_recursively c, xml_e.elements[c.name], parent_xml_e: xml_e
|
55
|
+
end
|
56
|
+
when 'choice'
|
57
|
+
validate_recursively c, xml_e, validated: validated
|
58
|
+
else
|
59
|
+
raise Error['unknown-element'].new('application', 'error', info: {'bad-element' => "#{c.name}: #{c.stmt}"}, message: 'Not implemented')
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end && (xml_e.elements.to_a.map{|e| e.name} - validated).empty?
|
63
|
+
when 'leaf'
|
64
|
+
case node.options['type']
|
65
|
+
when 'empty'
|
66
|
+
xml_e != nil && xml_e.has_text?.!
|
67
|
+
when 'enumeration'
|
68
|
+
if xml_e == nil && node.options['default']
|
69
|
+
parent_xml_e.add_element(node.name).text = node.options['default']
|
70
|
+
else
|
71
|
+
xml_e != nil && node.options['enum'].include?(xml_e.text)
|
72
|
+
end
|
73
|
+
when 'anyxml'
|
74
|
+
xml_e != nil && (REXML::Document.new(xml_e.text) rescue false)
|
75
|
+
when 'inet:uri'
|
76
|
+
xml_e != nil
|
77
|
+
raise Error['unknown-element'].new('application', 'error', info: {'bad-element' => "#{node.name}: #{node.stmt}"}, message: 'Not implemented: type inet:uri')
|
78
|
+
when 'integer'
|
79
|
+
if xml_e == nil && node.options['default']
|
80
|
+
parent_xml_e.add_element(node.name).text = node.options['default']
|
81
|
+
else
|
82
|
+
value = (Integer(xml_e.text) rescue false)
|
83
|
+
if node.options['range']
|
84
|
+
min, max = node.options['range']
|
85
|
+
xml_e != nil && value && min <= value && value <= max
|
86
|
+
else
|
87
|
+
xml_e != nil && value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
when 'string'
|
91
|
+
if xml_e == nil && node.options['default']
|
92
|
+
parent_xml_e.add_element(node.name).text = node.options['default']
|
93
|
+
else
|
94
|
+
if node.options['validation'].nil?
|
95
|
+
xml_e != nil && xml_e.has_text?
|
96
|
+
else
|
97
|
+
xml_e != nil && xml_e.has_text? && node.options['validation'].call(node.capability, xml_e)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
when 'choice'
|
102
|
+
if node.options['mandatory']
|
103
|
+
node.children.any?{ |c|
|
104
|
+
validated.push c.name
|
105
|
+
validate_recursively c, xml_e.elements[c.name]
|
106
|
+
}
|
107
|
+
else
|
108
|
+
node.children.empty? || node.children.any?{ |c|
|
109
|
+
validated.push c.name
|
110
|
+
validate_recursively c, xml_e.elements[c.name]
|
111
|
+
}
|
112
|
+
end
|
113
|
+
else
|
114
|
+
raise Error['unknown-element'].new('application', 'error', info: {'bad-element' => "#{c.name}: #{c.stmt}"}, message: 'Not implemented')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def validate input_e
|
119
|
+
validate_recursively @tree, input_e
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
module HrrRbNetconf
|
5
|
+
class Server
|
6
|
+
class Model
|
7
|
+
class Node
|
8
|
+
attr_reader :capability, :name, :stmt, :options, :children
|
9
|
+
def initialize capability, name, stmt, options
|
10
|
+
@capability = capability
|
11
|
+
@name = name
|
12
|
+
@stmt = stmt
|
13
|
+
@options = options
|
14
|
+
@children = Array.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'hrr_rb_netconf/logger'
|
5
|
+
require 'hrr_rb_netconf/server/model'
|
6
|
+
require 'hrr_rb_netconf/server/filter'
|
7
|
+
|
8
|
+
module HrrRbNetconf
|
9
|
+
class Server
|
10
|
+
class Operation
|
11
|
+
def initialize session, capabilities, datastore_session, strict_capabilities
|
12
|
+
@logger = Logger.new self.class.name
|
13
|
+
@session = session
|
14
|
+
@capabilities = capabilities
|
15
|
+
@datastore_session = datastore_session
|
16
|
+
@strict_capabilities = strict_capabilities
|
17
|
+
@models = Hash.new
|
18
|
+
@oper_procs = Hash.new
|
19
|
+
|
20
|
+
load_capabilities
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_capabilities
|
24
|
+
@capabilities.each_loadable{ |c|
|
25
|
+
@logger.debug { "Load capability: #{c.id}" }
|
26
|
+
c.oper_procs.each{ |k, v|
|
27
|
+
@oper_procs[k] = v
|
28
|
+
}
|
29
|
+
if @strict_capabilities
|
30
|
+
c.models.each{ |m|
|
31
|
+
oper_name, path, stmt, options = m
|
32
|
+
@models[oper_name] ||= Model.new oper_name
|
33
|
+
@models[oper_name].add c, path, stmt, options
|
34
|
+
}
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate input_e
|
40
|
+
oper_name = input_e.name
|
41
|
+
model = @models[oper_name]
|
42
|
+
if model
|
43
|
+
model.validate input_e
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def run xml_doc
|
50
|
+
unless xml_doc.root.name == 'rpc'
|
51
|
+
@logger.error { "Invalid root tag: must be rpc, but got #{xml_doc.root.name}" }
|
52
|
+
raise Error['operation-not-supported'].new('protocol', 'error')
|
53
|
+
end
|
54
|
+
|
55
|
+
message_id = xml_doc.attributes['message-id']
|
56
|
+
unless message_id
|
57
|
+
raise Error['missing-attribute'].new('rpc', 'error', info: {'bad-attribute' => 'message-id', 'bad-element' => 'rpc'})
|
58
|
+
end
|
59
|
+
|
60
|
+
input_e = xml_doc.elements[1]
|
61
|
+
|
62
|
+
unless @oper_procs.has_key? input_e.name
|
63
|
+
raise Error['operation-not-supported'].new('protocol', 'error')
|
64
|
+
end
|
65
|
+
|
66
|
+
if @strict_capabilities
|
67
|
+
unless validate input_e
|
68
|
+
raise Error['operation-not-supported'].new('application', 'error')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
raw_output = @oper_procs[input_e.name].call(@session, @datastore_session, input_e)
|
73
|
+
|
74
|
+
raw_output_e = case raw_output
|
75
|
+
when String
|
76
|
+
REXML::Document.new(raw_output, {:ignore_whitespace_nodes => :all}).root
|
77
|
+
when REXML::Document
|
78
|
+
raw_output.root
|
79
|
+
when REXML::Element
|
80
|
+
raw_output
|
81
|
+
else
|
82
|
+
raise "Unexpected output: #{raw_output.inspect}"
|
83
|
+
end
|
84
|
+
output_e = Filter.filter(raw_output_e, input_e)
|
85
|
+
rpc_reply_e = xml_doc.clone
|
86
|
+
rpc_reply_e.name = "rpc-reply"
|
87
|
+
rpc_reply_e.add output_e
|
88
|
+
rpc_reply_e
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'hrr_rb_netconf/logger'
|
6
|
+
require 'hrr_rb_netconf/server/capability'
|
7
|
+
require 'hrr_rb_netconf/server/operation'
|
8
|
+
|
9
|
+
module HrrRbNetconf
|
10
|
+
class Server
|
11
|
+
class Session
|
12
|
+
attr_reader :session_id
|
13
|
+
|
14
|
+
def initialize server, capabilities, datastore, session_id, io, strict_capabilities
|
15
|
+
@logger = Logger.new self.class.name
|
16
|
+
@server = server
|
17
|
+
@local_capabilities = capabilities
|
18
|
+
@remote_capabilities = Array.new
|
19
|
+
@datastore = datastore
|
20
|
+
@session_id = session_id
|
21
|
+
@io_r, @io_w = case io
|
22
|
+
when IO
|
23
|
+
[io, io]
|
24
|
+
when Array
|
25
|
+
[io[0], io[1]]
|
26
|
+
else
|
27
|
+
raise ArgumentError, "io must be an instance of IO or Array"
|
28
|
+
end
|
29
|
+
@strict_capabilities = strict_capabilities
|
30
|
+
@closed = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def start
|
34
|
+
begin
|
35
|
+
exchange_hello
|
36
|
+
negotiate_capabilities
|
37
|
+
initialize_sender_and_receiver
|
38
|
+
operation_loop
|
39
|
+
rescue
|
40
|
+
raise
|
41
|
+
ensure
|
42
|
+
close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
@logger.info { "Being closed" }
|
48
|
+
@closed = true
|
49
|
+
begin
|
50
|
+
@io_r.close_read
|
51
|
+
rescue
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def closed?
|
56
|
+
@closed
|
57
|
+
end
|
58
|
+
|
59
|
+
def exchange_hello
|
60
|
+
send_hello
|
61
|
+
receive_hello
|
62
|
+
end
|
63
|
+
|
64
|
+
def send_hello
|
65
|
+
@logger.info { "Local capabilities: #{@local_capabilities}" }
|
66
|
+
xml_doc = REXML::Document.new
|
67
|
+
hello_e = xml_doc.add_element 'hello'
|
68
|
+
hello_e.add_namespace('urn:ietf:params:xml:ns:netconf:base:1.0')
|
69
|
+
capabilities_e = hello_e.add_element 'capabilities'
|
70
|
+
@local_capabilities.list_loadable.each{ |c|
|
71
|
+
capability_e = capabilities_e.add_element 'capability'
|
72
|
+
capability_e.text = c
|
73
|
+
}
|
74
|
+
session_id_e = hello_e.add_element 'session-id'
|
75
|
+
session_id_e.text = @session_id.to_s
|
76
|
+
|
77
|
+
buf = String.new
|
78
|
+
formatter = REXML::Formatters::Pretty.new(2)
|
79
|
+
formatter.compact = true
|
80
|
+
formatter.write(xml_doc, buf)
|
81
|
+
@logger.debug { "Sending hello message: #{buf.inspect}" }
|
82
|
+
@io_w.write "#{buf}\n]]>]]>\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
def receive_hello
|
86
|
+
buf = String.new
|
87
|
+
loop do
|
88
|
+
buf += @io_r.read(1)
|
89
|
+
if buf[-6..-1] == ']]>]]>'
|
90
|
+
break
|
91
|
+
end
|
92
|
+
end
|
93
|
+
@logger.debug { "Received hello message: #{buf[0..-7].inspect}" }
|
94
|
+
remote_capabilities_xml_doc = REXML::Document.new(buf[0..-7], {:ignore_whitespace_nodes => :all})
|
95
|
+
remote_capabilities_xml_doc.each_element('/hello/capabilities/capability'){ |c| @remote_capabilities.push c.text }
|
96
|
+
@logger.info { "Remote capabilities: #{@remote_capabilities}" }
|
97
|
+
end
|
98
|
+
|
99
|
+
def negotiate_capabilities
|
100
|
+
@capabilities = @local_capabilities.negotiate @remote_capabilities
|
101
|
+
@logger.info { "Negotiated capabilities: #{@capabilities.list_loadable}" }
|
102
|
+
unless @capabilities.list_loadable.any?{ |c| /^urn:ietf:params:netconf:base:\d+\.\d+$/ =~ c }
|
103
|
+
@logger.error { "No base NETCONF capability negotiated" }
|
104
|
+
raise "No base NETCONF capability negotiated"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize_sender_and_receiver
|
109
|
+
base_capability = @capabilities.list_loadable.select{ |c| /^urn:ietf:params:netconf:base:\d+\.\d+$/ =~ c }.sort.last
|
110
|
+
@logger.info { "Base NETCONF capability: #{base_capability}" }
|
111
|
+
@sender = Capability[base_capability]::Sender.new @io_w
|
112
|
+
@receiver = Capability[base_capability]::Receiver.new @io_r
|
113
|
+
end
|
114
|
+
|
115
|
+
def operation_loop
|
116
|
+
datastore_session = @datastore.new_session self
|
117
|
+
operation = Operation.new self, @capabilities, datastore_session, @strict_capabilities
|
118
|
+
|
119
|
+
begin
|
120
|
+
loop do
|
121
|
+
if closed?
|
122
|
+
break
|
123
|
+
end
|
124
|
+
|
125
|
+
begin
|
126
|
+
begin
|
127
|
+
received_message = @receiver.receive_message
|
128
|
+
break unless received_message
|
129
|
+
rpc_reply_e = operation.run received_message
|
130
|
+
rescue Error => e
|
131
|
+
if received_message
|
132
|
+
rpc_reply_e = received_message.clone
|
133
|
+
rpc_reply_e.name = "rpc-reply"
|
134
|
+
else
|
135
|
+
rpc_reply_e = REXML::Element.new("rpc-reply")
|
136
|
+
rpc_reply_e.add_namespace("urn:ietf:params:xml:ns:netconf:base:1.0")
|
137
|
+
end
|
138
|
+
rpc_reply_e.add e.to_rpc_error
|
139
|
+
end
|
140
|
+
|
141
|
+
begin
|
142
|
+
@sender.send_message rpc_reply_e
|
143
|
+
rescue IOError
|
144
|
+
break
|
145
|
+
end
|
146
|
+
rescue => e
|
147
|
+
@logger.error { e.message }
|
148
|
+
raise
|
149
|
+
end
|
150
|
+
end
|
151
|
+
ensure
|
152
|
+
begin
|
153
|
+
datastore_session.close
|
154
|
+
rescue
|
155
|
+
end
|
156
|
+
begin
|
157
|
+
@io_w.close_write
|
158
|
+
rescue
|
159
|
+
end
|
160
|
+
end
|
161
|
+
@logger.info { "Exit operation_loop" }
|
162
|
+
end
|
163
|
+
|
164
|
+
def close_other session_id
|
165
|
+
@server.close_session session_id
|
166
|
+
end
|
167
|
+
|
168
|
+
def lock target
|
169
|
+
@server.lock target, @session_id
|
170
|
+
end
|
171
|
+
|
172
|
+
def unlock target
|
173
|
+
@server.unlock target, @session_id
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|