net-netconf 0.4.0

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