use duplicity
This commit is contained in:
parent
5b9939ce8e
commit
c148ae99df
125
backup-container
125
backup-container
@ -2,37 +2,27 @@
|
|||||||
require 'json'
|
require 'json'
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
require 'optparse'
|
require 'open3'
|
||||||
|
|
||||||
RSYNC_CMD="rsync"
|
|
||||||
RSYNC_ARGS=["--archive", "--delete", "--numeric-ids"]
|
|
||||||
ZFS_SNAPSHOT_CMD="zfs-auto-snapshot"
|
|
||||||
# --quiet --syslog --label=daily --keep=31 backup/rsync"
|
|
||||||
LXC_PATH=Pathname.new("/data/containers")
|
LXC_PATH=Pathname.new("/data/containers")
|
||||||
BACKUP_LOCATIONS = %w{home srv etc usr/local}
|
BACKUP_LOCATIONS = %w{home srv etc usr/local}
|
||||||
CONFIG_PATH="/etc/lxc/container.json"
|
CONFIG_PATH="/etc/lxc/container.json"
|
||||||
|
DUPLICITY_PATH= Pathname.new("/data/duplicity/")
|
||||||
BACKUP_PATH="/backup"
|
BACKUP_PATH="file:///mnt/backup/duplicity"
|
||||||
#ZFS_DATASET="backup/rsync"
|
FULL_BACKUP_COUNT=2
|
||||||
|
|
||||||
def load_config
|
def load_config
|
||||||
return JSON.load(File.open(CONFIG_PATH))
|
return JSON.load(File.open(CONFIG_PATH))
|
||||||
rescue SystemCallError => e
|
rescue SystemCallError => e
|
||||||
abort "failed to open configuration '#{CONFIG_PATH}', #{e}"
|
abort "failed to open configuration '#{CONFIG_PATH}', #{e}"
|
||||||
rescue JSON::ParserError => e
|
rescue JSON::ParserError => e
|
||||||
abort "failed to parse configuration '#{CONFIG_PATH}', #{e}"
|
abort "failed to parse configuration '#{CONFIG_PATH}', #{e}"
|
||||||
end
|
end
|
||||||
|
|
||||||
module Utils
|
def sh(cmd, env={}, *args)
|
||||||
def self.sh(cmd, *args)
|
pretty_args = args.map {|arg| "'#{arg}'"}
|
||||||
pretty_args = args.map {|arg| "'#{arg}'"}
|
puts ([cmd] + pretty_args).join(" ")
|
||||||
puts ([cmd] + pretty_args).join(" ")
|
system(env, cmd, *args)
|
||||||
system(cmd, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.backup(remote_path, local_path)
|
|
||||||
sh RSYNC_CMD, *RSYNC_ARGS, remote_path, local_path
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class Container
|
class Container
|
||||||
@ -42,26 +32,22 @@ class Container
|
|||||||
@backup_scripts = backup_scripts
|
@backup_scripts = backup_scripts
|
||||||
@path = LXC_PATH.join(name, "rootfs")
|
@path = LXC_PATH.join(name, "rootfs")
|
||||||
end
|
end
|
||||||
def backup
|
def backup_paths
|
||||||
backup_paths = BACKUP_LOCATIONS
|
paths = BACKUP_LOCATIONS
|
||||||
if @backup_paths.is_a?(Array)
|
if @backup_paths.is_a?(Array)
|
||||||
backup_paths += @backup_paths
|
paths += @backup_paths
|
||||||
end
|
end
|
||||||
backup_paths.each do |relative_path|
|
paths.map do |relative_path|
|
||||||
backup_path = @path.join(relative_path)
|
@path.join(relative_path)
|
||||||
local_path = Pathname.new(@name).join(relative_path)
|
|
||||||
FileUtils.mkdir_p(local_path)
|
|
||||||
|
|
||||||
if File.exists?(backup_path) && !empty_directory?(backup_path)
|
|
||||||
Utils.backup(backup_path.to_s, File.dirname(local_path))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
def run_backup_scripts
|
def run_backup_scripts
|
||||||
if @backup_scripts.is_a?(Array)
|
if @backup_scripts.is_a?(Array)
|
||||||
@backup_scripts.each do |script|
|
@backup_scripts.map do |script|
|
||||||
backup_script(script)
|
backup_script(script)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
[]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -78,12 +64,13 @@ class Container
|
|||||||
if backupname.nil?
|
if backupname.nil?
|
||||||
abort("backupname not set for backup-scripts for container '#{@name}'")
|
abort("backupname not set for backup-scripts for container '#{@name}'")
|
||||||
end
|
end
|
||||||
backupname = backupname.gsub("/", "")
|
backupname = DUPLICITY_PATH.join(backupname.gsub("/", ""))
|
||||||
FileUtils.mkdir_p(backupname)
|
FileUtils.mkdir_p(backupname)
|
||||||
puts "cd #{backupname}"
|
puts "cd #{backupname}"
|
||||||
Dir.chdir(backupname) do
|
Dir.chdir(backupname) do
|
||||||
Utils.sh(command)
|
sh(command)
|
||||||
end
|
end
|
||||||
|
backupname
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty_directory?(path)
|
def empty_directory?(path)
|
||||||
@ -92,29 +79,53 @@ class Container
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
options = {}
|
if ARGV.size > 0
|
||||||
OptionParser.new do |opts|
|
if ["full", "incr"].include?(ARGV.first)
|
||||||
opts.on("-l", "--label [LABEL]", String, "daily, hourly") do |label|
|
action = ARGV.first
|
||||||
options[:label] = label
|
else
|
||||||
|
$stderr.puts "action must be full or incr"
|
||||||
|
$stderr.puts "USAGE #{File.basename($0)} [full|incr]"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
opts.on("-k", "--keep [NUMBER]", Integer, "Number of backups to keep") do |keep|
|
else
|
||||||
options[:keep] = keep
|
action = "incr"
|
||||||
end
|
end
|
||||||
end.parse!
|
|
||||||
|
Dir.chdir(DUPLICITY_PATH) do
|
||||||
puts "cd #{BACKUP_PATH}"
|
config = load_config
|
||||||
Dir.chdir(BACKUP_PATH) do
|
backup_paths = BACKUP_LOCATIONS.map do |location|
|
||||||
BACKUP_LOCATIONS.each do |location|
|
"/#{location}"
|
||||||
FileUtils.mkdir_p(location)
|
end
|
||||||
Utils.backup("/" + location, "localhost")
|
config["network"].each do |container, data|
|
||||||
end
|
next if data["lxc"] == false
|
||||||
config = load_config
|
container = Container.new(container, data["backup-paths"], data["backup-scripts"])
|
||||||
config["network"].each do |container, data|
|
backup_paths += container.backup_paths
|
||||||
next if data["lxc"] == false
|
backup_paths += container.run_backup_scripts
|
||||||
container = Container.new(container, data["backup-paths"], data["backup-scripts"])
|
end
|
||||||
container.backup
|
gpg_args = ["--gpg-options", "--secret-keyring ./duplicity.sec --keyring ./duplicity.pub"]
|
||||||
container.run_backup_scripts
|
args = gpg_args + ["--verbosity", "notice",
|
||||||
end
|
"--encrypt-key", "AF5834A6",
|
||||||
#args = ["--label", options[:label], "--keep", options[:keep].to_s, ZFS_DATASET]
|
"--sign-key", "AF5834A6",
|
||||||
#Utils.sh(ZFS_SNAPSHOT_CMD, *args)
|
"--full-if-older-than", "60D",
|
||||||
|
"--num-retries", "3",
|
||||||
|
"--asynchronous-upload",
|
||||||
|
"--volsize", "250",
|
||||||
|
"--include-globbing-filelist", "/dev/stdin",
|
||||||
|
"--archive-dir", "cache",
|
||||||
|
"--log-file", "/var/log/duplicity.log",
|
||||||
|
action, "/", BACKUP_PATH]
|
||||||
|
env = { "PASSPHRASE" => File.read("pgp-passphrase") }
|
||||||
|
stdin, stdout, stderr = Open3.popen3(env, "duplicity", *args)
|
||||||
|
if action == "full"
|
||||||
|
sh("duplicity", env, "remove-all-but-n-full", FULL_BACKUP_COUNT.to_s, BACKUP_PATH, *gpg_args)
|
||||||
|
end
|
||||||
|
backup_paths.each do |path|
|
||||||
|
stdin.puts("+ #{path}")
|
||||||
|
puts("+ #{path}")
|
||||||
|
end
|
||||||
|
stdin.puts("- **")
|
||||||
|
puts("- **")
|
||||||
|
stdin.close
|
||||||
|
puts stdout.read
|
||||||
|
puts stderr.read
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user