121 lines
3.3 KiB
Ruby
Executable File
121 lines
3.3 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
require 'json'
|
|
require 'pathname'
|
|
require 'fileutils'
|
|
require 'optparse'
|
|
|
|
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")
|
|
BACKUP_LOCATIONS = %w{home srv etc usr/local}
|
|
CONFIG_PATH="/etc/lxc/container.json"
|
|
|
|
BACKUP_PATH="/backup"
|
|
#ZFS_DATASET="backup/rsync"
|
|
|
|
def load_config
|
|
return JSON.load(File.open(CONFIG_PATH))
|
|
rescue SystemCallError => e
|
|
abort "failed to open configuration '#{CONFIG_PATH}', #{e}"
|
|
rescue JSON::ParserError => e
|
|
abort "failed to parse configuration '#{CONFIG_PATH}', #{e}"
|
|
end
|
|
|
|
module Utils
|
|
def self.sh(cmd, *args)
|
|
pretty_args = args.map {|arg| "'#{arg}'"}
|
|
puts ([cmd] + pretty_args).join(" ")
|
|
system(cmd, *args)
|
|
end
|
|
|
|
def self.backup(remote_path, local_path)
|
|
sh RSYNC_CMD, *RSYNC_ARGS, remote_path, local_path
|
|
end
|
|
end
|
|
|
|
class Container
|
|
def initialize(name, backup_paths, backup_scripts)
|
|
@name = name
|
|
@backup_paths = backup_paths
|
|
@backup_scripts = backup_scripts
|
|
@path = LXC_PATH.join(name, "rootfs")
|
|
end
|
|
def backup
|
|
backup_paths = BACKUP_LOCATIONS
|
|
if @backup_paths.is_a?(Array)
|
|
backup_paths += @backup_paths
|
|
end
|
|
backup_paths.each do |relative_path|
|
|
backup_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
|
|
def run_backup_scripts
|
|
if @backup_scripts.is_a?(Array)
|
|
@backup_scripts.each do |script|
|
|
backup_script(script)
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
def backup_script(script)
|
|
unless script.is_a?(Hash)
|
|
abort("backup-scripts: Expected an Object, got #{script.class}")
|
|
end
|
|
command = script["command"]
|
|
if command.nil?
|
|
abort("command not set for backup-scripts for container '#{@name}'")
|
|
end
|
|
backupname = script["backupname"]
|
|
if backupname.nil?
|
|
abort("backupname not set for backup-scripts for container '#{@name}'")
|
|
end
|
|
backupname = backupname.gsub("/", "")
|
|
FileUtils.mkdir_p(backupname)
|
|
puts "cd #{backupname}"
|
|
Dir.chdir(backupname) do
|
|
Utils.sh(command)
|
|
end
|
|
end
|
|
|
|
def empty_directory?(path)
|
|
return false unless Dir.exists?(path)
|
|
return Dir.entries(path).size <= 2 # - [".", ".."]
|
|
end
|
|
end
|
|
|
|
options = {}
|
|
OptionParser.new do |opts|
|
|
opts.on("-l", "--label [LABEL]", String, "daily, hourly") do |label|
|
|
options[:label] = label
|
|
end
|
|
opts.on("-k", "--keep [NUMBER]", Integer, "Number of backups to keep") do |keep|
|
|
options[:keep] = keep
|
|
end
|
|
end.parse!
|
|
|
|
puts "cd #{BACKUP_PATH}"
|
|
Dir.chdir(BACKUP_PATH) do
|
|
BACKUP_LOCATIONS.each do |location|
|
|
FileUtils.mkdir_p(location)
|
|
Utils.backup("/" + location, "localhost")
|
|
end
|
|
config = load_config
|
|
config["network"].each do |container, data|
|
|
next if data["lxc"] == false
|
|
container = Container.new(container, data["backup-paths"], data["backup-scripts"])
|
|
container.backup
|
|
container.run_backup_scripts
|
|
end
|
|
#args = ["--label", options[:label], "--keep", options[:keep].to_s, ZFS_DATASET]
|
|
#Utils.sh(ZFS_SNAPSHOT_CMD, *args)
|
|
end
|