ztk 1.6.2 → 1.6.3
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/Gemfile +1 -0
- data/Rakefile +1 -1
- data/lib/ztk/command.rb +14 -160
- data/lib/ztk/command/download.rb +16 -0
- data/lib/ztk/command/exec.rb +139 -0
- data/lib/ztk/command/private.rb +26 -0
- data/lib/ztk/command/upload.rb +16 -0
- data/lib/ztk/locator.rb +13 -0
- data/lib/ztk/report.rb +11 -155
- data/lib/ztk/report/list.rb +67 -0
- data/lib/ztk/report/private.rb +44 -0
- data/lib/ztk/report/spreadsheet.rb +72 -0
- data/lib/ztk/ssh.rb +20 -114
- data/lib/ztk/ssh/bootstrap.rb +1 -1
- data/lib/ztk/ssh/console.rb +28 -0
- data/lib/ztk/ssh/core.rb +37 -0
- data/lib/ztk/ssh/download.rb +2 -2
- data/lib/ztk/ssh/file.rb +1 -1
- data/lib/ztk/ssh/private.rb +67 -0
- data/lib/ztk/ssh/upload.rb +2 -2
- data/lib/ztk/ui.rb +14 -0
- data/lib/ztk/version.rb +1 -1
- data/spec/ztk/logger_spec.rb +1 -1
- data/spec/ztk/ssh_spec.rb +8 -8
- metadata +18 -2
@@ -0,0 +1,67 @@
|
|
1
|
+
module ZTK
|
2
|
+
class Report
|
3
|
+
|
4
|
+
# Report List Functionality
|
5
|
+
module List
|
6
|
+
|
7
|
+
# Displays data in a key-value list style.
|
8
|
+
#
|
9
|
+
# +-------------------------------------------------------------------+
|
10
|
+
# | PROVIDER: Cucumber::Chef::Provider::Vagrant |
|
11
|
+
# | ID: default |
|
12
|
+
# | STATE: aborted |
|
13
|
+
# | USERNAME: vagrant |
|
14
|
+
# | IP ADDRESS: 127.0.0.1 |
|
15
|
+
# | PORT: 2222 |
|
16
|
+
# | CHEF-SERVER API: http://127.0.0.1:4000 |
|
17
|
+
# | CHEF-SERVER WEBUI: http://127.0.0.1:4040 |
|
18
|
+
# | CHEF-SERVER DEFAULT USER: admin |
|
19
|
+
# | CHEF-SERVER DEFAULT PASSWORD: p@ssw0rd1 |
|
20
|
+
# +-------------------------------------------------------------------+
|
21
|
+
#
|
22
|
+
# @param [Array<Object>,Object] dataset A single object or an array of
|
23
|
+
# objects for which we want to generate a report
|
24
|
+
# @param [Array] headers An array of headers used for ordering the output.
|
25
|
+
# @return [OpenStruct]
|
26
|
+
def list(dataset, headers, &block)
|
27
|
+
!block_given? and log_and_raise(ReportError, "You must supply a block!")
|
28
|
+
headers.nil? and log_and_raise(ReportError, "Headers can not be nil!")
|
29
|
+
dataset.nil? and log_and_raise(ReportError, "Dataset can not be nil!")
|
30
|
+
|
31
|
+
rows = Array.new
|
32
|
+
max_lengths = OpenStruct.new
|
33
|
+
headers = headers.map(&:to_s).map(&:downcase).map(&:to_sym)
|
34
|
+
|
35
|
+
if dataset.is_a?(Array)
|
36
|
+
dataset.each do |data|
|
37
|
+
rows << block.call(data)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
rows << block.call(dataset)
|
41
|
+
end
|
42
|
+
rows.compact!
|
43
|
+
|
44
|
+
if rows.count > 0
|
45
|
+
max_key_length = headers.collect{ |header| header.to_s.length }.max
|
46
|
+
max_value_length = rows.collect{ |row| headers.collect{ |header| row.send(:table)[header].to_s.length }.max }.max
|
47
|
+
|
48
|
+
width = (max_key_length + max_value_length + 2 + 2 + 2)
|
49
|
+
|
50
|
+
rows.each do |row|
|
51
|
+
config.ui.stdout.puts("+#{"-" * width}+")
|
52
|
+
headers.each do |header|
|
53
|
+
entry_line = format_entry(header, max_key_length, row.send(:table)[header], max_value_length)
|
54
|
+
config.ui.stdout.puts(entry_line)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
config.ui.stdout.puts("+#{"-" * width}+")
|
58
|
+
OpenStruct.new(:rows => rows, :max_key_length => max_key_length, :max_value_length => max_value_length, :width => width)
|
59
|
+
else
|
60
|
+
OpenStruct.new(:rows => rows, :max_key_length => 0, :max_value_length => 0, :width => 0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ZTK
|
2
|
+
class Report
|
3
|
+
|
4
|
+
# Report Private Functionality
|
5
|
+
module Private
|
6
|
+
|
7
|
+
def max_spreadsheet_lengths(headers, rows)
|
8
|
+
max_lengths = OpenStruct.new
|
9
|
+
headers.each do |header|
|
10
|
+
collection = [header, rows.collect{|r| r.send(:table)[header] } ].flatten
|
11
|
+
maximum = collection.map(&:to_s).map(&:length).max
|
12
|
+
max_lengths.send(:table)[header] = maximum
|
13
|
+
end
|
14
|
+
|
15
|
+
max_lengths
|
16
|
+
end
|
17
|
+
|
18
|
+
def calculate_spreadsheet_width(headers, max_lengths)
|
19
|
+
header_lengths = ((headers.count * 3) - 3)
|
20
|
+
max_length = max_lengths.send(:table).values.reduce(:+)
|
21
|
+
(2 + max_length + header_lengths + 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_header(headers, lengths)
|
25
|
+
line = headers.collect do |header|
|
26
|
+
"-" * lengths.send(:table)[header]
|
27
|
+
end
|
28
|
+
|
29
|
+
["+-", line.join("-+-"), "-+"].join.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_row(*args)
|
33
|
+
spacer = " "
|
34
|
+
[spacer, args, spacer].flatten.join(" | ").strip
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_entry(key, key_length, value, value_length)
|
38
|
+
"| %#{key_length}s: %-#{value_length}s |" % [key.to_s.upcase, value.to_s]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ZTK
|
2
|
+
class Report
|
3
|
+
|
4
|
+
# Report Spreadsheet Functionality
|
5
|
+
module Spreadsheet
|
6
|
+
|
7
|
+
# Displays data in a spreadsheet style.
|
8
|
+
#
|
9
|
+
# +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
|
10
|
+
# | NAME | ALIVE | ARCH | DISTRO | IP | MAC | CHEF VERSION | PERSIST |
|
11
|
+
# +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
|
12
|
+
# | sudo | false | amd64 | ubuntu | 192.168.99.110 | 00:00:5e:34:d6:aa | N/A | true |
|
13
|
+
# | timezone | false | amd64 | ubuntu | 192.168.122.47 | 00:00:5e:92:d7:f6 | N/A | true |
|
14
|
+
# | chef-client | false | amd64 | ubuntu | 192.168.159.98 | 00:00:5e:c7:ce:26 | N/A | true |
|
15
|
+
# | users | false | amd64 | ubuntu | 192.168.7.78 | 00:00:5e:89:f9:50 | N/A | true |
|
16
|
+
# +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
|
17
|
+
#
|
18
|
+
# @param [Array<Object>,Object] dataset A single object or an array of
|
19
|
+
# objects for which we want to generate a report
|
20
|
+
# @param [Array] headers An array of headers used for ordering the output.
|
21
|
+
# @return [OpenStruct]
|
22
|
+
def spreadsheet(dataset, headers, &block)
|
23
|
+
!block_given? and log_and_raise(ReportError, "You must supply a block!")
|
24
|
+
headers.nil? and log_and_raise(ReportError, "Headers can not be nil!")
|
25
|
+
dataset.nil? and log_and_raise(ReportError, "Dataset can not be nil!")
|
26
|
+
|
27
|
+
rows = Array.new
|
28
|
+
max_lengths = OpenStruct.new
|
29
|
+
headers = headers.map(&:to_s).map(&:downcase).map(&:to_sym)
|
30
|
+
|
31
|
+
if dataset.is_a?(Array)
|
32
|
+
dataset.each do |data|
|
33
|
+
rows << block.call(data)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
rows << block.call(dataset)
|
37
|
+
end
|
38
|
+
rows.compact!
|
39
|
+
|
40
|
+
if rows.count > 0
|
41
|
+
max_lengths = max_spreadsheet_lengths(headers, rows)
|
42
|
+
header_line = headers.collect { |header| "%-#{max_lengths.send(:table)[header]}s" % header.to_s.upcase }
|
43
|
+
header_line = format_row(header_line)
|
44
|
+
|
45
|
+
config.ui.stdout.puts(format_header(headers, max_lengths))
|
46
|
+
config.ui.stdout.puts(header_line)
|
47
|
+
config.ui.stdout.puts(format_header(headers, max_lengths))
|
48
|
+
|
49
|
+
rows.each do |row|
|
50
|
+
row_line = headers.collect do |header|
|
51
|
+
header_length = max_lengths.send(:table)[header]
|
52
|
+
content = row.send(:table)[header]
|
53
|
+
|
54
|
+
"%-#{header_length}s" % content
|
55
|
+
end
|
56
|
+
|
57
|
+
row_line = format_row(row_line)
|
58
|
+
config.ui.stdout.puts(row_line)
|
59
|
+
end
|
60
|
+
|
61
|
+
config.ui.stdout.puts(format_header(headers, max_lengths))
|
62
|
+
OpenStruct.new(:rows => rows, :max_lengths => max_lengths, :width => calculate_spreadsheet_width(headers, max_lengths))
|
63
|
+
else
|
64
|
+
OpenStruct.new(:rows => rows, :max_lengths => 0, :width => 0)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/lib/ztk/ssh.rb
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
require 'net/ssh'
|
3
|
-
require 'net/ssh/proxy/command'
|
4
|
-
require 'net/sftp'
|
5
|
-
|
6
1
|
module ZTK
|
7
2
|
|
8
3
|
# SSH Error Class
|
@@ -24,7 +19,7 @@ module ZTK
|
|
24
19
|
#
|
25
20
|
# If you want to specify SSH options you can:
|
26
21
|
#
|
27
|
-
# keys = File.expand_path(File.join(
|
22
|
+
# keys = File.expand_path(File.join(Dir.home, '.ssh', 'id_rsa'))
|
28
23
|
# ssh = ZTK::SSH.new(:host_name => '127.0.0.1', :user => ENV['USER'], :keys => keys)
|
29
24
|
#
|
30
25
|
# = Configuration Examples:
|
@@ -41,8 +36,8 @@ module ZTK
|
|
41
36
|
# Specify an identity file:
|
42
37
|
#
|
43
38
|
# ssh.config do |config|
|
44
|
-
# config.keys = File.expand_path(File.join(
|
45
|
-
# config.proxy_keys = File.expand_path(File.join(
|
39
|
+
# config.keys = File.expand_path(File.join(Dir.home, '.ssh', 'id_rsa'))
|
40
|
+
# config.proxy_keys = File.expand_path(File.join(Dir.home, '.ssh', 'id_rsa'))
|
46
41
|
# end
|
47
42
|
#
|
48
43
|
# Specify a timeout:
|
@@ -65,6 +60,11 @@ module ZTK
|
|
65
60
|
#
|
66
61
|
# @author Zachary Patten <zachary AT jovelabs DOT com>
|
67
62
|
class SSH < ZTK::Base
|
63
|
+
require 'ostruct'
|
64
|
+
require 'net/ssh'
|
65
|
+
require 'net/ssh/proxy/command'
|
66
|
+
require 'net/sftp'
|
67
|
+
|
68
68
|
# Exit Signal Mappings
|
69
69
|
EXIT_SIGNALS = {
|
70
70
|
1 => "SIGHUP",
|
@@ -96,15 +96,20 @@ module ZTK
|
|
96
96
|
27 => "SIGPROF"
|
97
97
|
}
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
99
|
+
require 'ztk/ssh/bootstrap'
|
100
|
+
require 'ztk/ssh/command'
|
101
|
+
require 'ztk/ssh/console'
|
102
|
+
require 'ztk/ssh/core'
|
103
|
+
require 'ztk/ssh/download'
|
104
|
+
require 'ztk/ssh/exec'
|
105
|
+
require 'ztk/ssh/file'
|
106
|
+
require 'ztk/ssh/private'
|
107
|
+
require 'ztk/ssh/upload'
|
105
108
|
|
106
109
|
include ZTK::SSH::Bootstrap
|
107
110
|
include ZTK::SSH::Command
|
111
|
+
include ZTK::SSH::Console
|
112
|
+
include ZTK::SSH::Core
|
108
113
|
include ZTK::SSH::Download
|
109
114
|
include ZTK::SSH::Exec
|
110
115
|
include ZTK::SSH::File
|
@@ -155,113 +160,14 @@ module ZTK
|
|
155
160
|
:exit_code => 0,
|
156
161
|
:silence => false
|
157
162
|
}.merge(configuration))
|
158
|
-
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
159
|
-
end
|
160
|
-
|
161
|
-
# Starts an SSH session. Can also be used to get the Net::SSH object.
|
162
|
-
#
|
163
|
-
# Primarily used internally.
|
164
|
-
def ssh
|
165
|
-
@ssh ||= Net::SSH.start(config.host_name, config.user, ssh_options)
|
166
|
-
end
|
167
|
-
|
168
|
-
# Starts an SFTP session. Can also be used to get the Net::SFTP object.
|
169
|
-
#
|
170
|
-
# Primarily used internally.
|
171
|
-
def sftp
|
172
|
-
@sftp ||= Net::SFTP.start(config.host_name, config.user, ssh_options)
|
173
|
-
end
|
174
|
-
|
175
|
-
# Close our session gracefully.
|
176
|
-
def close
|
177
|
-
config.ui.logger.debug { "close" }
|
178
|
-
ssh and !ssh.closed? and ssh.close
|
179
|
-
end
|
180
|
-
|
181
|
-
# The on_retry method we'll use with the RescueRetry class.
|
182
|
-
def on_retry(exception)
|
183
|
-
close
|
184
|
-
@ssh = nil
|
185
|
-
@sftp = nil
|
186
|
-
end
|
187
163
|
|
188
|
-
# Launches an SSH console, replacing the current process with the console
|
189
|
-
# process.
|
190
|
-
#
|
191
|
-
# @example Launch a console:
|
192
|
-
# ssh = ZTK::SSH.new
|
193
|
-
# ssh.config do |config|
|
194
|
-
# config.user = ENV["USER"]
|
195
|
-
# config.host_name = "127.0.0.1"
|
196
|
-
# end
|
197
|
-
# ssh.console
|
198
|
-
def console
|
199
164
|
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
200
|
-
config.ui.logger.info { "console(#{console_command.inspect})" }
|
201
|
-
|
202
|
-
config.ui.logger.fatal { "REPLACING CURRENT PROCESS - GOODBYE!" }
|
203
|
-
Kernel.exec(console_command)
|
204
165
|
end
|
205
166
|
|
206
167
|
|
207
168
|
private
|
208
169
|
|
209
|
-
|
210
|
-
def ssh_options
|
211
|
-
options = {}
|
212
|
-
|
213
|
-
# These are plainly documented on the Net::SSH config class.
|
214
|
-
options.merge!(:encryption => config.encryption) if config.encryption
|
215
|
-
options.merge!(:compression => config.compression) if config.compression
|
216
|
-
options.merge!(:compression_level => config.compression_level) if config.compression_level
|
217
|
-
options.merge!(:timeout => config.timeout) if config.timeout
|
218
|
-
options.merge!(:forward_agent => config.forward_agent) if config.forward_agent
|
219
|
-
options.merge!(:global_known_hosts_file => config.global_known_hosts_file) if config.global_known_hosts_file
|
220
|
-
options.merge!(:auth_methods => config.auth_methods) if config.auth_methods
|
221
|
-
options.merge!(:host_key => config.host_key) if config.host_key
|
222
|
-
options.merge!(:host_key_alias => config.host_key_alias) if config.host_key_alias
|
223
|
-
options.merge!(:host_name => config.host_name) if config.host_name
|
224
|
-
options.merge!(:keys => config.keys) if config.keys
|
225
|
-
options.merge!(:keys_only => config.keys_only) if config.keys_only
|
226
|
-
options.merge!(:hmac => config.hmac) if config.hmac
|
227
|
-
options.merge!(:port => config.port) if config.port
|
228
|
-
options.merge!(:proxy => Net::SSH::Proxy::Command.new(proxy_command)) if config.proxy_host_name
|
229
|
-
options.merge!(:rekey_limit => config.rekey_limit) if config.rekey_limit
|
230
|
-
options.merge!(:user => config.user) if config.user
|
231
|
-
options.merge!(:user_known_hosts_file => config.user_known_hosts_file) if config.user_known_hosts_file
|
232
|
-
|
233
|
-
# This is not plainly documented on the Net::SSH config class.
|
234
|
-
options.merge!(:password => config.password) if config.password
|
235
|
-
|
236
|
-
config.ui.logger.debug { "ssh_options(#{options.inspect})" }
|
237
|
-
options
|
238
|
-
end
|
239
|
-
|
240
|
-
# Builds a human readable tag about our connection. Used for internal
|
241
|
-
# logging purposes.
|
242
|
-
def tag
|
243
|
-
tags = Array.new
|
244
|
-
|
245
|
-
if config.proxy_host_name
|
246
|
-
proxy_user_host = "#{config.proxy_user}@#{config.proxy_host_name}"
|
247
|
-
proxy_port = (config.proxy_port ? ":#{config.proxy_port}" : nil)
|
248
|
-
tags << [proxy_user_host, proxy_port].compact.join
|
249
|
-
tags << " >>> "
|
250
|
-
end
|
251
|
-
|
252
|
-
user_host = "#{config.user}@#{config.host_name}"
|
253
|
-
port = (config.port ? ":#{config.port}" : nil)
|
254
|
-
tags << [user_host, port].compact.join
|
255
|
-
|
256
|
-
tags.join.strip
|
257
|
-
end
|
258
|
-
|
259
|
-
def log_header(what)
|
260
|
-
count = 8
|
261
|
-
sep = ("=" * count)
|
262
|
-
header = [sep, "[ #{what} ]", sep, "[ #{tag} ]", sep, "[ #{what} ]", sep].join
|
263
|
-
"#{header}\n"
|
264
|
-
end
|
170
|
+
include ZTK::SSH::Private
|
265
171
|
|
266
172
|
end
|
267
173
|
|
data/lib/ztk/ssh/bootstrap.rb
CHANGED
@@ -32,7 +32,7 @@ module ZTK
|
|
32
32
|
tempfile = Tempfile.new("bootstrap")
|
33
33
|
|
34
34
|
local_tempfile = tempfile.path
|
35
|
-
remote_tempfile = ::File.join(
|
35
|
+
remote_tempfile = ::File.join(ZTK::Locator.root, "tmp", ::File.basename(local_tempfile))
|
36
36
|
|
37
37
|
::File.open(local_tempfile, 'w') do |file|
|
38
38
|
file.puts(content)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ZTK
|
2
|
+
class SSH
|
3
|
+
|
4
|
+
# SSH Console Functionality
|
5
|
+
module Console
|
6
|
+
|
7
|
+
# Launches an SSH console, replacing the current process with the console
|
8
|
+
# process.
|
9
|
+
#
|
10
|
+
# @example Launch a console:
|
11
|
+
# ssh = ZTK::SSH.new
|
12
|
+
# ssh.config do |config|
|
13
|
+
# config.user = ENV["USER"]
|
14
|
+
# config.host_name = "127.0.0.1"
|
15
|
+
# end
|
16
|
+
# ssh.console
|
17
|
+
def console
|
18
|
+
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
19
|
+
config.ui.logger.info { "console(#{console_command.inspect})" }
|
20
|
+
|
21
|
+
config.ui.logger.fatal { "REPLACING CURRENT PROCESS - GOODBYE!" }
|
22
|
+
Kernel.exec(console_command)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/ztk/ssh/core.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module ZTK
|
2
|
+
class SSH
|
3
|
+
|
4
|
+
# SSH Core Functionality
|
5
|
+
module Core
|
6
|
+
|
7
|
+
# Starts an SSH session. Can also be used to get the Net::SSH object.
|
8
|
+
#
|
9
|
+
# Primarily used internally.
|
10
|
+
def ssh
|
11
|
+
@ssh ||= Net::SSH.start(config.host_name, config.user, ssh_options)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Starts an SFTP session. Can also be used to get the Net::SFTP object.
|
15
|
+
#
|
16
|
+
# Primarily used internally.
|
17
|
+
def sftp
|
18
|
+
@sftp ||= Net::SFTP.start(config.host_name, config.user, ssh_options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Close our session gracefully.
|
22
|
+
def close
|
23
|
+
config.ui.logger.debug { "close" }
|
24
|
+
ssh and !ssh.closed? and ssh.close
|
25
|
+
end
|
26
|
+
|
27
|
+
# The on_retry method we'll use with the RescueRetry class.
|
28
|
+
def on_retry(exception)
|
29
|
+
close
|
30
|
+
@ssh = nil
|
31
|
+
@sftp = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/ztk/ssh/download.rb
CHANGED
@@ -21,8 +21,8 @@ module ZTK
|
|
21
21
|
# config.user = ENV["USER"]
|
22
22
|
# config.host_name = "127.0.0.1"
|
23
23
|
# end
|
24
|
-
# local = File.expand_path(File.join("
|
25
|
-
# remote = File.expand_path(File.join(
|
24
|
+
# local = File.expand_path(File.join(ZTK::Locator.root, "tmp", "id_rsa.pub"))
|
25
|
+
# remote = File.expand_path(File.join(Dir.home, ".ssh", "id_rsa.pub"))
|
26
26
|
# ssh.download(remote, local)
|
27
27
|
def download(remote, local, options={})
|
28
28
|
options = {:recursive => ::File.directory?(local) }.merge(options)
|