foreman-tasks 0.6.9 → 0.6.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,9 +8,13 @@ module ForemanTasks
8
8
 
9
9
  def index
10
10
  params[:order] ||= 'started_at DESC'
11
- @tasks = Task.
12
- search_for(params[:search], :order => params[:order]).
13
- paginate(:page => params[:page])
11
+ @tasks = filter(Task)
12
+ end
13
+
14
+ def sub_tasks
15
+ task = Task.find(params[:id])
16
+ @tasks = filter(task.sub_tasks)
17
+ render :index
14
18
  end
15
19
 
16
20
  def cancel_step
@@ -62,5 +66,10 @@ module ForemanTasks
62
66
  ForemanTasks::Task::DynflowTask.find(params[:id])
63
67
  end
64
68
 
69
+ def filter(scope)
70
+ scope.search_for(params[:search], :order => params[:order]).
71
+ paginate(:page => params[:page])
72
+ end
73
+
65
74
  end
66
75
  end
@@ -1,6 +1,10 @@
1
1
  module Actions
2
2
  class Base < Dynflow::Action
3
3
 
4
+ def task
5
+ @task ||= ::ForemanTasks::Task::DynflowTask.find_by_external_id!(execution_plan_id)
6
+ end
7
+
4
8
  # This method says what data form input gets into the task details in Rest API
5
9
  # By default, it sends the whole input there.
6
10
  def task_input
@@ -0,0 +1,166 @@
1
+ module Actions
2
+
3
+ class BulkAction < Actions::EntryAction
4
+
5
+ middleware.use Actions::Middleware::KeepCurrentUser
6
+
7
+ SubPlanFinished = Algebrick.type do
8
+ fields! :execution_plan_id => String,
9
+ :success => type { variants TrueClass, FalseClass }
10
+ end
11
+
12
+ # == Parameters:
13
+ # actions_class::
14
+ # Class of action to trigger on targets
15
+ # targets::
16
+ # Array of objects on which the action_class should be triggered
17
+ # *args::
18
+ # Arguments that all the targets share
19
+ def plan(action_class, targets, *args)
20
+ check_targets!(targets)
21
+ plan_self(:action_class => action_class.to_s,
22
+ :target_ids => targets.map(&:id),
23
+ :target_class => targets.first.class.to_s,
24
+ :args => args)
25
+ end
26
+
27
+ def humanized_name
28
+ _("Bulk action")
29
+ end
30
+
31
+ def humanized_input
32
+ a_sub_task = task.sub_tasks.first
33
+ if a_sub_task
34
+ [a_sub_task.humanized[:action].to_s.downcase] +
35
+ Array(a_sub_task.humanized[:input]) + ['...']
36
+ end
37
+ end
38
+
39
+ def humanized_output
40
+ return unless counts_set?
41
+ _("%{total} tasks, %{success} success, %{failed} fail") %
42
+ { total: output[:total_count],
43
+ success: output[:success_count],
44
+ failed: output[:failed_count] }
45
+ end
46
+
47
+ def run(event = nil)
48
+ case(event)
49
+ when nil
50
+ if output[:total_count]
51
+ resume
52
+ else
53
+ initiate_sub_plans
54
+ end
55
+ when SubPlanFinished
56
+ mark_as_done(event.execution_plan_id, event.success)
57
+ if done?
58
+ check_for_errors!
59
+ else
60
+ suspend
61
+ end
62
+ end
63
+ end
64
+
65
+ # @api override when the logic for the initiation of the subtasks
66
+ # is different from the default one
67
+ def create_sub_plans
68
+ action_class = input[:action_class].constantize
69
+ target_class = input[:target_class].constantize
70
+ targets = target_class.where(:id => input[:target_ids])
71
+
72
+ targets.map do |target|
73
+ ForemanTasks.trigger(action_class, target, *input[:args])
74
+ end
75
+ end
76
+
77
+ def initiate_sub_plans
78
+ output.update(total_count: 0,
79
+ failed_count: 0,
80
+ success_count: 0)
81
+
82
+ planned, failed = create_sub_plans.partition(&:planned?)
83
+
84
+ sub_plan_ids = ((planned + failed).map(&:execution_plan_id))
85
+ set_parent_task_id(sub_plan_ids)
86
+
87
+ output[:total_count] = sub_plan_ids.size
88
+ output[:failed_count] = failed.size
89
+
90
+ if planned.any?
91
+ wait_for_sub_plans(planned)
92
+ else
93
+ check_for_errors!
94
+ end
95
+ end
96
+
97
+ def resume
98
+ if task.sub_tasks.active.any?
99
+ fail _("Some sub tasks are still not finished")
100
+ end
101
+ end
102
+
103
+ def rescue_strategy
104
+ Dynflow::Action::Rescue::Skip
105
+ end
106
+
107
+ def wait_for_sub_plans(plans)
108
+ suspend do |suspended_action|
109
+ plans.each do |plan|
110
+ plan.finished.do_then do |value|
111
+ suspended_action << SubPlanFinished[plan.execution_plan_id,
112
+ value.result == :success]
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def mark_as_done(plan_id, success)
119
+ if success
120
+ output[:success_count] += 1
121
+ else
122
+ output[:failed_count] += 1
123
+ end
124
+ end
125
+
126
+ def done?
127
+ if counts_set?
128
+ output[:total_count] - output[:success_count] - output[:failed_count] <= 0
129
+ else
130
+ false
131
+ end
132
+ end
133
+
134
+ def run_progress
135
+ if counts_set?
136
+ (output[:success_count] + output[:failed_count]).to_f / output[:total_count]
137
+ else
138
+ 0.1
139
+ end
140
+ end
141
+
142
+ def counts_set?
143
+ output[:total_count] && output[:success_count] && output[:failed_count]
144
+ end
145
+
146
+ def set_parent_task_id(sub_plan_ids)
147
+ ForemanTasks::Task::DynflowTask.
148
+ where(external_id: sub_plan_ids).
149
+ update_all(parent_task_id: task.id)
150
+ end
151
+
152
+ def check_targets!(targets)
153
+ if targets.empty?
154
+ fail _("Empty bulk action")
155
+ end
156
+ if targets.map(&:class).uniq.length > 1
157
+ fail _("The targets are of different types")
158
+ end
159
+ end
160
+
161
+ def check_for_errors!
162
+ fail _("A sub task failed") if output[:failed_count] > 0
163
+ end
164
+
165
+ end
166
+ end
@@ -1,10 +1,6 @@
1
1
  module Actions
2
2
  module Helpers
3
3
  module Lock
4
- def task
5
- ::ForemanTasks::Task::DynflowTask.find_by_external_id!(execution_plan_id)
6
- end
7
-
8
4
  # @see Lock.exclusive!
9
5
  def exclusive_lock!(resource)
10
6
  phase! Dynflow::Action::Plan
@@ -0,0 +1,41 @@
1
+
2
+ #
3
+ # Copyright 2014 Red Hat, Inc.
4
+ #
5
+ # This software is licensed to you under the GNU General Public
6
+ # License as published by the Free Software Foundation; either version
7
+ # 2 of the License (GPLv2) or (at your option) any later version.
8
+ # There is NO WARRANTY for this software, express or implied,
9
+ # including the implied warranties of MERCHANTABILITY,
10
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
11
+ # have received a copy of GPLv2 along with this software; if not, see
12
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13
+
14
+ module Actions
15
+ module Middleware
16
+
17
+ class KeepCurrentUser < Dynflow::Middleware
18
+ def plan(*args)
19
+ pass(*args).tap { action.input[:current_user_id] = User.current.id }
20
+ end
21
+
22
+ def run(*args)
23
+ restore_curent_user { pass(*args) }
24
+ end
25
+
26
+ def finalize
27
+ restore_curent_user { pass }
28
+ end
29
+
30
+ private
31
+
32
+ def restore_curent_user
33
+ User.current = User.find(action.input[:current_user_id])
34
+ yield
35
+ ensure
36
+ User.current = nil
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -8,6 +8,8 @@ module ForemanTasks
8
8
  self.primary_key = :id
9
9
  before_create :generate_id
10
10
 
11
+ belongs_to :parent_task, :class_name => 'ForemanTasks::Task'
12
+ has_many :sub_tasks, :class_name => 'ForemanTasks::Task', :foreign_key => :parent_task_id
11
13
  has_many :locks
12
14
 
13
15
  # in fact, the task has only one owner but Rails don't let you to
@@ -101,6 +101,16 @@
101
101
  <span class="param-name"><%= _("Params") %>:</span>
102
102
  <span class="param-value"><%= format_task_input(@task) %></span>
103
103
  </div>
104
+ <% if @task.parent_task %>
105
+ <div>
106
+ <span class="param-name"><%= link_to(_("Parent task"), foreman_tasks_task_path(@task.parent_task)) %></span>
107
+ </div>
108
+ <% end %>
109
+ <% if @task.sub_tasks.any? %>
110
+ <div>
111
+ <span class="param-name"><%= link_to(_("Sub tasks"), sub_tasks_foreman_tasks_task_path(@task)) %></span>
112
+ </div>
113
+ <% end %>
104
114
  <div class="row">
105
115
  <div class="col-xs-11">
106
116
  <div class="progress progress-striped <%= 'active' if @task.state == 'running' %>">
data/config/routes.rb CHANGED
@@ -5,6 +5,7 @@ Foreman::Application.routes.draw do
5
5
  get 'auto_complete_search'
6
6
  end
7
7
  member do
8
+ get :sub_tasks
8
9
  post :resume
9
10
  post :unlock
10
11
  post :force_unlock
@@ -0,0 +1,5 @@
1
+ class AddParentTaskId < ActiveRecord::Migration
2
+ def change
3
+ add_column :foreman_tasks_tasks, :parent_task_id, :string, index: true
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = "0.6.9"
2
+ VERSION = "0.6.10"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.9
4
+ version: 0.6.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-08-20 00:00:00.000000000 Z
12
+ date: 2014-09-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -133,6 +133,8 @@ files:
133
133
  - app/lib/actions/helpers/lock.rb
134
134
  - app/lib/actions/entry_action.rb
135
135
  - app/lib/actions/base.rb
136
+ - app/lib/actions/bulk_action.rb
137
+ - app/lib/actions/middleware/keep_current_user.rb
136
138
  - app/lib/actions/foreman/host/import_facts.rb
137
139
  - app/lib/actions/foreman/architecture/create.rb
138
140
  - app/lib/actions/foreman/architecture/update.rb
@@ -162,6 +164,7 @@ files:
162
164
  - db/migrate/20131209122644_create_foreman_tasks_locks.rb
163
165
  - db/migrate/20131205204140_create_foreman_tasks.rb
164
166
  - db/migrate/20140324104010_remove_foreman_tasks_progress.rb
167
+ - db/migrate/20140813215942_add_parent_task_id.rb
165
168
  - deploy/foreman-tasks.init
166
169
  - deploy/foreman-tasks.service
167
170
  - deploy/foreman-tasks.sysconfig