foreman-tasks 0.6.9 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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