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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Netconf
2
+ VERSION = "0.4.0"
3
+ end
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: []