Class: Rsyncbackup

Inherits:
Object
  • Object
show all
Includes:
Methadone::CLILogging
Defined in:
lib/rsyncbackup.rb,
lib/rsyncbackup/version.rb,
lib/rsyncbackup/utilities.rb

Constant Summary collapse

VERSION =
"2.0.3"
DEFAULT_EXCLUSIONS =
File.expand_path('.rsyncbackup.exclusions', ENV['HOME'])
DEFAULT_INCOMPLETE_DIR_NAME =
'.incomplete'
DEFAULT_LAST_FULL_DIR_NAME =
'.lastfull'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, target, opts = {}) ⇒ Rsyncbackup

Returns a new instance of Rsyncbackup.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rsyncbackup.rb', line 11

def initialize(source, target, opts={})
  logger.level = Logger::WARN
  logger.level = Logger::INFO if opts[:verbose]
  logger.error_level = Logger::DEBUG if opts[:debug]

  raise "Unknown target: #{target}" unless File.exist?(target) or opts[:dry_run]

  @source     = strip_trailing_separator_if_any(source,true)
  @target     = strip_trailing_separator_if_any(target)

  @options = {
    :dry_run         => false,
    :exclusions      => DEFAULT_EXCLUSIONS,
    :archive         => true,
    :one_file_system => true,
    :hard_links      => true,
    :human_readable  => true,
    :inplace         => true,
    :numeric_ids     => true,
    :delete          => true,
    :link_dest       => last_full_backup,
    :rsync_cmd       => rsync_executable
  }.merge(opts)
  
  @incomplete = File.join(target,DEFAULT_INCOMPLETE_DIR_NAME)
  @complete   = File.join(target,backup_dir_name)
  
  
  debug "#{caller(0,1).first} @source: #{@source}, @target: #{@target}, @options: #{@options.inspect}"

end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



9
10
11
# File 'lib/rsyncbackup.rb', line 9

def options
  @options
end

#sourceObject

Returns the value of attribute source.



9
10
11
# File 'lib/rsyncbackup.rb', line 9

def source
  @source
end

#targetObject

Returns the value of attribute target.



9
10
11
# File 'lib/rsyncbackup.rb', line 9

def target
  @target
end

Instance Method Details

#backup_dir_nameObject

returns the directory name for the current backup directory name consists of a time format: YYYY-MM-DDTHH-MM-SS



62
63
64
# File 'lib/rsyncbackup/utilities.rb', line 62

def backup_dir_name
  @backup_dir_name ||= Time.now.strftime("%FT%H-%M-%S")
end

#build_commandObject

returns the command string to execute with all parameters set



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rsyncbackup/utilities.rb', line 17

def build_command
  
  cmd = []
  cmd << options[:rsync_cmd]
  cmd << '--dry-run'             if options[:dry_run]
  cmd << '--verbose --progress --itemize-changes' if (options[:verbose] || options[:debug])
  cmd << '--archive'             if options[:archive]
  cmd << '--one-file-system'     if options[:one_file_system]
  cmd << '--hard-links'          if options[:hard_links]
  cmd << '--human-readable'      if options[:human_readable]
  cmd << '--inplace'             if options[:inplace]
  cmd << '--numeric-ids'         if options[:numeric_ids]
  cmd << '--delete'              if options[:delete]
  cmd << "--exclude-file #{options[:exclusions]}" if File.exist?(options[:exclusions])
  cmd << "--link-dest '#{options[:link_dest]}'" if options[:link_dest]
  cmd << ?" + @source + ?"
  cmd << ?" + temp_target_path + ?"
  
  cmd.join(' ')
  
end

#finalizeObject



63
64
65
66
67
# File 'lib/rsyncbackup.rb', line 63

def finalize
  File.rename(@incomplete, @complete) if File.exist?(@incomplete)
  File.write(File.join(@target,DEFAULT_LAST_FULL_DIR_NAME), backup_dir_name)
  info "Backup saved in #{@complete}"
end

#full_target_pathObject

returns the full target path, including backup directory name



67
68
69
# File 'lib/rsyncbackup/utilities.rb', line 67

def full_target_path
  @full_target_path ||= File.join(@target, backup_dir_name)
end

#last_full_backupObject

returns the directory name of the last full backup returns nil otherwise



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rsyncbackup/utilities.rb', line 41

def last_full_backup
  
  unless @last_full_backup
    lastfull = File.join(@target,DEFAULT_LAST_FULL_DIR_NAME)
    @last_full_backup = unless File.exist?(lastfull)
                          nil
                        else
                          last_full_directory = IO.readlines(lastfull).first.chomp
                       unless File.exist?(File.join(@target,last_full_directory))
                            nil
                          else
                            last_full_directory
                          end
                        end
  end
  @last_full_backup

end

#rsync_executableObject

returns the path to the rsync executable If none found, raises an Exception



80
81
82
83
84
# File 'lib/rsyncbackup/utilities.rb', line 80

def rsync_executable
  rsync = `which rsync`.chomp
  raise "No rsync executable. Are you sure it\'s installed?" if rsync.empty?
  rsync
end

#runObject



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rsyncbackup.rb', line 44

def run
  @cmd = build_command
  
  info "Backing up #{@source} to #{@target} on #{Time.now}"
  info "Rsync command: #{@cmd}"
  info "Dry run only" if options[:dry_run]
    
  if File.exist? temp_target_path
    warn "Preexisting temporary target. Moving it aside."
    File.rename temp_target_path, "#{temp_target_path}-#{Time.now.to_i}"
  end

  # the dry run option will be passed through to the rsync command,
  # so we still do want to run it.
  result = Open3.capture3(@cmd)
  raise "Rsync Error: exit status: #{s.exit_code}: error: #{e}" unless result[2].success?
  result
end

#strip_trailing_separator_if_any(s, keep_if_symlink = false) ⇒ Object

Strip the trailing directory separator from the rsync source or target.

s

string to strip



90
91
92
93
94
95
96
97
98
# File 'lib/rsyncbackup/utilities.rb', line 90

def strip_trailing_separator_if_any(s,keep_if_symlink=false)
  s = s.to_s
  s_s = s.sub(%r{#{File::SEPARATOR}+$},'')
  unless keep_if_symlink
    s_s
  else
    File.symlink?(s_s) ? s : s_s
  end
end

#temp_target_pathObject

returns the temporary target path



72
73
74
# File 'lib/rsyncbackup/utilities.rb', line 72

def temp_target_path
  @temp_target_path ||= File.join(@target, DEFAULT_INCOMPLETE_DIR_NAME)
end