lib/testlab/container/io.rb in testlab-1.7.1 vs lib/testlab/container/io.rb in testlab-1.8.0

- old
+ new

@@ -1,12 +1,46 @@ class TestLab class Container module IO + require 'net/http' + require 'net/https' if RUBY_VERSION < '1.9' require 'tempfile' - PBZIP2_MEMORY = 256 + PBZIP2_MEMORY = 1024 + READ_SIZE = ((64 * 1024) - 1) + TRANSFER_MESSAGE = "transferring '%s' at %0.2fMB/s -- %0.2fMB of %0.2fMB -- %d%% \r" + + def progress_callback(action, args) + @total_size ||= 0 + + case action + when :open then + @start_time = Time.now + if (@total_size == 0) + @total_size = args[0].size + @total_size_mb = (@total_size.to_f / (1024 * 1024).to_f) + end + + when :get, :put then + current_size = (args[1] + args[2].length) + current_size_mb = (current_size.to_f / (1024 * 1024).to_f) + + elapsed = (Time.now - @start_time) + speed_mb = (current_size.to_f / elapsed.to_f) / (1024 * 1024).to_f + + percentage_done = ((current_size * 100) / @total_size) + + @ui.stdout.print(format_message(TRANSFER_MESSAGE.yellow % [File.basename(args[0].local), speed_mb, current_size_mb, @total_size_mb, percentage_done])) + + when :finish + @ui.stdout.puts + @total_size = 0 + + end + end + # Export the container # # @return [Boolean] True if successful. def export(compression=9, local_file=nil) @ui.logger.debug { "Container Export: #{self.id} " } @@ -42,15 +76,24 @@ ls -lah #{remote_file} EOF end - please_wait(:ui => @ui, :message => format_object_action(self, 'Export', :cyan)) do - File.exists?(local_file) and FileUtils.rm_f(local_file) - self.node.download(remote_file, local_file) - end + File.exists?(local_file) and FileUtils.rm_f(local_file) + @total_size = self.node.ssh.sftp.stat!(remote_file).size + @total_size_mb = (@total_size.to_f / (1024 * 1024).to_f) + + self.node.download(remote_file, local_file, :on_progress => method(:progress_callback), :read_size => READ_SIZE) + + self.node.bootstrap(<<-EOF) +set -x +set -e + +rm -fv #{remote_file} +EOF + @ui.stdout.puts(format_message("Your shipping container is now exported and available at '#{local_file}'!".green.bold)) true end @@ -58,29 +101,37 @@ # # @return [Boolean] True if successful. def import(local_file) @ui.logger.debug { "Container Import: #{self.id} " } + if !File.exists?(local_file) + self.sc_url.nil? and raise ContainerError, "You failed to supply a filename or URL to import from!" + + @ui.stdout.puts(format_message("Downloading shipping container for #{self.id}...".green.bold)) + + local_file = File.expand_path("#{self.id}.sc") + sc_download(local_file, self.sc_url, 16) + end + # Ensure we are not in ephemeral mode. self.persistent self.down self.destroy + self.create import_tempfile = Tempfile.new('import') remote_filename = File.basename(import_tempfile.path.dup) import_tempfile.close! remote_file = File.join("", "tmp", remote_filename) local_file = File.expand_path(local_file) root_fs_path = self.lxc.fs_root.split(File::SEPARATOR).last - please_wait(:ui => @ui, :message => format_object_action(self, 'Import', :cyan)) do - self.node.exec(%(sudo rm -fv #{remote_file}), :silence => true, :ignore_exit_status => true) - self.node.upload(local_file, remote_file) - end + self.node.exec(%(sudo rm -fv #{remote_file}), :silence => true, :ignore_exit_status => true) + self.node.upload(local_file, remote_file, :on_progress => method(:progress_callback), :read_size => READ_SIZE) please_wait(:ui => @ui, :message => format_object_action(self, 'Expand', :cyan)) do self.node.bootstrap(<<-EOF) set -x set -e @@ -90,10 +141,12 @@ rm -rf #{self.lxc.fs_root} cd #{self.lxc.container_root} pbzip2 -vdcm#{PBZIP2_MEMORY} #{remote_file} | cpio -uid && rm -fv #{remote_file} du -sh #{self.lxc.container_root} + +rm -fv #{remote_file} EOF end @ui.stdout.puts(format_message("Your shipping container is now imported and available for use!".green.bold)) @@ -131,9 +184,76 @@ # bring the target container back online if it was running before the copy operation (target_state == :running) and target_container.up true + end + + # Downloads a given shipping container image + # + # @return [Boolean] True if successful. + def sc_download(local_file, url, count) + (count <= 0) and raise ContainerError, "Too many redirects, aborting!" + + uri = URI(url) + http = Net::HTTP.new(uri.host, uri.port) + + if (uri.scheme.downcase == 'https') + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE # lets be really permissive for now + end + + http.request_get(uri.path) do |response| + case response + when Net::HTTPNotFound then + raise ContainerError, "The supplied sc_url for this container was 404 Not Found!" + + when Net::HTTPClientError then + raise ContainerError, "Client Error: #{response.inspect}" + + when Net::HTTPRedirection then + location = response['location'] + @ui.stdout.puts(format_message("REDIRECTED #{url} --> #{location}".black)) + return sc_download(local_file, location, (count - 1)) + + when Net::HTTPOK then + tempfile = Tempfile.new(%(download-#{self.id})) + tempfile.binmode + + current_size = 0 + progress = 0 + total_size = response['content-length'].to_i + total_size_mb = total_size.to_f / (1024 * 1024).to_f + + start_time = Time.now + response.read_body do |chunk| + tempfile << chunk + + current_size += chunk.size + current_size_mb = current_size.to_f / (1024 * 1024).to_f + + new_progress = (current_size * 100) / total_size + unless new_progress == progress + elapsed = (Time.now - start_time) + speed_mb = (current_size.to_f / elapsed.to_f) / (1024 * 1024).to_f + @ui.stdout.print(format_message(TRANSFER_MESSAGE.yellow % [File.basename(local_file), speed_mb, current_size_mb, total_size_mb, new_progress])) + end + progress = new_progress + end + @ui.stdout.puts + + tempfile.close + + FileUtils.mkdir_p(File.dirname(local_file)) + File.exists?(local_file) and File.unlink(local_file) + FileUtils.mv(tempfile.path, local_file, :force => true) + + true + else + raise ContainerError, "Unknown HTTP response when attempt to download your shipping container!" + end + end + end end end