Page MenuHomeGRNET

No OneTemporary

File Metadata

Created
Fri, Aug 8, 8:44 PM
diff --git a/app/models/fileset.rb b/app/models/fileset.rb
index 4b31562..0b50436 100644
--- a/app/models/fileset.rb
+++ b/app/models/fileset.rb
@@ -1,131 +1,131 @@
# Fileset model is the application representation of Bacula's Fileset.
# It has references to a host and job templates.
class Fileset < ActiveRecord::Base
establish_connection ARCHIVING_CONF
serialize :exclude_directions, Array
serialize :include_directions, JSON
attr_accessor :include_files
belongs_to :host
has_many :job_templates
validates :name, presence: true, uniqueness: { scope: :host }
validate :has_included_files, on: :create
validates_with NameValidator
before_save :sanitize_exclude_directions, :sanitize_include_directions
DEFAULT_EXCLUDED = %w{/var/lib/bacula /proc /tmp /.journal /.fsck /bacula}
DEFAULT_INCLUDE_OPTIONS = { signature: :SHA1, compression: :GZIP }
DEFAULT_INCLUDE_FILE_LIST = ['/']
# Constructs an array where each element is a line for the Fileset's bacula config
#
# @return [Array]
def to_bacula_config_array
['FileSet {'] +
[" Name = \"#{name_for_config}\""] +
include_directions_to_config_array +
exclude_directions_to_config_array +
['}']
end
# Provides a human readable projection of the fileset
#
# @return [String]
def human_readable
result = "Directories:\n"
result << "\t* " << include_directions['file'].join("\n\t* ")
if exclude_directions.present?
result << "\n\nExcluded:\n"
result << "\t* " << exclude_directions.join("\n\t*")
end
result
end
# Generates a name that will be used for the configuration file.
# It is the name that will be sent to Bacula through the configuration
# files.
#
# @return [String]
def name_for_config
[host.name, name].join(' ')
end
# Returns the hosts that have enabled jobs that use this fileset
#
# @return [ActiveRecord::Relation] the participating hosts
def participating_hosts
Host.joins(:job_templates).where(job_templates: { enabled: true, fileset_id: id }).uniq
end
# Creates a default fileset resource for a simple config
- def default_resource
+ def default_resource(name, time_hex)
@include_files = DEFAULT_INCLUDE_FILE_LIST
- self.name = "default_fileset"
+ self.name = "files_#{name}_#{time_hex}"
self.exclude_directions = DEFAULT_EXCLUDED
save!
self
end
private
def has_included_files
if include_files.blank? || include_files.all?(&:blank?)
errors.add(:include_files, :cant_be_blank)
end
end
def sanitize_include_directions
files = include_files.compact.uniq.keep_if(&:present?)
return false if files.blank?
self.include_directions = { options: DEFAULT_INCLUDE_OPTIONS, file: files }
end
def sanitize_exclude_directions
self.exclude_directions = exclude_directions.keep_if(&:present?).uniq rescue nil
end
def exclude_directions_to_config_array
return [] if exclude_directions.empty?
[' Exclude {'] +
exclude_directions.map { |x| " File = \"#{x}\"" } +
[' }']
end
def include_directions_to_config_array
return [] if include_directions.blank?
[" Include {"] +
included_options +
included_files +
[' }']
end
def included_options
formatted = [" Options {"]
options = include_directions.deep_symbolize_keys[:options].
reverse_merge(DEFAULT_INCLUDE_OPTIONS)
options.each do |k,v|
if not [:wildfile].include? k
formatted << " #{k} = #{v}"
else
formatted << v.map { |f| " #{k} = \"#{f}\"" }
end
end
formatted << " }"
formatted
end
def included_files
include_directions['file'].map { |f| " File = #{f}" }
end
def included_wildfile
include_directions['wildfile'].map { |f| " wildfile = \"#{f}\"" }.join("\n")
end
end
diff --git a/app/models/job_template.rb b/app/models/job_template.rb
index f03f399..38f09b6 100644
--- a/app/models/job_template.rb
+++ b/app/models/job_template.rb
@@ -1,145 +1,145 @@
# JobTemplate class is a helper class that enables us to configure Bacula job
# configurations, without messing with Bacula's native models (Job).
# It has a unique:
#
# * host
# * fileset
# * schedule
class JobTemplate < ActiveRecord::Base
establish_connection ARCHIVING_CONF
enum job_type: { backup: 0, restore: 1, verify: 2, admin: 3 }
belongs_to :host
belongs_to :fileset
belongs_to :schedule
validates :name, :fileset_id, presence: true
validates :schedule_id, presence: true, unless: :restore?
validates :name, uniqueness: { scope: :host }
validates_with NameValidator
before_save :set_job_type
after_save :notify_host
scope :enabled, -> { where(enabled: true) }
# Constructs an array where each element is a line for the Job's bacula config
#
# @return [Array]
def to_bacula_config_array
['Job {'] +
options_array.map { |x| " #{x}" } +
runscript.map { |x| " #{x}" } +
job_settings.map { |k,v| " #{k.capitalize} = #{v}" } +
['}']
end
# Fetches the Job's priority
def priority
job_settings[:priority]
end
# Helper method for the job template's enabled status
def enabled_human
enabled? ? 'yes' : 'no'
end
# Helper method for the job template's schedule name
#
# @return [String] The schedule's name or nothing
def schedule_human
schedule.present? ? schedule.name : '-'
end
# Generates a name that will be used for the configuration file.
# It is the name that will be sent to Bacula through the configuration
# files.
#
# @return [String]
def name_for_config
"#{host.name} #{name}"
end
# Sends a hot backup request to Bacula via BaculaHandler
def backup_now
return false if not (enabled? && baculized? && backup?)
host.backup_now(name)
end
# Handles the returned attribues for api
#
# @return [Hash] of the desired attributes for api use
def api_json
{
id: id,
name: name,
fileset: fileset.name_for_config
}
end
# Creates a default job resource for a simple config
- def default_resource
- self.name = "default_backup_job"
+ def default_resource(name, time_hex)
+ self.name = "job_#{name}_#{time_hex}"
save!
self
end
private
def name_format
unless name =~ /^[a-zA-Z0-1\.-_ ]+$/
self.errors.add(:name, :format)
end
end
def notify_host
host.recalculate
end
# Sets the default job_type as backup
def set_job_type
self.job_type = :backup if job_type.nil?
end
def options_array
result = [
"Name = \"#{name_for_config}\"",
"Enabled = #{enabled_human}",
"FileSet = \"#{fileset.name_for_config}\"",
"Client = \"#{host.name}\"",
"Type = \"#{job_type.capitalize}\"",
"Schedule = \"#{schedule.name_for_config}\""
]
if client_before_run_file.present?
result += ["Client Run Before Job = \"#{client_before_run_file}\""]
end
if client_after_run_file.present?
result += ["Client Run After Job = \"#{client_after_run_file}\""]
end
result
end
def runscript
[
'RunScript {',
" command = \"bash #{Archiving.settings[:quota_checker]} \\\"%c\\\" #{host.quota}\"",
' RunsOnClient = no',
' RunsWhen = Before',
'}'
]
end
# Fetches and memoizes the general configuration settings for Jobs
#
# @see ConfigurationSetting.current_job_settings
# @return [Hash] containing the settings
def job_settings
messages = host.email_recipients.any? ? host.message_name : :Standard
@job_settings ||= ConfigurationSetting.current_job_settings.merge(messages: messages)
end
end
diff --git a/app/models/schedule.rb b/app/models/schedule.rb
index 8348edf..0f7a446 100644
--- a/app/models/schedule.rb
+++ b/app/models/schedule.rb
@@ -1,66 +1,66 @@
# Schedule model is the application representation of Bacula's Schedule.
# It has references to a host and multiple schedule run in order to provide
# the desired Bacula configuration
class Schedule < ActiveRecord::Base
establish_connection ARCHIVING_CONF
has_many :schedule_runs
belongs_to :host
has_many :job_templates
validates :name, presence: true
validates :name, uniqueness: { scope: :host }
validates_with NameValidator
accepts_nested_attributes_for :schedule_runs, allow_destroy: true
# Constructs an array where each element is a line for the Schedule's bacula config
#
# @return [Array]
def to_bacula_config_array
['Schedule {'] +
[" Name = \"#{name_for_config}\""] +
schedule_runs.map {|r| " Run = #{r.schedule_line}" } +
['}']
end
# Provide a human readable projection of the schedule
#
# @return [String]
def human_readable
schedule_runs.map(&:human_readable).join("\n")
end
# Generates a name that will be used for the configuration file.
# It is the name that will be sent to Bacula through the configuration
# files.
#
# @return [String]
def name_for_config
[host.name, name].join(' ')
end
# Returns the hosts that have enabled jobs that use this schedule
#
# @return [ActiveRecord::Colletion] the participating hosts
def participating_hosts
Host.joins(:job_templates).where(job_templates: { enabled: true, schedule_id: id }).uniq
end
# Creates a default schedule resource for a simple config
- def default_resource(day, hour, minute)
+ def default_resource(time_hex, day, hour, minute)
time = [hour, minute].map { |x| x.to_s.rjust(2, '0') }.join(':')
full_day = "first #{day}"
diff_days = "second-fifth #{day}"
inc_days = (SimpleConfiguration::DAYS.values - [day]).join(',')
- self.name = "daily_at_#{time.gsub(':','_')}"
+ self.name = "daily_at_#{time.gsub(':','_')}_#{time_hex}"
save!
self.schedule_runs.create(level: :full, time: time, day: full_day)
self.schedule_runs.create(level: :differential, time: time, day: diff_days)
self.schedule_runs.create(level: :incremental , time: time, day: inc_days)
self
end
end
diff --git a/app/models/simple_configuration.rb b/app/models/simple_configuration.rb
index 75bca93..c656c41 100644
--- a/app/models/simple_configuration.rb
+++ b/app/models/simple_configuration.rb
@@ -1,51 +1,54 @@
class SimpleConfiguration < ActiveRecord::Base
establish_connection ARCHIVING_CONF
DAYS = {
monday: :mon,
tuesday: :tue,
wednesday: :wed,
thursday: :thu,
friday: :fri,
saturday: :sat,
sunday: :sun
}
enum day: { monday: 0, tuesday: 1, wednesday: 2, thursday: 3, friday: 4, saturday: 5, sunday: 6 }
belongs_to :host
validates :host, :day, :hour, :minute, presence: true
validates :hour, numericality: { greater_than_or_equal: 0, less_then: 24 }
validates :minute, numericality: { greater_than_or_equal: 0, less_then: 60 }
validates_with NameValidator
# Initializes the configuration's 3 parameters randomnly.
# Default configurations must be randomized in order to distribute the backup server's
# load.
def randomize
self.day = SimpleConfiguration.days.keys.sample
self.hour = rand(24)
self.minute = rand(60)
end
# The day abbreviation
#
# @return [Symbol]
def day_short
DAYS[day.to_sym]
end
# Creates a default config, by adding resources for:
#
# * schedule
# * fileset
# * job
#
# Each resource handles its own defaults.
def create_config
- schedule = host.schedules.new.default_resource(day_short, hour, minute)
- fileset = host.filesets.new.default_resource
- job = host.job_templates.new(fileset_id: fileset.id, schedule_id: schedule.id).default_resource
+ time_hex = Digest::MD5.hexdigest(Time.now.to_f.to_s).first(4)
+
+ schedule = host.schedules.new.default_resource(time_hex, day_short, hour, minute)
+ fileset = host.filesets.new.default_resource(name, time_hex)
+ host.job_templates.new(fileset_id: fileset.id, schedule_id: schedule.id).
+ default_resource(name, time_hex)
end
end

Event Timeline