netconf 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/jnpr/edit-config-jnpr-set.rb +55 -0
- data/examples/jnpr/edit-config-jnpr-text.rb +64 -0
- data/examples/jnpr/edit-config-jnpr.rb +62 -0
- data/examples/jnpr/edit-config-std.rb +65 -0
- data/examples/jnpr/edit-config-text-std.rb +69 -0
- data/examples/jnpr/get-config-jnpr.rb +59 -0
- data/examples/jnpr/get-config-matching.rb +20 -0
- data/examples/jnpr/get-config-std.rb +49 -0
- data/examples/jnpr/get-inventory-serial-explicit.rb +29 -0
- data/examples/jnpr/get-inventory-serial.rb +29 -0
- data/examples/jnpr/get-inventory-telnet.rb +15 -0
- data/examples/jnpr/get-inventory.rb +19 -0
- data/examples/jnpr/scp.rb +22 -0
- data/lib/net/netconf.rb +14 -0
- data/lib/net/netconf/exception.rb +38 -0
- data/lib/net/netconf/jnpr.rb +8 -0
- data/lib/net/netconf/jnpr/rpc.rb +134 -0
- data/lib/net/netconf/jnpr/serial.rb +17 -0
- data/lib/net/netconf/jnpr/telnet.rb +23 -0
- data/lib/net/netconf/localhost.rb +9 -0
- data/lib/net/netconf/rpc.rb +71 -0
- data/lib/net/netconf/rpc_std.rb +137 -0
- data/lib/net/netconf/serial.rb +132 -0
- data/lib/net/netconf/ssh.rb +77 -0
- data/lib/net/netconf/telnet.rb +58 -0
- data/lib/net/netconf/transport.rb +113 -0
- metadata +103 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
module Netconf
|
2
|
+
module RPC
|
3
|
+
|
4
|
+
MSG_END = "]]>]]>"
|
5
|
+
MSG_END_RE = /\]\]>\]\]>[\r\n]*$/
|
6
|
+
MSG_CLOSE_SESSION = '<rpc><close-session/></rpc>'
|
7
|
+
MSG_HELLO = <<-EOM
|
8
|
+
<hello>
|
9
|
+
<capabilities>
|
10
|
+
<capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability>
|
11
|
+
<capability>urn:ietf:params:xml:ns:netconf:base:1.0#candidate</capability>
|
12
|
+
<capability>urn:ietf:params:xml:ns:netconf:base:1.0#confirmed-commit</capability>
|
13
|
+
<capability>urn:ietf:params:xml:ns:netconf:base:1.0#validate</capability>
|
14
|
+
<capability>urn:ietf:params:xml:ns:netconf:base:1.0#url?protocol=http,ftp,file</capability>
|
15
|
+
</capabilities>
|
16
|
+
</hello>
|
17
|
+
EOM
|
18
|
+
|
19
|
+
module Standard
|
20
|
+
|
21
|
+
def lock( target )
|
22
|
+
rpc = Nokogiri::XML( "<rpc><lock><target><#{target}/></target></lock></rpc>" ).root
|
23
|
+
Netconf::RPC.set_exception( rpc, Netconf::LockError )
|
24
|
+
@trans.rpc_exec( rpc )
|
25
|
+
end
|
26
|
+
|
27
|
+
def unlock( target )
|
28
|
+
rpc = Nokogiri::XML( "<rpc><unlock><target><#{target}/></target></unlock></rpc>" ).root
|
29
|
+
@trans.rpc_exec( rpc )
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate( source )
|
33
|
+
rpc = Nokogiri::XML( "<rpc><validate><source><#{source}/></source></validate></rpc>" ).root
|
34
|
+
Netconf::RPC.set_exception( rpc, Netconf::ValidateError )
|
35
|
+
@trans.rpc_exec( rpc )
|
36
|
+
end
|
37
|
+
|
38
|
+
def commit
|
39
|
+
rpc = Nokogiri::XML( "<rpc><commit/></rpc>" ).root
|
40
|
+
Netconf::RPC.set_exception( rpc, Netconf::CommitError )
|
41
|
+
@trans.rpc_exec( rpc )
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete_config( target )
|
45
|
+
rpc = Nokogiri::XML( "<rpc><delete-config><target><#{target}/></target></delete-config></rpc>" ).root
|
46
|
+
@trans.rpc_exec( rpc )
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_config( *args ) # :yeield: filter_builder
|
50
|
+
|
51
|
+
source = 'running' # default source is 'running'
|
52
|
+
filter = nil # no filter by default
|
53
|
+
|
54
|
+
while arg = args.shift
|
55
|
+
case arg.class.to_s
|
56
|
+
when /^Nokogiri/
|
57
|
+
filter = case arg
|
58
|
+
when Nokogiri::XML::Builder then arg.doc.root
|
59
|
+
when Nokogiri::XML::Document then arg.root
|
60
|
+
else arg
|
61
|
+
end
|
62
|
+
when 'Hash' then attrs = arg
|
63
|
+
when 'String' then source = arg
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
rpc = Nokogiri::XML("<rpc><get-config><source><#{source}/></source></get-config></rpc>").root
|
68
|
+
|
69
|
+
if block_given?
|
70
|
+
Nokogiri::XML::Builder.with( rpc.at( 'get-config' )){ |xml|
|
71
|
+
xml.filter( :type => 'subtree' ) {
|
72
|
+
yield( xml )
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
if filter
|
78
|
+
f_node = Nokogiri::XML::Node.new( 'filter', rpc )
|
79
|
+
f_node['type'] = 'subtree'
|
80
|
+
f_node << filter.dup # copy filter, don't mess with the original since it may be re-used
|
81
|
+
rpc.at('get-config') << f_node
|
82
|
+
end
|
83
|
+
|
84
|
+
@trans.rpc_exec( rpc )
|
85
|
+
end
|
86
|
+
|
87
|
+
def edit_config( *args ) # :yeield: config_builder
|
88
|
+
|
89
|
+
toplevel = 'config' # default toplevel config element
|
90
|
+
target = 'candidate' # default source is 'candidate' @@@/JLS hack; need to fix this
|
91
|
+
config = nil
|
92
|
+
options = {}
|
93
|
+
|
94
|
+
while arg = args.shift
|
95
|
+
case arg.class.to_s
|
96
|
+
when /^Nokogiri/
|
97
|
+
config = case arg
|
98
|
+
when Nokogiri::XML::Builder then arg.doc.root
|
99
|
+
when Nokogiri::XML::Document then arg.root
|
100
|
+
else arg
|
101
|
+
end
|
102
|
+
when 'Hash' then options = arg
|
103
|
+
when 'String' then target = arg
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
toplevel = options[:toplevel] if options[:toplevel]
|
108
|
+
|
109
|
+
rpc_str = <<-EO_RPC
|
110
|
+
<rpc>
|
111
|
+
<edit-config>
|
112
|
+
<target><#{target}/></target>
|
113
|
+
<#{toplevel}/>
|
114
|
+
</edit-config>
|
115
|
+
</rpc>
|
116
|
+
EO_RPC
|
117
|
+
|
118
|
+
rpc = Nokogiri::XML( rpc_str ).root
|
119
|
+
|
120
|
+
if block_given?
|
121
|
+
Nokogiri::XML::Builder.with(rpc.at( toplevel )){ |xml|
|
122
|
+
yield( xml )
|
123
|
+
}
|
124
|
+
elsif config
|
125
|
+
rpc.at( toplevel ) << config.dup
|
126
|
+
else
|
127
|
+
raise ArgumentError, "You must specify edit-config data!"
|
128
|
+
end
|
129
|
+
|
130
|
+
Netconf::RPC.set_exception( rpc, Netconf::EditError )
|
131
|
+
@trans.rpc_exec( rpc )
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end # module: RPC
|
137
|
+
end # module: Netconf
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
|
3
|
+
module Netconf
|
4
|
+
|
5
|
+
class Serial < Netconf::Transport
|
6
|
+
|
7
|
+
DEFAULT_BAUD = 9600
|
8
|
+
DEFAULT_DATABITS = 8
|
9
|
+
DEFAULT_STOPBITS = 1
|
10
|
+
DEFAULT_PARITY = SerialPort::NONE
|
11
|
+
DEFAULT_RDBLKSZ = (1024*1024)
|
12
|
+
|
13
|
+
attr_reader :args
|
14
|
+
|
15
|
+
def initialize( args_h, &block )
|
16
|
+
os_type = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE
|
17
|
+
|
18
|
+
raise Netconf::InitError, "Missing 'port' param" unless args_h[:port]
|
19
|
+
raise Netconf::InitError, "Missing 'username' param" unless args_h[:username]
|
20
|
+
|
21
|
+
@args = args_h.clone
|
22
|
+
@args[:prompt] ||= /([%>])\s+$/
|
23
|
+
|
24
|
+
# extend this instance with the capabilities of the specific console
|
25
|
+
# type; it needs to define #trans_start_netconf session
|
26
|
+
# this must be provided! if the caller does not, this will
|
27
|
+
# throw a NameError exception.
|
28
|
+
|
29
|
+
extend Netconf::TransSerial::const_get( os_type )
|
30
|
+
|
31
|
+
@trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT
|
32
|
+
@trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO
|
33
|
+
|
34
|
+
super( &block )
|
35
|
+
end
|
36
|
+
|
37
|
+
def login
|
38
|
+
|
39
|
+
begin
|
40
|
+
puts
|
41
|
+
waitfor(/ogin:/)
|
42
|
+
rescue Timeout::Error
|
43
|
+
puts
|
44
|
+
waitfor(/ogin:/)
|
45
|
+
end
|
46
|
+
|
47
|
+
puts @args[:username]
|
48
|
+
|
49
|
+
waitfor(/assword:/)
|
50
|
+
puts @args[:password]
|
51
|
+
|
52
|
+
waitfor( @args[:prompt] )
|
53
|
+
end
|
54
|
+
|
55
|
+
def trans_open # :yield: self
|
56
|
+
|
57
|
+
baud = @args[:speed] || DEFAULT_BAUD
|
58
|
+
data_bits = @args[:bits] || DEFAULT_DATABITS
|
59
|
+
stop_bits = @args[:stop] || DEFAULT_STOPBITS
|
60
|
+
parity = @args[:parity] || DEFAULT_PARITY
|
61
|
+
|
62
|
+
@trans = SerialPort.new( @args[:port], baud, data_bits, stop_bits, parity )
|
63
|
+
|
64
|
+
got = login()
|
65
|
+
yield self if block_given?
|
66
|
+
trans_start_netconf( got )
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def trans_hello
|
72
|
+
hello_str = trans_receive()
|
73
|
+
so_xml = hello_str.index("\n") + 1
|
74
|
+
hello_str.slice!(0, so_xml)
|
75
|
+
hello_str
|
76
|
+
end
|
77
|
+
|
78
|
+
def trans_close
|
79
|
+
@trans.write Netconf::RPC::MSG_CLOSE_SESSION
|
80
|
+
@trans.close
|
81
|
+
end
|
82
|
+
|
83
|
+
def trans_send( cmd_str )
|
84
|
+
@trans.write( cmd_str )
|
85
|
+
@trans.fsync
|
86
|
+
end
|
87
|
+
|
88
|
+
def trans_receive
|
89
|
+
got = waitfor( Netconf::RPC::MSG_END_RE )
|
90
|
+
msg_end = got.rindex( Netconf::RPC::MSG_END )
|
91
|
+
got[msg_end .. -1] = ''
|
92
|
+
got
|
93
|
+
end
|
94
|
+
|
95
|
+
def puts( str = nil )
|
96
|
+
@trans.puts str
|
97
|
+
@trans.fsync
|
98
|
+
end
|
99
|
+
|
100
|
+
def waitfor( this_re = nil )
|
101
|
+
on_re = this_re || @args[:prompt]
|
102
|
+
|
103
|
+
time_out = @trans_timeout
|
104
|
+
wait_io = @trans_waitio
|
105
|
+
|
106
|
+
time_out = nil if time_out == false
|
107
|
+
done = false
|
108
|
+
rx_buf = ''
|
109
|
+
|
110
|
+
until( rx_buf.match( on_re ) and not IO::select( [@trans], nil, nil, wait_io ) )
|
111
|
+
|
112
|
+
unless IO::select( [@trans], nil, nil, time_out )
|
113
|
+
raise TimeoutError, "Netconf IO timed out while waiting for more data"
|
114
|
+
end
|
115
|
+
|
116
|
+
begin
|
117
|
+
|
118
|
+
rx_some = @trans.readpartial( DEFAULT_RDBLKSZ )
|
119
|
+
rx_buf += rx_some
|
120
|
+
break if rx_buf.match( on_re )
|
121
|
+
|
122
|
+
rescue EOFError # End of file reached
|
123
|
+
rx_buf = nil if rx_buf == ''
|
124
|
+
break # out of outer 'until' loop
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
rx_buf
|
129
|
+
end
|
130
|
+
|
131
|
+
end # class: Serial
|
132
|
+
end # module: Netconf
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
module Netconf
|
4
|
+
class SSH < Netconf::Transport
|
5
|
+
|
6
|
+
NETCONF_PORT = 830
|
7
|
+
NETCONF_SUBSYSTEM = 'netconf'
|
8
|
+
|
9
|
+
def initialize( args_h, &block )
|
10
|
+
@args = args_h.clone
|
11
|
+
@trans = Hash.new
|
12
|
+
|
13
|
+
super( &block )
|
14
|
+
end
|
15
|
+
|
16
|
+
def trans_open( &block )
|
17
|
+
# open a connection to the NETCONF subsystem
|
18
|
+
start_args = Hash.new
|
19
|
+
start_args[:password] ||= @args[:password]
|
20
|
+
start_args[:passphrase] = @args[:passphrase] || nil
|
21
|
+
start_args[:port] = @args[:port] || NETCONF_PORT
|
22
|
+
|
23
|
+
@trans[:conn] = Net::SSH.start( @args[:target], @args[:username], start_args )
|
24
|
+
@trans[:chan] = @trans[:conn].open_channel{ |ch| ch.subsystem( NETCONF_SUBSYSTEM ) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def trans_close
|
28
|
+
@trans[:chan].close if @trans[:chan]
|
29
|
+
@trans[:conn].close if @trans[:conn]
|
30
|
+
end
|
31
|
+
|
32
|
+
def trans_receive
|
33
|
+
@trans[:rx_buf] = ''
|
34
|
+
@trans[:more] = true
|
35
|
+
|
36
|
+
# collect the response data as it comes back ...
|
37
|
+
# the "on" functions must be set before calling
|
38
|
+
# the #loop method
|
39
|
+
|
40
|
+
@trans[:chan].on_data do |ch, data|
|
41
|
+
if data.include?( RPC::MSG_END )
|
42
|
+
data.slice!( RPC::MSG_END )
|
43
|
+
@trans[:rx_buf] << data unless data.empty?
|
44
|
+
@trans[:more] = false
|
45
|
+
else
|
46
|
+
@trans[:rx_buf] << data
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# ... if there are errors ...
|
51
|
+
@trans[:chan].on_extended_data do |ch, type, data|
|
52
|
+
@trans[:rx_err] = data
|
53
|
+
@trans[:more] = false
|
54
|
+
end
|
55
|
+
|
56
|
+
# the #loop method is what actually performs
|
57
|
+
# ssh event processing ...
|
58
|
+
|
59
|
+
@trans[:conn].loop { @trans[:more] }
|
60
|
+
|
61
|
+
return @trans[:rx_buf]
|
62
|
+
end
|
63
|
+
|
64
|
+
def trans_send( cmd_str )
|
65
|
+
@trans[:chan].send_data( cmd_str )
|
66
|
+
end
|
67
|
+
|
68
|
+
# accessor to create an Net::SCP object so the caller can perform
|
69
|
+
# secure-copy operations (see Net::SCP) for details
|
70
|
+
def scp
|
71
|
+
@scp ||= Net::SCP.start( @args[:target], @args[:username], :password => @args[:password] )
|
72
|
+
end
|
73
|
+
|
74
|
+
end # class: SSH
|
75
|
+
end #module: Netconf
|
76
|
+
|
77
|
+
require 'net/netconf/ssh'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
|
3
|
+
module Netconf
|
4
|
+
|
5
|
+
class Telnet < Netconf::Transport
|
6
|
+
|
7
|
+
def initialize( args, trans_args = nil, &block )
|
8
|
+
os_type = args[:os_type] || Netconf::DEFAULT_OS_TYPE
|
9
|
+
@args = args.clone
|
10
|
+
|
11
|
+
# extend this instance with the capabilities of the specific console
|
12
|
+
# type; it needs to define #login and #start_netconf session
|
13
|
+
begin
|
14
|
+
extend Netconf::TransTelnet::const_get( os_type )
|
15
|
+
rescue NameError
|
16
|
+
# no extensions available ...
|
17
|
+
end
|
18
|
+
|
19
|
+
my_trans_args = {}
|
20
|
+
my_trans_args["Host"] = @args[:target]
|
21
|
+
my_trans_args["Port"] = @args[:port] if @args[:port]
|
22
|
+
|
23
|
+
@trans = Net::Telnet.new( my_trans_args )
|
24
|
+
|
25
|
+
@trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT
|
26
|
+
@trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO
|
27
|
+
|
28
|
+
super( &block )
|
29
|
+
end
|
30
|
+
|
31
|
+
def trans_open( &block )
|
32
|
+
trans_login()
|
33
|
+
trans_start_netconf()
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def trans_hello
|
38
|
+
hello_str = trans_receive()
|
39
|
+
so_xml = hello_str.index("\n") + 1 # skip over the last issued command
|
40
|
+
hello_str[so_xml .. -1]
|
41
|
+
end
|
42
|
+
|
43
|
+
def trans_close
|
44
|
+
@trans.write Netconf::RPC::MSG_CLOSE_SESSION
|
45
|
+
@trans.close
|
46
|
+
end
|
47
|
+
|
48
|
+
def trans_send( cmd_str )
|
49
|
+
@trans.write( cmd_str )
|
50
|
+
end
|
51
|
+
|
52
|
+
def trans_receive
|
53
|
+
rsp = @trans.waitfor( Netconf::RPC::MSG_END_RE )
|
54
|
+
rsp.chomp!( Netconf::RPC::MSG_END + "\n" )
|
55
|
+
end
|
56
|
+
|
57
|
+
end # class: Serial
|
58
|
+
end # module: Netconf
|
@@ -0,0 +1,113 @@
|
|
1
|
+
## -----------------------------------------------------------------------
|
2
|
+
## This file contains the Netconf::Transport parent class definition.
|
3
|
+
## All other transports, i.e. "ssh", "serial", "telnet" use this parent
|
4
|
+
## class to define their transport specific methods:
|
5
|
+
##
|
6
|
+
## trans_open: open the transport connection
|
7
|
+
## trans_close: close the transport connection
|
8
|
+
## trans_send: send XML command (String) via transport
|
9
|
+
## trans_receive: receive XML response (String) via transport
|
10
|
+
##
|
11
|
+
## -----------------------------------------------------------------------
|
12
|
+
|
13
|
+
module Netconf
|
14
|
+
class Transport
|
15
|
+
|
16
|
+
attr_reader :rpc, :state, :session_id, :capabilities
|
17
|
+
attr_writer :timeout, :waitio
|
18
|
+
|
19
|
+
def initialize( &block )
|
20
|
+
|
21
|
+
@state = :NETCONF_CLOSED
|
22
|
+
@os_type = @args[:os_type] || Netconf::DEFAULT_OS_TYPE
|
23
|
+
|
24
|
+
@rpc = Netconf::RPC::Executor.new( self, @os_type )
|
25
|
+
|
26
|
+
if block_given?
|
27
|
+
open( &block = nil ) # do not pass this block to open()
|
28
|
+
yield self
|
29
|
+
close()
|
30
|
+
end
|
31
|
+
|
32
|
+
end # initialize
|
33
|
+
|
34
|
+
def open( &block ) # :yield: specialized transport open, generally not used
|
35
|
+
|
36
|
+
raise Netconf::StateError if @state == :NETCONF_OPEN
|
37
|
+
|
38
|
+
# block is used to deal with special open processing ...
|
39
|
+
# this is *NOT* the block passed to initialize()
|
40
|
+
raise Netconf::OpenError unless trans_open( &block )
|
41
|
+
@state = :NETCONF_OPEN
|
42
|
+
|
43
|
+
hello_rsp = Nokogiri::XML( trans_hello() )
|
44
|
+
|
45
|
+
@capabilities = hello_rsp.xpath('//capability').map{ |c| c.text }
|
46
|
+
@session_id = hello_rsp.xpath('//session-id').text
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def trans_hello
|
51
|
+
trans_receive()
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_capability?( capability )
|
55
|
+
@capabilities.select{|c| c.include? capability }.pop
|
56
|
+
# note: the caller could also simply use #grep on @capabilities
|
57
|
+
end
|
58
|
+
|
59
|
+
def close
|
60
|
+
raise Netconf::StateError unless @state == :NETCONF_OPEN
|
61
|
+
trans_close()
|
62
|
+
@state = :NETCONF_CLOSED
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# string in; string out
|
67
|
+
def send_and_receive( cmd_str )
|
68
|
+
trans_send( cmd_str )
|
69
|
+
trans_receive()
|
70
|
+
end
|
71
|
+
|
72
|
+
def rpc_exec( cmd_nx )
|
73
|
+
raise Netconf::StateError unless @state == :NETCONF_OPEN
|
74
|
+
|
75
|
+
# send the XML command through the transport and
|
76
|
+
# receive the response; then covert it to a Nokogiri XML
|
77
|
+
# object so we can process it.
|
78
|
+
|
79
|
+
rsp_nx = Nokogiri::XML( send_and_receive( cmd_nx.to_xml ))
|
80
|
+
|
81
|
+
# the following removes only the default namespace (xmlns)
|
82
|
+
# definitions from the document. This is an alternative
|
83
|
+
# to using #remove_namespaces! which would remove everything
|
84
|
+
# including vendor specific namespaces. So this approach is a
|
85
|
+
# nice "compromise" ... just don't know what it does
|
86
|
+
# performance-wise on large datasets.
|
87
|
+
|
88
|
+
rsp_nx.traverse{ |n| n.namespace = nil }
|
89
|
+
|
90
|
+
# set the response context to the root node; <rpc-reply>
|
91
|
+
|
92
|
+
rsp_nx = rsp_nx.root
|
93
|
+
|
94
|
+
# the <rpc-error> could either be at the toplevel or
|
95
|
+
# as a child element of toplevel.
|
96
|
+
|
97
|
+
if rsp_nx.xpath( "rpc-error|*/rpc-error" )[0]
|
98
|
+
exception = Netconf::RPC.get_exception( cmd_nx )
|
99
|
+
raise exception.new( self, cmd_nx, rsp_nx )
|
100
|
+
end
|
101
|
+
|
102
|
+
# return the XML with context at toplevel element; i.e.
|
103
|
+
# after the <rpc-reply> element
|
104
|
+
# @@@/JLS: might this be <ok> ? isn't for Junos, but need to check
|
105
|
+
# @@@/JLS: the generic case.
|
106
|
+
|
107
|
+
rsp_nx.first_element_child
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
end #--class: Transport
|
113
|
+
end #--module: Netconf
|