foreman-tasks 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a31c8423a352c5eeb6ff93b23ce8ed3d868ee4bdb16d456233f2c3df475342cd
4
- data.tar.gz: c181fdc4c13d8c3cfb7e8d8469c9188a8972b97ca223e68cb50d9a901e23d552
3
+ metadata.gz: 1c6773ac47a0c5b8e3e28da1461a348510e8595da0bd2d08feafd78092394fb2
4
+ data.tar.gz: 40d4c506cd3d31b72355e8c2d4255c854c703be6be49644b8d30a55032bd9b2b
5
5
  SHA512:
6
- metadata.gz: c7f2b880bb70b4fc2781ab9c5bff2c7bcd0434bc067669fae601f1815f6720e7dd771a170cdde8a4922565f1bd87c61d5b3dc1c0b10192bd895508d123aa84da
7
- data.tar.gz: a4652958efa52fec83b6c0a8093914b1d22ad574bc4763cb66c3bc41e82bd67e5b1448be0fd79ae3ce64225cb44d6c25faeb324cd10a846dfbe77abee5d55b02
6
+ metadata.gz: 1d05cdb24aea11a647028a4b1c314cf5f0db3d95459bbcfa0c52aa8f53263b6deb3d27ee23bbb7ec2a227e1ebbead0a432c0ecf6701131b2f7921b39145085ab
7
+ data.tar.gz: 777127d69a84b7c94780f78ad5a8aa5754ad246e6019a9f35c518e0b246d73b8f6f31acc84029b1b0648b8ff3ba8f167efc4fad7f4116cb4eb2f3fad7b850591
@@ -4,6 +4,7 @@ env:
4
4
  RAILS_ENV: test
5
5
  DATABASE_URL: postgresql://postgres:@localhost/test
6
6
  DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true
7
+ BUNDLE_WITHOUT: "journald:development:console:mysql2:sqlite:libvirt"
7
8
  jobs:
8
9
  rubocop:
9
10
  runs-on: ubuntu-latest
@@ -13,10 +14,7 @@ jobs:
13
14
  uses: ruby/setup-ruby@v1
14
15
  with:
15
16
  ruby-version: 2.7
16
- - name: Setup
17
- run: |
18
- gem install bundler
19
- bundle install --jobs=3 --retry=3
17
+ bundler-cache: true
20
18
  - name: Run rubocop
21
19
  run: bundle exec rubocop
22
20
  test_ruby:
@@ -43,30 +41,21 @@ jobs:
43
41
  - uses: actions/checkout@v2
44
42
  with:
45
43
  path: foreman-tasks
44
+ - name: Setup Plugin in Foreman
45
+ run: |
46
+ echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
47
+ echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
46
48
  - name: Setup Ruby
47
49
  uses: ruby/setup-ruby@v1
48
50
  with:
49
51
  ruby-version: ${{ matrix.ruby-version }}
52
+ bundler-cache: true
50
53
  - name: Setup Node
51
54
  uses: actions/setup-node@v1
52
55
  with:
53
56
  node-version: ${{ matrix.node-version }}
54
- - uses: actions/cache@v2
55
- with:
56
- path: vendor/bundle
57
- key: ${{ runner.os }}-fgems-${{ matrix.ruby-version }}-${{ hashFiles('Gemfile.lock') }}
58
- restore-keys: |
59
- ${{ runner.os }}-fgems-${{ matrix.ruby-version }}-
60
- - name: Setup Bundler
61
- run: |
62
- echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
63
- echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
64
- gem install bundler
65
- bundle config set without journald development console libvirt
66
- bundle config set path vendor/bundle
67
57
  - name: Prepare test env
68
58
  run: |
69
- bundle install --jobs=3 --retry=3
70
59
  bundle exec rake db:create
71
60
  bundle exec rake db:migrate
72
61
  - name: Run plugin tests
data/.rubocop.yml CHANGED
@@ -9,6 +9,7 @@ AllCops:
9
9
  Exclude:
10
10
  - 'node_modules/**/*'
11
11
  - 'locale/*'
12
+ - 'vendor/**/*'
12
13
  TargetRubyVersion: 2.5
13
14
 
14
15
  Lint/ShadowingOuterLocalVariable:
@@ -4,6 +4,8 @@ module ForemanTasks
4
4
  include Foreman::Controller::CsvResponder
5
5
  include ForemanTasks::FindTasksCommon
6
6
 
7
+ before_action :find_dynflow_task, only: [:unlock, :force_unlock, :cancel, :cancel_step, :resume]
8
+
7
9
  def show
8
10
  @task = resource_base.find(params[:id])
9
11
  render :layout => !request.xhr?
@@ -31,8 +33,7 @@ module ForemanTasks
31
33
  end
32
34
 
33
35
  def cancel_step
34
- task = find_dynflow_task
35
- result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
36
+ result = ForemanTasks.dynflow.world.event(@dynflow_task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
36
37
  if result.rejected?
37
38
  render json: { error: result.reason }, status: :bad_request
38
39
  else
@@ -41,8 +42,7 @@ module ForemanTasks
41
42
  end
42
43
 
43
44
  def cancel
44
- task = find_dynflow_task
45
- if task.cancel
45
+ if @dynflow_task.cancel
46
46
  render json: { statusText: 'OK' }
47
47
  else
48
48
  render json: {}, status: :bad_request
@@ -50,19 +50,17 @@ module ForemanTasks
50
50
  end
51
51
 
52
52
  def abort
53
- task = find_dynflow_task
54
- if task.abort
53
+ if @dynflow_task.abort
55
54
  flash[:info] = _('Trying to abort the task')
56
55
  else
57
56
  flash[:warning] = _('The task cannot be aborted at the moment.')
58
57
  end
59
- redirect_back(:fallback_location => foreman_tasks_task_path(task))
58
+ redirect_back(:fallback_location => foreman_tasks_task_path(@dynflow_task))
60
59
  end
61
60
 
62
61
  def resume
63
- task = find_dynflow_task
64
- if task.resumable?
65
- ForemanTasks.dynflow.world.execute(task.execution_plan.id)
62
+ if @dynflow_task.resumable?
63
+ ForemanTasks.dynflow.world.execute(@dynflow_task.execution_plan.id)
66
64
  render json: { statusText: 'OK' }
67
65
  else
68
66
  render json: {}, status: :bad_request
@@ -70,10 +68,8 @@ module ForemanTasks
70
68
  end
71
69
 
72
70
  def unlock
73
- task = find_dynflow_task
74
- if task.paused?
75
- task.state = :stopped
76
- task.save!
71
+ if @dynflow_task.paused?
72
+ unlock_task(@dynflow_task)
77
73
  render json: { statusText: 'OK' }
78
74
  else
79
75
  render json: {}, status: :bad_request
@@ -81,9 +77,7 @@ module ForemanTasks
81
77
  end
82
78
 
83
79
  def force_unlock
84
- task = find_dynflow_task
85
- task.state = :stopped
86
- task.save!
80
+ unlock_task(@dynflow_task)
87
81
  render json: { statusText: 'OK' }
88
82
  end
89
83
 
@@ -98,6 +92,12 @@ module ForemanTasks
98
92
 
99
93
  private
100
94
 
95
+ def unlock_task(task)
96
+ task.state = :stopped
97
+ task.locks.destroy_all
98
+ task.save!
99
+ end
100
+
101
101
  def respond_with_tasks(scope)
102
102
  @tasks = filter(scope, paginate: false).with_duration
103
103
  csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :duration, :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'Duration', 'User'])
@@ -123,7 +123,7 @@ module ForemanTasks
123
123
  end
124
124
 
125
125
  def find_dynflow_task
126
- resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
126
+ @dynflow_task = resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
127
127
  end
128
128
 
129
129
  def filter(scope, paginate: true)
@@ -40,7 +40,8 @@ module Actions
40
40
  output[:planned_count] += group.size
41
41
  end
42
42
  rescue => e
43
- action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
43
+ action_logger.warn "Could not trigger task on the smart proxy"
44
+ action_logger.warn e
44
45
  batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
45
46
  output[:failed_count] += batch.size
46
47
  end
@@ -90,7 +90,7 @@ module ForemanTasks
90
90
  def next_occurrence_time(time = Time.zone.now)
91
91
  @parser ||= CronParser.new(cron_line, Time.zone)
92
92
  # @parser.next(start_time) is not inclusive of the start_time hence stepping back one run to include checking start_time for the first run.
93
- before_next = @parser.next(@parser.last(time))
93
+ before_next = @parser.next(@parser.last(time.in_time_zone))
94
94
  return before_next if before_next >= time && tasks.count == 0
95
95
  @parser.next(time)
96
96
  end
@@ -18,7 +18,8 @@ module ForemanTasks
18
18
  response = begin
19
19
  proxy.launch_tasks('single', :action_class => proxy_action_name, :action_input => input)
20
20
  rescue RestClient::Exception => e
21
- logger.warn "Could not trigger task on the smart proxy: #{e.message}"
21
+ logger.warn "Could not trigger task on the smart proxy"
22
+ logger.warn e
22
23
  {}
23
24
  end
24
25
  update_from_batch_trigger(response)
@@ -26,13 +27,13 @@ module ForemanTasks
26
27
  end
27
28
 
28
29
  def self.batch_trigger(operation, remote_tasks)
29
- remote_tasks.group_by(&:proxy_url).values.map do |group|
30
+ remote_tasks.group_by(&:proxy_url).each_value do |group|
30
31
  input_hash = group.reduce({}) do |acc, remote_task|
31
32
  acc.merge(remote_task.execution_plan_id => { :action_input => remote_task.proxy_input,
32
33
  :action_class => remote_task.proxy_action_name })
33
34
  end
34
- results = remote_tasks.first.proxy.launch_tasks(operation, input_hash)
35
- remote_tasks.each do |remote_task|
35
+ results = group.first.proxy.launch_tasks(operation, input_hash)
36
+ group.each do |remote_task|
36
37
  remote_task.update_from_batch_trigger results.fetch(remote_task.execution_plan_id, {}),
37
38
  results.fetch('parent', {})
38
39
  end
@@ -10,4 +10,4 @@
10
10
  <div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
11
11
  <%= react_component('ForemanTasks') %>
12
12
  <% end %>
13
- <%= render file: "layouts/base" %>
13
+ <%= render template: "layouts/base" %>
@@ -27,16 +27,23 @@
27
27
  #
28
28
  :cleanup:
29
29
  #
30
- # the period after which to delete all the tasks (by default all tasks are not being deleted after some period)
31
- # will be deprecated in Foreman 1.18 and the use of rules is recommended.
32
- #
33
- # :after: 30d
34
- #
35
30
  # per action settings to override the default defined in the actions (self.cleanup_after method)
36
31
  #
37
32
  # :actions:
38
33
  # - :name: Actions::Foreman::Host::ImportFacts
39
34
  # :after: 10d
35
+ # # Actions name can also be a list of names
36
+ # - :name:
37
+ # - Actions::Katello::Host::Erratum::Install
38
+ # - Actions::Katello::Host::Erratum::ApplicableErrataInstall
39
+ # :after: 90d
40
+ # # Actions can also define a condition
41
+ # - :name:
42
+ # - Actions::RemoteExecution::RunHostJob
43
+ # - Actions::RemoteExecution::RunHostsJob
44
+ # :filter: remote_execution_feature.label = katello_errata_install
45
+ # :after: 90d
46
+
40
47
  #
41
48
  # Rules defined in this section by default don't operate
42
49
  # on tasks specified in the actions section. This behavior
@@ -2,6 +2,50 @@ require 'csv'
2
2
 
3
3
  module ForemanTasks
4
4
  # Represents the cleanup mechanism for tasks
5
+ class ActionRule
6
+ attr_reader :klass, :after, :condition
7
+ def initialize(klass, after, condition = nil)
8
+ @klass = klass
9
+ @after = after
10
+ @condition = condition
11
+ end
12
+
13
+ def exclude_search
14
+ "NOT (#{include_search})"
15
+ end
16
+
17
+ def include_search
18
+ parts = if klass.is_a? Array
19
+ ["label ^ (#{klass.join(', ')})"]
20
+ else
21
+ ["label = \"#{klass}\""]
22
+ end
23
+ parts << "(#{@condition})" if @condition
24
+ '(' + parts.join(' AND ') + ')'
25
+ end
26
+
27
+ def self.compose_include_rules(rules)
28
+ rules.group_by { |rule| [rule.after, rule.condition] }
29
+ .map do |(after, condition), rules|
30
+ ActionRule.new(rules.map(&:klass), after, condition)
31
+ end
32
+ end
33
+ end
34
+
35
+ class CompositeActionRule
36
+ def initialize(*rules)
37
+ @rules = rules
38
+ end
39
+
40
+ def exclude_search
41
+ partial_condition = @rules.group_by(&:condition)
42
+ .map do |condition, rules|
43
+ ActionRule.new(rules.map(&:klass), nil, condition).include_search
44
+ end.join(' OR ')
45
+ "NOT (#{partial_condition})"
46
+ end
47
+ end
48
+
5
49
  class Cleaner
6
50
  def self.run(options)
7
51
  if options.key?(:filter)
@@ -12,36 +56,30 @@ module ForemanTasks
12
56
  raise "The option #{invalid_option} is not valid unless the filter specified"
13
57
  end
14
58
  end
15
- if cleanup_settings[:after]
16
- Foreman::Deprecation.deprecation_warning('1.18', _(':after setting in tasks cleanup section is deprecated, use :after in :rules section to set the value. to cleanup rules'))
17
- new(options.merge(:filter => '', :after => cleanup_settings[:after])).delete
18
- end
19
59
  with_periods = actions_with_default_cleanup
20
- with_periods.each do |action_class, period|
21
- new(options.merge(:filter => "label = #{action_class.name}", :after => period)).delete
60
+ ActionRule.compose_include_rules(with_periods).each do |rule|
61
+ new(options.merge(:filter => rule.include_search, :after => rule.after)).delete
22
62
  end
23
- actions_by_rules(with_periods).each do |hash|
63
+ actions_by_rules(CompositeActionRule.new(*with_periods)).each do |hash|
24
64
  new(options.merge(hash)).delete
25
65
  end
26
66
  end
27
67
  end
28
68
 
29
69
  def self.actions_with_default_cleanup
30
- actions_with_periods = {}
31
- if cleanup_settings[:actions]
32
- cleanup_settings[:actions].each do |action|
33
- action_class = action[:name].constantize
34
- actions_with_periods[action_class] = action[:after]
35
- rescue => e
36
- Foreman::Logging.exception("Error handling #{action} cleanup settings", e)
37
- end
38
- end
39
- (ForemanTasks.dynflow.world.action_classes - actions_with_periods.keys).each do |action_class|
40
- if action_class.respond_to?(:cleanup_after)
41
- actions_with_periods[action_class] = action_class.cleanup_after
70
+ actions = cleanup_settings.fetch(:actions, [])
71
+ .flat_map do |action|
72
+ Array(action[:name]).map do |klass|
73
+ ActionRule.new(klass.safe_constantize || klass, action[:after], action[:filter])
42
74
  end
43
- end
44
- actions_with_periods
75
+ rescue => e
76
+ Foreman::Logging.exception("Error handling #{action} cleanup settings", e)
77
+ nil
78
+ end.compact
79
+ hardcoded = (ForemanTasks.dynflow.world.action_classes - actions.map(&:klass))
80
+ .select { |klass| klass.respond_to?(:cleanup_after) || klass.respond_to?(:cleanup_rules) }
81
+ .flat_map { |klass| klass.respond_to?(:cleanup_rules) ? klass.cleanup_rules : ActionRule.new(klass, klass.cleanup_after) }
82
+ actions + hardcoded
45
83
  end
46
84
 
47
85
  def self.cleanup_settings
@@ -49,8 +87,8 @@ module ForemanTasks
49
87
  @cleanup_settings = SETTINGS[:'foreman-tasks'] && SETTINGS[:'foreman-tasks'][:cleanup] || {}
50
88
  end
51
89
 
52
- def self.actions_by_rules(actions_with_periods)
53
- disable_actions_with_periods = "label !^ (#{actions_with_periods.keys.join(', ')})"
90
+ def self.actions_by_rules(action_rules)
91
+ disable_actions_with_periods = action_rules.exclude_search
54
92
  cleanup_settings.fetch(:rules, []).map do |hash|
55
93
  next if hash[:after].nil?
56
94
  conditions = []
@@ -25,7 +25,7 @@ module ForemanTasks
25
25
 
26
26
  initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
27
27
  Foreman::Plugin.register :"foreman-tasks" do
28
- requires_foreman '>= 3.2.0'
28
+ requires_foreman '>= 3.3.0'
29
29
  divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
30
30
  menu :top_menu, :tasks,
31
31
  :url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
@@ -42,17 +42,17 @@ module ForemanTasks
42
42
  security_block :foreman_tasks do |_map|
43
43
  permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :summary_sub_tasks, :show],
44
44
  :'foreman_tasks/react' => [:index],
45
- :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :summary_sub_tasks, :details, :sub_tasks] }, :resource_type => ForemanTasks::Task.name
45
+ :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :summary_sub_tasks, :details, :sub_tasks] }, :resource_type => 'ForemanTasks::Task'
46
46
  permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
47
- :'foreman_tasks/api/tasks' => [:bulk_resume, :bulk_cancel, :bulk_stop] }, :resource_type => ForemanTasks::Task.name
47
+ :'foreman_tasks/api/tasks' => [:bulk_resume, :bulk_cancel, :bulk_stop] }, :resource_type => 'ForemanTasks::Task'
48
48
 
49
- permission :create_recurring_logics, {}, :resource_type => ForemanTasks::RecurringLogic.name
49
+ permission :create_recurring_logics, {}, :resource_type => 'ForemanTasks::RecurringLogic'
50
50
 
51
51
  permission :view_recurring_logics, { :'foreman_tasks/recurring_logics' => [:auto_complete_search, :index, :show],
52
- :'foreman_tasks/api/recurring_logics' => [:index, :show] }, :resource_type => ForemanTasks::RecurringLogic.name
52
+ :'foreman_tasks/api/recurring_logics' => [:index, :show] }, :resource_type => 'ForemanTasks::RecurringLogic'
53
53
 
54
54
  permission :edit_recurring_logics, { :'foreman_tasks/recurring_logics' => [:cancel, :enable, :disable, :clear_cancelled],
55
- :'foreman_tasks/api/recurring_logics' => [:cancel, :update, :bulk_destroy] }, :resource_type => ForemanTasks::RecurringLogic.name
55
+ :'foreman_tasks/api/recurring_logics' => [:cancel, :update, :bulk_destroy] }, :resource_type => 'ForemanTasks::RecurringLogic'
56
56
  end
57
57
 
58
58
  add_all_permissions_to_default_roles
@@ -116,7 +116,7 @@ module ForemanTasks
116
116
  register_graphql_query_field :recurring_logic, '::Types::RecurringLogic', :record_field
117
117
  register_graphql_query_field :recurring_logics, '::Types::RecurringLogic', :collection_field
118
118
 
119
- register_graphql_mutation_field :cancel_recurring_logic, ::Mutations::RecurringLogics::Cancel
119
+ register_graphql_mutation_field :cancel_recurring_logic, '::Mutations::RecurringLogics::Cancel'
120
120
 
121
121
  logger :dynflow, :enabled => true
122
122
  logger :action, :enabled => true
@@ -39,23 +39,18 @@ namespace :foreman_tasks do
39
39
 
40
40
  desc 'Show the current configuration for auto-cleanup'
41
41
  task :config => ['environment', 'dynflow:client'] do
42
- if ForemanTasks::Cleaner.cleanup_settings[:after]
43
- puts _('The tasks will be deleted after %{after}') % { :after => ForemanTasks::Cleaner.cleanup_settings[:after] }
44
- else
45
- puts _('Global period for cleaning up tasks is not set')
46
- end
47
-
48
42
  if ForemanTasks::Cleaner.actions_with_default_cleanup.empty?
49
43
  puts _('No actions are configured to be cleaned automatically')
50
44
  else
51
45
  puts _('The following actions are configured to be deleted automatically after some time:')
52
- printf("%-50s %s\n", _('name'), _('delete after'))
53
- ForemanTasks::Cleaner.actions_with_default_cleanup.each do |action, after|
54
- printf("%-50s %s\n", action.name, after)
46
+ printf("%-75s %-20s %-50s\n", _('name'), _('delete after'), _('filter'))
47
+ ForemanTasks::Cleaner.actions_with_default_cleanup.each do |action, _after|
48
+ klass = action.klass
49
+ printf("%-75s %-20s %-50s\n", klass.try(:name) || klass, action.after, action.condition)
55
50
  end
56
51
  end
57
52
  puts
58
- by_rules = ForemanTasks::Cleaner.actions_by_rules(ForemanTasks::Cleaner.actions_with_default_cleanup)
53
+ by_rules = ForemanTasks::Cleaner.actions_by_rules(ForemanTasks::CompositeActionRule.new(*ForemanTasks::Cleaner.actions_with_default_cleanup))
59
54
  if by_rules.empty?
60
55
  puts _('No cleanup rules are configured')
61
56
  else
@@ -241,35 +241,42 @@ namespace :foreman_tasks do
241
241
  end
242
242
  end
243
243
 
244
- def csv_export(export_filename, tasks)
244
+ def csv_export(export_filename, id_scope, task_scope)
245
245
  CSV.open(export_filename, 'wb') do |csv|
246
246
  csv << %w[id state type label result parent_task_id started_at ended_at duration]
247
- tasks.find_each do |task|
248
- with_error_handling(task) do
249
- csv << [task.id, task.state, task.type, task.label, task.result,
250
- task.parent_task_id, task.started_at, task.ended_at, task.duration]
247
+ id_scope.pluck(:id).each_slice(1000).each do |ids|
248
+ task_scope.where(id: ids).each do |task|
249
+ with_error_handling(task) do
250
+ csv << [task.id, task.state, task.type, task.label, task.result,
251
+ task.parent_task_id, task.started_at, task.ended_at, task.duration]
252
+ end
251
253
  end
252
254
  end
253
255
  end
254
256
  end
255
257
 
256
- def html_export(workdir, tasks)
258
+ def html_export(workdir, id_scope, task_scope)
257
259
  PageHelper.copy_assets(workdir)
258
260
 
261
+ ids = id_scope.pluck(:id)
259
262
  renderer = TaskRender.new
260
- total = tasks.count(:all)
263
+ count = 0
264
+ total = ids.count
261
265
  index = File.open(File.join(workdir, 'index.html'), 'w')
262
266
 
263
267
  File.open(File.join(workdir, 'index.html'), 'w') do |index|
264
268
  PageHelper.pagify(index) do |io|
265
269
  PageHelper.generate_with_index(io) do |index|
266
- tasks.find_each.each_with_index do |task, count|
267
- content = with_error_handling(task) { renderer.render_task(task) }
268
- if content
269
- File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
270
- with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
270
+ ids.each_slice(1000).each do |ids|
271
+ task_scope.where(id: ids).each do |task|
272
+ content = with_error_handling(task) { renderer.render_task(task) }
273
+ if content
274
+ File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
275
+ with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
276
+ end
277
+ count += 1
278
+ puts "#{count}/#{total}"
271
279
  end
272
- puts "#{count + 1}/#{total}"
273
280
  end
274
281
  end
275
282
  end
@@ -317,21 +324,22 @@ namespace :foreman_tasks do
317
324
  format = ENV['TASK_FORMAT'] || 'html'
318
325
  export_filename = ENV['TASK_FILE'] || generate_filename(format)
319
326
 
320
- tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc).with_duration.distinct
327
+ task_scope = ForemanTasks::Task.search_for(filter).with_duration.order(:started_at => :desc)
328
+ id_scope = task_scope.group(:id, :started_at)
321
329
 
322
330
  puts _("Exporting all tasks matching filter #{filter}")
323
- puts _("Gathering #{tasks.count(:all)} tasks.")
331
+ puts _("Gathering #{id_scope.count(:all).count} tasks.")
324
332
  case format
325
333
  when 'html'
326
334
  Dir.mktmpdir('task-export') do |tmp_dir|
327
- html_export(tmp_dir, tasks)
335
+ html_export(tmp_dir, id_scope, task_scope)
328
336
  system("tar", "czf", export_filename, tmp_dir)
329
337
  end
330
338
  when 'html-dir'
331
339
  FileUtils.mkdir_p(export_filename)
332
- html_export(export_filename, tasks)
340
+ html_export(export_filename, id_scope, task_scope)
333
341
  when 'csv'
334
- csv_export(export_filename, tasks)
342
+ csv_export(export_filename, id_scope, task_scope)
335
343
  else
336
344
  raise "Unkonwn export format '#{format}'"
337
345
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '6.0.0'.freeze
2
+ VERSION = '7.0.0'.freeze
3
3
  end
@@ -1,6 +1,5 @@
1
1
  # Autogenerated!
2
- _("Preupgrade job")
3
- _("Remote action:")
4
2
  _("Import Puppet classes")
5
3
  _("Import facts")
6
- _("Action with sub plans")
4
+ _("Action with sub plans")
5
+ _("Remote action:")
@@ -56,6 +56,9 @@ msgstr ""
56
56
  msgid "A paused task represents a process that has not finished properly. Any task in paused state can lead to potential inconsistency and needs to be resolved."
57
57
  msgstr ""
58
58
 
59
+ msgid "A special label for tracking a recurring job. There can be only one active job with a given purpose at a time."
60
+ msgstr ""
61
+
59
62
  msgid "Action"
60
63
  msgstr ""
61
64
 
@@ -68,6 +71,9 @@ msgstr ""
68
71
  msgid "Active Filters:"
69
72
  msgstr ""
70
73
 
74
+ msgid "Active or disabled recurring logic with purpose %s already exists"
75
+ msgstr ""
76
+
71
77
  msgid "All %s tasks are selected. "
72
78
  msgstr ""
73
79
 
@@ -276,6 +282,9 @@ msgstr ""
276
282
  msgid "Fri"
277
283
  msgstr ""
278
284
 
285
+ msgid "I understand that this may cause harm and have working database backups of all backend services."
286
+ msgstr ""
287
+
279
288
  msgid "ID"
280
289
  msgstr ""
281
290
 
@@ -427,9 +436,6 @@ msgstr ""
427
436
  msgid "Polling multiplier which is used to multiply the default polling intervals. This can be used to prevent polling too frequently for long running tasks."
428
437
  msgstr ""
429
438
 
430
- msgid "Preupgrade job"
431
- msgstr ""
432
-
433
439
  msgid "Proxy action retry count"
434
440
  msgstr ""
435
441
 
@@ -442,6 +448,9 @@ msgstr ""
442
448
  msgid "Proxy tasks batch size"
443
449
  msgstr ""
444
450
 
451
+ msgid "Purpose"
452
+ msgstr ""
453
+
445
454
  msgid "Raw"
446
455
  msgstr ""
447
456
 
@@ -487,6 +496,9 @@ msgstr ""
487
496
  msgid "Resource search_params requires resource_type and resource_id to be specified"
488
497
  msgstr ""
489
498
 
499
+ msgid "Resources for %s task(s) will be unlocked and will not prevent other tasks from being run. As the task(s) might be still running, it should be avoided to use this unless you are really sure the task(s) got stuck."
500
+ msgstr ""
501
+
490
502
  msgid "Result"
491
503
  msgstr ""
492
504
 
@@ -663,6 +675,12 @@ msgstr[1] ""
663
675
  msgid "This action will delete all cancelled recurring logics. Please note that this action can't be reversed."
664
676
  msgstr ""
665
677
 
678
+ msgid "This will %(action)s %(number)s task(s), putting them in the %(state)s state. Are you sure?"
679
+ msgstr ""
680
+
681
+ msgid "This will unlock the resources that the task is running against. Please note that this might lead to inconsistent state and should be used with caution, after making sure that the task can't be resumed."
682
+ msgstr ""
683
+
666
684
  msgid "Thu"
667
685
  msgstr ""
668
686
 
@@ -717,6 +735,9 @@ msgstr ""
717
735
  msgid "Yes"
718
736
  msgstr ""
719
737
 
738
+ msgid "You can find resource locks on this page. Exclusive lock marked with locked icon means that no other task can use locked resource while this task is running. Non-exclusive lock marked with unlocked icon means other tasks can access the resource freely, it is only used to indicate the relation of this task with the resource"
739
+ msgstr ""
740
+
720
741
  msgid "You do not have permission"
721
742
  msgstr ""
722
743
 
@@ -758,6 +779,9 @@ msgstr ""
758
779
  msgid "is month (range: 1-12)"
759
780
  msgstr ""
760
781
 
782
+ msgid "is not a valid format"
783
+ msgstr ""
784
+
761
785
  msgid "last"
762
786
  msgstr ""
763
787