db_sucker 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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