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.
- 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,49 @@
|
|
1
|
+
require 'net/netconf'
|
2
|
+
|
3
|
+
puts "NETCONF v.#{Netconf::VERSION}"
|
4
|
+
|
5
|
+
login = { :target => 'vsrx', :username => "jeremy", :password => "jeremy1" }
|
6
|
+
|
7
|
+
puts "Connecting to device: #{login[:target]}"
|
8
|
+
|
9
|
+
Netconf::SSH.new( login ){ |dev|
|
10
|
+
puts "Connected."
|
11
|
+
|
12
|
+
# ----------------------------------------------------------------------
|
13
|
+
# retrieve the full config. Default source is 'running'
|
14
|
+
# Alternatively you can pass the source name as a string parameter
|
15
|
+
# to #get_config
|
16
|
+
|
17
|
+
puts "Retrieving full config, please wait ... "
|
18
|
+
cfgall = dev.rpc.get_config
|
19
|
+
puts "Showing 'system' hierarchy ..."
|
20
|
+
puts cfgall.xpath('configuration/system') # JUNOS toplevel config element is <configuration>
|
21
|
+
|
22
|
+
# ----------------------------------------------------------------------
|
23
|
+
# specifying a filter as a block to get_config
|
24
|
+
|
25
|
+
cfgsvc1 = dev.rpc.get_config{ |x|
|
26
|
+
x.configuration { x.system { x.services }}
|
27
|
+
}
|
28
|
+
|
29
|
+
puts "Retrieved services as BLOCK:"
|
30
|
+
cfgsvc1.xpath('//services/*').each{|s| puts s.name }
|
31
|
+
|
32
|
+
# ----------------------------------------------------------------------
|
33
|
+
# specifying a filter as a parameter to get_config
|
34
|
+
|
35
|
+
filter = Nokogiri::XML::Builder.new{ |x|
|
36
|
+
x.configuration { x.system { x.services }}
|
37
|
+
}
|
38
|
+
|
39
|
+
cfgsvc2 = dev.rpc.get_config( filter )
|
40
|
+
puts "Retrieved services as PARAM:"
|
41
|
+
cfgsvc2.xpath('//services/*').each{|s| puts s.name }
|
42
|
+
|
43
|
+
cfgsvc3 = dev.rpc.get_config( filter )
|
44
|
+
puts "Retrieved services as PARAM, re-used filter"
|
45
|
+
cfgsvc3.xpath('//services/*').each{|s| puts s.name }
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'net/netconf/jnpr/serial'
|
2
|
+
|
3
|
+
puts "NETCONF v.#{Netconf::VERSION}"
|
4
|
+
|
5
|
+
login = {
|
6
|
+
:port => '/dev/ttyS4',
|
7
|
+
:username => "root", :password => "juniper1"
|
8
|
+
}
|
9
|
+
|
10
|
+
# we want to mount the USB drive, so we need to explicity
|
11
|
+
# do something special when opening the serial console ...
|
12
|
+
# therefore, we can *NOT* pass a block directly to new()
|
13
|
+
|
14
|
+
dev = Netconf::Serial.new( login )
|
15
|
+
dev.open { |con|
|
16
|
+
# login has occurred successfully
|
17
|
+
|
18
|
+
con.puts 'mount_msdosfs /dev/da1s1 /mnt'
|
19
|
+
|
20
|
+
# netconf will be started once block completes
|
21
|
+
}
|
22
|
+
|
23
|
+
inv = dev.rpc.get_chassis_inventory
|
24
|
+
|
25
|
+
dev.close
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'net/netconf/jnpr/serial'
|
2
|
+
|
3
|
+
puts "NETCONF v.#{Netconf::VERSION}"
|
4
|
+
|
5
|
+
serial_port = '/dev/ttyS4'
|
6
|
+
|
7
|
+
login = { :port => serial_port,
|
8
|
+
:username => "jeremy", :password => "jeremy1" }
|
9
|
+
|
10
|
+
puts "Connecting to SERIAL: #{serial_port} ... please wait."
|
11
|
+
|
12
|
+
Netconf::Serial.new( login ){ |dev|
|
13
|
+
|
14
|
+
puts "Connected."
|
15
|
+
puts "Nabbing Inventory ..."
|
16
|
+
|
17
|
+
inv = dev.rpc.get_chassis_inventory
|
18
|
+
|
19
|
+
puts "Chassis: " + inv.xpath('chassis/description').text
|
20
|
+
puts "Chassis Serial-Number: " + inv.xpath('chassis/serial-number').text
|
21
|
+
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'net/netconf/jnpr/telnet'
|
2
|
+
|
3
|
+
puts "NETCONF v#{Netconf::VERSION}"
|
4
|
+
|
5
|
+
login = { :target => 'vsrx', :username => "jeremy", :password => "jeremy1" }
|
6
|
+
|
7
|
+
Netconf::Telnet.new( login ){ |dev|
|
8
|
+
inv = dev.rpc.get_chassis_inventory
|
9
|
+
puts "Chassis: " + inv.xpath('chassis/description').text
|
10
|
+
puts "Chassis Serial-Number: " + inv.xpath('chassis/serial-number').text
|
11
|
+
|
12
|
+
}
|
13
|
+
|
14
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'net/netconf'
|
2
|
+
|
3
|
+
puts "NETCONF v#{Netconf::VERSION}"
|
4
|
+
|
5
|
+
login = { :target => 'vsrx', :username => "jeremy", :password => "jeremy1" }
|
6
|
+
|
7
|
+
Netconf::SSH.new( login ){ |dev|
|
8
|
+
|
9
|
+
inv = dev.rpc.get_chassis_inventory
|
10
|
+
|
11
|
+
puts "Chassis: " + inv.xpath('chassis/description').text
|
12
|
+
puts "Chassis Serial-Number: " + inv.xpath('chassis/serial-number').text
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'net/netconf'
|
2
|
+
require 'net/scp'
|
3
|
+
|
4
|
+
login = { :target => 'vsrx', :username => "jeremy", :password => "jeremy1" }
|
5
|
+
|
6
|
+
file_name = __FILE__
|
7
|
+
|
8
|
+
Netconf::SSH.new( login ){ |dev|
|
9
|
+
|
10
|
+
inv = dev.rpc.get_chassis_inventory
|
11
|
+
|
12
|
+
puts "Chassis: " + inv.xpath('chassis/description').text
|
13
|
+
puts "Chassis Serial-Number: " + inv.xpath('chassis/serial-number').text
|
14
|
+
|
15
|
+
puts "Copying file #{file_name} to home directory ..."
|
16
|
+
dev.scp.upload! file_name, file_name
|
17
|
+
|
18
|
+
puts "Copying latest config file from target to local machine ..."
|
19
|
+
|
20
|
+
dev.scp.download! "/config/juniper.conf.gz", "/var/tmp/config.gz"
|
21
|
+
}
|
22
|
+
|
data/lib/net/netconf.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
require 'net/netconf/version'
|
4
|
+
require 'net/netconf/rpc'
|
5
|
+
require 'net/netconf/exception'
|
6
|
+
require 'net/netconf/transport'
|
7
|
+
require 'net/netconf/ssh'
|
8
|
+
|
9
|
+
module Netconf
|
10
|
+
NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0"
|
11
|
+
DEFAULT_OS_TYPE = :Junos
|
12
|
+
DEFAULT_TIMEOUT = 10
|
13
|
+
DEFAULT_WAITIO = 0
|
14
|
+
|
15
|
+
@raise_on_warning = false # rpc-error with <error-severity> = 'warning' will not raise RpcError excption
|
16
|
+
|
17
|
+
def self.raise_on_warning=( bool )
|
18
|
+
@raise_on_warning = bool
|
19
|
+
end
|
20
|
+
def self.raise_on_warning
|
21
|
+
@raise_on_warning
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Netconf
|
2
|
+
|
3
|
+
class InitError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class StateError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class OpenError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class RpcError < StandardError
|
13
|
+
attr_reader :trans
|
14
|
+
attr_reader :cmd, :rsp
|
15
|
+
|
16
|
+
def initialize( trans, cmd, rsp )
|
17
|
+
@trans = trans
|
18
|
+
@cmd = cmd; @rsp = rsp;
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"RPC command error: #{cmd.first_element_child.name}\n#{rsp.to_xml}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class EditError < Netconf::RpcError
|
27
|
+
end
|
28
|
+
|
29
|
+
class LockError < Netconf::RpcError
|
30
|
+
end
|
31
|
+
|
32
|
+
class CommitError < Netconf::RpcError
|
33
|
+
end
|
34
|
+
|
35
|
+
class ValidateError < Netconf::RpcError
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Netconf
|
2
|
+
|
3
|
+
class IOProc < Netconf::Transport
|
4
|
+
|
5
|
+
DEFAULT_RDBLKSZ = (1024*1024)
|
6
|
+
|
7
|
+
attr_reader :args
|
8
|
+
|
9
|
+
def initialize( args_h = {}, &block )
|
10
|
+
os_type = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE
|
11
|
+
|
12
|
+
@args = args_h.clone
|
13
|
+
|
14
|
+
# an OS specific implementation must exist to support this transport type
|
15
|
+
extend Netconf::const_get( os_type )::IOProc
|
16
|
+
|
17
|
+
@trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT
|
18
|
+
@trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO
|
19
|
+
|
20
|
+
super( &block )
|
21
|
+
end
|
22
|
+
|
23
|
+
# the OS specific transport must implement this method
|
24
|
+
def trans_open # :yield: self
|
25
|
+
raise "Unsupported IOProc"
|
26
|
+
end
|
27
|
+
|
28
|
+
def trans_receive_hello
|
29
|
+
trans_receive()
|
30
|
+
end
|
31
|
+
|
32
|
+
def trans_send_hello
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def trans_close
|
37
|
+
@trans.write Netconf::RPC::MSG_CLOSE_SESSION
|
38
|
+
@trans.close
|
39
|
+
end
|
40
|
+
|
41
|
+
def trans_send( cmd_str )
|
42
|
+
@trans.write( cmd_str )
|
43
|
+
end
|
44
|
+
|
45
|
+
def trans_receive
|
46
|
+
got = waitfor( Netconf::RPC::MSG_END_RE )
|
47
|
+
msg_end = got.rindex( Netconf::RPC::MSG_END )
|
48
|
+
got[msg_end .. -1] = ''
|
49
|
+
got
|
50
|
+
end
|
51
|
+
|
52
|
+
def puts( str = nil )
|
53
|
+
@trans.puts( str )
|
54
|
+
end
|
55
|
+
|
56
|
+
def waitfor( on_re )
|
57
|
+
|
58
|
+
time_out = @trans_timeout
|
59
|
+
wait_io = @trans_waitio
|
60
|
+
|
61
|
+
time_out = nil if time_out == false
|
62
|
+
done = false
|
63
|
+
rx_buf = ''
|
64
|
+
|
65
|
+
until( rx_buf.match( on_re ) and not IO::select( [@trans], nil, nil, wait_io ) )
|
66
|
+
|
67
|
+
unless IO::select( [@trans], nil, nil, time_out )
|
68
|
+
raise TimeoutError, "Netconf IO timed out while waiting for more data"
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
|
73
|
+
rx_some = @trans.readpartial( DEFAULT_RDBLKSZ )
|
74
|
+
|
75
|
+
rx_buf += rx_some
|
76
|
+
break if rx_buf.match( on_re )
|
77
|
+
|
78
|
+
rescue EOFError # End of file reached
|
79
|
+
rx_buf = nil if rx_buf == ''
|
80
|
+
break # out of outer 'until' loop
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
rx_buf
|
85
|
+
end
|
86
|
+
|
87
|
+
end # class: IOProc
|
88
|
+
end # module: Netconf
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 Juniper Networks, Inc.
|
3
|
+
# All Rights Reserved
|
4
|
+
#
|
5
|
+
# JUNIPER PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
6
|
+
|
7
|
+
module Netconf
|
8
|
+
|
9
|
+
class JunosConfig
|
10
|
+
|
11
|
+
DELETE = { :delete => 'delete' }
|
12
|
+
REPLACE = { :replace => 'replace' }
|
13
|
+
|
14
|
+
attr_reader :doc
|
15
|
+
attr_reader :collection
|
16
|
+
|
17
|
+
def initialize( options )
|
18
|
+
@doc_ele = "configuration"
|
19
|
+
|
20
|
+
if options == :TOP
|
21
|
+
@doc = Nokogiri::XML("<#{@doc_ele}/>")
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
unless options[:TOP].nil?
|
26
|
+
@doc_ele = options[:TOP]
|
27
|
+
@doc = Nokogiri::XML("<#{@doc_ele}/>")
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
unless defined? @collection
|
32
|
+
edit = "#{@doc_ele}/#{options[:edit].strip}"
|
33
|
+
@at_name = edit[edit.rindex('/') + 1, edit.length]
|
34
|
+
@edit_path = edit
|
35
|
+
@collection = Hash.new
|
36
|
+
@to_xml = options[:build]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def <<( obj )
|
41
|
+
if defined? @collection
|
42
|
+
@collection[obj[:name]] = obj
|
43
|
+
elsif defined? @doc
|
44
|
+
obj.build_xml( @doc )
|
45
|
+
else
|
46
|
+
# TBD:error
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_xml( ng_xml, &block )
|
51
|
+
at_ele = ng_xml.at( @edit_path )
|
52
|
+
if at_ele.nil?
|
53
|
+
# no xpath anchor point, so we need to create it
|
54
|
+
at_ele = edit_path( ng_xml, @edit_path )
|
55
|
+
end
|
56
|
+
build_proc = (block_given?) ? block : @to_xml
|
57
|
+
|
58
|
+
@collection.each do |k,v|
|
59
|
+
with( at_ele ) do |e|
|
60
|
+
build_proc.call( e, v )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def edit_path( ng_xml, xpath )
|
66
|
+
# junos configuration always begins with
|
67
|
+
# the 'configuration' element, so don't make
|
68
|
+
# the user enter it all the time
|
69
|
+
|
70
|
+
cfg_xpath = xpath
|
71
|
+
dot = ng_xml.at( cfg_xpath )
|
72
|
+
return dot if dot
|
73
|
+
|
74
|
+
# we need to determine how much of the xpath
|
75
|
+
# we need to create. walk down the xpath
|
76
|
+
# children to determine what exists and
|
77
|
+
# what needs to be added
|
78
|
+
|
79
|
+
xpath_a = cfg_xpath.split('/')
|
80
|
+
need_a = []
|
81
|
+
until xpath_a.empty? or dot
|
82
|
+
need_a.unshift xpath_a.pop
|
83
|
+
check_xpath = xpath_a.join('/')
|
84
|
+
dot = ng_xml.at( check_xpath )
|
85
|
+
end
|
86
|
+
|
87
|
+
# start at the deepest level of what
|
88
|
+
# actually exists and then start adding
|
89
|
+
# the children that were missing
|
90
|
+
|
91
|
+
dot = ng_xml.at(xpath_a.join('/'))
|
92
|
+
need_a.each do |ele|
|
93
|
+
dot = dot.add_child( Nokogiri::XML::Node.new( ele, ng_xml ))
|
94
|
+
end
|
95
|
+
return dot
|
96
|
+
end
|
97
|
+
|
98
|
+
def with( ng_xml, &block )
|
99
|
+
Nokogiri::XML::Builder.with( ng_xml, &block )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
#-- class end
|
103
|
+
end
|
104
|
+
|