ztk 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/lib/ztk/ssh/bootstrap.rb +48 -0
- data/lib/ztk/ssh/command.rb +73 -0
- data/lib/ztk/ssh/download.rb +68 -0
- data/lib/ztk/ssh/exec.rb +148 -0
- data/lib/ztk/ssh/upload.rb +68 -0
- data/lib/ztk/ssh.rb +12 -246
- data/lib/ztk/template.rb +23 -0
- data/lib/ztk/version.rb +1 -1
- metadata +9 -4
data/README.md
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Bootstrap
|
24
|
+
require 'tempfile'
|
25
|
+
|
26
|
+
def bootstrap(content, use_sudo=true)
|
27
|
+
tempfile = Tempfile.new("bootstrap")
|
28
|
+
|
29
|
+
File.open(tempfile, 'w') do |file|
|
30
|
+
file.puts(content)
|
31
|
+
file.respond_to?(:flush) and file.flush
|
32
|
+
end
|
33
|
+
|
34
|
+
self.upload(tempfile.path, tempfile.path)
|
35
|
+
|
36
|
+
command = Array.new
|
37
|
+
command << %(sudo) if (use_sudo == true)
|
38
|
+
command << %(/bin/bash)
|
39
|
+
command << tempfile.path
|
40
|
+
command = command.join(' ')
|
41
|
+
|
42
|
+
self.exec(command, :silence => true)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Command
|
24
|
+
|
25
|
+
# Builds our SSH console command.
|
26
|
+
def console_command
|
27
|
+
verbosity = ((ENV['LOG_LEVEL'].upcase == "DEBUG") ? '-vv' : '-q')
|
28
|
+
|
29
|
+
command = [ "/usr/bin/env ssh" ]
|
30
|
+
command << [ verbosity ]
|
31
|
+
command << [ "-x" ]
|
32
|
+
command << [ "-a" ]
|
33
|
+
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
34
|
+
command << [ "-o", "StrictHostKeyChecking=no" ]
|
35
|
+
command << [ "-o", "KeepAlive=yes" ]
|
36
|
+
command << [ "-o", "ServerAliveInterval=60" ]
|
37
|
+
command << [ "-o", %(ProxyCommand="#{proxy_command}") ] if config.proxy_host_name
|
38
|
+
command << [ "-i", config.keys ] if config.keys
|
39
|
+
command << [ "-p", config.port ] if config.port
|
40
|
+
command << "#{config.user}@#{config.host_name}"
|
41
|
+
command = command.flatten.compact.join(' ')
|
42
|
+
config.ui.logger.debug { "console_command(#{command.inspect})" }
|
43
|
+
command
|
44
|
+
end
|
45
|
+
|
46
|
+
# Builds our SSH proxy command.
|
47
|
+
def proxy_command
|
48
|
+
!config.proxy_user and log_and_raise(SSHError, "You must specify an proxy user in order to SSH proxy.")
|
49
|
+
!config.proxy_host_name and log_and_raise(SSHError, "You must specify an proxy host_name in order to SSH proxy.")
|
50
|
+
|
51
|
+
verbosity = ((ENV['LOG_LEVEL'].upcase == "DEBUG") ? '-vv' : '-q')
|
52
|
+
|
53
|
+
command = ["/usr/bin/env ssh"]
|
54
|
+
command << [ verbosity ]
|
55
|
+
command << [ "-x" ]
|
56
|
+
command << [ "-a" ]
|
57
|
+
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
58
|
+
command << [ "-o", "StrictHostKeyChecking=no" ]
|
59
|
+
command << [ "-o", "KeepAlive=yes" ]
|
60
|
+
command << [ "-o", "ServerAliveInterval=60" ]
|
61
|
+
command << [ "-i", config.proxy_keys ] if config.proxy_keys
|
62
|
+
command << [ "-p", config.proxy_port ] if config.proxy_port
|
63
|
+
command << "#{config.proxy_user}@#{config.proxy_host_name}"
|
64
|
+
command << "'/usr/bin/env nc %h %p'"
|
65
|
+
command = command.flatten.compact.join(' ')
|
66
|
+
config.ui.logger.debug { "proxy_command(#{command.inspect})" }
|
67
|
+
command
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Download
|
24
|
+
|
25
|
+
# Downloads a remote file to the local host.
|
26
|
+
#
|
27
|
+
# @param [String] remote The remote file/path you with to download from.
|
28
|
+
# @param [String] local The local file/path you wish to download to.
|
29
|
+
#
|
30
|
+
# @example Download a file:
|
31
|
+
# $logger = ZTK::Logger.new(STDOUT)
|
32
|
+
# ssh = ZTK::SSH.new
|
33
|
+
# ssh.config do |config|
|
34
|
+
# config.user = ENV["USER"]
|
35
|
+
# config.host_name = "127.0.0.1"
|
36
|
+
# end
|
37
|
+
# local = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
38
|
+
# remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
39
|
+
# ssh.download(remote, local)
|
40
|
+
def download(remote, local)
|
41
|
+
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
42
|
+
config.ui.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
|
43
|
+
|
44
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
45
|
+
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
46
|
+
sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
|
47
|
+
case event
|
48
|
+
when :open
|
49
|
+
config.ui.logger.debug { "download(#{args[0].remote} -> #{args[0].local})" }
|
50
|
+
when :close
|
51
|
+
config.ui.logger.debug { "close(#{args[0].local})" }
|
52
|
+
when :mkdir
|
53
|
+
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
54
|
+
when :get
|
55
|
+
config.ui.logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
56
|
+
when :finish
|
57
|
+
config.ui.logger.debug { "finish" }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ztk/ssh/exec.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Exec
|
24
|
+
|
25
|
+
# Executes a command on the remote host.
|
26
|
+
#
|
27
|
+
# @param [String] command The command to execute.
|
28
|
+
# @param [Hash] options The options hash for executing the command.
|
29
|
+
# @option options [Boolean] :silence Squelch output to STDOUT and STDERR.
|
30
|
+
# If the log level is :debug, STDOUT and STDERR will go to the log file
|
31
|
+
# regardless of this setting. STDOUT and STDERR are always returned in
|
32
|
+
# the output return value regardless of this setting.
|
33
|
+
#
|
34
|
+
# @return [OpenStruct#output] The output of the command, both STDOUT and
|
35
|
+
# STDERR.
|
36
|
+
# @return [OpenStruct#exit] The exit status (i.e. $?).
|
37
|
+
#
|
38
|
+
# @example Execute a command:
|
39
|
+
#
|
40
|
+
# ssh = ZTK::SSH.new
|
41
|
+
# ssh.config do |config|
|
42
|
+
# config.user = ENV["USER"]
|
43
|
+
# config.host_name = "127.0.0.1"
|
44
|
+
# end
|
45
|
+
# puts ssh.exec("hostname").inspect
|
46
|
+
def exec(command, options={})
|
47
|
+
options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(config.send(:table)).merge(options))
|
48
|
+
|
49
|
+
options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
|
50
|
+
options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
|
51
|
+
options.ui.logger.info { "exec(#{command.inspect})" }
|
52
|
+
|
53
|
+
output = ""
|
54
|
+
exit_code = -1
|
55
|
+
exit_signal = nil
|
56
|
+
stdout_header = false
|
57
|
+
stderr_header = false
|
58
|
+
|
59
|
+
begin
|
60
|
+
Timeout.timeout(options.timeout) do
|
61
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
62
|
+
@ssh = Net::SSH.start(options.host_name, options.user, ssh_options)
|
63
|
+
|
64
|
+
channel = ssh.open_channel do |chan|
|
65
|
+
options.ui.logger.debug { "Channel opened." }
|
66
|
+
|
67
|
+
(options.request_pty == true) and chan.request_pty do |ch, success|
|
68
|
+
if success
|
69
|
+
options.ui.logger.debug { "PTY obtained." }
|
70
|
+
else
|
71
|
+
options.ui.logger.warn { "Could not obtain PTY." }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
direct_log(:info) { log_header("COMMAND") }
|
76
|
+
direct_log(:info) { "#{command}\n" }
|
77
|
+
direct_log(:info) { log_header("OPENED") }
|
78
|
+
|
79
|
+
chan.exec(command) do |ch, success|
|
80
|
+
success or log_and_raise(SSHError, "Could not execute '#{command}'.")
|
81
|
+
|
82
|
+
ch.on_data do |c, data|
|
83
|
+
if !stdout_header
|
84
|
+
direct_log(:info) { log_header("STDOUT") }
|
85
|
+
stdout_header = true
|
86
|
+
stderr_header = false
|
87
|
+
end
|
88
|
+
direct_log(:info) { data }
|
89
|
+
|
90
|
+
options.ui.stdout.print(data) unless options.silence
|
91
|
+
output += data
|
92
|
+
end
|
93
|
+
|
94
|
+
ch.on_extended_data do |c, type, data|
|
95
|
+
if !stderr_header
|
96
|
+
direct_log(:warn) { log_header("STDERR") }
|
97
|
+
stderr_header = true
|
98
|
+
stdout_header = false
|
99
|
+
end
|
100
|
+
direct_log(:warn) { data }
|
101
|
+
|
102
|
+
options.ui.stderr.print(data) unless options.silence
|
103
|
+
output += data
|
104
|
+
end
|
105
|
+
|
106
|
+
ch.on_request("exit-status") do |ch, data|
|
107
|
+
exit_code = data.read_long
|
108
|
+
end
|
109
|
+
|
110
|
+
ch.on_request("exit-signal") do |ch, data|
|
111
|
+
exit_signal = data.read_long
|
112
|
+
end
|
113
|
+
|
114
|
+
ch.on_open_failed do |c, code, desc|
|
115
|
+
options.ui.logger.fatal { "Open failed! (#{code.inspect} - #{desc.inspect})" }
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
channel.wait
|
121
|
+
|
122
|
+
direct_log(:info) { log_header("CLOSED") }
|
123
|
+
options.ui.logger.debug { "Channel closed." }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
rescue Timeout::Error => e
|
128
|
+
direct_log(:fatal) { log_header("TIMEOUT") }
|
129
|
+
log_and_raise(SSHError, "Session timed out after #{options.timeout} seconds!")
|
130
|
+
end
|
131
|
+
|
132
|
+
message = [
|
133
|
+
"exit_code=#{exit_code}",
|
134
|
+
(exit_signal.nil? ? nil : "exit_signal=#{exit_signal} (#{EXIT_SIGNALS[exit_signal]})")
|
135
|
+
].compact.join(", ")
|
136
|
+
|
137
|
+
options.ui.logger.debug { message }
|
138
|
+
|
139
|
+
if !options.ignore_exit_status && (exit_code != options.exit_code)
|
140
|
+
log_and_raise(SSHError, message)
|
141
|
+
end
|
142
|
+
OpenStruct.new(:command => command, :output => output, :exit_code => exit_code, :exit_signal => exit_signal)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Upload
|
24
|
+
|
25
|
+
# Uploads a local file to a remote host.
|
26
|
+
#
|
27
|
+
# @param [String] local The local file/path you wish to upload from.
|
28
|
+
# @param [String] remote The remote file/path you with to upload to.
|
29
|
+
#
|
30
|
+
# @example Upload a file:
|
31
|
+
# $logger = ZTK::Logger.new(STDOUT)
|
32
|
+
# ssh = ZTK::SSH.new
|
33
|
+
# ssh.config do |config|
|
34
|
+
# config.user = ENV["USER"]
|
35
|
+
# config.host_name = "127.0.0.1"
|
36
|
+
# end
|
37
|
+
# local = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
38
|
+
# remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
39
|
+
# ssh.upload(local, remote)
|
40
|
+
def upload(local, remote)
|
41
|
+
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
42
|
+
config.ui.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
|
43
|
+
|
44
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
45
|
+
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
46
|
+
sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
|
47
|
+
case event
|
48
|
+
when :open
|
49
|
+
config.ui.logger.debug { "upload(#{args[0].local} -> #{args[0].remote})" }
|
50
|
+
when :close
|
51
|
+
config.ui.logger.debug { "close(#{args[0].remote})" }
|
52
|
+
when :mkdir
|
53
|
+
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
54
|
+
when :put
|
55
|
+
config.ui.logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
56
|
+
when :finish
|
57
|
+
config.ui.logger.debug { "finish" }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ztk/ssh.rb
CHANGED
@@ -84,7 +84,6 @@ module ZTK
|
|
84
84
|
#
|
85
85
|
# @author Zachary Patten <zachary@jovelabs.net>
|
86
86
|
class SSH < ZTK::Base
|
87
|
-
|
88
87
|
# Exit Signal Mappings
|
89
88
|
EXIT_SIGNALS = {
|
90
89
|
1 => "SIGHUP",
|
@@ -116,6 +115,18 @@ module ZTK
|
|
116
115
|
27 => "SIGPROF"
|
117
116
|
}
|
118
117
|
|
118
|
+
autoload :Bootstrap, 'ztk/ssh/bootstrap'
|
119
|
+
autoload :Command, 'ztk/ssh/command'
|
120
|
+
autoload :Download, 'ztk/ssh/download'
|
121
|
+
autoload :Exec, 'ztk/ssh/exec'
|
122
|
+
autoload :Upload, 'ztk/ssh/upload'
|
123
|
+
|
124
|
+
include ZTK::SSH::Bootstrap
|
125
|
+
include ZTK::SSH::Command
|
126
|
+
include ZTK::SSH::Download
|
127
|
+
include ZTK::SSH::Exec
|
128
|
+
include ZTK::SSH::Upload
|
129
|
+
|
119
130
|
# @param [Hash] configuration Configuration options hash.
|
120
131
|
# @option config [String] :host_name Server hostname to connect to.
|
121
132
|
# @option config [String] :user Username to use for authentication.
|
@@ -185,254 +196,9 @@ module ZTK
|
|
185
196
|
Kernel.exec(console_command)
|
186
197
|
end
|
187
198
|
|
188
|
-
# Executes a command on the remote host.
|
189
|
-
#
|
190
|
-
# @param [String] command The command to execute.
|
191
|
-
# @param [Hash] options The options hash for executing the command.
|
192
|
-
# @option options [Boolean] :silence Squelch output to STDOUT and STDERR.
|
193
|
-
# If the log level is :debug, STDOUT and STDERR will go to the log file
|
194
|
-
# regardless of this setting. STDOUT and STDERR are always returned in
|
195
|
-
# the output return value regardless of this setting.
|
196
|
-
#
|
197
|
-
# @return [OpenStruct#output] The output of the command, both STDOUT and
|
198
|
-
# STDERR.
|
199
|
-
# @return [OpenStruct#exit] The exit status (i.e. $?).
|
200
|
-
#
|
201
|
-
# @example Execute a command:
|
202
|
-
#
|
203
|
-
# ssh = ZTK::SSH.new
|
204
|
-
# ssh.config do |config|
|
205
|
-
# config.user = ENV["USER"]
|
206
|
-
# config.host_name = "127.0.0.1"
|
207
|
-
# end
|
208
|
-
# puts ssh.exec("hostname").inspect
|
209
|
-
def exec(command, options={})
|
210
|
-
options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(config.send(:table)).merge(options))
|
211
|
-
|
212
|
-
options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
|
213
|
-
options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
|
214
|
-
options.ui.logger.info { "exec(#{command.inspect})" }
|
215
|
-
|
216
|
-
output = ""
|
217
|
-
exit_code = -1
|
218
|
-
exit_signal = nil
|
219
|
-
stdout_header = false
|
220
|
-
stderr_header = false
|
221
|
-
|
222
|
-
begin
|
223
|
-
Timeout.timeout(options.timeout) do
|
224
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
225
|
-
@ssh = Net::SSH.start(options.host_name, options.user, ssh_options)
|
226
|
-
|
227
|
-
channel = ssh.open_channel do |chan|
|
228
|
-
options.ui.logger.debug { "Channel opened." }
|
229
|
-
|
230
|
-
(options.request_pty == true) and chan.request_pty do |ch, success|
|
231
|
-
if success
|
232
|
-
options.ui.logger.debug { "PTY obtained." }
|
233
|
-
else
|
234
|
-
options.ui.logger.warn { "Could not obtain PTY." }
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
direct_log(:info) { log_header("COMMAND") }
|
239
|
-
direct_log(:info) { "#{command}\n" }
|
240
|
-
direct_log(:info) { log_header("OPENED") }
|
241
|
-
|
242
|
-
chan.exec(command) do |ch, success|
|
243
|
-
success or log_and_raise(SSHError, "Could not execute '#{command}'.")
|
244
|
-
|
245
|
-
ch.on_data do |c, data|
|
246
|
-
if !stdout_header
|
247
|
-
direct_log(:info) { log_header("STDOUT") }
|
248
|
-
stdout_header = true
|
249
|
-
stderr_header = false
|
250
|
-
end
|
251
|
-
direct_log(:info) { data }
|
252
|
-
|
253
|
-
options.ui.stdout.print(data) unless options.silence
|
254
|
-
output += data
|
255
|
-
end
|
256
|
-
|
257
|
-
ch.on_extended_data do |c, type, data|
|
258
|
-
if !stderr_header
|
259
|
-
direct_log(:warn) { log_header("STDERR") }
|
260
|
-
stderr_header = true
|
261
|
-
stdout_header = false
|
262
|
-
end
|
263
|
-
direct_log(:warn) { data }
|
264
|
-
|
265
|
-
options.ui.stderr.print(data) unless options.silence
|
266
|
-
output += data
|
267
|
-
end
|
268
|
-
|
269
|
-
ch.on_request("exit-status") do |ch, data|
|
270
|
-
exit_code = data.read_long
|
271
|
-
end
|
272
|
-
|
273
|
-
ch.on_request("exit-signal") do |ch, data|
|
274
|
-
exit_signal = data.read_long
|
275
|
-
end
|
276
|
-
|
277
|
-
ch.on_open_failed do |c, code, desc|
|
278
|
-
options.ui.logger.fatal { "Open failed! (#{code.inspect} - #{desc.inspect})" }
|
279
|
-
end
|
280
|
-
|
281
|
-
end
|
282
|
-
end
|
283
|
-
channel.wait
|
284
|
-
|
285
|
-
direct_log(:info) { log_header("CLOSED") }
|
286
|
-
options.ui.logger.debug { "Channel closed." }
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
rescue Timeout::Error => e
|
291
|
-
direct_log(:fatal) { log_header("TIMEOUT") }
|
292
|
-
log_and_raise(SSHError, "Session timed out after #{options.timeout} seconds!")
|
293
|
-
end
|
294
|
-
|
295
|
-
message = [
|
296
|
-
"exit_code=#{exit_code}",
|
297
|
-
(exit_signal.nil? ? nil : "exit_signal=#{exit_signal} (#{EXIT_SIGNALS[exit_signal]})")
|
298
|
-
].compact.join(", ")
|
299
|
-
|
300
|
-
options.ui.logger.debug { message }
|
301
|
-
|
302
|
-
if !options.ignore_exit_status && (exit_code != options.exit_code)
|
303
|
-
log_and_raise(SSHError, message)
|
304
|
-
end
|
305
|
-
OpenStruct.new(:command => command, :output => output, :exit_code => exit_code, :exit_signal => exit_signal)
|
306
|
-
end
|
307
|
-
|
308
|
-
# Uploads a local file to a remote host.
|
309
|
-
#
|
310
|
-
# @param [String] local The local file/path you wish to upload from.
|
311
|
-
# @param [String] remote The remote file/path you with to upload to.
|
312
|
-
#
|
313
|
-
# @example Upload a file:
|
314
|
-
# $logger = ZTK::Logger.new(STDOUT)
|
315
|
-
# ssh = ZTK::SSH.new
|
316
|
-
# ssh.config do |config|
|
317
|
-
# config.user = ENV["USER"]
|
318
|
-
# config.host_name = "127.0.0.1"
|
319
|
-
# end
|
320
|
-
# local = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
321
|
-
# remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
322
|
-
# ssh.upload(local, remote)
|
323
|
-
def upload(local, remote)
|
324
|
-
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
325
|
-
config.ui.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
|
326
|
-
|
327
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
328
|
-
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
329
|
-
sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
|
330
|
-
case event
|
331
|
-
when :open
|
332
|
-
config.ui.logger.debug { "upload(#{args[0].local} -> #{args[0].remote})" }
|
333
|
-
when :close
|
334
|
-
config.ui.logger.debug { "close(#{args[0].remote})" }
|
335
|
-
when :mkdir
|
336
|
-
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
337
|
-
when :put
|
338
|
-
config.ui.logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
339
|
-
when :finish
|
340
|
-
config.ui.logger.debug { "finish" }
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
true
|
346
|
-
end
|
347
|
-
|
348
|
-
# Downloads a remote file to the local host.
|
349
|
-
#
|
350
|
-
# @param [String] remote The remote file/path you with to download from.
|
351
|
-
# @param [String] local The local file/path you wish to download to.
|
352
|
-
#
|
353
|
-
# @example Download a file:
|
354
|
-
# $logger = ZTK::Logger.new(STDOUT)
|
355
|
-
# ssh = ZTK::SSH.new
|
356
|
-
# ssh.config do |config|
|
357
|
-
# config.user = ENV["USER"]
|
358
|
-
# config.host_name = "127.0.0.1"
|
359
|
-
# end
|
360
|
-
# local = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
361
|
-
# remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
362
|
-
# ssh.download(remote, local)
|
363
|
-
def download(remote, local)
|
364
|
-
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
365
|
-
config.ui.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
|
366
|
-
|
367
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
368
|
-
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
369
|
-
sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
|
370
|
-
case event
|
371
|
-
when :open
|
372
|
-
config.ui.logger.debug { "download(#{args[0].remote} -> #{args[0].local})" }
|
373
|
-
when :close
|
374
|
-
config.ui.logger.debug { "close(#{args[0].local})" }
|
375
|
-
when :mkdir
|
376
|
-
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
377
|
-
when :get
|
378
|
-
config.ui.logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
379
|
-
when :finish
|
380
|
-
config.ui.logger.debug { "finish" }
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
true
|
386
|
-
end
|
387
|
-
|
388
199
|
|
389
200
|
private
|
390
201
|
|
391
|
-
# Builds our SSH console command.
|
392
|
-
def console_command
|
393
|
-
command = [ "/usr/bin/env ssh" ]
|
394
|
-
command << [ "-q" ]
|
395
|
-
command << [ "-4" ]
|
396
|
-
command << [ "-x" ]
|
397
|
-
command << [ "-a" ]
|
398
|
-
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
399
|
-
command << [ "-o", "StrictHostKeyChecking=no" ]
|
400
|
-
command << [ "-o", "KeepAlive=yes" ]
|
401
|
-
command << [ "-o", "ServerAliveInterval=60" ]
|
402
|
-
command << [ "-o", %(ProxyCommand="#{proxy_command}") ] if config.proxy_host_name
|
403
|
-
command << [ "-i", config.keys ] if config.keys
|
404
|
-
command << [ "-p", config.port ] if config.port
|
405
|
-
# command << [ "-vv" ]
|
406
|
-
command << "#{config.user}@#{config.host_name}"
|
407
|
-
command = command.flatten.compact.join(' ')
|
408
|
-
config.ui.logger.debug { "console_command(#{command.inspect})" }
|
409
|
-
command
|
410
|
-
end
|
411
|
-
|
412
|
-
# Builds our SSH proxy command.
|
413
|
-
def proxy_command
|
414
|
-
!config.proxy_user and log_and_raise(SSHError, "You must specify an proxy user in order to SSH proxy.")
|
415
|
-
!config.proxy_host_name and log_and_raise(SSHError, "You must specify an proxy host_name in order to SSH proxy.")
|
416
|
-
|
417
|
-
command = ["/usr/bin/env ssh"]
|
418
|
-
command << [ "-q" ]
|
419
|
-
command << [ "-4" ]
|
420
|
-
command << [ "-x" ]
|
421
|
-
command << [ "-a" ]
|
422
|
-
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
423
|
-
command << [ "-o", "StrictHostKeyChecking=no" ]
|
424
|
-
command << [ "-o", "KeepAlive=yes" ]
|
425
|
-
command << [ "-o", "ServerAliveInterval=60" ]
|
426
|
-
command << [ "-i", config.proxy_keys ] if config.proxy_keys
|
427
|
-
command << [ "-p", config.proxy_port ] if config.proxy_port
|
428
|
-
# command << [ "-vv" ]
|
429
|
-
command << "#{config.proxy_user}@#{config.proxy_host_name}"
|
430
|
-
command << "'/usr/bin/env nc %h %p'"
|
431
|
-
command = command.flatten.compact.join(' ')
|
432
|
-
config.ui.logger.debug { "proxy_command(#{command.inspect})" }
|
433
|
-
command
|
434
|
-
end
|
435
|
-
|
436
202
|
# Builds our SSH options hash.
|
437
203
|
def ssh_options
|
438
204
|
options = {}
|
data/lib/ztk/template.rb
CHANGED
@@ -57,6 +57,29 @@ module ZTK
|
|
57
57
|
render_template(load_template(template), context)
|
58
58
|
end
|
59
59
|
|
60
|
+
# Renders a "DO NOT EDIT" notice for placement in generated files.
|
61
|
+
#
|
62
|
+
# @param [Hash] options Options hash.
|
63
|
+
# @option options [String] :message An optional message to display in the notice.
|
64
|
+
# @option options [String] :char The comment character; defaults to '#'.
|
65
|
+
#
|
66
|
+
# @return [String] The rendered noticed.
|
67
|
+
def do_not_edit_notice(options={})
|
68
|
+
message = options[:message]
|
69
|
+
char = (options[:char] || '#')
|
70
|
+
notice = Array.new
|
71
|
+
|
72
|
+
notice << char
|
73
|
+
notice << "#{char} WARNING: AUTOMATICALLY GENERATED FILE; DO NOT EDIT!"
|
74
|
+
notice << char
|
75
|
+
notice << "#{char} #{message}" if !message.nil?
|
76
|
+
notice << char
|
77
|
+
notice << "#{char} Generated @ #{Time.now.utc}"
|
78
|
+
notice << char
|
79
|
+
|
80
|
+
notice.join("\n")
|
81
|
+
end
|
82
|
+
|
60
83
|
|
61
84
|
private
|
62
85
|
|
data/lib/ztk/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ztk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: erubis
|
@@ -200,6 +200,11 @@ files:
|
|
200
200
|
- lib/ztk/rescue_retry.rb
|
201
201
|
- lib/ztk/spinner.rb
|
202
202
|
- lib/ztk/ssh.rb
|
203
|
+
- lib/ztk/ssh/bootstrap.rb
|
204
|
+
- lib/ztk/ssh/command.rb
|
205
|
+
- lib/ztk/ssh/download.rb
|
206
|
+
- lib/ztk/ssh/exec.rb
|
207
|
+
- lib/ztk/ssh/upload.rb
|
203
208
|
- lib/ztk/tcp_socket_check.rb
|
204
209
|
- lib/ztk/template.rb
|
205
210
|
- lib/ztk/ui.rb
|
@@ -240,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
240
245
|
version: '0'
|
241
246
|
segments:
|
242
247
|
- 0
|
243
|
-
hash:
|
248
|
+
hash: 1781664085234422223
|
244
249
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
245
250
|
none: false
|
246
251
|
requirements:
|
@@ -249,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
254
|
version: '0'
|
250
255
|
segments:
|
251
256
|
- 0
|
252
|
-
hash:
|
257
|
+
hash: 1781664085234422223
|
253
258
|
requirements: []
|
254
259
|
rubyforge_project:
|
255
260
|
rubygems_version: 1.8.25
|