Skip to content

Commit

Permalink
Refs #10755 - Recurring logic, task groups
Browse files Browse the repository at this point in the history
  • Loading branch information
adamruzicka committed Nov 30, 2015
1 parent 0da7d80 commit b86be5c
Show file tree
Hide file tree
Showing 31 changed files with 966 additions and 2 deletions.
41 changes: 41 additions & 0 deletions app/assets/javascripts/trigger_form.js
@@ -0,0 +1,41 @@
function trigger_form_selector_binds(form_name, form_object_name) {
var form = $('form#' + form_name);
var trigger_mode_selector = form.find('input.trigger_mode_selector');
var input_type_selector = form.find('.form-control#input_type_selector');

trigger_mode_selector.on('click', function () {
["future", "immediate", "recurring"].forEach(function(type) {
form.find('fieldset.trigger_mode_form#trigger_mode_' + type).hide();
});
form.find('fieldset.trigger_mode_form#trigger_mode_' + $(this).val()).show();
});

input_type_selector.on('change', function () {
var fieldset = form.find('fieldset.trigger_mode_form#trigger_mode_recurring');
['cronline', 'monthly', 'weekly', 'hourly', 'daily'].forEach(function(type) {
fieldset.find('fieldset.input_type_form#input_type_' + type).hide();
})
fieldset.find('fieldset.input_type_form#input_type_' + $(this).val()).show();
var timepicker = fieldset.find('fieldset.input_type_form#time_picker');
if($(this).val() == 'cronline') {
timepicker.hide();
} else {
var hour_picker = timepicker.find('#s2id_triggering_time_time_4i');
if($(this).val() == 'hourly') {
hour_picker.hide();
} else {
hour_picker.show();
}
timepicker.show();
}
});

form.find('input.end_time_limit_selector').on('click', function() {
var o = form.find('fieldset#trigger_mode_recurring fieldset#end_time_limit_form');
if($(this).val() === 'true') {
o.show();
} else {
o.hide();
};
});
};
33 changes: 33 additions & 0 deletions app/controllers/foreman_tasks/recurring_logics_controller.rb
@@ -0,0 +1,33 @@
module ForemanTasks
class RecurringLogicsController < ::ApplicationController

before_filter :find_recurring_logic, :only => [:show, :cancel]

def index
@recurring_logics = filter(resource_base)
end

def show
end

def cancel
@recurring_logic.cancel
redirect_to :action => :index
end

def controller_name
'foreman_tasks_recurring_logics'
end

private

def find_recurring_logic
@recurring_logic ||= ::ForemanTasks::RecurringLogic.find(params[:id])
end

def filter(scope)
scope.search_for(params[:search]).paginate(:page => params[:page])
end

end
end
144 changes: 144 additions & 0 deletions app/helpers/foreman_tasks/foreman_tasks_helper.rb
@@ -0,0 +1,144 @@
module ForemanTasks
module ForemanTasksHelper

def recurring_logic_state(recurring_logic)
icon, status = case recurring_logic.state
when 'active'
'glyphicon-info-sign'
when 'finished'
['glyphicon-ok-sign', 'status-ok']
when 'cancelled'
['glyphicon-warning-sign', 'status-error']
else
'glyphicon-question-sign'
end
content_tag(:i, '&nbsp'.html_safe, :class => "glyphicon #{icon}") + content_tag(:span, recurring_logic.humanized_state, :class => status)
end

def recurring_logic_action_buttons(recurring_logic)
buttons = []
if authorized_for(:permission => :edit_recurring_logics, :auth_object => recurring_logic)
buttons << link_to(N_("Cancel"), cancel_foreman_tasks_recurring_logic_path(recurring_logic), :method => :post, :class => 'btn btn-danger') unless %w(cancelled finished).include? recurring_logic.state
end
button_group buttons
end

def recurring_logic_next_occurrence(recurring_logic)
if %w(finished cancelled).include? recurring_logic.state
N_('-')
else
recurring_logic.next_occurrence_time
end
end

def time_f(f, attr, field_options = {}, time_options = {}, html_options = {})
f.fields_for attr do |fields|
field(fields, attr, field_options) do
fields.time_select attr, time_options, html_options
end
end
end

def datetime_f(f, attr, field_options = {}, datetime_options = {}, html_options = {})
f.fields_for attr do |fields|
field(fields, attr, field_options) do
fields.datetime_select attr, datetime_options, html_options
end
end
end

def inline_checkboxes_f(f, attr, field_options = {}, checkboxes = {}, options = {})
field(f, attr, field_options) do
checkboxes.map do |key, name|
[f.check_box(key, options), " #{name} "]
end.flatten.join('')
end
end

def trigger_selector(f, triggering = Triggering.new, options = {})
render :partial => 'common/trigger_form', :locals => { :f => f, :triggering => triggering }
end

private

def future_mode_fieldset(f, triggering)
tags = []
tags << text_f(f, :start_at_raw, :label => _('Start at'), :placeholder => 'YYYY-mm-dd HH:MM')
tags << text_f(f, :start_before_raw, :label => _('Start before'), :placeholder => 'YYYY-mm-dd HH:MM')
content_tag(:fieldset, nil, :id => "trigger_mode_future", :class => "trigger_mode_form #{'hidden' unless triggering.future?}") do
tags.join.html_safe
end
end

def recurring_mode_fieldset(f, triggering)
tags = []
tags << selectable_f(f, :input_type, %w(cronline monthly weekly daily hourly), {}, :label => _("Repeats"), :id => 'input_type_selector')
tags += [
cronline_fieldset(f, triggering),
monthly_fieldset(f, triggering),
weekly_fieldset(f, triggering),
time_picker_fieldset(f, triggering)
]

content_tag(:fieldset, nil, :id => 'trigger_mode_recurring', :class => "trigger_mode_form #{'hidden' unless triggering.recurring?}") do
tags.join.html_safe
end
end

def cronline_fieldset(f, triggering)
options = [
_('is minute (range: 0-59)'),
_('is hour (range: 0-23)'),
_('is day of month (range: 1-31)'),
_('is month (range: 1-12)'),
_('is day of week (range: 0-6)')
].map { |opt| content_tag(:li, opt) }.join
help = content_tag(:span, nil, :class => 'help-inline') do
popover(_('Explanation'),
_("Cron line format 'a b c d e', where:<br><ol type=\"a\">#{options}</ol>"))
end
content_tag(:fieldset, nil, :class => "input_type_form #{'hidden' unless triggering.input_type == :cronline}", :id => "input_type_cronline") do
text_f f, :cronline, :label => _('Cron line'), :placeholder => '* * * * *', :help_inline => help
end
end

def monthly_fieldset(f, triggering)
content_tag(:fieldset, nil, :id => "input_type_monthly", :class => "input_type_form #{'hidden' unless triggering.input_type == :monthly}") do
text_f(f, :days, :label => _('Days'), :placeholder => '1,2...')
end
end

def weekly_fieldset(f, triggering)
content_tag(:fieldset, nil, :id => 'input_type_weekly', :class => "input_type_form #{'hidden' unless triggering.input_type == :weekly}") do
f.fields_for :days_of_week do |days_of_week|
inline_checkboxes_f(days_of_week,
:weekday,
{ :label => _("Days of week") },
{ 1 => _("Mon"),
2 => _("Tue"),
3 => _("Wed"),
4 => _("Thu"),
5 => _("Fri"),
6 => _("Sat"),
7 => _("Sun") })
end
end
end

def time_picker_fieldset(f, triggering)
tags = []
tags << content_tag(:fieldset, nil, :id => 'time_picker', :class => "input_type_form #{'hidden' if triggering.input_type == :cronline}") do
time_f(f, :time, { :label => _("At"), :id => 'something' }, { :time_separator => '' })
end
tags << number_f(f, :max_iteration, :label => _('Repeat N times'), :min => 1, :placeholder => 'N')
tags << field(f, :end_time_limit_select, :label => _("Ends"), :control_group_id => "end_time_limit_select") do
radio_button_f(f, :end_time_limited, :value => false, :checked=> true, :text => _("Never"), :class => 'end_time_limit_selector') +
radio_button_f(f, :end_time_limited, :value => true, :text => _("On"), :class => 'end_time_limit_selector')
end
tags << content_tag(:fieldset, nil, :id => 'end_time_limit_form', :class => "input_type_form #{'hidden' unless triggering.end_time_limited}") do
datetime_f f, :end_time, { :label => _("Ends at") }, { :use_month_numbers => true, :use_two_digit_numbers => true, :time_separator => '' }
end
tags.join.html_safe
end
end
end
8 changes: 8 additions & 0 deletions app/helpers/foreman_tasks/tasks_helper.rb
Expand Up @@ -12,5 +12,13 @@ def format_task_input(task, include_action = false)
end.join('; ')
parts.join(" ")
end

def format_recurring_logic_limit(thing)
if thing.nil?
content_tag(:i, N_('Unlimited'))
else
thing
end
end
end
end
4 changes: 4 additions & 0 deletions app/lib/actions/base.rb
Expand Up @@ -55,5 +55,9 @@ def already_running?
ForemanTasks::Task::DynflowTask.for_action(self.class).
running.where('external_id != ?', execution_plan_id).any?
end

def serializer_class
::Actions::Serializers::ActiveRecordSerializer
end
end
end
6 changes: 5 additions & 1 deletion app/lib/actions/entry_action.rb
Expand Up @@ -57,7 +57,11 @@ def self.all_action_names
end

def delay(_schedule_options, *args)
Serializers::ActiveRecordSerializer.new args
self.serializer_class.new args
end

def self.serializer_class
Serializers::ActiveRecordSerializer
end

end
Expand Down
39 changes: 39 additions & 0 deletions app/lib/actions/middleware/inherit_task_groups.rb
@@ -0,0 +1,39 @@
module Actions
module Middleware
class InheritTaskGroups < Dynflow::Middleware

def delay(*args)
pass *args
end

def plan(*args)
inherit_task_groups
pass *args
end

def run(*args)
pass *args
collect_children_task_groups
end

def finalize
pass
end

private

def inherit_task_groups
task.add_missing_task_groups(task.parent_task.task_groups) if task.parent_task
end

def collect_children_task_groups
task.add_missing_task_groups task.sub_tasks.map(&:task_groups).flatten
end

def task
@task ||= action.task
end
end
end
end

42 changes: 42 additions & 0 deletions app/lib/actions/middleware/recurring_logic.rb
@@ -0,0 +1,42 @@
module Actions
module Middleware
class RecurringLogic < ::Dynflow::Middleware

# ::Actions::Middleware::RecurringLogic
#
# A middleware designed to make action repeatable.
# After an action is delayed, it checks whether the delay_options
# hash contains an id of a recurring logic. If so, it adds the task
# to the recurring logic's task group, otherwise does nothing.
#
# After the action's plan phase the middleware checks if the task
# is associated with a task group of any recurring logic, in which case
# it triggers another repeat using the task group's recurring logic,
# otherwise does nothing.
def delay(delay_options, *args)
pass(delay_options, *args).tap do
if delay_options[:recurring_logic_id]
task.add_missing_task_groups(recurring_logic(delay_options).task_group)
end
end
end

def plan(*args)
pass(*args).tap do
task_group = task.task_groups.find { |tg| tg.is_a? ::ForemanTasks::TaskGroups::RecurringLogicTaskGroup }
task_group.recurring_logic.trigger_repeat(action.class, *args) if task_group
end
end

private

def recurring_logic(delay_options)
ForemanTasks::RecurringLogic.find(delay_options[:recurring_logic_id])
end

def task
@task ||= action.task
end
end
end
end

0 comments on commit b86be5c

Please sign in to comment.