diff --git a/app/assets/javascripts/jobs.js b/app/assets/javascripts/jobs.js index 40a62e0..f899386 100644 --- a/app/assets/javascripts/jobs.js +++ b/app/assets/javascripts/jobs.js @@ -1,22 +1,23 @@ $(document).ready(function() { $("#job_template_schedule_id").change(function() { showSchedule(); }); $("#job_template_fileset_id").change(function() { showFileset(); }); }); function showSchedule() { var scheduleId = $("#job_template_schedule_id").val(); $.ajax({ - url: "/hosts/" + hostId + "/schedules/" + scheduleId + url: "/hosts/" + hostId + "/schedules/" + scheduleId, + data: jobIdParam }); } function showFileset() { var filesetId = $("#job_template_fileset_id").val(); $.ajax({ url: "/hosts/" + hostId + "/filesets/" + filesetId }); } diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb index 5816f06..de15270 100644 --- a/app/controllers/jobs_controller.rb +++ b/app/controllers/jobs_controller.rb @@ -1,80 +1,81 @@ class JobsController < ApplicationController before_action :require_logged_in before_action :fetch_host, only: [:new, :edit, :show, :create, :update, :toggle_enable, :backup_now] before_action :fetch_job, only: [:show, :edit, :update, :destroy, :toggle_enable, :backup_now] # GET /jobs def new @job = @host.job_templates.new + @schedule = @host.schedules.find(params[:schedule_id]) if params[:schedule_id] end # POST /jobs def create @job = @host.job_templates.new(fetch_params) if @job.save flash[:success] = 'Job created successfully' redirect_to host_path(@host) else render :new end end # GET /jobs/1 def show; end # GET /jobs/1/edit def edit @fileset = @host.filesets.find(params[:fileset_id]) if params[:fileset_id] @schedule = @host.schedules.find(params[:schedule_id]) if params[:schedule_id] end # PUT /jobs/1 def update if @job.update_attributes(fetch_params) flash[:success] = 'Job updated' redirect_to host_job_path(@host, @job) else render :edit end end # DELETE /jobs/1 def destroy end # PATCH /hosts/1/jobs/1/enable def toggle_enable @job.enabled = !@job.enabled @job.save flash[:success] = @job.enabled? ? 'Job enabled' : 'Job disabled' redirect_to host_path(@host) end # POST /hosts/1/jobs/1/backup_now def backup_now if @job.backup_now flash[:success] = 'Backup directive was sent to bacula. Backup will be taken in a while' else flash[:error] = 'Backup was not sent, try again later' end redirect_to client_path(@host.client) end private def fetch_job @job = @host.job_templates.find(params[:id]) end def fetch_host @host = current_user.hosts.find(params[:host_id]) end def fetch_params params.require(:job_template). permit(:name, :fileset_id, :schedule_id, :client_before_run_file, :client_after_run_file) end end diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index fcfb33f..5345ba4 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -1,58 +1,72 @@ class SchedulesController < ApplicationController before_action :require_logged_in - before_action :fetch_host, only: [:new, :create, :show] - before_action :fetch_job_id, only: [:new, :create] + before_action :fetch_host, only: [:new, :create, :show, :edit, :update] + before_action :fetch_job_id, only: [:new, :create, :show, :edit, :update] + before_action :fetch_schedule, only: [:show, :edit, :update] def new @schedule = @host.schedules.new + @schedule.schedule_runs.build end def show - @schedule = @host.schedules.find(params[:id]) - respond_to do |format| format.js {} end end def edit end def update + if @schedule.update(fetch_params) + flash[:success] = 'Schedule updated successfully' + if @job_id + redirect_to edit_host_job_path(@host, @job_id, schedule_id: @schedule.id) + else + redirect_to new_host_job_path(@host, schedule_id: @schedule.id) + end + else + render :edit + end end def create @schedule = @host.schedules.new(fetch_params) @schedule.runtime = params[:schedule][:runtime] if params[:schedule][:runtime] if @schedule.save flash[:success] = 'Schedule created successfully' if @job_id.present? redirect_to edit_host_job_path(@host, @job_id, schedule_id: @schedule.id) else redirect_to new_host_job_path(@host, schedule_id: @schedule.id) end else render :new end end def destroy end private + def fetch_schedule + @schedule = @host.schedules.find(params[:id]) + end + def fetch_host @host = current_user.hosts.find(params[:host_id]) end def fetch_job_id @job_id = @host.job_templates.find(params[:job_id]).id if params[:job_id].present? end def fetch_params params.require(:schedule). - permit(:name, { schedule_runs_attributes: [[:level, :month, :day, :time]] }) + permit(:name, { schedule_runs_attributes: [[:id, :level, :month, :day, :time]] }) end end diff --git a/app/models/schedule_run.rb b/app/models/schedule_run.rb index af2ef1a..5d60ebb 100644 --- a/app/models/schedule_run.rb +++ b/app/models/schedule_run.rb @@ -1,133 +1,137 @@ # 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 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 + # Composes the schedule line for the bacula configuration # # @return [String] def schedule_line [ level_to_config, 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 end diff --git a/app/views/jobs/_form.html.erb b/app/views/jobs/_form.html.erb index 652ea6f..d5524b5 100644 --- a/app/views/jobs/_form.html.erb +++ b/app/views/jobs/_form.html.erb @@ -1,33 +1,38 @@ <%= bootstrap_form_for(@job, url: @job.new_record? ? host_jobs_path : host_job_path(@host, @job), layout: :horizontal, label_col: 'col-xs-5', control_col: 'col-xs-5') do |f| %> <%= f.text_field :name %> <%= select_with_errors_and_button( @job, :job_template, :fileset_id, 'Fileset', options_from_collection_for_select( @host.filesets, :id, :name, params[:fileset_id] || @job.fileset_id), new_host_fileset_path(@host, job_id: @job.id)) %> <% if !@job.restore? %> <%= select_with_errors_and_button( @job, :job_template, :schedule_id, 'Schedule', options_from_collection_for_select( @host.schedules, :id, :name, params[:schedule_id] || @job.schedule_id), new_host_schedule_path(@host, job_id: @job.id)) %> <% end %> <%= f.text_field :client_before_run_file, label: 'Client Run Before Job' %> <%= f.text_field :client_after_run_file, label: 'Client Run After Job' %>
- <%= schedule.to_bacula_config_array.join("\n") %> +<%= schedule.to_bacula_config_array.join("\n") %>
eg: jan-mar, feb, monthly
<%= r.text_field :day, placeholder: '[week | week-range] day | day-range', required: true %>eg: first sun, second-fifth mon, mon-sat
- <%= r.time_field :time, placeholder: 'HH:MM', required: true %> + <%= r.text_field :time, placeholder: 'HH:MM', required: true %>