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,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