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/LICENSE
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Expect4r is copyrighted free software by Jean-Michel Esnault.
|
2
|
+
|
3
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
4
|
+
(see the COPYING file), or the conditions below:
|
5
|
+
|
6
|
+
1. You may make and give away verbatim copies of the source form of the
|
7
|
+
software without restriction, provided that you duplicate all of the
|
8
|
+
original copyright notices and associated disclaimers.
|
9
|
+
|
10
|
+
2. You may modify your copy of the software in any way, provided that
|
11
|
+
you do at least ONE of the following:
|
12
|
+
|
13
|
+
a) place your modifications in the Public Domain or otherwise
|
14
|
+
make them Freely Available, such as by posting said
|
15
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
16
|
+
the author to include your modifications in the software.
|
17
|
+
|
18
|
+
b) use the modified software only within your corporation or
|
19
|
+
organization.
|
20
|
+
|
21
|
+
c) rename any non-standard executables so the names do not conflict
|
22
|
+
with standard executables, which must also be provided.
|
23
|
+
|
24
|
+
d) make other distribution arrangements with the author.
|
25
|
+
|
26
|
+
3. You may distribute the software in object code or executable
|
27
|
+
form, provided that you do at least ONE of the following:
|
28
|
+
|
29
|
+
a) distribute the executables and library files of the software,
|
30
|
+
together with instructions (in the manual page or equivalent)
|
31
|
+
on where to get the original distribution.
|
32
|
+
|
33
|
+
b) accompany the distribution with the machine-readable source of
|
34
|
+
the software.
|
35
|
+
|
36
|
+
c) give non-standard executables non-standard names, with
|
37
|
+
instructions on where to get the original software distribution.
|
38
|
+
|
39
|
+
d) make other distribution arrangements with the author.
|
40
|
+
|
41
|
+
4. You may modify and include the part of the software into any other
|
42
|
+
software (possibly commercial).
|
43
|
+
|
44
|
+
5. The scripts and library files supplied as input to or produced as
|
45
|
+
output from the software do not automatically fall under the
|
46
|
+
copyright of the software, but belong to whomever generated them,
|
47
|
+
and may be sold commercially, and may be aggregated with this
|
48
|
+
software.
|
49
|
+
|
50
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
51
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
52
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
53
|
+
PURPOSE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
Expect4r is a library that enables a ruby program to 'talk' to routers following a written ruby script.
|
2
|
+
You talk to routers by connecting to them using ssh or telnet and send exec or config command and collect router output than can be parsed using ruby.
|
3
|
+
|
4
|
+
* Connect to routers
|
5
|
+
|
6
|
+
ios = Ios.new_telnet :host=> "hostname", :user=> "username", :pwd => "password"
|
7
|
+
ios.login
|
8
|
+
|
9
|
+
|
10
|
+
iox = Iox.new_telnet 'host', 'username', 'password'
|
11
|
+
iox.login
|
12
|
+
|
13
|
+
j = J.new_telnet :host=> '', :user=> 'lab', :pwd => 'lab'
|
14
|
+
j.login
|
15
|
+
|
16
|
+
* Push configigurations to routers
|
17
|
+
|
18
|
+
ios.config 'no logging console'
|
19
|
+
|
20
|
+
ios.config %{
|
21
|
+
interface loopback0
|
22
|
+
shutdown
|
23
|
+
}
|
24
|
+
|
25
|
+
|
26
|
+
iox.config
|
27
|
+
iox.exp_send 'interface GigabitEthernet0/2/0/0'
|
28
|
+
iox.exp_send 'desc to switch port 13'
|
29
|
+
iox.exp_send 'ipv4 address 190.0.0.9 255.255.255.252'
|
30
|
+
iox.exp_send 'no shut'
|
31
|
+
iox.commit
|
32
|
+
|
33
|
+
|
34
|
+
j.config %{
|
35
|
+
edit logical-router Orleans protocols bgp
|
36
|
+
edit group session-to-200
|
37
|
+
set type external
|
38
|
+
set peer-as 200
|
39
|
+
set neighbor 40.0.2.1 peer-as 200
|
40
|
+
}
|
41
|
+
|
42
|
+
* exec commands
|
43
|
+
|
44
|
+
|
45
|
+
j.exec 'set cli screen-length 0'
|
46
|
+
|
47
|
+
iox.exec "terminal len 0\nterminal width 0"
|
48
|
+
|
49
|
+
|
50
|
+
* exec shell commands
|
51
|
+
|
52
|
+
iox.shell 'pidin'
|
53
|
+
|
54
|
+
|
55
|
+
* interact with CLI.
|
56
|
+
|
57
|
+
|
58
|
+
irb> r.interact
|
59
|
+
|
60
|
+
#
|
61
|
+
# ^Z to terminate.
|
62
|
+
#
|
63
|
+
|
64
|
+
router#clear line 2
|
65
|
+
[confirm]
|
66
|
+
[OK]
|
67
|
+
router# ^Z
|
68
|
+
=> nil
|
69
|
+
irb>
|
70
|
+
|
71
|
+
|
72
|
+
irb(main):210:0* j.interact
|
73
|
+
|
74
|
+
#
|
75
|
+
# ^Z to terminate.
|
76
|
+
#
|
77
|
+
|
78
|
+
jme@router> configure
|
79
|
+
Entering configuration mode
|
80
|
+
The configuration has been changed but not committed
|
81
|
+
|
82
|
+
[edit]
|
83
|
+
jme@router# rollback
|
84
|
+
load complete
|
85
|
+
|
86
|
+
[edit]
|
87
|
+
jme@router# show | compare
|
88
|
+
|
89
|
+
[edit]
|
90
|
+
jme@router# ^Z
|
91
|
+
=> nil
|
92
|
+
irb(main):211:0>
|
93
|
+
|
94
|
+
* Parsing output
|
95
|
+
|
96
|
+
The output is returned as an array of arrays of responses to commands.
|
97
|
+
Each response is returned as an array of lines.
|
98
|
+
Parsing a response becomes a matter of iterating an array using the
|
99
|
+
Arrays's enumerator methods such as find, find_all ...
|
100
|
+
|
101
|
+
irb(main):025:0> output = ts.exec %{
|
102
|
+
irb(main):026:0" show terminal | include History
|
103
|
+
irb(main):027:0" show line | include AUX
|
104
|
+
irb(main):028:0" }
|
105
|
+
=> [
|
106
|
+
["show terminal | include History", "History is enabled, history size is 10.", "PEA#"],
|
107
|
+
["show line | include AUX", " 17 AUX 9600/9600 - 0/0 -", "PEA#"]
|
108
|
+
]
|
109
|
+
|
110
|
+
output[0] contains output for 1st command 'show terminal'.
|
111
|
+
output[1] contains output for 1st command 'show line'.
|
112
|
+
...
|
113
|
+
output[n-1] contains output for nth command.
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
data/lib/expect/io.rb
ADDED
@@ -0,0 +1,484 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'pty'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
Thread.abort_on_exception=true
|
6
|
+
|
7
|
+
class IO
|
8
|
+
def _io_save(no_echo=false, match_string=nil, ch=nil)
|
9
|
+
s = _io_string!
|
10
|
+
exp_internal match_string
|
11
|
+
exp_internal s
|
12
|
+
return if no_echo
|
13
|
+
s = s.chomp(ch) if ch
|
14
|
+
_io_buf1 << s unless no_echo
|
15
|
+
end
|
16
|
+
def _io_more? ; ! IO.select([self],nil,nil,0.20).nil? ; end
|
17
|
+
def _io_exit? ; _io_buf0.last.nil? ; end
|
18
|
+
def _io_buf0 ; @_buf0_ ; end
|
19
|
+
def _io_buf1 ; @_buf1_ ; end
|
20
|
+
def _io_string ; @_buf0_.join ; end
|
21
|
+
def _io_string!
|
22
|
+
b = _io_string
|
23
|
+
@_buf0_=[]
|
24
|
+
b
|
25
|
+
end
|
26
|
+
def _io_sync
|
27
|
+
@_buf0_ ||=[]
|
28
|
+
while _io_more?
|
29
|
+
break if _io_read_char.nil?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def _io_read_char
|
33
|
+
c = getc
|
34
|
+
@_buf0_ << c.chr unless c.nil?
|
35
|
+
exp_internal "buf0: [#{_io_string.gsub(/\n/,'\n').gsub(/\r/,'\r')}]"
|
36
|
+
c
|
37
|
+
end
|
38
|
+
def readbuf(ti=10)
|
39
|
+
@_buf0_, @_buf1_=[],[]
|
40
|
+
loop do
|
41
|
+
if IO.select([self],nil,nil,ti).nil?
|
42
|
+
exp_internal "IO.select is NIL (TIMEOUT=#{ti})"
|
43
|
+
@_buf0_ << nil
|
44
|
+
else
|
45
|
+
_io_read_char
|
46
|
+
end
|
47
|
+
yield(self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Expect4r
|
53
|
+
|
54
|
+
class ConnectionError < RuntimeError
|
55
|
+
def initialize(output)
|
56
|
+
@output = output
|
57
|
+
end
|
58
|
+
def err_msg
|
59
|
+
"Connection Error: #{@output}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ExpTimeoutError < RuntimeError
|
64
|
+
def initialize(output, elapsed)
|
65
|
+
@output, @elapsed = output, elapsed
|
66
|
+
end
|
67
|
+
def err_msg
|
68
|
+
"Timeout Error: #{@output}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class NoChildError < RuntimeError
|
73
|
+
end
|
74
|
+
class SpawnError < RuntimeError
|
75
|
+
def initialize(cmd)
|
76
|
+
@cmd = cmd
|
77
|
+
end
|
78
|
+
def err_msg
|
79
|
+
"Error: #{msg}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
class Expect4rIO_Error < RuntimeError
|
83
|
+
end
|
84
|
+
|
85
|
+
def child_exit
|
86
|
+
if @pid
|
87
|
+
Process.kill(:KILL, @pid)
|
88
|
+
@thread.kill
|
89
|
+
@lp, @r, @w, @pid = [nil]*4
|
90
|
+
end
|
91
|
+
rescue Errno::ESRCH, Errno::ECHILD => e
|
92
|
+
ensure
|
93
|
+
@lp, @r, @w, @pid = [nil]*4
|
94
|
+
end
|
95
|
+
|
96
|
+
def spawn(cmd)
|
97
|
+
begin
|
98
|
+
child_exited = false
|
99
|
+
# STDOUT.puts "*** FALSE ***"
|
100
|
+
@thread = Thread.new do
|
101
|
+
PTY.spawn(cmd) do |pipe_read, pipe_write, pid|
|
102
|
+
@r, @w, @pid = pipe_read, pipe_write, pid
|
103
|
+
begin
|
104
|
+
Process.wait(@pid,0)
|
105
|
+
rescue
|
106
|
+
ensure
|
107
|
+
child_exited = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@thread.priority = -2
|
112
|
+
# STDOUT.puts "*** #{child_exited} ***"
|
113
|
+
unless child_exited
|
114
|
+
while @r.nil?
|
115
|
+
sleep(0.05)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue => e
|
119
|
+
raise SpawnError.new(cmd)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def logout
|
124
|
+
child_exit
|
125
|
+
end
|
126
|
+
|
127
|
+
def putc(c)
|
128
|
+
return unless @w || c.nil?
|
129
|
+
exp_internal "[#{c}]"
|
130
|
+
@w.putc(c) and flush
|
131
|
+
rescue Errno::EIO
|
132
|
+
child_exit
|
133
|
+
raise
|
134
|
+
end
|
135
|
+
|
136
|
+
def exp_puts(s)
|
137
|
+
exp_print "#{s}\r"
|
138
|
+
end
|
139
|
+
def exp_print(s)
|
140
|
+
exp_internal "print: #{s.inspect}, io_writer: #{@w}"
|
141
|
+
return unless @w
|
142
|
+
@w.print(s) and flush
|
143
|
+
rescue Errno::EIO, Errno::ECHILD
|
144
|
+
child_exit
|
145
|
+
raise
|
146
|
+
end
|
147
|
+
def getc
|
148
|
+
@r.getc if @r
|
149
|
+
rescue Errno::EIO, Errno::ECHILD
|
150
|
+
child_exit
|
151
|
+
raise
|
152
|
+
end
|
153
|
+
|
154
|
+
def interact(k=?\C-z)
|
155
|
+
STDOUT.puts "\n\#\n\# #{ctrl_key k} to terminate.\n\#\n"
|
156
|
+
reader :start
|
157
|
+
writer k
|
158
|
+
rescue
|
159
|
+
ensure
|
160
|
+
begin
|
161
|
+
reader :terminate
|
162
|
+
putline ' ', :no_trim=>true, :no_echo=>true
|
163
|
+
rescue => e
|
164
|
+
exp_internal e.to_s
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def putcr
|
169
|
+
putline '', :no_trim=>true, :no_echo=>true
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
|
173
|
+
def exp_send(lines, arg={})
|
174
|
+
r=[]
|
175
|
+
lines.each_line do |l|
|
176
|
+
l = l.chomp("\n").chomp("\r").strip
|
177
|
+
r << putline(l, arg) if l.size>0
|
178
|
+
end
|
179
|
+
lines.size==1 ? r[0] : r
|
180
|
+
end
|
181
|
+
|
182
|
+
def expect(match, ti=5)
|
183
|
+
ev, buf = catch(:done) do
|
184
|
+
@r.readbuf(ti) do |r|
|
185
|
+
if r._io_exit?
|
186
|
+
throw :done, [ :timeout, r._io_string!]
|
187
|
+
end
|
188
|
+
case r._io_string
|
189
|
+
when match
|
190
|
+
throw :done, [:ok, r._io_string!.chomp("\r\n")]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
exp_internal "#{ev.inspect} buf: #{buf.inspect}"
|
195
|
+
raise RuntimeError, buf if ev == :timeout
|
196
|
+
[buf, ev]
|
197
|
+
end
|
198
|
+
|
199
|
+
def readline(ti=2)
|
200
|
+
expect(/(.+)\r\n/, ti)
|
201
|
+
end
|
202
|
+
|
203
|
+
def connected?
|
204
|
+
@r && (not child_exited?)
|
205
|
+
end
|
206
|
+
|
207
|
+
def login(c, o={})
|
208
|
+
return if connected?
|
209
|
+
|
210
|
+
spawn c
|
211
|
+
|
212
|
+
o={:timeout=>13, :no_echo=>false}.merge(o)
|
213
|
+
timeout = o[:timeout]
|
214
|
+
no_echo = o[:no_echo]
|
215
|
+
|
216
|
+
output=[]
|
217
|
+
t0 = Time.now
|
218
|
+
ev, buf = catch(:done) do
|
219
|
+
@r.readbuf(timeout) do |read_pipe|
|
220
|
+
if read_pipe._io_exit?
|
221
|
+
exp_internal "readbuf: _io_exit?"
|
222
|
+
throw :done, [ :abort, output]
|
223
|
+
end
|
224
|
+
case read_pipe._io_string
|
225
|
+
when spawnee_prompt
|
226
|
+
read_pipe._io_save false, "match PROMPT"
|
227
|
+
throw(:done, [:ok, output])
|
228
|
+
when /(user\s*name\s*|login):\r*$/i
|
229
|
+
read_pipe._io_save no_echo, "match USERNAME"
|
230
|
+
exp_puts spawnee_username
|
231
|
+
when /password:\s*$/i
|
232
|
+
read_pipe._io_save no_echo, "match PASSWORD"
|
233
|
+
@w.print(spawnee_password+"\r") and flush
|
234
|
+
when /Escape character is/
|
235
|
+
read_pipe._io_save no_echo, "match Escape char"
|
236
|
+
io_escape_char_cb
|
237
|
+
when /.*\r\n/
|
238
|
+
exp_internal "match EOL"
|
239
|
+
read_pipe._io_save no_echo, "match EOL", "\r\n"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
case ev
|
244
|
+
when :abort
|
245
|
+
elapsed = Time.now - t0
|
246
|
+
if elapsed < timeout
|
247
|
+
raise ConnectionError.new(c)
|
248
|
+
else
|
249
|
+
raise ExpTimeoutError.new(c, elapsed)
|
250
|
+
end
|
251
|
+
else
|
252
|
+
@lp = buf.last
|
253
|
+
end
|
254
|
+
[buf, ev]
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
#FIXME ? putline to send_cmd ?
|
261
|
+
# hide putline and expose cmd
|
262
|
+
def putline(line, arg={})
|
263
|
+
raise NoChildError if child_exited?
|
264
|
+
|
265
|
+
arg = {:ti=>13, :no_echo=>false, :debug=>0, :sync=> false, :no_trim=>false}.merge(arg)
|
266
|
+
no_echo = arg[:no_echo]
|
267
|
+
ti = arg[:ti]
|
268
|
+
unless arg[:no_trim]
|
269
|
+
line = line.gsub(/\s+/,' ').gsub(/^\s+/,'') unless arg[:no_trim]
|
270
|
+
return [[], :empty_line] unless line.size>0
|
271
|
+
end
|
272
|
+
sync if arg[:sync]
|
273
|
+
t0 = Time.now
|
274
|
+
exp_puts line
|
275
|
+
output=[]
|
276
|
+
rc, buf = catch(:done) do
|
277
|
+
@r.readbuf(arg[:ti]) do |r|
|
278
|
+
if r._io_exit?
|
279
|
+
r._io_save(no_echo)
|
280
|
+
throw :done, [ :abort, r._io_buf1]
|
281
|
+
end
|
282
|
+
case r._io_string
|
283
|
+
when @ps1, @ps1_bis
|
284
|
+
unless r._io_more?
|
285
|
+
r._io_save false, "matching PROMPT"
|
286
|
+
throw(:done, [:ok, r._io_buf1])
|
287
|
+
end
|
288
|
+
exp_internal "more..."
|
289
|
+
when /(.+)\r\n/, "\r\n"
|
290
|
+
r._io_save no_echo, "matching EOL", "\r\n"
|
291
|
+
when @more
|
292
|
+
r._io_save no_echo, "matching MORE"
|
293
|
+
putc ' '
|
294
|
+
end
|
295
|
+
|
296
|
+
@matches.each { |match, _send|
|
297
|
+
if r._io_string =~ match
|
298
|
+
r._io_save no_echo, "match #{match}"
|
299
|
+
exp_puts _send
|
300
|
+
end
|
301
|
+
}
|
302
|
+
|
303
|
+
end
|
304
|
+
end
|
305
|
+
case rc
|
306
|
+
when :abort
|
307
|
+
elapsed = Time.now - t0
|
308
|
+
if elapsed < ti
|
309
|
+
raise ConnectionError.new(line)
|
310
|
+
else
|
311
|
+
raise ExpTimeoutError.new(line, elapsed)
|
312
|
+
end
|
313
|
+
else
|
314
|
+
@lp = buf.last
|
315
|
+
end
|
316
|
+
[buf, rc]
|
317
|
+
end
|
318
|
+
def ctrl_key(k)
|
319
|
+
case k
|
320
|
+
when ?\C-c ; '^C'
|
321
|
+
when ?\C-q ; '^Q'
|
322
|
+
when ?\C-z ; '^Z'
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def writer(k)
|
327
|
+
stty_raw
|
328
|
+
begin
|
329
|
+
loop do
|
330
|
+
break if (c = STDIN.getc) == k
|
331
|
+
putc(c)
|
332
|
+
end
|
333
|
+
rescue PTY::ChildExited => e
|
334
|
+
child_exit
|
335
|
+
ensure
|
336
|
+
stty_cooked
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def reader(arg=:start)
|
341
|
+
case arg
|
342
|
+
when :start
|
343
|
+
stty_raw
|
344
|
+
@reader = Thread.new do
|
345
|
+
begin
|
346
|
+
loop do
|
347
|
+
c = getc
|
348
|
+
break if c.nil?
|
349
|
+
STDOUT.putc c
|
350
|
+
end
|
351
|
+
rescue => e
|
352
|
+
p e
|
353
|
+
p '7777777'
|
354
|
+
ensure
|
355
|
+
stty_cooked
|
356
|
+
end
|
357
|
+
end
|
358
|
+
when :terminate
|
359
|
+
@reader.terminate if @reader
|
360
|
+
stty_cooked
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def sync
|
365
|
+
@r._io_sync
|
366
|
+
end
|
367
|
+
|
368
|
+
def child_exited?
|
369
|
+
@pid == nil
|
370
|
+
end
|
371
|
+
|
372
|
+
def stty_cooked
|
373
|
+
system "stty echo -raw"
|
374
|
+
end
|
375
|
+
|
376
|
+
def stty_raw
|
377
|
+
system "stty -echo raw"
|
378
|
+
end
|
379
|
+
|
380
|
+
def flush
|
381
|
+
@w.flush
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
module Kernel
|
387
|
+
def exp_debug(state=:enable)
|
388
|
+
case state
|
389
|
+
when :disable, :off
|
390
|
+
Kernel.instance_eval {
|
391
|
+
define_method("exp_internal") do |s|
|
392
|
+
end
|
393
|
+
}
|
394
|
+
:disable
|
395
|
+
when :enable, :on
|
396
|
+
Kernel.instance_eval {
|
397
|
+
define_method("exp_internal") do |s|
|
398
|
+
STDOUT.print "\ndebug: #{s}"
|
399
|
+
end
|
400
|
+
}
|
401
|
+
:enable
|
402
|
+
else
|
403
|
+
nil
|
404
|
+
end
|
405
|
+
end
|
406
|
+
exp_debug :disable
|
407
|
+
end
|
408
|
+
|
409
|
+
module Expect4r
|
410
|
+
class BaseObject
|
411
|
+
class << self
|
412
|
+
def new_telnet(*args)
|
413
|
+
new :telnet, *args
|
414
|
+
end
|
415
|
+
def new_ssh(*args)
|
416
|
+
new :ssh, *args
|
417
|
+
end
|
418
|
+
attr_reader :routers
|
419
|
+
def add(r)
|
420
|
+
@routers ||=[]
|
421
|
+
@routers << r
|
422
|
+
end
|
423
|
+
end
|
424
|
+
attr_reader :host, :user, :method, :port
|
425
|
+
alias :username :user
|
426
|
+
alias :hostname :host
|
427
|
+
def initialize(*args)
|
428
|
+
ciphered_pwd=nil
|
429
|
+
if args.size>2 and args[1].is_a?(String)
|
430
|
+
@method, host, @user, pwd = args
|
431
|
+
elsif args.size == 2 and args[1].is_a?(Hash) and args[0].is_a?(Symbol)
|
432
|
+
@method = args[0]
|
433
|
+
host = args[1][:host] || args[1][:hostname]
|
434
|
+
@user = args[1][:user]|| args[1][:username]
|
435
|
+
pwd = args[1][:pwd] || args[1][:password]
|
436
|
+
ciphered_pwd = args[1][:ciphered_pwd]
|
437
|
+
end
|
438
|
+
@host, port = host.split
|
439
|
+
@port = port.to_i
|
440
|
+
@pwd = if ciphered_pwd
|
441
|
+
ciphered_pwd
|
442
|
+
else
|
443
|
+
Expect4r.cipher(pwd) if pwd
|
444
|
+
end
|
445
|
+
@ps1 = /(.*)(>|#|\$)\s*$/
|
446
|
+
@more = / --More-- /
|
447
|
+
@matches=Set.new
|
448
|
+
BaseObject.add(self)
|
449
|
+
self
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
if __FILE__ != $0
|
455
|
+
|
456
|
+
at_exit {
|
457
|
+
if Expect4r::BaseObject.routers
|
458
|
+
Expect4r::BaseObject.routers.each { |r| r.logout if r.respond_to? :logout }
|
459
|
+
end
|
460
|
+
}
|
461
|
+
|
462
|
+
else
|
463
|
+
|
464
|
+
require "test/unit"
|
465
|
+
|
466
|
+
class Expect4r::BaseObject
|
467
|
+
def initialize
|
468
|
+
Expect4r::BaseObject.add(self)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
class TestRouterBaseObject < Test::Unit::TestCase
|
473
|
+
include Expect4r
|
474
|
+
|
475
|
+
def test_add
|
476
|
+
assert [], BaseObject.routers
|
477
|
+
BaseObject.new
|
478
|
+
assert 1, BaseObject.routers.size
|
479
|
+
BaseObject.new
|
480
|
+
assert_equal 2, BaseObject.routers.size
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
end
|