net-netconf 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|