dmitryv-backup 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +125 -0
- data/LICENSE +20 -0
- data/README.md +180 -0
- data/VERSION +1 -0
- data/bin/backup +108 -0
- data/generators/backup/backup_generator.rb +69 -0
- data/generators/backup/templates/backup.rake +56 -0
- data/generators/backup/templates/backup.rb +229 -0
- data/generators/backup/templates/create_backup_tables.rb +18 -0
- data/generators/backup_update/backup_update_generator.rb +50 -0
- data/generators/backup_update/templates/migrations/update_backup_tables.rb +27 -0
- data/lib/backup.rb +131 -0
- data/lib/backup/adapters/archive.rb +34 -0
- data/lib/backup/adapters/base.rb +138 -0
- data/lib/backup/adapters/custom.rb +41 -0
- data/lib/backup/adapters/mysql.rb +60 -0
- data/lib/backup/adapters/postgresql.rb +56 -0
- data/lib/backup/adapters/sqlite.rb +25 -0
- data/lib/backup/command_helper.rb +11 -0
- data/lib/backup/compressors/base.rb +7 -0
- data/lib/backup/compressors/gzip.rb +9 -0
- data/lib/backup/compressors/seven_zip.rb +9 -0
- data/lib/backup/configuration/adapter.rb +21 -0
- data/lib/backup/configuration/adapter_options.rb +8 -0
- data/lib/backup/configuration/attributes.rb +19 -0
- data/lib/backup/configuration/base.rb +77 -0
- data/lib/backup/configuration/helpers.rb +24 -0
- data/lib/backup/configuration/mail.rb +20 -0
- data/lib/backup/configuration/smtp.rb +8 -0
- data/lib/backup/configuration/storage.rb +8 -0
- data/lib/backup/connection/cloudfiles.rb +75 -0
- data/lib/backup/connection/s3.rb +85 -0
- data/lib/backup/environment/base.rb +12 -0
- data/lib/backup/environment/rails_configuration.rb +15 -0
- data/lib/backup/environment/unix_configuration.rb +109 -0
- data/lib/backup/mail/base.rb +97 -0
- data/lib/backup/mail/mail.txt +7 -0
- data/lib/backup/record/base.rb +65 -0
- data/lib/backup/record/cloudfiles.rb +28 -0
- data/lib/backup/record/ftp.rb +39 -0
- data/lib/backup/record/local.rb +26 -0
- data/lib/backup/record/s3.rb +26 -0
- data/lib/backup/record/scp.rb +33 -0
- data/lib/backup/record/sftp.rb +38 -0
- data/lib/backup/storage/base.rb +10 -0
- data/lib/backup/storage/cloudfiles.rb +16 -0
- data/lib/backup/storage/ftp.rb +38 -0
- data/lib/backup/storage/local.rb +22 -0
- data/lib/backup/storage/s3.rb +17 -0
- data/lib/backup/storage/scp.rb +30 -0
- data/lib/backup/storage/sftp.rb +31 -0
- data/lib/generators/backup/USAGE +10 -0
- data/lib/generators/backup/backup_generator.rb +47 -0
- data/lib/generators/backup/templates/backup.rake +56 -0
- data/lib/generators/backup/templates/backup.rb +229 -0
- data/lib/generators/backup/templates/create_backup_tables.rb +18 -0
- data/setup/backup.rb +231 -0
- data/setup/backup.sqlite3 +0 -0
- metadata +271 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module Backup
|
2
|
+
module Record
|
3
|
+
class Base < ActiveRecord::Base
|
4
|
+
|
5
|
+
if DB_CONNECTION_SETTINGS
|
6
|
+
establish_connection(DB_CONNECTION_SETTINGS)
|
7
|
+
end
|
8
|
+
|
9
|
+
set_table_name 'backup'
|
10
|
+
|
11
|
+
default_scope :order => 'created_at desc'
|
12
|
+
|
13
|
+
# Callbacks
|
14
|
+
after_save :clean_backups
|
15
|
+
|
16
|
+
# Attributes
|
17
|
+
attr_accessor :adapter_config, :keep_backups
|
18
|
+
|
19
|
+
# Receives the options hash and stores it
|
20
|
+
def load_adapter(adapter)
|
21
|
+
self.adapter_config = adapter
|
22
|
+
self.trigger = adapter.procedure.trigger
|
23
|
+
self.adapter = adapter.procedure.adapter_name.to_s
|
24
|
+
self.filename = adapter.final_file
|
25
|
+
self.keep_backups = adapter.procedure.attributes['keep_backups']
|
26
|
+
|
27
|
+
# TODO calculate md5sum of file
|
28
|
+
load_specific_settings(adapter) if respond_to?(:load_specific_settings)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Destroys all backups for the specified trigger from Remote Server (FTP)
|
32
|
+
def self.destroy_all_backups(procedure, trigger)
|
33
|
+
backups = self.all(:conditions => {:trigger => trigger})
|
34
|
+
unless backups.empty?
|
35
|
+
# Derived classes must implement this method!
|
36
|
+
self.destroy_backups(procedure, backups)
|
37
|
+
|
38
|
+
puts "\nAll \"#{procedure.trigger}\" backups destroyed.\n\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Maintains the backup file amount on the remote server
|
45
|
+
# This is invoked after a successful record save
|
46
|
+
# This deletes the oldest files when the backup limit has been exceeded
|
47
|
+
def clean_backups
|
48
|
+
if keep_backups.is_a?(Integer)
|
49
|
+
backups = self.class.all(:conditions => {:trigger => trigger})
|
50
|
+
backups_to_destroy = backups[keep_backups, backups.size] || []
|
51
|
+
|
52
|
+
unless backups_to_destroy.empty?
|
53
|
+
# Derived classes must implement this method!
|
54
|
+
self.class.destroy_backups(adapter_config.procedure, backups_to_destroy)
|
55
|
+
|
56
|
+
puts "\nBackup storage for \"#{trigger}\" is limited to #{keep_backups} backups."
|
57
|
+
puts "\nThe #{keep_backups} most recent backups are now stored on the remote server.\n\n"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'backup/connection/cloudfiles'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class CloudFiles < Backup::Record::Base
|
6
|
+
|
7
|
+
alias_attribute :container, :bucket
|
8
|
+
|
9
|
+
def load_specific_settings(adapter)
|
10
|
+
self.container = adapter.procedure.get_storage_configuration.attributes['container']
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.destroy_backups(procedure, backups)
|
16
|
+
cf = Backup::Connection::CloudFiles.new
|
17
|
+
cf.static_initialize(procedure)
|
18
|
+
cf.connect
|
19
|
+
backups.each do |backup|
|
20
|
+
puts "\nDestroying backup \"#{backup.filename}\" from container \"#{backup.container}\"."
|
21
|
+
cf.destroy(backup.filename, backup.container)
|
22
|
+
backup.destroy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class FTP < Backup::Record::Base
|
6
|
+
|
7
|
+
attr_accessor :ip, :user, :password
|
8
|
+
|
9
|
+
def load_specific_settings(adapter)
|
10
|
+
%w(ip user password path).each do |method|
|
11
|
+
send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.destroy_backups(procedure, backups)
|
18
|
+
ip = procedure.get_storage_configuration.attributes['ip']
|
19
|
+
user = procedure.get_storage_configuration.attributes['user']
|
20
|
+
password = procedure.get_storage_configuration.attributes['password']
|
21
|
+
|
22
|
+
Net::FTP.open(ip, user, password) do |ftp|
|
23
|
+
backups.each do |backup|
|
24
|
+
puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
|
25
|
+
begin
|
26
|
+
ftp.chdir(backup.path)
|
27
|
+
ftp.delete(backup.filename)
|
28
|
+
backup.destroy
|
29
|
+
rescue
|
30
|
+
puts "Could not find backup #{backup.path}/#{backup.filename}."
|
31
|
+
backup.destroy
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Backup
|
2
|
+
module Record
|
3
|
+
class Local < Backup::Record::Base
|
4
|
+
|
5
|
+
def load_specific_settings(adapter)
|
6
|
+
self.path = adapter.procedure.get_storage_configuration.attributes['path']
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
class << self
|
12
|
+
include Backup::CommandHelper
|
13
|
+
|
14
|
+
def destroy_backups(procedure, backups)
|
15
|
+
backups.each do |backup|
|
16
|
+
puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
|
17
|
+
run "rm #{File.join(backup.path, backup.filename)}"
|
18
|
+
backup.destroy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'backup/connection/s3'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class S3 < Backup::Record::Base
|
6
|
+
|
7
|
+
def load_specific_settings(adapter)
|
8
|
+
self.bucket = adapter.procedure.get_storage_configuration.attributes['bucket']
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.destroy_backups(procedure, backups)
|
14
|
+
s3 = Backup::Connection::S3.new
|
15
|
+
s3.static_initialize(procedure)
|
16
|
+
s3.connect
|
17
|
+
backups.each do |backup|
|
18
|
+
puts "\nDestroying backup \"#{backup.filename}\" from bucket \"#{backup.bucket}\"."
|
19
|
+
s3.destroy(backup.filename, backup.bucket)
|
20
|
+
backup.destroy
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'net/scp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class SCP < Backup::Record::Base
|
6
|
+
|
7
|
+
attr_accessor :ip, :user, :password
|
8
|
+
|
9
|
+
def load_specific_settings(adapter)
|
10
|
+
%w(ip user password path).each do |method|
|
11
|
+
send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.destroy_backups(procedure, backups)
|
18
|
+
ip = procedure.get_storage_configuration.attributes['ip']
|
19
|
+
user = procedure.get_storage_configuration.attributes['user']
|
20
|
+
password = procedure.get_storage_configuration.attributes['password']
|
21
|
+
|
22
|
+
Net::SSH.start(ip, user, :password => password) do |ssh|
|
23
|
+
backups.each do |backup|
|
24
|
+
puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
|
25
|
+
ssh.exec("rm #{File.join(backup.path, backup.filename)}")
|
26
|
+
backup.destroy
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class SFTP < Backup::Record::Base
|
6
|
+
|
7
|
+
attr_accessor :ip, :user, :password
|
8
|
+
|
9
|
+
def load_specific_settings(adapter)
|
10
|
+
%w(ip user password path).each do |method|
|
11
|
+
send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.destroy_backups(procedure, backups)
|
18
|
+
ip = procedure.get_storage_configuration.attributes['ip']
|
19
|
+
user = procedure.get_storage_configuration.attributes['user']
|
20
|
+
password = procedure.get_storage_configuration.attributes['password']
|
21
|
+
|
22
|
+
Net::SFTP.start(ip, user, :password => password) do |sftp|
|
23
|
+
backups.each do |backup|
|
24
|
+
puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
|
25
|
+
begin
|
26
|
+
sftp.remove!(File.join(backup.path, backup.filename))
|
27
|
+
backup.destroy
|
28
|
+
rescue
|
29
|
+
puts "Could not find backup #{backup.path}/#{backup.filename}.."
|
30
|
+
backup.destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'backup/connection/cloudfiles'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class CloudFiles < Base
|
6
|
+
|
7
|
+
# Stores the backup file on the remote server using Rackspace Cloud Files
|
8
|
+
def initialize(adapter)
|
9
|
+
cf = Backup::Connection::CloudFiles.new(adapter)
|
10
|
+
cf.connect
|
11
|
+
cf.store
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class FTP < Backup::Storage::Base
|
6
|
+
|
7
|
+
attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on the remote server using FTP
|
10
|
+
def initialize(adapter)
|
11
|
+
%w(ip user password path).each do |method|
|
12
|
+
send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
13
|
+
end
|
14
|
+
|
15
|
+
final_file = adapter.final_file
|
16
|
+
tmp_path = adapter.tmp_path
|
17
|
+
|
18
|
+
Net::FTP.open(ip, user, password) do |ftp|
|
19
|
+
begin
|
20
|
+
ftp.chdir(path)
|
21
|
+
rescue
|
22
|
+
puts "Could not find or access \"#{path}\" on \"#{ip}\", please ensure this directory exists and is accessible by the user \"#{user}\"."
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
|
28
|
+
ftp.putbinaryfile(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
|
29
|
+
rescue
|
30
|
+
puts "Could not save file to backup server. Is the \"#{path}\" directory writable?"
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Backup
|
2
|
+
module Storage
|
3
|
+
class Local < Base
|
4
|
+
|
5
|
+
# Store on same machine, preferentially in a different hard drive or in
|
6
|
+
# a mounted network path (NFS, Samba, etc)
|
7
|
+
attr_accessor :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on local machine
|
10
|
+
def initialize(adapter)
|
11
|
+
self.path = adapter.procedure.get_storage_configuration.attributes['path']
|
12
|
+
self.tmp_path = adapter.tmp_path
|
13
|
+
self.final_file = adapter.final_file
|
14
|
+
|
15
|
+
run "mkdir -p #{path}"
|
16
|
+
run "cp #{File.join(tmp_path, final_file).gsub('\ ', ' ')} #{File.join(path, final_file)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'backup/connection/s3'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class S3 < Base
|
6
|
+
|
7
|
+
# Stores the backup file on the remote server using S3
|
8
|
+
def initialize(adapter)
|
9
|
+
s3 = Backup::Connection::S3.new(adapter)
|
10
|
+
s3.connect
|
11
|
+
log("Saving '#{s3.final_file}' to s3 bucket '#{s3.s3_bucket}'")
|
12
|
+
s3.store
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'net/scp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class SCP < Base
|
6
|
+
|
7
|
+
attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on the remote server using SCP
|
10
|
+
def initialize(adapter)
|
11
|
+
%w(ip user password path).each do |method|
|
12
|
+
send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
13
|
+
end
|
14
|
+
|
15
|
+
final_file = adapter.final_file
|
16
|
+
tmp_path = adapter.tmp_path
|
17
|
+
|
18
|
+
Net::SSH.start(ip, user, :password => password) do |ssh|
|
19
|
+
ssh.exec "mkdir -p #{path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
|
23
|
+
Net::SCP.start(ip, user, :password => password) do |scp|
|
24
|
+
scp.upload! File.join(tmp_path, final_file).gsub('\ ', ' '), path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class SFTP < Base
|
6
|
+
|
7
|
+
attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on the remote server using SFTP
|
10
|
+
def initialize(adapter)
|
11
|
+
%w(ip user password path).each do |method|
|
12
|
+
send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
|
13
|
+
end
|
14
|
+
|
15
|
+
final_file = adapter.final_file
|
16
|
+
tmp_path = adapter.tmp_path
|
17
|
+
|
18
|
+
Net::SFTP.start(ip, user, :password => password) do |sftp|
|
19
|
+
begin
|
20
|
+
puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
|
21
|
+
sftp.upload!(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
|
22
|
+
rescue
|
23
|
+
puts "Could not find \"#{path}\" on \"#{ip}\", please ensure this directory exists."
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|