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,162 @@
1
+ ## -----------------------------------------------------------------------
2
+ ## This file contains the Junos specific RPC methods that are generated
3
+ ## specifically and different as generated by the Netconf::RPC::Builder
4
+ ## module. These are specifically the following:
5
+ ##
6
+ ## get_configuration - alternative NETCONF: 'get-config'
7
+ ## load_configuration - alternative NETCONF: 'edit-config'
8
+ ## lock_configuration - alternative NETCONF: 'lock'
9
+ ## commit_configuration - alternative NETCONF: 'commit'
10
+ ##
11
+ ## note: unlock_configuration is not included in this file since
12
+ ## the Netconf::RPC::Builder works "as-is" in this case
13
+ ## -----------------------------------------------------------------------
14
+
15
+ module Netconf
16
+ module RPC
17
+ module Junos
18
+
19
+ def lock_configuration
20
+ lock( 'candidate' )
21
+ end
22
+
23
+ def check_configuration
24
+ validate( 'candidate' )
25
+ end
26
+
27
+ def commit_configuration( params = nil, attrs = nil )
28
+ rpc = Netconf::RPC::Builder.commit_configuration( params, attrs )
29
+ Netconf::RPC.set_exception( rpc, Netconf::CommitError )
30
+ @trans.rpc_exec( rpc )
31
+ end
32
+
33
+ def get_configuration( *args )
34
+
35
+ filter = nil
36
+
37
+ while arg = args.shift
38
+ case arg.class.to_s
39
+ when /^Nokogiri/
40
+ filter = case arg
41
+ when Nokogiri::XML::Builder then arg.doc.root
42
+ when Nokogiri::XML::Document then arg.root
43
+ else arg
44
+ end
45
+ when 'Hash' then attrs = arg
46
+ end
47
+ end
48
+
49
+ rpc = Nokogiri::XML('<rpc><get-configuration/></rpc>').root
50
+ Netconf::RPC.add_attributes( rpc.first_element_child, attrs ) if attrs
51
+
52
+ if block_given?
53
+ Nokogiri::XML::Builder.with(rpc.at( 'get-configuration' )){ |xml|
54
+ xml.configuration {
55
+ yield( xml )
56
+ }}
57
+ elsif filter
58
+ # filter must have toplevel = <configuration>
59
+ rpc.first_element_child << filter.dup # *MUST* use the .dup so we don't disrupt the original filter
60
+ end
61
+
62
+ @trans.rpc_exec( rpc )
63
+ end
64
+
65
+ def load_configuration( *args )
66
+
67
+ config = nil
68
+
69
+ # default format is XML
70
+ attrs = { :format => 'xml' }
71
+
72
+ while arg = args.shift
73
+ case arg.class.to_s
74
+ when /^Nokogiri/
75
+ config = case arg
76
+ when Nokogiri::XML::Builder then arg.doc.root
77
+ when Nokogiri::XML::Document then arg.root
78
+ else arg
79
+ end
80
+ when 'Hash' then attrs.merge! arg
81
+ when 'Array' then config = arg.join("\n")
82
+ when 'String' then config = arg
83
+ end
84
+ end
85
+
86
+ case attrs[:format]
87
+ when 'set'
88
+ toplevel = 'configuration-set'
89
+ attrs[:format] = 'text'
90
+ attrs[:action] = 'set'
91
+ when 'text'
92
+ toplevel = 'configuration-text'
93
+ when 'xml'
94
+ toplevel = 'configuration'
95
+ end
96
+
97
+ rpc = Nokogiri::XML('<rpc><load-configuration/></rpc>').root
98
+ ld_cfg = rpc.first_element_child
99
+ Netconf::RPC.add_attributes( ld_cfg, attrs ) if attrs
100
+
101
+ if block_given?
102
+ if attrs[:format] == 'xml'
103
+ Nokogiri::XML::Builder.with( ld_cfg ){ |xml|
104
+ xml.send( toplevel ) {
105
+ yield( xml )
106
+ }}
107
+ else
108
+ config = yield # returns String | Array(of stringable)
109
+ config = config.join("\n") if config.class == Array
110
+ end
111
+ end
112
+
113
+ if config
114
+ if attrs[:format] == 'xml'
115
+ # config assumes toplevel = <configuration> given
116
+ ld_cfg << config.dup # duplicate the config so as to not distrupt it
117
+ else
118
+ # config is stringy, so just add it as the text node
119
+ c_node = Nokogiri::XML::Node.new( toplevel, rpc )
120
+ c_node.content = config
121
+ ld_cfg << c_node
122
+ end
123
+ end
124
+
125
+ # set a specific exception class on this RPC so it can be
126
+ # properlly handled by the calling enviornment
127
+
128
+ Netconf::RPC::set_exception( rpc, Netconf::EditError )
129
+ @trans.rpc_exec( rpc )
130
+ end # load_configuration
131
+
132
+ def command( cmd_str, attrs = nil )
133
+ rpc = Nokogiri::XML("<rpc><command>#{cmd_str}</command></rpc>").root
134
+ Netconf::RPC.add_attributes( rpc.at('command'), attrs ) if attrs
135
+ @trans.rpc_exec( rpc )
136
+ end
137
+
138
+ ## contributed by 'dgjnpr'
139
+ def request_pfe_execute( params = nil )
140
+ raise ArgumentError, 'Manditorary argument :target missing' unless params[:target]
141
+ raise ArgumentError, 'Manditorary argument :command missing' unless params[:command]
142
+
143
+ rpc_nx = Nokogiri::XML::Builder.new { |xml|
144
+ xml.rpc {
145
+ xml.send( 'request-pfe-execute' ) {
146
+ xml.send( 'target', params[:target] )
147
+ if params[:command].class.to_s =~ /^Array/
148
+ params[:command].each { |cmd|
149
+ xml.send( 'command', cmd )
150
+ }
151
+ elsif params[:command].class.to_s =~ /^String/
152
+ xml.send( 'command', params[:command] )
153
+ end
154
+ }
155
+ }
156
+ }
157
+ @trans.rpc_exec( rpc_nx )
158
+ end
159
+
160
+ end # module: JUNOS
161
+ end # module: RPC
162
+ end # module: Netconf
@@ -0,0 +1,15 @@
1
+ require 'net/netconf'
2
+ require 'net/netconf/serial'
3
+ require 'net/netconf/jnpr'
4
+
5
+ module Netconf
6
+ module Junos
7
+ module TransSerial
8
+ def trans_start_netconf( last_console )
9
+ last_console.match(/[^%]\s+$/)
10
+ netconf_cmd = ($1 == '%') ? Netconf::Junos::NETCONF_SHELL : Netconf::Junos::NETCONF_CLI
11
+ puts netconf_cmd
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'net/netconf'
2
+ require 'net/netconf/jnpr'
3
+
4
+ module Netconf
5
+ module Junos
6
+ module TransSSH
7
+
8
+ ##
9
+ ## this is used to handle the case where NETCONF (port 830) is disabled. We can still access
10
+ ## the NETCONF subsystem from the CLI using a hidden command 'netconf'
11
+ ##
12
+
13
+ def trans_on_connect_refused( start_args )
14
+ start_args[:port] = 22
15
+ @trans[:conn] = Net::SSH.start( @args[:target], @args[:username], start_args )
16
+ do_once = true
17
+ @trans[:conn].exec( NETCONF_CLI ) do |chan, success|
18
+ @trans[:chan] = chan
19
+ do_once = false
20
+ end
21
+ @trans[:conn].loop { do_once }
22
+ @trans[:chan]
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'net/netconf'
2
+ require 'net/netconf/telnet'
3
+ require 'net/netconf/jnpr'
4
+
5
+ module Netconf
6
+ module Junos
7
+ module TransTelnet
8
+
9
+ def trans_login
10
+ l_rsp = @trans.login( @args[:username], @args[:password] )
11
+ # @@@/JLS: need to rescue the timeout ... ???
12
+ l_rsp.match("([>%])\s+$")
13
+ @exec_netconf = ($1 == '%') ? Netconf::Junos::NETCONF_SHELL : Netconf::Junos::NETCONF_CLI
14
+ end
15
+
16
+ def trans_start_netconf
17
+ @trans.puts @exec_netconf
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,71 @@
1
+ require 'net/netconf/rpc_std'
2
+
3
+ module Netconf
4
+ module RPC
5
+
6
+ def RPC.add_attributes( ele_nx, attr_h )
7
+ attr_h.each{ |k,v| ele_nx[k] = v }
8
+ end
9
+
10
+ def RPC.set_exception( rpc_nx, exception )
11
+ rpc_nx.instance_variable_set(:@netconf_exception, exception )
12
+ end
13
+
14
+ def RPC.get_exception( rpc_nx )
15
+ rpc_nx.instance_variable_get(:@netconf_exception) || Netconf::RpcError
16
+ end
17
+
18
+ module Builder
19
+ # autogenerate an <rpc>, converting underscores (_)
20
+ # to hyphens (-) along the way ...
21
+
22
+ def Builder.method_missing( method, params = nil, attrs = nil )
23
+
24
+ rpc_name = method.to_s.tr('_','-').to_sym
25
+
26
+ if params
27
+ # build the XML starting at <rpc>, envelope the <method> toplevel element,
28
+ # and then create name/value elements for each of the additional params. An element
29
+ # without a value should simply be set to true
30
+ rpc_nx = Nokogiri::XML::Builder.new { |xml|
31
+ xml.rpc { xml.send( rpc_name ) {
32
+ params.each{ |k,v|
33
+ sym = k.to_s.tr('_','-').to_sym
34
+ xml.send(sym, (v==true) ? nil : v )
35
+ }
36
+ }}
37
+ }.doc.root
38
+ else
39
+ # -- no params
40
+ rpc_nx = Nokogiri::XML("<rpc><#{rpc_name}/></rpc>").root
41
+ end
42
+
43
+ # if a block is given it is used to set the attributes of the toplevel element
44
+ RPC.add_attributes( rpc_nx.at( rpc_name ), attrs ) if attrs
45
+
46
+ # return the rpc command
47
+ rpc_nx
48
+ end # def: method-missing?
49
+
50
+ end # module: Builder
51
+
52
+ class Executor
53
+ include Netconf::RPC::Standard
54
+
55
+ def initialize( trans, os_type )
56
+ @trans = trans
57
+ begin
58
+ extend Netconf::RPC::const_get( os_type )
59
+ rescue NameError
60
+ # no extensions available ...
61
+ end
62
+ end
63
+
64
+ def method_missing( method, params = nil, attrs = nil )
65
+ @trans.rpc_exec( Netconf::RPC::Builder.send( method, params, attrs ))
66
+ end
67
+ end # class: Executor
68
+
69
+ end # module: RPC
70
+ end # module: Netconf
71
+
@@ -0,0 +1,133 @@
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 xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
9
+ <capabilities>
10
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
11
+ </capabilities>
12
+ </hello>
13
+ EOM
14
+
15
+ module Standard
16
+
17
+ def lock( target )
18
+ rpc = Nokogiri::XML( "<rpc><lock><target><#{target}/></target></lock></rpc>" ).root
19
+ Netconf::RPC.set_exception( rpc, Netconf::LockError )
20
+ @trans.rpc_exec( rpc )
21
+ end
22
+
23
+ def unlock( target )
24
+ rpc = Nokogiri::XML( "<rpc><unlock><target><#{target}/></target></unlock></rpc>" ).root
25
+ @trans.rpc_exec( rpc )
26
+ end
27
+
28
+ def validate( source )
29
+ rpc = Nokogiri::XML( "<rpc><validate><source><#{source}/></source></validate></rpc>" ).root
30
+ Netconf::RPC.set_exception( rpc, Netconf::ValidateError )
31
+ @trans.rpc_exec( rpc )
32
+ end
33
+
34
+ def commit
35
+ rpc = Nokogiri::XML( "<rpc><commit/></rpc>" ).root
36
+ Netconf::RPC.set_exception( rpc, Netconf::CommitError )
37
+ @trans.rpc_exec( rpc )
38
+ end
39
+
40
+ def delete_config( target )
41
+ rpc = Nokogiri::XML( "<rpc><delete-config><target><#{target}/></target></delete-config></rpc>" ).root
42
+ @trans.rpc_exec( rpc )
43
+ end
44
+
45
+ def get_config( *args ) # :yeield: filter_builder
46
+
47
+ source = 'running' # default source is 'running'
48
+ filter = nil # no filter by default
49
+
50
+ while arg = args.shift
51
+ case arg.class.to_s
52
+ when /^Nokogiri/
53
+ filter = case arg
54
+ when Nokogiri::XML::Builder then arg.doc.root
55
+ when Nokogiri::XML::Document then arg.root
56
+ else arg
57
+ end
58
+ when 'Hash' then attrs = arg
59
+ when 'String' then source = arg
60
+ end
61
+ end
62
+
63
+ rpc = Nokogiri::XML("<rpc><get-config><source><#{source}/></source></get-config></rpc>").root
64
+
65
+ if block_given?
66
+ Nokogiri::XML::Builder.with( rpc.at( 'get-config' )){ |xml|
67
+ xml.filter( :type => 'subtree' ) {
68
+ yield( xml )
69
+ }
70
+ }
71
+ end
72
+
73
+ if filter
74
+ f_node = Nokogiri::XML::Node.new( 'filter', rpc )
75
+ f_node['type'] = 'subtree'
76
+ f_node << filter.dup # copy filter, don't mess with the original since it may be re-used
77
+ rpc.at('get-config') << f_node
78
+ end
79
+
80
+ @trans.rpc_exec( rpc )
81
+ end
82
+
83
+ def edit_config( *args ) # :yeield: config_builder
84
+
85
+ toplevel = 'config' # default toplevel config element
86
+ target = 'candidate' # default source is 'candidate' @@@/JLS hack; need to fix this
87
+ config = nil
88
+ options = {}
89
+
90
+ while arg = args.shift
91
+ case arg.class.to_s
92
+ when /^Nokogiri/
93
+ config = case arg
94
+ when Nokogiri::XML::Builder then arg.doc.root
95
+ when Nokogiri::XML::Document then arg.root
96
+ else arg
97
+ end
98
+ when 'Hash' then options = arg
99
+ when 'String' then target = arg
100
+ end
101
+ end
102
+
103
+ toplevel = options[:toplevel] if options[:toplevel]
104
+
105
+ rpc_str = <<-EO_RPC
106
+ <rpc>
107
+ <edit-config>
108
+ <target><#{target}/></target>
109
+ <#{toplevel}/>
110
+ </edit-config>
111
+ </rpc>
112
+ EO_RPC
113
+
114
+ rpc = Nokogiri::XML( rpc_str ).root
115
+
116
+ if block_given?
117
+ Nokogiri::XML::Builder.with(rpc.at( toplevel )){ |xml|
118
+ yield( xml )
119
+ }
120
+ elsif config
121
+ rpc.at( toplevel ) << config.dup
122
+ else
123
+ raise ArgumentError, "You must specify edit-config data!"
124
+ end
125
+
126
+ Netconf::RPC.set_exception( rpc, Netconf::EditError )
127
+ @trans.rpc_exec( rpc )
128
+ end
129
+
130
+ end
131
+
132
+ end # module: RPC
133
+ end # module: Netconf
@@ -0,0 +1,135 @@
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::const_get( os_type )::TransSerial
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_receive_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_send_hello
79
+ nil
80
+ end
81
+
82
+ def trans_close
83
+ @trans.write Netconf::RPC::MSG_CLOSE_SESSION
84
+ @trans.close
85
+ end
86
+
87
+ def trans_send( cmd_str )
88
+ @trans.write( cmd_str )
89
+ end
90
+
91
+ def trans_receive
92
+ got = waitfor( Netconf::RPC::MSG_END_RE )
93
+ msg_end = got.rindex( Netconf::RPC::MSG_END )
94
+ got[msg_end .. -1] = ''
95
+ got
96
+ end
97
+
98
+ def puts( str = nil )
99
+ @trans.puts str
100
+ end
101
+
102
+ def waitfor( this_re = nil )
103
+ on_re = this_re || @args[:prompt]
104
+
105
+ time_out = @trans_timeout
106
+ wait_io = @trans_waitio
107
+
108
+ time_out = nil if time_out == false
109
+ done = false
110
+ rx_buf = ''
111
+
112
+ until( rx_buf.match( on_re ) and not IO::select( [@trans], nil, nil, wait_io ) )
113
+
114
+ unless IO::select( [@trans], nil, nil, time_out )
115
+ raise TimeoutError, "Netconf IO timed out while waiting for more data"
116
+ end
117
+
118
+ begin
119
+
120
+ rx_some = @trans.readpartial( DEFAULT_RDBLKSZ )
121
+
122
+ rx_buf += rx_some
123
+ break if rx_buf.match( on_re )
124
+
125
+ rescue EOFError # End of file reached
126
+ rx_buf = nil if rx_buf == ''
127
+ break # out of outer 'until' loop
128
+ end
129
+
130
+ end
131
+ rx_buf
132
+ end
133
+
134
+ end # class: Serial
135
+ end # module: Netconf