client_management/app/models/site_server_file_backup.rb

180 lines
6.4 KiB
Ruby

class SiteServerFileBackup
include Mongoid::Document
include Mongoid::Timestamps
PeriodsTypes=['daily','weekly','monthly']
DefaultConf='/etc/rsnapshot.conf'
belongs_to :site_server
field :disable, type: Boolean, default: false
field :backup_time, type: String, default: '04:15'
field :path , type: String ,default: '/home/backup/orbit'
field :period, type: Integer, default: 0 # 0 => daily , 1 => weekly , 2 => monthly
field :retain_count, type: Integer, default: 7
field :rsnapshot_conf_path, type: String, default: DefaultConf
field :backup_dir, type: String, default: '/home/{{user_name}}'
field :backup_prefix, type: String, default: 'localhost/'
field :need_rewrite, type: Boolean, default: true
before_create do
unless @skip_callback
other_rsnapshot_conf_path = self.class.where(:site_server_id=>self.site_server_id).pluck(:rsnapshot_conf_path)
if other_rsnapshot_conf_path.include?(self.rsnapshot_conf_path)
max_postfix = other_rsnapshot_conf_path.select{|s| s.start_with?('/etc/')}.map{|s| tmp = File.basename(s).split('.conf')[0].match(/\d+/); tmp ? tmp[0].to_i : 0}.max
self.rsnapshot_conf_path = "#{self.rsnapshot_conf_path.split('.conf')[0]}#{max_postfix + 1}.conf"
end
end
end
before_save do
unless @skip_callback
if self.changed?
self.need_rewrite = true
end
end
end
def to_crontab_script
# minute , hour, day(of month, 1~31), month(1~12), day(of week,0~6)
crontab_time = ['*', '*', '*', '*', '*']
time = self.backup_time.split(':').map{|s| s.sub(/0(\d)/){$1}} rescue []
crontab_time[0] = time[1].to_i.to_s rescue '0'
crontab_time[1] = time[0].to_i.to_s rescue '0'
if self.period == 1 #weekly
crontab_time[4] = '0' #every Sunday
elsif self.period == 2 #monthly
crontab_time[2] = '1' #day 1 of every month
end
extra_arg = ''
if self.rsnapshot_conf_path != DefaultConf
extra_arg = " -c #{rsnapshot_conf_path} "
end
"#{crontab_time.join(' ')} /usr/bin/rsnapshot #{extra_arg}#{period_text}"
end
def period_text
PeriodsTypes[self.period]
end
def retain_text
"retain\t#{self.period_text}\t#{self.retain_count}"
end
def override_text(contents, org_regex, new_text)
exist_flag = false
contents = contents.gsub(org_regex).with_index do |s, i|
if i == 0
exist_flag = true
"#{new_text}#{(s[-1] == "\n" ? "\n" : '')}"
else
"##{s}"
end
end
unless exist_flag
contents += "\n#{new_text}"
end
contents
end
def gsub_rsnapshot_conf(contents)
contents = self.override_text(contents, /^retain\s+[^\n]*(\n|$)/m, self.retain_text)
contents = self.override_text(contents, /^snapshot_root\s+[^\n]*(\n|$)/m, self.snapshot_root)
contents = self.override_text(contents, /^backup\s+[^\n]*(\n|$)/m, self.backup_path)
end
def backup_path
home_dir = self.backup_dir
if home_dir.include?('{{user_name}}')
user_name = self.site_server.account
home_dir = home_dir.sub('{{user_name}}', user_name)
self.backup_dir = home_dir
self.save
end
backup_text = "backup\t#{home_dir}/\t#{self.backup_prefix}"
extra_exclude_path = []
if self.class.class_variable_defined?(:@@db_backup_paths)
extra_exclude_path = @@db_backup_paths.select{|p| p.start_with?(home_dir)}
end
if self.path.start_with?(home_dir)
extra_exclude_path << self.path if extra_exclude_path.exclude?(self.path)
end
if extra_exclude_path.count != 0
tmp = []
extra_exclude_path.each do |s|
if tmp.count == 0
tmp << s
else
if tmp.exclude?(s)
s2 = File.dirname(s)
idx = tmp.index{|ss| File.dirname(ss) == s2}
if idx
tmp[idx] = s2
else
tmp << s
end
end
end
end
backup_text += "\t#{tmp.map{|p| "exclude=#{p}"}.join(',')}"
end
backup_text
end
def snapshot_root
"snapshot_root\t#{self.path}"
end
def self.read_from_crontab_line(crontab_line)
if crontab_line.include?('rsnapshot')
tmp = crontab_line.split(/\s+[^\s]*rsnapshot\s+/) # ex: ["15 4 * * *", "daily"], ["15 4 * * *", "-c /etc/rsnapshot.conf daily"]
if tmp.count == 2
crontab_time = tmp[0].split(/\s+/)
period_text = tmp[1]
rsnapshot_conf_path = DefaultConf
if period_text.start_with?('-c')
tmp2 = period_text.split(/\s+/)
if tmp2.count == 3
rsnapshot_conf_path = tmp2[1]
period_text = tmp2[2]
else
period_text = tmp2.last
end
end
if PeriodsTypes.include?(period_text)
period = PeriodsTypes.index(period_text)
else
period = 0
end
time = "#{crontab_time[1].rjust(2, "0")}:#{crontab_time[0].rjust(2, "0")}"
return [time, rsnapshot_conf_path, period]
else
return nil
end
else
return nil
end
end
def self.clean_crontab_setting(crontab_lines_str)
crontab_lines = crontab_lines_str.split("\n")
crontab_lines_str = crontab_lines.map do |crontab_line|
if crontab_line.include?('rsnapshot')
nil
else
crontab_line
end
end.compact.join("\n")
end
def self.init_class_variables(site_server)
@@db_backup_paths = SiteServerDbBackup.where(:site_server_id=> site_server.id, :disable=>false).pluck(:path)
end
def self.write_crontab_setting(site_server, crontab_lines_str)
new_crontab_lines = self.where(:site_server_id=> site_server.id, :disable=>false).flat_map{|file_backup| file_backup.to_crontab_script.split("\n")}
crontab_lines = crontab_lines_str.split("\n")
exists_indices = new_crontab_lines.map do |new_crontab_line|
match_reg = ::Regexp.new(new_crontab_line.gsub(/\s+/, '\s+'))
crontab_lines.index{|l| l.match(match_reg)}
end
crontab_lines = crontab_lines.map.with_index do |crontab_line, i|
if crontab_line.include?('rsnapshot') && exists_indices.exclude?(i)
nil
else
crontab_line
end
end
exists_indices.each_with_index do |idx, i|
if idx.nil?
crontab_lines << new_crontab_lines[i]
end
end
crontab_lines_str = crontab_lines.compact.join("\n")
end
end