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