sshexpect 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +2 -0
- data/lib/sshexpect.rb +219 -0
- metadata +54 -0
data/README
ADDED
data/lib/sshexpect.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'expect'
|
2
|
+
require 'pty'
|
3
|
+
|
4
|
+
module SshExpect
|
5
|
+
#Signal.trap( 'CHLD', 'IGNORE')
|
6
|
+
|
7
|
+
# A class for interacting with ssh through pty and expect.
|
8
|
+
# Useful for when sshv2 isnt supported or extended login prompts are used.
|
9
|
+
class Client
|
10
|
+
|
11
|
+
attr_reader :hostname, :timeout, :command, :ignore, :quiet, :port, :cipher, :username, :output, :error
|
12
|
+
|
13
|
+
#==============================================================================#
|
14
|
+
# initialize()
|
15
|
+
#==============================================================================#
|
16
|
+
|
17
|
+
#===Synopsis
|
18
|
+
#Create a new SshExpect::Single object.
|
19
|
+
#
|
20
|
+
#===Usage
|
21
|
+
# SshExpect.new(options)
|
22
|
+
#
|
23
|
+
#===Arguments
|
24
|
+
#Hash with the following fields:
|
25
|
+
# :hostname => [String] -- ip/hostname the device.
|
26
|
+
# :username => [String] -- username to connect with.
|
27
|
+
# :verbose => [TrueClass|FalseClass] -- Suppress output and warnings if set True.
|
28
|
+
# :noverify => [TrueClass|FalseClass] -- Ignore changed host keys if set True.
|
29
|
+
# :timeout => [Integer] -- timeout for device to respond on any given sequence step.
|
30
|
+
# :port => [Integer] -- TCP port to connect with.
|
31
|
+
# :cipher => [String] -- encryption cipher.
|
32
|
+
# :command => [String] -- full path to ssh including the binary.
|
33
|
+
|
34
|
+
def initialize(options)
|
35
|
+
|
36
|
+
@hostname = nil
|
37
|
+
@username = nil
|
38
|
+
@verbose = true
|
39
|
+
@noverify = true
|
40
|
+
@timeout = 30
|
41
|
+
@port = 22
|
42
|
+
@cipher = "des"
|
43
|
+
@command = "/usr/bin/ssh"
|
44
|
+
@cmd = nil
|
45
|
+
@output = Array.new
|
46
|
+
@error = nil
|
47
|
+
|
48
|
+
|
49
|
+
if (!options.kind_of?(Hash))
|
50
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
51
|
+
end
|
52
|
+
|
53
|
+
if (options.has_key?(:hostname))
|
54
|
+
@hostname = options[:hostname]
|
55
|
+
if (!@hostname.kind_of?(String))
|
56
|
+
raise ArgumentError, "Expected String for :hostname, but #{@server.class} provided."
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise ArgumentError, "Missing argument :hostname."
|
60
|
+
end
|
61
|
+
|
62
|
+
if (options.has_key?(:username))
|
63
|
+
@username = options[:username]
|
64
|
+
if (!@username.kind_of?(String))
|
65
|
+
raise ArgumentError, "Expected String for :username, but #{@server.class} provided."
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise ArgumentError, "Missing argument :username."
|
69
|
+
end
|
70
|
+
|
71
|
+
if (options.has_key?(:verbose))
|
72
|
+
@verbose = options[:verbose]
|
73
|
+
if (!@verbose.kind_of?(TrueClass) && !@verbose.kind_of?(FalseClass))
|
74
|
+
raise ArgumentError, "Expected True or False for :verbose, but was #{@verbose.class}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if (options.has_key?(:noverify))
|
79
|
+
@noverify = options[:noverify]
|
80
|
+
if (!@noverify.kind_of?(TrueClass) && !@noverify.kind_of?(FalseClass))
|
81
|
+
raise ArgumentError, "Expected True or False for :noverify, but was #{@ignore.class}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if (options.has_key?(:timeout))
|
86
|
+
@timeout = options[:timeout]
|
87
|
+
if (!@timeout.kind_of?(Integer))
|
88
|
+
raise ArgumentError, "Expected Integer for :timeout, but #{@timeout.class} provided."
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if (options.has_key?(:port))
|
93
|
+
@port = options[:port]
|
94
|
+
if (!@port.kind_of?(Integer))
|
95
|
+
raise ArgumentError, "Expected Integer for :port, but #{@port.class} provided."
|
96
|
+
end
|
97
|
+
if (@port >= 2**16)
|
98
|
+
raise ArgumentError, "#{@port} is not a valid TCP port."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#Build the command line for PTY
|
103
|
+
@cmd = @command + " #{@username}@#{@hostname} -p #{@port}"
|
104
|
+
|
105
|
+
if(@verbose)
|
106
|
+
@cmd = @cmd + " -q"
|
107
|
+
end
|
108
|
+
|
109
|
+
if(@noverify)
|
110
|
+
@cmd = @cmd + " -o StrictHostKeyChecking=no"
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
#==============================================================================#
|
116
|
+
# exec()
|
117
|
+
#==============================================================================#
|
118
|
+
|
119
|
+
#===Synopsis
|
120
|
+
#Iterate through a sequence of send/expect statements.
|
121
|
+
#
|
122
|
+
#===Usage
|
123
|
+
# SshExpect.exec(sequence)
|
124
|
+
#
|
125
|
+
#===Arguments
|
126
|
+
#Array of Hashes with the following format:
|
127
|
+
#sequence = []
|
128
|
+
#sequence[0] = {}
|
129
|
+
#sequence[0]["expect"] = "word:"
|
130
|
+
#sequence[0]["send"] = "mypassword\n"
|
131
|
+
#sequence[1] = {}
|
132
|
+
#sequence[1]["expect"] = ">"
|
133
|
+
#sequence[1]["send"] = "en\n"
|
134
|
+
#sequence[2] = {}
|
135
|
+
#sequence[2]["expect"] = "word:"
|
136
|
+
#sequence[2]["send"] = "enable\n"
|
137
|
+
#sequence[3] = {}
|
138
|
+
#sequence[3]["expect"] = "#"
|
139
|
+
#sequence[3]["send"] = "conf t\n"
|
140
|
+
#sequence[4] = {}
|
141
|
+
#sequence[4]["expect"] = "#"
|
142
|
+
#sequence[4]["send"] = "no pager\n sho run\n"
|
143
|
+
#sequence[5] = {}
|
144
|
+
#sequence[5]["expect"] = "#"
|
145
|
+
#sequence[5]["send"] = "exit\n"
|
146
|
+
#sequence[6] = {}
|
147
|
+
#sequence[6]["expect"] = "#"
|
148
|
+
#sequence[6]["send"] = "exit\n"
|
149
|
+
|
150
|
+
|
151
|
+
def exec(sequence)
|
152
|
+
|
153
|
+
reader = nil
|
154
|
+
writer = nil
|
155
|
+
pid = nil
|
156
|
+
|
157
|
+
begin
|
158
|
+
PTY.spawn(@cmd) do |reader,writer,pid|
|
159
|
+
|
160
|
+
sleep 0.1
|
161
|
+
|
162
|
+
PTY.protect_signal do
|
163
|
+
|
164
|
+
$expect_verbose = @verbose
|
165
|
+
sequence.each do |step|
|
166
|
+
exp = step["expect"]
|
167
|
+
snd = step["send"]
|
168
|
+
|
169
|
+
begin
|
170
|
+
reader.expect(exp,@timeout) do |receive|
|
171
|
+
if receive == nil then
|
172
|
+
@output.push("Exception: Timeout talking to device")
|
173
|
+
|
174
|
+
writer.close
|
175
|
+
reader.close
|
176
|
+
return @output
|
177
|
+
else
|
178
|
+
writer.puts(snd)
|
179
|
+
@output.push(receive)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
rescue Exception => ee
|
184
|
+
@output.push "Exception trying to handle IO #{ee.to_s}\n"
|
185
|
+
return @output
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
writer.close
|
190
|
+
reader.close
|
191
|
+
|
192
|
+
begin
|
193
|
+
Process.kill(9,@pid)
|
194
|
+
rescue Exception => ee
|
195
|
+
@output.push "Exception trying to kill process #{@pid} #{ee.to_s} \n"
|
196
|
+
return @output
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
return @output
|
201
|
+
|
202
|
+
|
203
|
+
rescue PTY::ChildExited => ee
|
204
|
+
@output.push "Exception child died early #{ee.to_s}\n"
|
205
|
+
return @output
|
206
|
+
|
207
|
+
rescue Errno::EIO => ee
|
208
|
+
@output.push "Exception connection closed early #{ee.to_s}\n"
|
209
|
+
return @output
|
210
|
+
|
211
|
+
rescue Exception => ee
|
212
|
+
@output.push "Exception #{ee.to_s}\n"
|
213
|
+
return @output
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sshexpect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Lister
|
8
|
+
autorequire: sshexpect
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-25 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: david @nospam@ lister.ch
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/sshexpect.rb
|
26
|
+
- README
|
27
|
+
has_rdoc: true
|
28
|
+
homepage:
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: "0"
|
39
|
+
version:
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.0.1
|
50
|
+
signing_key:
|
51
|
+
specification_version: 2
|
52
|
+
summary: This is a module for using expect and ssh for automation.
|
53
|
+
test_files: []
|
54
|
+
|