-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclone_with_backup
executable file
·140 lines (118 loc) · 3.16 KB
/
clone_with_backup
1
2
3
4
5
6
7
8
9
10
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env ruby
require 'fileutils'
require 'yaml'
require 'shellwords'
include FileUtils
def copy_backup base, target, last_target, remove_last = false
if File.exists?(last_target)
rm_r last_target
end
if File.exists?(target)
mv target, last_target
end
system "cp", "-al", base, target
if remove_last and File.exists?(last_target)
rm_r last_target
end
end
require 'optparse'
verbose = false
dry_run = false
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
verbose = v
end
opts.on("-n", "--[no-]dry-run", "Dont process") do |v|
dry_run = v
end
end.parse!
if ARGV.size != 1
raise "usage: #$0 [-v] [-n] <root|config>"
end
root = ARGV.first
if File.directory? root
config_file = File.join(root, "config.yml")
else
config_file = root
root = nil
end
config = YAML.load(File.read(config_file))
remote = config["remote"]
id_file = config["key"]
id_files = ([id_file] + config.fetch("keys", [])).compact
paths = config["paths"]
root = config["root"] || root
options = config["options"]
timeout_hours = config["timeout_hours"] || 6
timeout = timeout_hours * 3600
bwlimit = config["bwlimit"] || 100
remove_last = config["remove_last"] || false
ssh_options = config["ssh_options"] || {}
raise "No root defined" unless root
if paths.nil?
paths = { "root" => "/" }
end
known_hosts_file = File.expand_path("~/.ssh/known_hosts")
if File.exists? known_hosts_file
known_hosts = File.read(known_hosts_file).split("\n")
else
known_hosts = []
end
if known_hosts.grep(/^#{Regexp.escape remote} /).empty?
host_key = %x(ssh-keyscan -t rsa,dsa #{remote} 2>/dev/null)
puts "Adding host key #{host_key}"
known_hosts << host_key
File.open(known_hosts_file, "w") { |f| f.puts known_hosts.join("\n") }
end
now = Time.now
name = "current"
paths.each do |path_name, path|
dest = File.join(root, path_name)
mkpath dest
backup_dir = File.join(dest, name)
ssh_args = []
id_files.each do |id_file|
ssh_args += ["-i", id_file]
end
ssh_options.each do |name, value|
ssh_args += ["-o", "#{name}=#{Shellwords.escape(value)}"]
end
ssh_args = ssh_args.join(" ")
cmd = [
"/usr/bin/ionice", "-c", "3",
"/usr/bin/nice",
"/usr/bin/timeout", timeout.to_s,
"/usr/bin/rsync",
"--archive",
"--one-file-system",
"--delete-delay",
"--numeric-ids",
"--partial",
"--bwlimit=#{bwlimit}",
# "--rsync-path='nice rsync'",
"--rsh", "ssh #{ssh_args}",
]
cmd << "--verbose" if verbose
cmd << "--dry-run" if dry_run
cmd += options if options
cmd += [
remote + ":" + path,
backup_dir,
]
puts cmd.inspect if verbose
unless system(*cmd)
exit_status = $?.exitstatus
STDERR.puts("rsync failed (exit status #{exit_status})")
end
target = File.join(dest, "month")
last_target = File.join(dest, "month.last")
if now.day == 1 or !File.exists?(target)
copy_backup(backup_dir, target, last_target, remove_last)
end
target = File.join(dest, "week")
last_target = File.join(dest, "week.last")
if now.wday == 0 or !File.exists?(target)
copy_backup(backup_dir, target, last_target, remove_last)
end
end