net-netconf 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|