net-netconf 0.4.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/examples/confd/get-running.rb +27 -0
- 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 +73 -0
- data/examples/jnpr/edit-config-std.rb +64 -0
- data/examples/jnpr/edit-config-text-std.rb +68 -0
- data/examples/jnpr/get-config-jnpr.rb +62 -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 +27 -0
- data/examples/jnpr/get-inventory-serial.rb +25 -0
- data/examples/jnpr/get-inventory-telnet.rb +14 -0
- data/examples/jnpr/get-inventory.rb +16 -0
- data/examples/jnpr/scp.rb +22 -0
- data/lib/net/netconf.rb +24 -0
- data/lib/net/netconf/exception.rb +38 -0
- data/lib/net/netconf/ioproc.rb +88 -0
- data/lib/net/netconf/jnpr.rb +9 -0
- data/lib/net/netconf/jnpr/ioproc.rb +14 -0
- data/lib/net/netconf/jnpr/junos_config.rb +104 -0
- data/lib/net/netconf/jnpr/rpc.rb +162 -0
- data/lib/net/netconf/jnpr/serial.rb +15 -0
- data/lib/net/netconf/jnpr/ssh.rb +27 -0
- data/lib/net/netconf/jnpr/telnet.rb +23 -0
- data/lib/net/netconf/rpc.rb +71 -0
- data/lib/net/netconf/rpc_std.rb +133 -0
- data/lib/net/netconf/serial.rb +135 -0
- data/lib/net/netconf/ssh.rb +94 -0
- data/lib/net/netconf/telnet.rb +52 -0
- data/lib/net/netconf/transport.rb +156 -0
- data/lib/net/netconf/version.rb +3 -0
- metadata +116 -0
@@ -0,0 +1,94 @@
|
|
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
|
+
@args[:os_type] = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE
|
12
|
+
|
13
|
+
# extend this instance with the capabilities of the specific os_type
|
14
|
+
begin
|
15
|
+
extend Netconf::const_get( @args[:os_type] )::TransSSH
|
16
|
+
rescue NameError
|
17
|
+
# no extensions available ...
|
18
|
+
end
|
19
|
+
|
20
|
+
@trans = Hash.new
|
21
|
+
super( &block )
|
22
|
+
end
|
23
|
+
|
24
|
+
def trans_open( &block )
|
25
|
+
# open a connection to the NETCONF subsystem
|
26
|
+
start_args = Hash.new
|
27
|
+
start_args[:password] ||= @args[:password]
|
28
|
+
start_args[:passphrase] = @args[:passphrase] || nil
|
29
|
+
start_args[:port] = @args[:port] || NETCONF_PORT
|
30
|
+
start_args.merge!(@args[:ssh_args]) if @args[:ssh_args]
|
31
|
+
|
32
|
+
begin
|
33
|
+
@trans[:conn] = Net::SSH.start( @args[:target], @args[:username], start_args )
|
34
|
+
@trans[:chan] = @trans[:conn].open_channel{ |ch| ch.subsystem( NETCONF_SUBSYSTEM ) }
|
35
|
+
rescue Errno::ECONNREFUSED => e
|
36
|
+
if self.respond_to? 'trans_on_connect_refused'
|
37
|
+
return trans_on_connect_refused( start_args )
|
38
|
+
end
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
@trans[:chan]
|
42
|
+
end
|
43
|
+
|
44
|
+
def trans_close
|
45
|
+
@trans[:chan].close if @trans[:chan]
|
46
|
+
@trans[:conn].close if @trans[:conn]
|
47
|
+
end
|
48
|
+
|
49
|
+
def trans_receive
|
50
|
+
@trans[:rx_buf] = ''
|
51
|
+
@trans[:more] = true
|
52
|
+
|
53
|
+
# collect the response data as it comes back ...
|
54
|
+
# the "on" functions must be set before calling
|
55
|
+
# the #loop method
|
56
|
+
|
57
|
+
@trans[:chan].on_data do |ch, data|
|
58
|
+
if data.include?( RPC::MSG_END )
|
59
|
+
data.slice!( RPC::MSG_END )
|
60
|
+
@trans[:rx_buf] << data unless data.empty?
|
61
|
+
@trans[:more] = false
|
62
|
+
else
|
63
|
+
@trans[:rx_buf] << data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# ... if there are errors ...
|
68
|
+
@trans[:chan].on_extended_data do |ch, type, data|
|
69
|
+
@trans[:rx_err] = data
|
70
|
+
@trans[:more] = false
|
71
|
+
end
|
72
|
+
|
73
|
+
# the #loop method is what actually performs
|
74
|
+
# ssh event processing ...
|
75
|
+
|
76
|
+
@trans[:conn].loop { @trans[:more] }
|
77
|
+
|
78
|
+
return @trans[:rx_buf]
|
79
|
+
end
|
80
|
+
|
81
|
+
def trans_send( cmd_str )
|
82
|
+
@trans[:chan].send_data( cmd_str )
|
83
|
+
end
|
84
|
+
|
85
|
+
# accessor to create an Net::SCP object so the caller can perform
|
86
|
+
# secure-copy operations (see Net::SCP) for details
|
87
|
+
def scp
|
88
|
+
@scp ||= Net::SCP.start( @args[:target], @args[:username], :password => @args[:password] )
|
89
|
+
end
|
90
|
+
|
91
|
+
end # class: SSH
|
92
|
+
end #module: Netconf
|
93
|
+
|
94
|
+
require 'net/netconf/ssh'
|
@@ -0,0 +1,52 @@
|
|
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::const_get( os_type )::TransTelnet
|
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_close
|
38
|
+
@trans.write Netconf::RPC::MSG_CLOSE_SESSION
|
39
|
+
@trans.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def trans_send( cmd_str )
|
43
|
+
@trans.write( cmd_str )
|
44
|
+
end
|
45
|
+
|
46
|
+
def trans_receive
|
47
|
+
rsp = @trans.waitfor( Netconf::RPC::MSG_END_RE )
|
48
|
+
rsp.chomp!( Netconf::RPC::MSG_END + "\n" )
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class: Serial
|
52
|
+
end # module: Netconf
|
@@ -0,0 +1,156 @@
|
|
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
|
+
@rpc_message_id = 1
|
26
|
+
|
27
|
+
if block_given?
|
28
|
+
open( &block = nil ) # do not pass this block to open()
|
29
|
+
yield self
|
30
|
+
close()
|
31
|
+
end
|
32
|
+
|
33
|
+
end # initialize
|
34
|
+
|
35
|
+
def open?
|
36
|
+
@state == :NETCONF_OPEN
|
37
|
+
end
|
38
|
+
|
39
|
+
def closed?
|
40
|
+
@state == :NECONF_CLOSED
|
41
|
+
end
|
42
|
+
|
43
|
+
def open( &block ) # :yield: specialized transport open, generally not used
|
44
|
+
|
45
|
+
raise Netconf::StateError if @state == :NETCONF_OPEN
|
46
|
+
|
47
|
+
# block is used to deal with special open processing ...
|
48
|
+
# this is *NOT* the block passed to initialize()
|
49
|
+
raise Netconf::OpenError unless trans_open( &block )
|
50
|
+
|
51
|
+
# read the <hello> from the server and parse out
|
52
|
+
# the capabilities and session-id
|
53
|
+
|
54
|
+
hello_rsp = Nokogiri::XML( trans_receive_hello() )
|
55
|
+
hello_rsp.remove_namespaces!
|
56
|
+
|
57
|
+
@capabilities = hello_rsp.xpath('//capability').map{ |c| c.text }
|
58
|
+
@session_id = hello_rsp.xpath('//session-id').text
|
59
|
+
|
60
|
+
# send the <hello>
|
61
|
+
trans_send_hello()
|
62
|
+
|
63
|
+
@state = :NETCONF_OPEN
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def trans_receive_hello
|
68
|
+
trans_receive()
|
69
|
+
end
|
70
|
+
|
71
|
+
def trans_send_hello
|
72
|
+
trans_send( Netconf::RPC::MSG_HELLO )
|
73
|
+
end
|
74
|
+
|
75
|
+
def has_capability?( capability )
|
76
|
+
@capabilities.select{|c| c.include? capability }.pop
|
77
|
+
# note: the caller could also simply use #grep on @capabilities
|
78
|
+
end
|
79
|
+
|
80
|
+
def close
|
81
|
+
raise Netconf::StateError unless @state == :NETCONF_OPEN
|
82
|
+
trans_close()
|
83
|
+
@state = :NETCONF_CLOSED
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
# string in; string out
|
88
|
+
def send_and_receive( cmd_str )
|
89
|
+
trans_send( cmd_str )
|
90
|
+
trans_send( RPC::MSG_END )
|
91
|
+
trans_receive()
|
92
|
+
end
|
93
|
+
|
94
|
+
def rpc_exec( cmd_nx )
|
95
|
+
raise Netconf::StateError unless @state == :NETCONF_OPEN
|
96
|
+
|
97
|
+
# add the mandatory message-id and namespace to the RPC
|
98
|
+
|
99
|
+
rpc_nx = cmd_nx.parent.root
|
100
|
+
rpc_nx.default_namespace = Netconf::NAMESPACE
|
101
|
+
rpc_nx['message-id'] = @rpc_message_id.to_s
|
102
|
+
@rpc_message_id += 1
|
103
|
+
|
104
|
+
# send the XML command through the transport and
|
105
|
+
# receive the response; then covert it to a Nokogiri XML
|
106
|
+
# object so we can process it.
|
107
|
+
|
108
|
+
rsp_nx = Nokogiri::XML( send_and_receive( cmd_nx.to_xml ))
|
109
|
+
|
110
|
+
# the following removes only the default namespace (xmlns)
|
111
|
+
# definitions from the document. This is an alternative
|
112
|
+
# to using #remove_namespaces! which would remove everything
|
113
|
+
# including vendor specific namespaces. So this approach is a
|
114
|
+
# nice "compromise" ... just don't know what it does
|
115
|
+
# performance-wise on large datasets.
|
116
|
+
|
117
|
+
rsp_nx.traverse{ |n| n.namespace = nil }
|
118
|
+
|
119
|
+
# set the response context to the root node; <rpc-reply>
|
120
|
+
|
121
|
+
rsp_nx = rsp_nx.root
|
122
|
+
|
123
|
+
# check for rpc-error elements. these could be
|
124
|
+
# located anywhere in the structured response
|
125
|
+
|
126
|
+
rpc_errs = rsp_nx.xpath('//self::rpc-error')
|
127
|
+
if rpc_errs.count > 0
|
128
|
+
|
129
|
+
# look for rpc-errors that have a severity == 'error'
|
130
|
+
# in some cases the rpc-error is generated with
|
131
|
+
# severity == 'warning'
|
132
|
+
|
133
|
+
sev_err = rpc_errs.xpath('error-severity[. = "error"]')
|
134
|
+
|
135
|
+
# if there are rpc-error with severity == 'error'
|
136
|
+
# or if the caller wants to raise if severity == 'warning'
|
137
|
+
# then generate the exception
|
138
|
+
|
139
|
+
if(( sev_err.count > 0 ) || Netconf::raise_on_warning )
|
140
|
+
exception = Netconf::RPC.get_exception( cmd_nx )
|
141
|
+
raise exception.new( self, cmd_nx, rsp_nx )
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# return the XML with context at toplevel element; i.e.
|
146
|
+
# after the <rpc-reply> element
|
147
|
+
# @@@/JLS: might this be <ok> ? isn't for Junos, but need to check
|
148
|
+
# @@@/JLS: the generic case.
|
149
|
+
|
150
|
+
rsp_nx.first_element_child
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
end #--class: Transport
|
156
|
+
end #--module: Netconf
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: net-netconf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Kirsche
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-ssh
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.9'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: net-scp
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.2'
|
55
|
+
description: Ruby NetConf client
|
56
|
+
email: kev.kirsche@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- examples/confd/get-running.rb
|
62
|
+
- examples/jnpr/edit-config-jnpr-set.rb
|
63
|
+
- examples/jnpr/edit-config-jnpr-text.rb
|
64
|
+
- examples/jnpr/edit-config-jnpr.rb
|
65
|
+
- examples/jnpr/edit-config-std.rb
|
66
|
+
- examples/jnpr/edit-config-text-std.rb
|
67
|
+
- examples/jnpr/get-config-jnpr.rb
|
68
|
+
- examples/jnpr/get-config-matching.rb
|
69
|
+
- examples/jnpr/get-config-std.rb
|
70
|
+
- examples/jnpr/get-inventory-serial-explicit.rb
|
71
|
+
- examples/jnpr/get-inventory-serial.rb
|
72
|
+
- examples/jnpr/get-inventory-telnet.rb
|
73
|
+
- examples/jnpr/get-inventory.rb
|
74
|
+
- examples/jnpr/scp.rb
|
75
|
+
- lib/net/netconf.rb
|
76
|
+
- lib/net/netconf/exception.rb
|
77
|
+
- lib/net/netconf/ioproc.rb
|
78
|
+
- lib/net/netconf/jnpr.rb
|
79
|
+
- lib/net/netconf/jnpr/ioproc.rb
|
80
|
+
- lib/net/netconf/jnpr/junos_config.rb
|
81
|
+
- lib/net/netconf/jnpr/rpc.rb
|
82
|
+
- lib/net/netconf/jnpr/serial.rb
|
83
|
+
- lib/net/netconf/jnpr/ssh.rb
|
84
|
+
- lib/net/netconf/jnpr/telnet.rb
|
85
|
+
- lib/net/netconf/rpc.rb
|
86
|
+
- lib/net/netconf/rpc_std.rb
|
87
|
+
- lib/net/netconf/serial.rb
|
88
|
+
- lib/net/netconf/ssh.rb
|
89
|
+
- lib/net/netconf/telnet.rb
|
90
|
+
- lib/net/netconf/transport.rb
|
91
|
+
- lib/net/netconf/version.rb
|
92
|
+
homepage: https://github.com/kkirsche/net-netconf
|
93
|
+
licenses:
|
94
|
+
- BSD 2
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.4.6
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: NetConf client
|
116
|
+
test_files: []
|