expect4r 0.0.1.dev
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.
- data/COPYING +674 -0
- data/LICENSE +53 -0
- data/README.rdoc +126 -0
- data/lib/expect/io.rb +484 -0
- data/lib/expect4r.rb +49 -0
- data/lib/misc/passwd.rb +19 -0
- data/lib/misc/shell.rb +19 -0
- data/lib/router/cisco/common/common.rb +25 -0
- data/lib/router/cisco/common/ping.rb +39 -0
- data/lib/router/cisco/common/show.rb +23 -0
- data/lib/router/cisco/ios/ios.rb +65 -0
- data/lib/router/cisco/ios/modes.rb +97 -0
- data/lib/router/cisco/ios/termserver.rb +20 -0
- data/lib/router/cisco/iox/iox.rb +54 -0
- data/lib/router/cisco/iox/modes.rb +101 -0
- data/lib/router/common.rb +42 -0
- data/lib/router/error.rb +76 -0
- data/lib/router/juniper/junos/junos.rb +53 -0
- data/lib/router/juniper/junos/modes.rb +109 -0
- data/lib/router/juniper/junos/show.rb +23 -0
- data/lib/router/modes.rb +37 -0
- data/test/misc/passwd_test.rb +9 -0
- data/test/router/cisco/iox/iox_test.rb +67 -0
- metadata +105 -0
data/lib/expect4r.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'expect/io'
|
2
|
+
require 'misc/passwd'
|
3
|
+
|
4
|
+
module Expect4r
|
5
|
+
|
6
|
+
autoload :Iox, 'router/cisco/iox/iox'
|
7
|
+
autoload :Ios, 'router/cisco/ios/ios'
|
8
|
+
autoload :J, 'router/juniper/junos/junos'
|
9
|
+
autoload :Shell, 'misc/shell'
|
10
|
+
|
11
|
+
module Router
|
12
|
+
autoload :Common, 'router/common'
|
13
|
+
module Common
|
14
|
+
autoload :Modes, 'router/modes'
|
15
|
+
end
|
16
|
+
module Error
|
17
|
+
autoload :RouterError, 'router/error'
|
18
|
+
autoload :SyntaxError, 'router/error'
|
19
|
+
autoload :SemanticError, 'router/error'
|
20
|
+
autoload :PingError, 'router/error'
|
21
|
+
end
|
22
|
+
module CiscoCommon
|
23
|
+
autoload :Show, 'router/cisco/common/show'
|
24
|
+
autoload :Ping, 'router/cisco/common/ping'
|
25
|
+
end
|
26
|
+
module Ios
|
27
|
+
autoload :TermServer, 'router/cisco/ios/termserver'
|
28
|
+
autoload :Modes, 'router/cisco/ios/modes'
|
29
|
+
end
|
30
|
+
module Iox
|
31
|
+
autoload :Modes, 'router/cisco/iox/modes'
|
32
|
+
end
|
33
|
+
module Junos
|
34
|
+
autoload :Modes, 'router/juniper/junos/modes'
|
35
|
+
autoload :Show, 'router/juniper/junos/show'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
if __FILE__ == $0
|
42
|
+
module Expect4r
|
43
|
+
p Expect4r::Iox.new_ssh 'hostname', 'username'
|
44
|
+
p Expect4r::Iox.new_telnet 'hostname', 'username'
|
45
|
+
p Expect4r::Ios.new_ssh 'hostname', 'username'
|
46
|
+
p Expect4r::J.new_ssh 'hostname', 'username'
|
47
|
+
p Expect4r::Shell.new
|
48
|
+
end
|
49
|
+
end
|
data/lib/misc/passwd.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'openssl'
|
3
|
+
require 'digest/sha1'
|
4
|
+
module Expect4r
|
5
|
+
def self.cipher(this, pwd='expect4r')
|
6
|
+
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
7
|
+
c.encrypt
|
8
|
+
c.key = key = Digest::SHA1.hexdigest(pwd)
|
9
|
+
e = c.update(this)
|
10
|
+
e << c.final
|
11
|
+
end
|
12
|
+
def self.decipher(cipher,pwd='expect4r')
|
13
|
+
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
14
|
+
c.decrypt
|
15
|
+
c.key = key = Digest::SHA1.hexdigest(pwd)
|
16
|
+
d = c.update(cipher)
|
17
|
+
d << c.final
|
18
|
+
end
|
19
|
+
end
|
data/lib/misc/shell.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'expect/io'
|
2
|
+
|
3
|
+
module Expect4r
|
4
|
+
|
5
|
+
class Shell
|
6
|
+
include Expect4r
|
7
|
+
def initialize()
|
8
|
+
ENV['PROMPT_COMMAND']="date +%k:%m:%S"
|
9
|
+
ENV['PS1']="shell>"
|
10
|
+
@ps1 = /shell>$/
|
11
|
+
@shell = ENV['SHELL'] || 'bash'
|
12
|
+
login
|
13
|
+
end
|
14
|
+
def login
|
15
|
+
spawn @shell
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Expect4r::Router
|
3
|
+
module CiscoCommon
|
4
|
+
|
5
|
+
def config_lvl?
|
6
|
+
return -1 unless config?
|
7
|
+
@lp =~ /\(config(|.+)\)/
|
8
|
+
lvl = Regexp.last_match(1).split('-').size
|
9
|
+
lvl == 0 ? 1 : lvl
|
10
|
+
end
|
11
|
+
|
12
|
+
def top
|
13
|
+
return unless config?
|
14
|
+
1.upto(config_lvl? - 1) { putline 'exit'}
|
15
|
+
end
|
16
|
+
|
17
|
+
def top?
|
18
|
+
return false unless config?
|
19
|
+
@lp =~ /\(config(|.+)\)/
|
20
|
+
Regexp.last_match(1).size == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'router/error'
|
2
|
+
|
3
|
+
module Expect4r::Router::CiscoCommon
|
4
|
+
module Ping
|
5
|
+
include ::Expect4r::Router::Error
|
6
|
+
def ping(arg={})
|
7
|
+
if arg.is_a?(Hash)
|
8
|
+
arg = {:pct_success=>100}.merge(arg)
|
9
|
+
pct_success = arg[:pct_success]
|
10
|
+
else
|
11
|
+
pct_success = 100
|
12
|
+
end
|
13
|
+
case arg
|
14
|
+
when String
|
15
|
+
dest = arg
|
16
|
+
when Hash
|
17
|
+
dest = arg[:dest]
|
18
|
+
else
|
19
|
+
raise ArgumentError, "Invalid argument: #{arg.inspect}"
|
20
|
+
end
|
21
|
+
output = exec "ping #{dest}", arg
|
22
|
+
r = output[0].find { |x| x =~/Success.*[^\d](\d+) percent \((\d+)\/(\d+)\)/}
|
23
|
+
if r &&
|
24
|
+
Regexp.last_match(1) &&
|
25
|
+
Regexp.last_match(2) &&
|
26
|
+
Regexp.last_match(3)
|
27
|
+
|
28
|
+
if $1.to_i < pct_success
|
29
|
+
raise PingError('router name here', dest, pct_success, $1.to_i, $2.to_i,$3.to_i, output)
|
30
|
+
else
|
31
|
+
[$1.to_i,[$2.to_i,$3.to_i],output]
|
32
|
+
end
|
33
|
+
|
34
|
+
else
|
35
|
+
raise PingError.new('router name here', dest, pct_success, $1.to_i, $2.to_i,$3.to_i, output)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Expect4r::Router::CiscoCommon
|
2
|
+
module Show
|
3
|
+
|
4
|
+
def show(s, arg={})
|
5
|
+
output = []
|
6
|
+
s.each_line { |l|
|
7
|
+
output << exec("show #{l}", arg) if l.strip.size>0
|
8
|
+
}
|
9
|
+
output
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(name, *args, &block)
|
13
|
+
if name.to_s =~ /^show_/
|
14
|
+
cmd = name.to_s.split('_').join(' ') + args.join(' ')
|
15
|
+
cmd.gsub!(/running config/, 'running-config')
|
16
|
+
output = __send__ :exec, cmd, *args
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Expect4r::Ios < ::Expect4r::BaseObject
|
2
|
+
|
3
|
+
include Expect4r
|
4
|
+
include Expect4r::Router::Common
|
5
|
+
include Expect4r::Router::Common::Modes
|
6
|
+
include Expect4r::Router::Ios::Modes
|
7
|
+
include Expect4r::Router::CiscoCommon
|
8
|
+
include Expect4r::Router::CiscoCommon::Show
|
9
|
+
include Expect4r::Router::CiscoCommon::Ping
|
10
|
+
include Expect4r::Router::Ios::TermServer
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
@ps1 = /(.*)(>|#|\$)\s*$/
|
15
|
+
@more = / --More-- /
|
16
|
+
end
|
17
|
+
|
18
|
+
def enable
|
19
|
+
@enable_password ||= @pwd
|
20
|
+
enable_pwd = [/^Password: $/, enable_password]
|
21
|
+
@matches << enable_pwd
|
22
|
+
exp_send 'enable'
|
23
|
+
rescue
|
24
|
+
raise
|
25
|
+
ensure
|
26
|
+
@matches.delete enable_pwd
|
27
|
+
end
|
28
|
+
|
29
|
+
def enable_password
|
30
|
+
@enable_password ||= @pwd # FIXME
|
31
|
+
Expect4r.decipher(@pwd) # password is ciphered ...
|
32
|
+
end
|
33
|
+
|
34
|
+
def login
|
35
|
+
super(spawnee)
|
36
|
+
enable
|
37
|
+
exec "term len 0\nterm width 0"
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def putline(line,*args)
|
42
|
+
output, rc = super
|
43
|
+
return output unless error?(output)
|
44
|
+
raise SyntaxError.new(self.class.to_s,line)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
if "a"[0]==97
|
50
|
+
def string_start_with_pct_char?(s)
|
51
|
+
return unless s
|
52
|
+
s[0].chr == '%' if s[0]
|
53
|
+
end
|
54
|
+
else
|
55
|
+
def string_start_with_pct_char?(s)
|
56
|
+
return unless s
|
57
|
+
s[0] == '%'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def error?(output)
|
62
|
+
string_start_with_pct_char?(output[-2]) || string_start_with_pct_char?(output[-3])
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'router/modes'
|
2
|
+
|
3
|
+
module Expect4r::Router::Ios
|
4
|
+
module Modes
|
5
|
+
|
6
|
+
def in?(mode=:none)
|
7
|
+
login unless connected?
|
8
|
+
case mode
|
9
|
+
when :exec ; exec?
|
10
|
+
when :user ; user?
|
11
|
+
when :config ; config?
|
12
|
+
else
|
13
|
+
_mode_?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def config(config=nil, arg={})
|
19
|
+
login unless connected?
|
20
|
+
if config
|
21
|
+
mode = in?
|
22
|
+
change_mode_to :config
|
23
|
+
output = exp_send(config, arg)
|
24
|
+
change_mode_to mode
|
25
|
+
output
|
26
|
+
else
|
27
|
+
change_mode_to :config
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def exec(cmd=nil, arg={})
|
32
|
+
login unless connected?
|
33
|
+
if cmd.nil?
|
34
|
+
change_mode_to :exec
|
35
|
+
else
|
36
|
+
if exec?
|
37
|
+
output = exp_send(cmd, arg)
|
38
|
+
elsif config?
|
39
|
+
output = exp_send("do #{cmd}", arg)
|
40
|
+
else
|
41
|
+
mode = in?
|
42
|
+
change_mode_to :exec
|
43
|
+
output = exp_send(cmd, arg)
|
44
|
+
change_mode_to mode
|
45
|
+
output
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def _mode_?
|
51
|
+
if user?
|
52
|
+
:user
|
53
|
+
elsif config?
|
54
|
+
:config
|
55
|
+
elsif exec?
|
56
|
+
:exec
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def config?
|
61
|
+
@lp =~ /\(config(|.+)\)/
|
62
|
+
end
|
63
|
+
|
64
|
+
def exec?
|
65
|
+
! user? and ! config?
|
66
|
+
end
|
67
|
+
|
68
|
+
def user?
|
69
|
+
@lp =~ /.+>\s*$/
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_config
|
73
|
+
return :config if config?
|
74
|
+
to_exec
|
75
|
+
putline 'configure terminal' if exec?
|
76
|
+
raise RuntimeError, "unable to got to config mode" unless config?
|
77
|
+
:config
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_exec
|
81
|
+
return :exec if exec?
|
82
|
+
if config?
|
83
|
+
1.upto(config_lvl?) { putline 'exit'}
|
84
|
+
end
|
85
|
+
raise RuntimeError, "unable to got to exec mode" unless exec?
|
86
|
+
:exec
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_user
|
90
|
+
return if user?
|
91
|
+
putline "exit"
|
92
|
+
raise RuntimeError, "unable to got to user mode" unless exec?
|
93
|
+
:user
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Expect4r::Router::Ios
|
2
|
+
module TermServer
|
3
|
+
|
4
|
+
def powercycle(lineno)
|
5
|
+
config "line #{lineno}\nmodem dtr\nmodem dtr-active"
|
6
|
+
sleep 2
|
7
|
+
config "line #{lineno}\nno modem dtr\nno modem dtr-active"
|
8
|
+
end
|
9
|
+
def clear_line(lineno)
|
10
|
+
confirm = [/\[confirm\]/, ""]
|
11
|
+
@matches << confirm
|
12
|
+
putline "clear line #{lineno}"
|
13
|
+
rescue Expect4r::Router::Error::SyntaxError => e
|
14
|
+
puts e.err_msg
|
15
|
+
ensure
|
16
|
+
@matches.delete confirm
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Expect4r::Iox < ::Expect4r::BaseObject
|
2
|
+
|
3
|
+
include Expect4r
|
4
|
+
include Expect4r::Router
|
5
|
+
include Expect4r::Router::Common
|
6
|
+
include Expect4r::Router::Common::Modes
|
7
|
+
include Expect4r::Router::CiscoCommon
|
8
|
+
include Expect4r::Router::Iox::Modes
|
9
|
+
include Expect4r::Router::CiscoCommon::Show
|
10
|
+
include Expect4r::Router::CiscoCommon::Ping
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
@ps1 = /(.*)(>|#|\$)\s*$/
|
15
|
+
@more = / --More-- /
|
16
|
+
end
|
17
|
+
|
18
|
+
def login
|
19
|
+
super(spawnee)
|
20
|
+
config 'no logging console' if port>0
|
21
|
+
exec "terminal len 0\nterminal width 0"
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def commit(arg={})
|
26
|
+
return unless config?
|
27
|
+
output = putline "commit", arg
|
28
|
+
if /\% Failed to commit/.match(output.join)
|
29
|
+
err = show_configuration_failed
|
30
|
+
abort_config
|
31
|
+
raise Iox::SemanticError.new(show_configuration_failed)
|
32
|
+
end
|
33
|
+
output
|
34
|
+
end
|
35
|
+
|
36
|
+
def putline(line,*args)
|
37
|
+
output, rc = super
|
38
|
+
raise SyntaxError.new(line) if output.join =~ /\% Invalid input detected at/
|
39
|
+
output
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def abort_config
|
45
|
+
return unless config?
|
46
|
+
putline 'abort' # make sure buffer config is cleaned up.
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def show_configuration_failed
|
51
|
+
putline 'show configuration failed'
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'router/modes'
|
2
|
+
|
3
|
+
module Expect4r::Router::Iox
|
4
|
+
module Modes
|
5
|
+
|
6
|
+
def config(config=nil, arg={})
|
7
|
+
login unless connected?
|
8
|
+
if config
|
9
|
+
mode = in?
|
10
|
+
change_mode_to :config
|
11
|
+
output = exp_send(config, arg)
|
12
|
+
output << commit
|
13
|
+
change_mode_to mode
|
14
|
+
output
|
15
|
+
else
|
16
|
+
change_mode_to :config
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def exec(cmd=nil, arg={})
|
21
|
+
login unless connected?
|
22
|
+
if cmd.nil?
|
23
|
+
change_mode_to :exec
|
24
|
+
else
|
25
|
+
if exec?
|
26
|
+
output = exp_send(cmd, arg)
|
27
|
+
elsif config?
|
28
|
+
output = exp_send("do #{cmd}", arg)
|
29
|
+
else
|
30
|
+
mode = in?
|
31
|
+
change_mode_to :exec
|
32
|
+
output = exp_send(cmd, arg)
|
33
|
+
change_mode_to mode
|
34
|
+
output
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def shell(cmd=nil, arg={})
|
40
|
+
connected = connected?
|
41
|
+
login unless connected?
|
42
|
+
if cmd.nil?
|
43
|
+
to_shell
|
44
|
+
else
|
45
|
+
if shell?
|
46
|
+
output = exp_send(cmd, arg)
|
47
|
+
elsif config?
|
48
|
+
output = exp_send("do run #{cmd}", arg)
|
49
|
+
elsif exec?
|
50
|
+
output = exp_send("run #{cmd}", arg)
|
51
|
+
else
|
52
|
+
raise RuntimeError # TODO
|
53
|
+
end
|
54
|
+
output
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def config?
|
59
|
+
@lp =~ /\(config(|.+)\)/
|
60
|
+
end
|
61
|
+
|
62
|
+
def submode?
|
63
|
+
return '' unless config?
|
64
|
+
@lp =~ /\(config(|.+)\)/
|
65
|
+
Regexp.last_match(1).split('-')[1..-1].join('-')
|
66
|
+
end
|
67
|
+
|
68
|
+
def exec?
|
69
|
+
! shell? and ! config?
|
70
|
+
end
|
71
|
+
|
72
|
+
def shell?
|
73
|
+
@lp == '# '
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_config
|
77
|
+
return :config if config?
|
78
|
+
to_exec
|
79
|
+
putline 'configure'
|
80
|
+
raise RuntimeError, "unable to got to config mode" unless config?
|
81
|
+
:config
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_shell
|
85
|
+
return :shell if shell?
|
86
|
+
to_exec
|
87
|
+
putline 'run'
|
88
|
+
raise RuntimeError, "unable to got to shell mode" unless shell?
|
89
|
+
:shell
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_exec
|
93
|
+
return :exec if exec?
|
94
|
+
putline "exit" if shell?
|
95
|
+
putline "abort" if config?
|
96
|
+
raise RuntimeError, "unable to got to exec mode" unless exec?
|
97
|
+
:exec
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|