Compare commits
	
		
			2 Commits
		
	
	
		
			f433321fb8
			...
			58b6de55a2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 58b6de55a2 | ||
|  | 1d546c93b1 | 
							
								
								
									
										263
									
								
								bucket-tool
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								bucket-tool
									
									
									
									
									
								
							| @@ -1,5 +1,10 @@ | |||||||
| #!/usr/bin/ruby | #!/usr/bin/ruby | ||||||
|  |  | ||||||
|  | # BEGIN: Requires | ||||||
|  |  | ||||||
|  | require 'digest' | ||||||
|  |  | ||||||
|  | # END: Requires | ||||||
|  |  | ||||||
| # BEGIN: Helper Functions | # BEGIN: Helper Functions | ||||||
|  |  | ||||||
| @@ -14,15 +19,24 @@ Description: | |||||||
|   search the filebucket and restore from it. |   search the filebucket and restore from it. | ||||||
|  |  | ||||||
| Actions: | Actions: | ||||||
|   search <term>        : Search for bucket entries matching a portion of the filepath |   search <term>             : Search for bucket entries matching a portion of the filepath | ||||||
|   list                 : List all Bucket entries |   list                      : List all Bucket entries | ||||||
|   list-files           : List all files/paths that have been backed up to the bucket |   list-files                : List all files/paths that have been backed up to the bucket | ||||||
|   get <entry-hash>     : Get the content of a specific entry (by hash) |   get <entry-hash>          : Get the content of a specific entry (by hash) | ||||||
|   restore <entry-hash> : Restore previous state of file stored in bucket (by-hash) |   restore <value>           : Restore previous state of file stored in bucket. Value can be hash or filename/filepath | ||||||
|  |  | ||||||
| Global Flags: | Global Flags: | ||||||
|   -d | --debug         : Set debug flag |   -d | --debug              : Set debug flag | ||||||
|   -h | --help          : This help message |   -h | --help               : This help message | ||||||
|  |  | ||||||
|  | Info Format Flags: | ||||||
|  |   -i | --inline             : Set the info format to inline (MTIME : HASH : FILENAME) | ||||||
|  |   -l | --long               : Set the info format to long | ||||||
|  |                             : Entry [HASH]: | ||||||
|  |                             :   Paths: path1,path2,...,pathn | ||||||
|  |                             :   MTIME: YYYY-MM-DD HH:MM:SS -#### | ||||||
|  |                             : | ||||||
|  |   -c | --csv                : Set the info format to csv ( MTIME,HASH,FILENAME1[;FILENAMEn] ) | ||||||
|  |  | ||||||
| Author: | Author: | ||||||
|   Name: Tristan Ancelet |   Name: Tristan Ancelet | ||||||
| @@ -62,6 +76,18 @@ def log (message, level = 0) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | def which(cmd) | ||||||
|  |   #https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby | ||||||
|  |   exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] | ||||||
|  |   ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| | ||||||
|  |     exts.each do |ext| | ||||||
|  |       exe = File.join(path, "#{cmd}#{ext}") | ||||||
|  |       return exe if File.executable?(exe) && !File.directory?(exe) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   nil | ||||||
|  | end | ||||||
|  |  | ||||||
| def get_verification (prompt = "Do you want to continue?") | def get_verification (prompt = "Do you want to continue?") | ||||||
|   while true |   while true | ||||||
|     puts "#{prompt} (y/n): " |     puts "#{prompt} (y/n): " | ||||||
| @@ -78,6 +104,54 @@ def get_verification (prompt = "Do you want to continue?") | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | def get_selections (reference_array, prompt = "Which of the following do you want to select? ", options = { :multiple => false, }, &procedure) | ||||||
|  |   ## Making clone of array since the selections were passed by reference | ||||||
|  |   selections = reference_array.clone | ||||||
|  |  | ||||||
|  |   def put_prompt (selections, prompt) | ||||||
|  |     puts prompt | ||||||
|  |     selections.each_with_index do |value,index| | ||||||
|  |       puts "#{index} : #{value}" | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   if options[:multiple] == true | ||||||
|  |     output = Array.new | ||||||
|  |   else | ||||||
|  |     output = "" | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   put_prompt selections, prompt | ||||||
|  |  | ||||||
|  |   while true | ||||||
|  |     choice = Integer(STDIN.gets.strip()) | ||||||
|  |     if choice.is_a? Integer | ||||||
|  |      if  choice >= 0 and choice < selections.count  | ||||||
|  |        if options[:multiple] == true | ||||||
|  |          output.push(selections[choice]) | ||||||
|  |          selections.delete_at(choice) | ||||||
|  |          if get_verification "Are you done selecting?"  | ||||||
|  |            break | ||||||
|  |          end | ||||||
|  |          put_prompt selections, prompt | ||||||
|  |        else | ||||||
|  |          output = selections[choice] | ||||||
|  |          break | ||||||
|  |        end | ||||||
|  |      else | ||||||
|  |        puts "#{choice} is not between the values of 0 and #{selections.count}. Please try again." | ||||||
|  |      end | ||||||
|  |     else | ||||||
|  |       puts "#{choice} is not a valid option. Please try again." | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   if procedure.respond_to? "call" | ||||||
|  |     output = procedure.call(output) | ||||||
|  |   end | ||||||
|  |   output | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
| # END: Helper Functions | # END: Helper Functions | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -97,11 +171,19 @@ end | |||||||
|  |  | ||||||
| $DEBUG=false | $DEBUG=false | ||||||
|  |  | ||||||
|  | puppet_exe = which "puppet" | ||||||
|  |  | ||||||
|  | if puppet_exe == nil | ||||||
|  |   puts "The puppet utility was not found in $PATH. This utility will not be able to function" | ||||||
|  |   exit | ||||||
|  | end | ||||||
|  |  | ||||||
| $CONFIG = Hash.new | $CONFIG = Hash.new | ||||||
| $CONFIG[:bucket_dir]=` puppet agent --configprint clientbucketdir `.strip() | $CONFIG[:bucket_dir]=` #{puppet_exe} agent --configprint clientbucketdir `.strip() | ||||||
| $CONFIG[:action]="" | $CONFIG[:action]="" | ||||||
| $CONFIG[:search_term]="" | $CONFIG[:search_term]="" | ||||||
| $CONFIG[:log_file]="" | $CONFIG[:log_file]="" | ||||||
|  | $CONFIG[:info_format]="inline" | ||||||
| File.open('/etc/hostname') do |file| | File.open('/etc/hostname') do |file| | ||||||
|   $HOSTNAME=file.read().strip() |   $HOSTNAME=file.read().strip() | ||||||
| end | end | ||||||
| @@ -150,6 +232,25 @@ case ARGV[i] | |||||||
|     exit |     exit | ||||||
| end | end | ||||||
|  |  | ||||||
|  | while i < ARGV.count | ||||||
|  |   case ARGV[i] | ||||||
|  |     when '-c', '--csv' | ||||||
|  |       $CONFIG[:info_format]='csv' | ||||||
|  |       log "$CONFIG[:info_format] was set to #{$CONFIG[:info_format]}" | ||||||
|  |       i+=1 | ||||||
|  |     when '-l', '--long' | ||||||
|  |       $CONFIG[:info_format]='long' | ||||||
|  |       log "$CONFIG[:info_format] was set to #{$CONFIG[:info_format]}" | ||||||
|  |       i+=1 | ||||||
|  |     when '-i', '--inline' | ||||||
|  |       $CONFIG[:info_format]='inline' | ||||||
|  |       log "$CONFIG[:info_format] was set to #{$CONFIG[:info_format]}" | ||||||
|  |       i+=1 | ||||||
|  |     else | ||||||
|  |       i+=1 | ||||||
|  |   end | ||||||
|  | end | ||||||
|  |  | ||||||
| ## BEGIN: Checks | ## BEGIN: Checks | ||||||
|  |  | ||||||
| if $CONFIG[:action] == "" | if $CONFIG[:action] == "" | ||||||
| @@ -163,6 +264,7 @@ case $CONFIG[:action] | |||||||
|       usage |       usage | ||||||
|       exit |       exit | ||||||
|     end |     end | ||||||
|  |  | ||||||
| end | end | ||||||
|  |  | ||||||
| ## END: Checks | ## END: Checks | ||||||
| @@ -177,12 +279,8 @@ class BucketEntry | |||||||
|   def initialize (entry_dir) |   def initialize (entry_dir) | ||||||
|     @entry_dir = entry_dir |     @entry_dir = entry_dir | ||||||
|     @hash = File.basename(entry_dir) |     @hash = File.basename(entry_dir) | ||||||
|     @filepaths = Array.new |  | ||||||
|     File.open("#{entry_dir}/paths") do |file| |     File.open("#{entry_dir}/paths") do |file| | ||||||
|       file.read().split(/\n/).each do |path| |       @filepaths = file.read.split(/\n/) | ||||||
|         log "BucketEntry[#{@hash}] adding #{path} to @filepaths" |  | ||||||
|         @filepaths.push(path) |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
|     @mtime = File.mtime(entry_dir) |     @mtime = File.mtime(entry_dir) | ||||||
|     log "BucketEntry was created from #{entry_dir}" |     log "BucketEntry was created from #{entry_dir}" | ||||||
| @@ -190,10 +288,10 @@ class BucketEntry | |||||||
|  |  | ||||||
|   def path_include? (path_string) |   def path_include? (path_string) | ||||||
|     log "BucketEntry[#{hash}] was called with #{path_string}" |     log "BucketEntry[#{hash}] was called with #{path_string}" | ||||||
|     @filepaths.each.any? {|path| path.include? path_string} |     @filepaths.any?{|path| path.include? path_string} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def infostring |   def long_info | ||||||
|     "Entry [#{@hash}]: |     "Entry [#{@hash}]: | ||||||
|       Paths: #{@filepaths.join(',')} |       Paths: #{@filepaths.join(',')} | ||||||
|       MTIME: #{@mtime} |       MTIME: #{@mtime} | ||||||
| @@ -201,10 +299,25 @@ class BucketEntry | |||||||
| " | " | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def csv_info | ||||||
|  |     [@mtime,@hash,@filepaths.join(';')].join(',') | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def inline_info |   def inline_info | ||||||
|     "#{@mtime} : #{@hash} : #{@filepaths.join(',')}" |     "#{@mtime} : #{@hash} : #{@filepaths.join(',')}" | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def info | ||||||
|  |     case $CONFIG[:info_format] | ||||||
|  |       when 'long' | ||||||
|  |         long_info | ||||||
|  |       when 'inline' | ||||||
|  |         inline_info | ||||||
|  |       when 'csv' | ||||||
|  |         csv_info | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def content  |   def content  | ||||||
|     log "BucketEntry[#{@hash}] getting contents" |     log "BucketEntry[#{@hash}] getting contents" | ||||||
|     File.open("#{@entry_dir}/contents",'r') do |file| |     File.open("#{@entry_dir}/contents",'r') do |file| | ||||||
| @@ -223,6 +336,15 @@ class Bucket | |||||||
|     load_bucket |     load_bucket | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def select(&proc) | ||||||
|  |     @entries.each_value.select &proc | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def any?(&proc) | ||||||
|  |     @entries.each_value.any? &proc | ||||||
|  |   end | ||||||
|  |      | ||||||
|  |  | ||||||
|   def load_bucket |   def load_bucket | ||||||
|     log "Bucket[#{@bucketdir}] is loading entries" |     log "Bucket[#{@bucketdir}] is loading entries" | ||||||
|     Dir["#{@bucketdir}/**/paths"].each.map{|path| File.dirname(path)}.each do |directory| |     Dir["#{@bucketdir}/**/paths"].each.map{|path| File.dirname(path)}.each do |directory| | ||||||
| @@ -233,6 +355,18 @@ class Bucket | |||||||
|     end |     end | ||||||
|     log "Bucket[#{@bucketdir}] was loaded" |     log "Bucket[#{@bucketdir}] was loaded" | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def filenames | ||||||
|  |     filenames = Array.new | ||||||
|  |     @entries.each_value do |entry| | ||||||
|  |       entry.filepaths.each do |path| | ||||||
|  |         if not filenames.include? path | ||||||
|  |           filenames.push(path) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     filenames | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| # END: Classes | # END: Classes | ||||||
| @@ -242,17 +376,10 @@ end | |||||||
| # BEGIN: Work Functions | # BEGIN: Work Functions | ||||||
|  |  | ||||||
| def search_entries_paths (bucket) | def search_entries_paths (bucket) | ||||||
|   log "user entered" |   puts bucket.select{|entry| entry.path_include? $CONFIG[:search_term]}.sort_by(&:mtime).map(&:info) | ||||||
|   bucket.entries.each_value do |entry| |  | ||||||
|     log "checking Entry[#{entry.hash}]" |  | ||||||
|     if entry.path_include? $CONFIG[:search_term] |  | ||||||
|       puts entry.inline_info |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end | end | ||||||
|  |  | ||||||
| def get_content_of_entry_hash (bucket) | def get_content_of_entry_hash (bucket) | ||||||
|   log "user entered" |  | ||||||
|   if bucket.entries.has_key? $CONFIG[:search_term] |   if bucket.entries.has_key? $CONFIG[:search_term] | ||||||
|     puts bucket.entries[$CONFIG[:search_term]].content |     puts bucket.entries[$CONFIG[:search_term]].content | ||||||
|   else |   else | ||||||
| @@ -262,40 +389,84 @@ def get_content_of_entry_hash (bucket) | |||||||
| end | end | ||||||
|  |  | ||||||
| def list_all_entries (bucket) | def list_all_entries (bucket) | ||||||
|   puts bucket.entries.each_value.each.map{|entry| entry.inline_info}.sort.join("\n") |   puts bucket.filenames.map{|filename| bucket.select{|entry| entry.path_include? filename}.sort_by(&:mtime).map(&:info)} | ||||||
| end | end | ||||||
|  |  | ||||||
| def list_entry_files (bucket) | def list_entry_files (bucket) | ||||||
|   filenames = Array.new |   puts bucket.filenames.sort.join("\n") | ||||||
|   bucket.entries.each_value do |entry|  | end | ||||||
|     entry.filepaths.each do |path| |  | ||||||
|       if not filenames.include? path | def get_entry_by_file (bucket, filenames) | ||||||
|         filenames.push(path) |   entry = nil | ||||||
|  |   if filenames.count == 1 | ||||||
|  |     filename = filenames[0] | ||||||
|  |   else | ||||||
|  |     filename = get_selections filenames, "Your filename matched multiple files. Please select one to restore" | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   entries = bucket.select{|entry| entry.path_include? filename} | ||||||
|  |   if entries.count == 1 | ||||||
|  |     entry = entries.first | ||||||
|  |   else | ||||||
|  |     while true  | ||||||
|  |       mtimes = entries.map{|entry| entry.mtime} | ||||||
|  |       entry_mtime = get_selections(mtimes , "Which timestamp to you want to revert the file to?")  | ||||||
|  |       entry = entries.lazy.select{|entry| entry.mtime == entry_mtime}.first | ||||||
|  |  | ||||||
|  |       if get_verification "Do you want to see the contents of #{filename} at this time?" | ||||||
|  |         puts entry.content | ||||||
|  |         if get_verification "Is this the entry you want to overwrite #{filename} with?" | ||||||
|  |           break | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         break | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|   puts filenames.sort.join("\n") |  | ||||||
|  |   if filename[0] != '/' | ||||||
|  |     filename = "/#{filename}" | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   return entry, filename | ||||||
|  | end | ||||||
|  |  | ||||||
|  | def get_entry_by_hash (bucket) | ||||||
|  |   if bucket.entries.has_key? $CONFIG[:search_term] | ||||||
|  |     entry = bucket.entries[$CONFIG[:search_term]] | ||||||
|  |     filepath = "" | ||||||
|  |     if entry.filepaths.count == 1 | ||||||
|  |       filepath = entry.filepaths[0] | ||||||
|  |     else  | ||||||
|  |       filepath = get_selections entry.filepaths, "What filepath do you wish to restore to?" | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     if filepath[0] != '/' | ||||||
|  |       filepath = "/#{filepath}" | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     return entry, filepath | ||||||
|  |  | ||||||
|  |   else | ||||||
|  |     puts "There were no entries corresponding to #{$CONFIG[:search_term]}" | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| def restore_entry (bucket) | def restore_entry (bucket) | ||||||
|   if bucket.entries.has_key? $CONFIG[:search_term] |   entry = nil | ||||||
|     entry = bucket.entries[$CONFIG[:search_term]] |   if bucket.any?{ |entry| entry.path_include? $CONFIG[:search_term] } | ||||||
|     if entry.filepaths.count == 1 |     filenames = bucket.filenames.select {|filename| filename.include? $CONFIG[:search_term]} | ||||||
|       filepath = entry.filepaths[0] |     entry, filepath = get_entry_by_file bucket, filenames | ||||||
|       if filepath[0] != '/' |   else | ||||||
|         filepath = "/#{filepath}" |     entry, filepath = get_entry_by_hash bucket | ||||||
|       end |   end | ||||||
|       if get_verification "Are you sure you want to overwrite #{filepath}?"  |  | ||||||
|         File.open(filepath,'w') do |file| |   if get_verification "Are you sure you want to overwrite #{filepath}?"  | ||||||
|           file.write(entry.content) |     File.open(filepath,'w') do |file| | ||||||
|         end |       file.write(entry.content) | ||||||
|       else |  | ||||||
|         puts "Ok not overwriting." |  | ||||||
|       end |  | ||||||
|     else |  | ||||||
|     end |     end | ||||||
|   else |   else | ||||||
|     puts "There were no entries corresponding to #{$CONFIG[:search_term]}" |     puts "Ok not overwriting." | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user