db_sucker 3.0.0

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +193 -0
  7. data/Rakefile +1 -0
  8. data/VERSION +1 -0
  9. data/bin/db_sucker +12 -0
  10. data/bin/db_sucker.sh +14 -0
  11. data/db_sucker.gemspec +29 -0
  12. data/doc/config_example.rb +53 -0
  13. data/doc/container_example.yml +150 -0
  14. data/lib/db_sucker/adapters/mysql2.rb +103 -0
  15. data/lib/db_sucker/application/colorize.rb +28 -0
  16. data/lib/db_sucker/application/container/accessors.rb +60 -0
  17. data/lib/db_sucker/application/container/ssh.rb +225 -0
  18. data/lib/db_sucker/application/container/validations.rb +53 -0
  19. data/lib/db_sucker/application/container/variation/accessors.rb +45 -0
  20. data/lib/db_sucker/application/container/variation/helpers.rb +21 -0
  21. data/lib/db_sucker/application/container/variation/worker_api.rb +65 -0
  22. data/lib/db_sucker/application/container/variation.rb +60 -0
  23. data/lib/db_sucker/application/container.rb +70 -0
  24. data/lib/db_sucker/application/container_collection.rb +47 -0
  25. data/lib/db_sucker/application/core.rb +222 -0
  26. data/lib/db_sucker/application/dispatch.rb +364 -0
  27. data/lib/db_sucker/application/evented_resultset.rb +149 -0
  28. data/lib/db_sucker/application/fake_channel.rb +22 -0
  29. data/lib/db_sucker/application/output_helper.rb +197 -0
  30. data/lib/db_sucker/application/sklaven_treiber/log_spool.rb +57 -0
  31. data/lib/db_sucker/application/sklaven_treiber/worker/accessors.rb +105 -0
  32. data/lib/db_sucker/application/sklaven_treiber/worker/core.rb +168 -0
  33. data/lib/db_sucker/application/sklaven_treiber/worker/helpers.rb +144 -0
  34. data/lib/db_sucker/application/sklaven_treiber/worker/io/base.rb +240 -0
  35. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_copy.rb +81 -0
  36. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_gunzip.rb +58 -0
  37. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_import_sql.rb +80 -0
  38. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_shasum.rb +49 -0
  39. data/lib/db_sucker/application/sklaven_treiber/worker/io/pv_wrapper.rb +73 -0
  40. data/lib/db_sucker/application/sklaven_treiber/worker/io/sftp_download.rb +57 -0
  41. data/lib/db_sucker/application/sklaven_treiber/worker/io/throughput.rb +219 -0
  42. data/lib/db_sucker/application/sklaven_treiber/worker/routines.rb +313 -0
  43. data/lib/db_sucker/application/sklaven_treiber/worker.rb +48 -0
  44. data/lib/db_sucker/application/sklaven_treiber.rb +281 -0
  45. data/lib/db_sucker/application/slot_pool.rb +137 -0
  46. data/lib/db_sucker/application/tie.rb +25 -0
  47. data/lib/db_sucker/application/window/core.rb +185 -0
  48. data/lib/db_sucker/application/window/dialog.rb +142 -0
  49. data/lib/db_sucker/application/window/keypad/core.rb +85 -0
  50. data/lib/db_sucker/application/window/keypad.rb +174 -0
  51. data/lib/db_sucker/application/window/prompt.rb +124 -0
  52. data/lib/db_sucker/application/window.rb +329 -0
  53. data/lib/db_sucker/application.rb +168 -0
  54. data/lib/db_sucker/patches/beta-warning.rb +374 -0
  55. data/lib/db_sucker/patches/developer.rb +29 -0
  56. data/lib/db_sucker/patches/net-sftp.rb +20 -0
  57. data/lib/db_sucker/patches/thread-count.rb +30 -0
  58. data/lib/db_sucker/version.rb +4 -0
  59. data/lib/db_sucker.rb +81 -0
  60. metadata +217 -0
@@ -0,0 +1,222 @@
1
+ module DbSucker
2
+ class Application
3
+ module Core
4
+ def sandboxed &block
5
+ block.call
6
+ rescue StandardError => ex
7
+ e = ["#{ex.class}: #{ex.message}"]
8
+ ex.backtrace.each{|l| e << "\t#{l}" }
9
+ error(e.join("\n"))
10
+ return false
11
+ end
12
+
13
+ def notify_exception label_or_ex, ex = nil
14
+ error [].tap{|e|
15
+ e << label_or_ex if ex
16
+ e << "#{"\t" if ex}#{ex ? ex.class : label_or_ex.class}: #{ex ? ex.message : label_or_ex.message}"
17
+ (ex ? ex : label_or_ex).backtrace.each{|l| e << "\t#{" " if ex}#{l}" }
18
+ }.join("\n")
19
+ end
20
+
21
+ def dump_file ctx, open = false, &block
22
+ "#{core_tmp_path}/#{ctx}-#{Time.current.to_i}-#{uniqid}.log".tap do |df|
23
+ FileUtils.mkdir_p(File.dirname(df))
24
+ File.open(df, "wb", &block) if block
25
+ if block && open && sdf = Shellwords.shellescape(df)
26
+ fork { exec("#{opts[:core_dump_editor]} #{sdf} ; rm #{sdf}") }
27
+ end
28
+ end
29
+ end
30
+
31
+ def dump_core
32
+ dump_file "coredump", true do |f|
33
+ # thread info
34
+ f.puts "#{Thread.list.length} threads:\n"
35
+ Thread.list.each do |thr|
36
+ f.puts "#{thr.inspect}"
37
+ f.puts " iType: #{thr == Thread.main ? :main_thread : thr[:itype] || :uncategorized}"
38
+ f.puts " Aborts?: #{thr.abort_on_exception}"
39
+ f.puts " Priority: #{thr.priority}"
40
+ f.puts " T-Vars: #{thr.thread_variables.inspect}"
41
+ thr.thread_variables.each {|k| f.puts " #{k} => #{thr.thread_variable(k)}" }
42
+ f.puts " F-Vars: #{thr.keys.inspect}"
43
+ thr.keys.each {|k| f.puts " #{k} => #{thr[k]}" }
44
+ f.puts " Backtrace: #{thr.backtrace.length}"
45
+ thr.backtrace.each {|l| f.puts " #{l}" }
46
+ #f.puts " Caller: #{thr.backtrace_locations.try(:length) || "unknown"}"
47
+ #thr.backtrace_locations.each {|l| f.puts " #{l}" } if thr.backtrace_locations
48
+ f.puts
49
+ end
50
+
51
+ # worker info
52
+ f.puts "\n\n#{sklaventreiber.workers.length} workers:\n"
53
+ sklaventreiber.workers.each do |w|
54
+ f.puts "#{"[SSH] " if w.sshing} #{w.descriptive} #{w.state}".strip
55
+ end
56
+
57
+ # slot pool
58
+ f.puts "\n\n#{sklaventreiber.slot_pools.length} slot pools:\n"
59
+ sklaventreiber.slot_pools.each do |name, pool|
60
+ f.puts "#{name}: #{pool.slots}"
61
+ pool.active.each do |thr|
62
+ f.puts "\tactive\t #{thr} [#{thr[:itype]}]"
63
+ end
64
+ pool.waiting.each do |wthr, tthr|
65
+ f.puts "\twaiting\t #{tthr} [#{tthr[:itype]}]"
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def sync &block
72
+ @monitor.synchronize(&block)
73
+ end
74
+
75
+ def outsync &block
76
+ @output_monitor.synchronize(&block)
77
+ end
78
+
79
+ def uniqid
80
+ Digest::SHA1.hexdigest(SecureRandom.urlsafe_base64(128))
81
+ end
82
+
83
+ def spawn_thread type, &block
84
+ waitlock = Queue.new
85
+ sync do
86
+ if !@opts[:"tp_#{type}"]
87
+ warning "Thread type `#{type}' has no priority setting, defaulting to 0..."
88
+ end
89
+
90
+ # spawn thread
91
+ Thread.new do
92
+ waitlock.pop
93
+ block.call(Thread.current)
94
+ end.tap do |thr|
95
+ # set type and priority
96
+ thr[:itype] = type
97
+ thr.priority = @opts[:"tp_#{type}"]
98
+ thr.abort_on_exception = true
99
+
100
+ # add signal methods
101
+ signalify_thread(thr)
102
+
103
+ # start thread
104
+ waitlock << true
105
+ end
106
+ end
107
+ end
108
+
109
+ def signalify_thread thr
110
+ # set lock and signal
111
+ thr[:monitor] = Monitor.new
112
+ thr[:signal] = thr[:monitor].new_cond
113
+
114
+ # define helper methods
115
+ def thr.signal
116
+ self[:monitor].synchronize{ self[:signal].broadcast } ; self
117
+ end
118
+ def thr.wait(*a)
119
+ self[:monitor].synchronize{ self[:signal].wait(*a) }
120
+ end
121
+ def thr.sync(&b)
122
+ self[:monitor].synchronize(&b)
123
+ end
124
+ end
125
+
126
+ def channelfy_thread thr
127
+ def thr.active?
128
+ alive?
129
+ end
130
+
131
+ def thr.closed?
132
+ !alive?
133
+ end
134
+
135
+ def thr.closing?
136
+ false
137
+ end
138
+
139
+ thr
140
+ end
141
+
142
+ def fake_channel &block
143
+ FakeChannel.new(&block).tap do |ch|
144
+ channelfy_thread(ch)
145
+ signalify_thread(ch)
146
+ end
147
+ end
148
+
149
+ def wakeup_handlers
150
+ Thread.list.each{|thr| thr.try(:signal) }
151
+ end
152
+
153
+
154
+ # =================
155
+ # = Configuration =
156
+ # =================
157
+ def core_cfg_path
158
+ File.expand_path(ENV["DBS_CFGDIR"].presence || "~/.db_sucker")
159
+ end
160
+
161
+ def core_tmp_path
162
+ "#{File.expand_path(ENV["DBS_TMPDIR"] || ENV["TMPDIR"] || "/tmp")}/db_sucker_temp"
163
+ end
164
+
165
+ def core_cfg_configfile
166
+ "#{core_cfg_path}/config.rb"
167
+ end
168
+
169
+ def load_appconfig
170
+ return unless File.exist?(core_cfg_configfile)
171
+ instance_eval File.read(core_cfg_configfile, encoding: "utf-8"), core_cfg_configfile
172
+ end
173
+
174
+
175
+ # ===================
176
+ # = Signal trapping =
177
+ # ===================
178
+ def trap_signals
179
+ debug "Trapping signals..."
180
+ Signal.trap("INT") do
181
+ $core_runtime_exiting = 1
182
+ Kernel.puts "Interrupting..."
183
+ end
184
+ Signal.trap("TERM") do
185
+ $core_runtime_exiting = 2
186
+ Kernel.puts "Terminating..."
187
+ end
188
+ Signal.trap("USR1") do
189
+ dump_core
190
+ end
191
+ end
192
+
193
+ def release_signals
194
+ debug "Releasing signal traps..."
195
+ Signal.trap("INT", "DEFAULT")
196
+ Signal.trap("TERM", "DEFAULT")
197
+ Signal.trap("USR1", "DEFAULT")
198
+ end
199
+
200
+ def haltpoint
201
+ raise Interrupt if $core_runtime_exiting && !$core_runtime_graceful
202
+ end
203
+
204
+
205
+ # ==========
206
+ # = Events =
207
+ # ==========
208
+ def hook *which, &hook_block
209
+ which.each do |w|
210
+ @hooks[w.to_sym] ||= []
211
+ @hooks[w.to_sym] << hook_block
212
+ end
213
+ end
214
+
215
+ def fire which, *args
216
+ return if @disable_event_firing
217
+ sync { debug "[Event] Firing #{which} (#{@hooks[which].try(:length) || 0} handlers) #{args.map(&:to_s)}", 99 }
218
+ @hooks[which] && @hooks[which].each{|h| h.call(self, *args) }
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,364 @@
1
+ module DbSucker
2
+ class Application
3
+ module Dispatch
4
+ def dispatch action = (@opts[:dispatch] || :help)
5
+ if respond_to?("dispatch_#{action}")
6
+ has_action = true
7
+ fire(:dispatch_before, action)
8
+ send("dispatch_#{action}")
9
+ else
10
+ fire(:dispatch_before, false)
11
+ abort("unknown action `#{action}'\nRegistered actions: #{available_actions.join(", ")}")
12
+ end
13
+ rescue StandardError => ex
14
+ fire(:dispatch_after, has_action ? action : false, ex)
15
+ raise ex
16
+ else
17
+ fire(:dispatch_after, has_action ? action : false)
18
+ end
19
+
20
+ def available_actions
21
+ methods.select{|m| m.to_s.start_with?("dispatch_") }.map{|s| s.to_s.gsub(/\Adispatch_/, "").to_sym }
22
+ end
23
+
24
+ def configful_dispatch identifier, variation, &block
25
+ log "Using config directory #{c core_cfg_path.to_s, :magenta}"
26
+
27
+ all_cfgs = cfg.yml_configs(true)
28
+ enabled = cfg.yml_configs(false)
29
+ disabled = all_cfgs - enabled
30
+ r = c("Found #{c all_cfgs.count, :blue} #{c "config files"}")
31
+ r << c(" (#{c "#{disabled.count} disabled", :red}#{c ")"}") if disabled.any?
32
+ log r << c("...")
33
+
34
+ cfg.load_all_configs
35
+ ctn = cfg.get(identifier)
36
+
37
+ if identifier.present? && !ctn
38
+ abort "Identifier `#{identifier}' couldn't be found!", 1
39
+ end
40
+
41
+ if ctn && variation && !(var = ctn.variation(variation))
42
+ abort "Variation `#{variation}' for identifier `#{identifier}' couldn't be found!", 1
43
+ end
44
+
45
+ begin
46
+ ctn.try(:ssh_begin)
47
+ block.call(identifier, ctn, variation, var)
48
+ ensure
49
+ ctn.try(:ssh_end)
50
+ end
51
+ end
52
+
53
+ # ----------------------------------------------------------------------
54
+
55
+ # ====================
56
+ # = Dev/Test actions =
57
+ # ====================
58
+
59
+ # via -a/--action cloop
60
+ def dispatch_cloop
61
+ begin
62
+ trap_signals
63
+ @sklaventreiber = SklavenTreiber.new(self, uniqid)
64
+ @sklaventreiber.spooled do
65
+ @opts[:stdout].disable
66
+ hook(:prompt_start) { @opts[:stdout].enable }
67
+ hook(:prompt_stop) { @opts[:stdout].disable(true) }
68
+ begin
69
+ @sklaventreiber._init_window
70
+ loop do
71
+ break if $core_runtime_exiting
72
+ sleep 0.1
73
+ end
74
+ ensure
75
+ sandboxed { @sklaventreiber.window.try(:stop) }
76
+ end
77
+ end
78
+ ensure
79
+ release_signals
80
+ end
81
+ end
82
+
83
+ # via -a/--action console
84
+ def dispatch_console
85
+ configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
86
+ begin ; require "pry" ; rescue LoadError ; end
87
+ ::Kernel.binding.pry
88
+ end
89
+ end
90
+
91
+ # via -a/--action threadtest
92
+ def dispatch_threadtest
93
+ t = Thread.new { Thread.stop }
94
+ i, m = 1, [0, nil, nil]
95
+ while i < 3
96
+ m[0] += i == 1 ? -1 : 1
97
+ t.priority = m[0]
98
+ if t.priority != m[0]
99
+ m[i] = t.priority
100
+ m[0] = 0
101
+ i += 1
102
+ end
103
+ end
104
+ t.kill
105
+ puts "Thread priority: -#{m[1].abs}..+#{m[2].abs}"
106
+ end
107
+
108
+ # via -a/--action sshdiag
109
+ def dispatch_sshdiag
110
+ log c("\nPlease wait while we run some tests...\n", :blue)
111
+ _identifier, _ctn = false, false, false
112
+ idstr = ARGV.shift
113
+ varstr = ARGV.shift
114
+
115
+ configful_dispatch(idstr, varstr) do |identifier, ctn, variation, var|
116
+ _identifier = identifier
117
+ _ctn = ctn
118
+ channels = []
119
+ stop = false
120
+ maxsessions = :unknown
121
+ begin
122
+ t = Thread.new {
123
+ begin
124
+ loop do
125
+ ctn.loop_ssh(0.1)
126
+ end
127
+ rescue DbSucker::Application::Container::SSH::ChannelOpenFailedError
128
+ maxsessions = channels.length - channels.select{|c| c[:open_failed] }.length
129
+ stop = true
130
+ retry
131
+ end
132
+ }
133
+ 250.times do
134
+ break if stop
135
+ c, r = ctn.blocking_channel_result("sleep 60", blocking: false, channel: true, use_sh: true)
136
+ channels << c
137
+ sleep 0.1
138
+ end
139
+ ensure
140
+ debug "Stopping sessions (#{channels.length})..."
141
+ channels.each_with_index do |c, i|
142
+ debug "Channel ##{i+1} #{c[:pid] ? "with PID #{c[:pid]}" : "has no PID"}"
143
+ ctn.kill_remote_process(c[:pid]) if c[:pid]
144
+ end
145
+ log c("\nSSH MaxSessions: #{c maxsessions, :magenta}", :cyan)
146
+ log "This value determines how many sessions we can multiplex over a single TCP connection."
147
+ log "Currently, DbSucker can only utilize one connection, thus this value defines the maxmium concurrency."
148
+ log "If you get errors you can either"
149
+ log " * increase the SSHd `MaxSessions' setting on the remote (if you can)"
150
+ log " * reduce the amount of workers and/or remote slots"
151
+ log " * fix the mess that is this tool, visit #{c "https://github.com/2called-chaos/db_sucker", :blue}"
152
+ t.kill
153
+ end
154
+ end
155
+ rescue Net::SSH::AuthenticationFailed => ex
156
+ notify_exception(ex)
157
+ log "\nDbSucker can't authenticate with the remote host, see exception."
158
+ log " * username correct?"
159
+ log " * password/keyfile correct?"
160
+ log " * check remote /var/log/auth.log?"
161
+ rescue SocketError => ex
162
+ notify_exception(ex)
163
+ log "\nDbSucker can't establish a connection to the remote host " << c(cfg.get(idstr).source["ssh"]["hostname"], :magenta)
164
+ log " * typo in hostname / IP?"
165
+ log " * firewall?"
166
+ end
167
+
168
+
169
+ # ================
170
+ # = Main actions =
171
+ # ================
172
+
173
+ # via -h/--help
174
+ def dispatch_help
175
+ colorized_help = @optparse.to_s.split("\n").map do |l|
176
+ if l.start_with?("Usage:")
177
+ lc = l.split(" ")
178
+ "#{c lc[0]} #{c lc[1], :blue} #{c lc[2..-1].join(" "), :cyan}"
179
+ elsif l.start_with?("#")
180
+ c(l, :blue)
181
+ elsif l.strip.start_with?("-")
182
+ "#{c l.to_s[0...33], :cyan}#{c l[33..-1]}"
183
+ else
184
+ c(l)
185
+ end
186
+ end
187
+ puts nil, colorized_help, nil
188
+ puts c("The current config directory is #{c core_cfg_path.to_s, :magenta}"), nil
189
+ end
190
+
191
+ # via -v/--version
192
+ def dispatch_info
193
+ your_version = Gem::Version.new(DbSucker::VERSION)
194
+ puts c ""
195
+ puts c(" Your version: ", :blue) << c("#{your_version}", :magenta)
196
+
197
+ print c(" Current version: ", :blue)
198
+ if @opts[:check_for_updates]
199
+ require "net/http"
200
+ print c("checking...", :blue)
201
+
202
+ begin
203
+ current_version = Gem::Version.new Net::HTTP.get_response(URI.parse(DbSucker::UPDATE_URL)).body.strip
204
+
205
+ if current_version > your_version
206
+ status = c("#{current_version} (consider update)", :red)
207
+ elsif current_version < your_version
208
+ status = c("#{current_version} (ahead, beta)", :green)
209
+ else
210
+ status = c("#{current_version} (up2date)", :green)
211
+ end
212
+ rescue
213
+ status = c("failed (#{$!.message})", :red)
214
+ end
215
+
216
+ print "#{"\b" * 11}#{" " * 11}#{"\b" * 11}" # reset line
217
+ puts status
218
+ else
219
+ puts c("check disabled", :red)
220
+ end
221
+
222
+ # more info
223
+ puts c ""
224
+ puts c " DbSucker is brought to you by #{c "bmonkeys.net", :green}"
225
+ puts c " Contribute @ #{c "github.com/2called-chaos/db_sucker", :cyan}"
226
+ puts c " Eat bananas every day!"
227
+ puts c ""
228
+ end
229
+
230
+ # via --generate
231
+ def dispatch_generate_config
232
+ cfg_name = @opts[:config_name] || "default"
233
+ cfg_file = "#{core_cfg_path}/#{cfg_name}.yml"
234
+ puts c("Generating container configuration file `#{cfg_name}'")
235
+ if File.exist?(cfg_file)
236
+ abort "Conflict, file already exists: #{cfg_file}", 1
237
+ else
238
+ puts c("Writing #{cfg_file}", :green)
239
+ FileUtils.mkdir_p(core_cfg_path)
240
+ FileUtils.cp("#{ROOT}/doc/container_example.yml", cfg_file)
241
+ editor = ENV["EDITOR"].presence
242
+ exec("#{editor.chomp} #{cfg_file}") if editor
243
+ end
244
+ end
245
+
246
+ # helper for #dispatch_stat_tmp
247
+ def _dispatch_stat_tmp_display files, directories, managed, cleanup = false, sftp = false
248
+ log "Directories: #{c directories.count, :blue}"
249
+ log " Files: #{c files.count, :blue} #{c "("}#{c managed.count, :blue}#{c " managed)"}"
250
+ log " Size: #{c human_bytes(files.map(&:second).sum), :blue} #{c "("}#{c human_bytes(managed.map(&:second).sum), :blue} #{c "managed)"}"
251
+
252
+ if cleanup
253
+ if managed.any?
254
+ log c("WE ONLY SIMULATE! Nothing will be deleted!", :green) if opts[:simulate]
255
+ warning "----------- Removing #{managed.count} managed files! Press Ctrl-C to abort -----------"
256
+ managed.each{|f, s| warning " REMOVE #{c "#{f}", :magenta} #{c human_bytes(s), :cyan}" }
257
+ warning "----------- Removing #{managed.count} managed files! Press Ctrl-C to abort -----------"
258
+ sleep 3
259
+ 4.times {|n| log "Cleaning up in #{3 - n}..." ; sleep 1 ; rll }
260
+
261
+ managed.each do |f, s|
262
+ if opts[:simulate]
263
+ warning "(simulate) Removing #{f}..."
264
+ else
265
+ warning "Removing #{f}..."
266
+ sftp ? sftp.remove!(f) : File.unlink(f)
267
+ end
268
+ end
269
+ else
270
+ log c("No managed files found, nothing to cleanup.", :green)
271
+ end
272
+ end
273
+ end
274
+
275
+ # via --stat-tmp
276
+ def dispatch_stat_tmp cleanup = false
277
+ configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
278
+ if ctn
279
+ ctn.sftp_begin do |sftp|
280
+ log c("Analyzing ") << c("remote", :cyan) << c(" temp directory #{c ctn.tmp_path, :magenta}")
281
+ begin
282
+ files = sftp.dir.glob("#{ctn.tmp_path}", "**/*")
283
+ rescue Net::SFTP::StatusException => ex
284
+ if ex.message["no such file"]
285
+ abort "Destination directory `#{ctn.tmp_path}' does not exist on the remote side!"
286
+ else
287
+ raise
288
+ end
289
+ end
290
+
291
+ d_files = files.select(&:file?).map{|f| ["#{ctn.tmp_path}/#{f.name}", f.attributes.size] }
292
+ d_directories = files.select(&:directory?).map{|f| ["#{ctn.tmp_path}/#{f.name}"] }
293
+ d_managed = files.select(&:file?).select{|f| f.name.end_with?(".dbsc", ".dbsc.tmp", ".dbsc.gz") }.map{|f| ["#{ctn.tmp_path}/#{f.name}", f.attributes.size] }
294
+ _dispatch_stat_tmp_display(d_files, d_directories, d_managed, cleanup, sftp)
295
+ end
296
+ else
297
+ log c("Analyzing ") << c("local", :cyan) << c(" temp directory #{c core_tmp_path, :magenta}")
298
+ files = Dir.glob("#{core_tmp_path}/**/*")
299
+
300
+ managed_files = files
301
+ d_files = files.select{|f| File.file?(f) }.map{|f| [f, File.size(f)] }
302
+ d_directories = files.select{|f| File.directory?(f) }
303
+ d_managed = files.select{|f| File.file?(f) && File.basename(f).end_with?(".dbsc", ".dbsc.tmp", ".dbsc.gz") }.map{|f| [f, File.size(f)] }
304
+ _dispatch_stat_tmp_display(d_files, d_directories, d_managed, cleanup)
305
+ end
306
+ end
307
+ end
308
+
309
+ # via --cleanup-tmp
310
+ def dispatch_cleanup_tmp
311
+ dispatch_stat_tmp(true)
312
+ end
313
+
314
+ # via -l/--list-databases
315
+ def _list_databases identifier, ctn, variation, var
316
+ return unless opts[:list_databases]
317
+ log "Listing databases for identifier #{c identifier, :magenta}#{c "..."}"
318
+ dbs = ctn.database_list(opts[:list_tables])
319
+ print_db_table_list ctn.hostname, dbs
320
+ throw :dispatch_handled
321
+ end
322
+
323
+ # via -t/--list-tables
324
+ def _list_tables identifier, ctn, variation, var
325
+ return if !(opts[:list_tables].present? && opts[:list_tables] != :all)
326
+ print_db_table_list ctn.hostname, [[opts[:list_tables], ctn.table_list(opts[:list_tables])]]
327
+ throw :dispatch_handled
328
+ end
329
+
330
+ # default action if variation given
331
+ def _suck_variation identifier, ctn, variation, var
332
+ if ctn && (var || (@opts[:suck_only].any? || @opts[:suck_except].any?))
333
+ var ||= ctn.variation("default")
334
+ begin
335
+ trap_signals
336
+ @sklaventreiber = SklavenTreiber.new(self, uniqid)
337
+ @sklaventreiber.spooled do
338
+ @sklaventreiber.whip_it!(ctn, var)
339
+ end
340
+ ensure
341
+ release_signals
342
+ end
343
+ throw :dispatch_handled
344
+ end
345
+ end
346
+
347
+ # default action if no variation is given
348
+ def _default_listing identifier, ctn, variation, var
349
+ db_table_listing(ctn ? [[identifier, ctn]] : cfg)
350
+ end
351
+
352
+ # default actions in order
353
+ def dispatch_index
354
+ configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
355
+ catch :dispatch_handled do
356
+ [:_list_databases, :_list_tables, :_suck_variation, :_default_listing].each do |meth|
357
+ __send__(meth, identifier, ctn, variation, var)
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end