diff --git a/app/models/schedule.rb b/app/models/schedule.rb index d81bd2b..11cb7c9 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -1,43 +1,50 @@ # 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 end diff --git a/app/models/schedule_run.rb b/app/models/schedule_run.rb index 455835a..1d65c76 100644 --- a/app/models/schedule_run.rb +++ b/app/models/schedule_run.rb @@ -1,152 +1,159 @@ # ScheduleRun is a helper class that modelizes the run directives of a schedule # resource. # # It can have 3 levels: # # * full # * differential # * incremental # # Each ScheduleRun instance holds info about the execution time of the schedule: # # * month specification is optional and is described by: # - month: full name (eg april) | month name three first letters (eg jul) # - month_range: month-month # - monthly: 'monthly' # * day specification is required and is described by: # - week_number (optional): weeks number in full text (eg: third, fourth) # - week_range (optional): week_number-week_number # - day: first three letters of day (eg: mon, fri) # - day_range: day-day # * time specification is required and is described by: # - 24h time (eg: 03:00) # # Schedule Run examples: # # Level=Full monthly first mon at 12:21 # Level=Full first sun at 12:34 # Level=Differential second-fifth sat at 15:23 # Level=Incremental mon-sat at 08:00 class ScheduleRun < ActiveRecord::Base establish_connection ARCHIVING_CONF enum level: { full: 0, differential: 1, incremental: 2 } MONTH_KW = %w{jan feb mar apr may jun jul aug sep oct nov dec january february march april may june july august september october november december} DAYS = (1..31).to_a WDAY_KW = %w{mon tue wed thu fri sat sun} WEEK_KW = %w{first second third fourth fifth} belongs_to :schedule validates :day, :time, :level, presence: true validate :correct_chars validate :month_valid, if: "month.present?" validate :time_valid validate :day_valid def self.options_for_select levels.keys.zip levels.keys end # Builds a sane default schedule_run # # @return [ScheduleRun] def default_run self.level = :full self.day = "first sun" self.time = '04:00' end # Composes the schedule line for the bacula configuration # # @return [String] def schedule_line [ level_to_config, pool_to_config, month, day, "at #{time}"].join(" ") end + # Provides a human readable projection of the schedule run + # + # @return [String] + def human_readable + ["#{level.capitalize} backup", month, day, "at #{time}"].join(' ') + end + private def correct_chars [:month, :day, :time].each do |x| if self.send(x) && self.send(x).to_s.gsub(/[0-9a-zA-Z\-:]/, '').present? self.errors.add(x, 'Invalid characters') end end end def month_valid if !month_format? && !valid_month_range? self.errors.add(:month, 'Invalid month') end end def month_format? month_regex = "^(#{MONTH_KW.join('|')}|monthly)$" month.match(month_regex).present? end def valid_month_range? months = month.split('-') return false if months.length != 2 MONTH_KW.index(months.last) % 12 - MONTH_KW.index(months.first) % 12 > 0 end def time_valid if !time.match(/^([01][0-9]|2[0-3]):[0-5][0-9]$/) self.errors.add(:time, 'Invalid time') end end def day_valid components = day.split(' ') if components.length < 1 || components.length > 2 self.errors.add(:day, 'Invalid day') return false end if !valid_day?(components.last) && !valid_day_range?(components.last) self.errors.add(:day, 'Invalid day') return false end if components.length == 2 && !valid_week?(components.first) && !valid_week_range?(components.first) self.errors.add(:day, 'Invalid day') return false end true end def valid_day?(a_day) WDAY_KW.include? a_day end def valid_day_range?(a_range) days = a_range.split('-') return false if days.length != 2 WDAY_KW.index(days.last) - WDAY_KW.index(days.first) > 0 end def valid_week?(a_week) WEEK_KW.include? a_week end def valid_week_range?(a_range) weeks = a_range.split('-') return false if weeks.length != 2 WEEK_KW.index(weeks.last) - WEEK_KW.index(weeks.first) > 0 end def level_to_config "Level=#{level.capitalize}" end def pool_to_config "Pool=#{ConfigurationSetting.current_pool_settings[level.to_sym]}" end end diff --git a/app/views/jobs/_job_details.html.erb b/app/views/jobs/_job_details.html.erb index 08cc79c..ed0c5f3 100644 --- a/app/views/jobs/_job_details.html.erb +++ b/app/views/jobs/_job_details.html.erb @@ -1,51 +1,51 @@
<% if @job.restore? %> <% else %> <% end %>
Name <%= @job.name %>
Type <%= @job.job_type %>
Client <%= @host.name %>
Fileset
<%= @job.fileset.human_readable %>
Restore Location <%= @job.restore_location %>
Schedule -
<%= @job.schedule.to_bacula_config_array.join("\n") %>
+
<%= @job.schedule.human_readable %>
Client Run Before Job <%= @job.client_before_run_file %>
Client Run After Job <%= @job.client_after_run_file %>
Created <%= I18n.l(@job.created_at, format: :long) %>
Enabled <%= @job.enabled_human %>
diff --git a/app/views/jobs/_modals.html.erb b/app/views/jobs/_modals.html.erb index 2b3107b..c60c95a 100644 --- a/app/views/jobs/_modals.html.erb +++ b/app/views/jobs/_modals.html.erb @@ -1,34 +1,34 @@ <% @schedules.each do |schedule| %> <% end %> <% @filesets.each do |fileset| %> <% end %> diff --git a/app/views/jobs/_schedule.html.erb b/app/views/jobs/_schedule.html.erb index 0b32487..42937b2 100644 --- a/app/views/jobs/_schedule.html.erb +++ b/app/views/jobs/_schedule.html.erb @@ -1,20 +1,20 @@ <% if job_id = (@job.try(:id) || @job_id) %> <% url_options = { job_id: job_id } %> <% else %> <% url_options = {} %> <% end %>

Schedule "<%= schedule.name %>" <%= link_to edit_host_schedule_path(@host, schedule.id, url_options) do %> <% end %>


-<%= schedule.to_bacula_config_array.join("\n") %>
+<%= schedule.human_readable %>