foreman-tasks 0.8.6 → 0.9.0

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +35 -0
  3. data/.rubocop_todo.yml +138 -0
  4. data/app/controllers/foreman_tasks/api/recurring_logics_controller.rb +3 -4
  5. data/app/controllers/foreman_tasks/api/tasks_controller.rb +56 -72
  6. data/app/controllers/foreman_tasks/concerns/hosts_controller_extension.rb +2 -4
  7. data/app/controllers/foreman_tasks/recurring_logics_controller.rb +2 -5
  8. data/app/controllers/foreman_tasks/tasks_controller.rb +7 -8
  9. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +44 -46
  10. data/app/helpers/foreman_tasks/tasks_helper.rb +1 -1
  11. data/app/lib/actions/action_with_sub_plans.rb +6 -8
  12. data/app/lib/actions/base.rb +6 -7
  13. data/app/lib/actions/bulk_action.rb +13 -9
  14. data/app/lib/actions/entry_action.rb +1 -3
  15. data/app/lib/actions/foreman/host/import_facts.rb +2 -5
  16. data/app/lib/actions/foreman/puppetclass/import.rb +1 -1
  17. data/app/lib/actions/helpers/args_serialization.rb +0 -1
  18. data/app/lib/actions/helpers/humanizer.rb +16 -21
  19. data/app/lib/actions/helpers/with_continuous_output.rb +0 -1
  20. data/app/lib/actions/helpers/with_delegated_action.rb +2 -2
  21. data/app/lib/actions/middleware/inherit_task_groups.rb +3 -5
  22. data/app/lib/actions/middleware/keep_current_user.rb +0 -3
  23. data/app/lib/actions/middleware/recurring_logic.rb +0 -1
  24. data/app/lib/actions/proxy_action.rb +8 -8
  25. data/app/lib/actions/serializers/active_record_serializer.rb +0 -3
  26. data/app/lib/proxy_api/foreman_dynflow/dynflow_proxy.rb +3 -3
  27. data/app/models/foreman_tasks/concerns/action_subject.rb +4 -6
  28. data/app/models/foreman_tasks/concerns/action_triggering.rb +20 -33
  29. data/app/models/foreman_tasks/concerns/host_action_subject.rb +5 -5
  30. data/app/models/foreman_tasks/lock.rb +29 -37
  31. data/app/models/foreman_tasks/recurring_logic.rb +23 -24
  32. data/app/models/foreman_tasks/task.rb +65 -39
  33. data/app/models/foreman_tasks/task/dynflow_task.rb +23 -24
  34. data/app/models/foreman_tasks/task/status_explicator.rb +3 -3
  35. data/app/models/foreman_tasks/task/summarizer.rb +3 -3
  36. data/app/models/foreman_tasks/task_group.rb +0 -2
  37. data/app/models/foreman_tasks/task_group_member.rb +0 -2
  38. data/app/models/foreman_tasks/task_groups/recurring_logic_task_group.rb +1 -4
  39. data/app/models/foreman_tasks/triggering.rb +19 -19
  40. data/app/models/setting/foreman_tasks.rb +8 -11
  41. data/app/services/foreman_tasks/proxy_selector.rb +4 -5
  42. data/app/views/foreman_tasks/tasks/_details.html.erb +1 -1
  43. data/bin/dynflow-executor +1 -1
  44. data/bin/foreman-tasks +1 -1
  45. data/config/routes.rb +1 -1
  46. data/db/migrate/20150814204140_add_task_type_value_index.rb +1 -1
  47. data/db/migrate/20160924213030_change_tasks_widget_names.rb +8 -8
  48. data/db/seeds.d/61-foreman_tasks_bookmarks.rb +3 -3
  49. data/deploy/foreman-tasks.sysconfig +6 -0
  50. data/extra/dynflow-debug.sh +12 -0
  51. data/foreman-tasks.gemspec +1 -1
  52. data/lib/foreman_tasks.rb +3 -3
  53. data/lib/foreman_tasks/authorizer_ext.rb +1 -1
  54. data/lib/foreman_tasks/cleaner.rb +14 -16
  55. data/lib/foreman_tasks/dynflow.rb +11 -9
  56. data/lib/foreman_tasks/dynflow/configuration.rb +8 -10
  57. data/lib/foreman_tasks/dynflow/console_authorizer.rb +4 -5
  58. data/lib/foreman_tasks/dynflow/daemon.rb +17 -19
  59. data/lib/foreman_tasks/dynflow/persistence.rb +5 -8
  60. data/lib/foreman_tasks/engine.rb +30 -31
  61. data/lib/foreman_tasks/task_error.rb +1 -3
  62. data/lib/foreman_tasks/tasks/cleanup.rake +7 -19
  63. data/lib/foreman_tasks/tasks/dynflow.rake +1 -1
  64. data/lib/foreman_tasks/tasks/export_tasks.rake +51 -59
  65. data/lib/foreman_tasks/test_extensions.rb +1 -1
  66. data/lib/foreman_tasks/version.rb +1 -1
  67. data/lib/tasks/gettext.rake +10 -7
  68. data/locale/action_names.rb +3 -6
  69. data/locale/en/foreman_tasks.po +189 -177
  70. data/locale/foreman_tasks.pot +177 -137
  71. data/test/controllers/api/recurring_logics_controller_test.rb +3 -5
  72. data/test/controllers/api/tasks_controller_test.rb +5 -7
  73. data/test/factories/task_factory.rb +8 -8
  74. data/test/factories/triggering_factory.rb +2 -3
  75. data/test/helpers/foreman_tasks/tasks_helper_test.rb +11 -11
  76. data/test/support/dummy_proxy_action.rb +3 -4
  77. data/test/unit/actions/action_with_sub_plans_test.rb +5 -6
  78. data/test/unit/actions/proxy_action_test.rb +5 -8
  79. data/test/unit/cleaner_test.rb +11 -12
  80. data/test/unit/dynflow_console_authorizer_test.rb +4 -4
  81. data/test/unit/proxy_selector_test.rb +3 -3
  82. data/test/unit/recurring_logic_test.rb +19 -17
  83. data/test/unit/task_groups_test.rb +3 -4
  84. data/test/unit/task_test.rb +72 -5
  85. data/test/unit/triggering_test.rb +0 -1
  86. metadata +7 -6
  87. data/app/controllers/foreman_tasks/concerns/environments_extension.rb +0 -24
@@ -5,7 +5,7 @@ module ForemanTasks
5
5
  include ForemanTasks::Concerns::ActionSubject
6
6
 
7
7
  def action_input_key
8
- "host"
8
+ 'host'
9
9
  end
10
10
 
11
11
  def available_locks
@@ -16,15 +16,15 @@ module ForemanTasks
16
16
  # TODO: This should get into the Foreman core, extracting the
17
17
  # +importHostAndFacts+ method into two
18
18
  def import_host(hostname, certname, facts, proxy_id = nil)
19
- raise(::Foreman::Exception.new("Invalid Facts, must be a Hash")) unless facts.is_a?(Hash)
20
- raise(::Foreman::Exception.new("Invalid Hostname, must be a String")) unless hostname.is_a?(String)
19
+ raise Foreman::Exception, 'Invalid Facts, must be a Hash' unless facts.is_a?(Hash)
20
+ raise Foreman::Exception, 'Invalid Hostname, must be a String' unless hostname.is_a?(String)
21
21
 
22
22
  # downcase everything
23
23
  hostname.try(:downcase!)
24
24
  certname.try(:downcase!)
25
25
 
26
- host = certname.present? ? Host.find_by_certname(certname) : nil
27
- host ||= Host.find_by_name hostname
26
+ host = certname.present? ? Host.find_by(certname: certname) : nil
27
+ host ||= Host.find_by name: hostname
28
28
  host ||= Host.new(:name => hostname, :certname => certname) if Setting[:create_new_host_when_facts_are_uploaded]
29
29
 
30
30
  return Host.new if host.nil?
@@ -1,32 +1,31 @@
1
1
  module ForemanTasks
2
2
  class Lock < ActiveRecord::Base
3
-
4
3
  LINK_LOCK_NAME = :link_resource
5
4
  OWNER_LOCK_NAME = :task_owner
6
5
 
7
- # not really intedet to be created in database, but it's used for
6
+ # not really intended to be created in database, but it's used for
8
7
  # explicitly stating that the all the locks for resource should be used
9
8
  ALL_LOCK_NAME = :all
10
9
 
11
- RESERVED_LOCK_NAMES = [LINK_LOCK_NAME, OWNER_LOCK_NAME, ALL_LOCK_NAME]
10
+ RESERVED_LOCK_NAMES = [LINK_LOCK_NAME, OWNER_LOCK_NAME, ALL_LOCK_NAME].freeze
12
11
 
13
12
  class LockConflict < StandardError
14
13
  attr_reader :required_lock, :conflicting_locks
15
14
  def initialize(required_lock, conflicting_locks)
16
- header = _("Required lock is already taken by other running tasks.")
15
+ header = _('Required lock is already taken by other running tasks.')
17
16
  header << "\n"
18
- header << _("Please inspect their state, fix their errors and resume them.")
17
+ header << _('Please inspect their state, fix their errors and resume them.')
19
18
  header << "\n\n"
20
- header << _("Required lock: %s") % required_lock.name
19
+ header << _('Required lock: %s') % required_lock.name
21
20
  header << "\n"
22
- header << _("Conflicts with tasks:")
21
+ header << _('Conflicts with tasks:')
23
22
  header << "\n"
24
23
  url_helpers = Rails.application.routes.url_helpers
25
- conflicting_tasks = conflicting_locks.
26
- map(&:task).
27
- uniq.
28
- map { |task| "- #{Setting['foreman_url'] + url_helpers.foreman_tasks_task_path(task)}" }.
29
- join("\n")
24
+ conflicting_tasks = conflicting_locks
25
+ .map(&:task)
26
+ .uniq
27
+ .map { |task| "- #{Setting['foreman_url'] + url_helpers.foreman_tasks_task_path(task)}" }
28
+ .join("\n")
30
29
 
31
30
  super header + conflicting_tasks
32
31
  @required_lock = required_lock
@@ -38,21 +37,17 @@ module ForemanTasks
38
37
 
39
38
  belongs_to :resource, polymorphic: true
40
39
 
41
- scope :active, -> do
42
- joins(:task).where('foreman_tasks_tasks.state != ?', :stopped)
43
- end
40
+ scope :active, -> { joins(:task).where('foreman_tasks_tasks.state != ?', :stopped) }
44
41
 
45
42
  validates :task_id, :name, :resource_id, :resource_type, presence: true
46
43
 
47
44
  validate do
48
- unless available?
49
- raise LockConflict.new(self, colliding_locks)
50
- end
45
+ raise LockConflict.new(self, colliding_locks) unless available?
51
46
  end
52
47
 
53
48
  # returns true if it's possible to aquire this kind of lock
54
49
  def available?
55
- not colliding_locks.exists?
50
+ !colliding_locks.exists?
56
51
  end
57
52
 
58
53
  # returns a scope of the locks colliding with this one
@@ -62,14 +57,13 @@ module ForemanTasks
62
57
  colliding_locks_scope = colliding_locks_scope.where(name: name,
63
58
  resource_id: resource_id,
64
59
  resource_type: resource_type)
65
- unless self.exclusive?
60
+ unless exclusive?
66
61
  colliding_locks_scope = colliding_locks_scope.where(:exclusive => true)
67
62
  end
68
- return colliding_locks_scope
63
+ colliding_locks_scope
69
64
  end
70
65
 
71
66
  class << self
72
-
73
67
  # Locks the resource so that no other task can lock it while running.
74
68
  # No other task related to the resource is not allowed (even not-locking ones)
75
69
  # A typical usecase is resource deletion, where it's good idea to make sure
@@ -82,7 +76,6 @@ module ForemanTasks
82
76
  build_exclusive_locks(resource).all?(&:available?)
83
77
  end
84
78
 
85
-
86
79
  # Locks the resource so that no other task can lock it while running.
87
80
  # Other not-locking tasks are tolerated.
88
81
  #
@@ -103,12 +96,12 @@ module ForemanTasks
103
96
  end
104
97
 
105
98
  def locked?(resource, uuid, *lock_names)
106
- not lockable?(resource, uuid, *lock_names)
99
+ !lockable?(resource, uuid, *lock_names)
107
100
  end
108
101
 
109
102
  def colliding_locks(resource, uuid, *lock_names)
110
- build_locks(resource, lock_names, uuid).
111
- inject([]) { |collisions, lock| collisions.concat lock.colliding_locks.to_a }
103
+ build_locks(resource, lock_names, uuid)
104
+ .inject([]) { |collisions, lock| collisions.concat lock.colliding_locks.to_a }
112
105
  end
113
106
 
114
107
  # Assigns the resource to the task to easily track the task in context of
@@ -136,7 +129,7 @@ module ForemanTasks
136
129
  def all_lock_names(resource, include_links = false)
137
130
  lock_names = []
138
131
  if resource.class.respond_to?(:available_locks) &&
139
- resource.class.available_locks.any?
132
+ resource.class.available_locks.any?
140
133
  lock_names.concat(resource.class.available_locks)
141
134
  else
142
135
  raise "The resource #{resource.class.name} doesn't define any available lock"
@@ -145,7 +138,7 @@ module ForemanTasks
145
138
  raise "Lock name #{lock_name} is reserved"
146
139
  end
147
140
  lock_names.concat([LINK_LOCK_NAME, OWNER_LOCK_NAME]) if include_links
148
- return lock_names
141
+ lock_names
149
142
  end
150
143
 
151
144
  def build_exclusive_locks(resource, uuid = nil)
@@ -161,7 +154,7 @@ module ForemanTasks
161
154
  locks << build(uuid, resource, lock_name, true)
162
155
  end
163
156
  locks.concat(build_links(resource, uuid))
164
- return locks
157
+ locks
165
158
  end
166
159
 
167
160
  def build_links(resource, uuid = nil)
@@ -179,23 +172,22 @@ module ForemanTasks
179
172
  end
180
173
 
181
174
  def build(uuid, resource, lock_name, exclusive)
182
- self.new(task_id: uuid,
183
- name: lock_name,
184
- resource_type: resource.class.name,
185
- resource_id: resource.id,
186
- exclusive: !!exclusive)
175
+ new(task_id: uuid,
176
+ name: lock_name,
177
+ resource_type: resource.class.name,
178
+ resource_id: resource.id,
179
+ exclusive: !!exclusive)
187
180
  end
188
181
 
189
182
  # recursively search for related resources of the resource (using
190
183
  # the +related_resources+ method, avoiding the cycles
191
184
  def related_resources(resource)
192
185
  if resource.respond_to?(:all_related_resources)
193
- return resource.all_related_resources
186
+ resource.all_related_resources
194
187
  else
195
- return []
188
+ []
196
189
  end
197
190
  end
198
191
  end
199
-
200
192
  end
201
193
  end
@@ -1,5 +1,4 @@
1
1
  module ForemanTasks
2
-
3
2
  require 'parse-cron'
4
3
 
5
4
  class RecurringLogic < ActiveRecord::Base
@@ -15,13 +14,13 @@ module ForemanTasks
15
14
  has_many :task_groups, -> { uniq }, :through => :tasks
16
15
  end
17
16
 
18
- scoped_search :on => :id, :complete_value => false
17
+ scoped_search :on => :id, :complete_value => false, :validator => ScopedSearch::Validators::INTEGER
19
18
  scoped_search :on => :max_iteration, :complete_value => false, :rename => :iteration_limit
20
19
  scoped_search :on => :iteration, :complete_value => false
21
20
  scoped_search :on => :cron_line, :complete_value => true
22
21
 
23
22
  before_create do
24
- self.task_group.save
23
+ task_group.save
25
24
  end
26
25
 
27
26
  def self.allowed_states
@@ -35,16 +34,16 @@ module ForemanTasks
35
34
  end
36
35
 
37
36
  def trigger_repeat(action_class, *args)
38
- unless can_continue?
39
- self.state = 'finished'
40
- save!
41
- return
42
- else
37
+ if can_continue?
43
38
  self.iteration += 1
44
39
  save!
45
40
  ::ForemanTasks.delay action_class,
46
41
  generate_delay_options,
47
42
  *args
43
+ else
44
+ self.state = 'finished'
45
+ save!
46
+ nil
48
47
  end
49
48
  end
50
49
 
@@ -54,21 +53,21 @@ module ForemanTasks
54
53
  tasks.active.each(&:cancel)
55
54
  end
56
55
 
57
- def next_occurrence_time(time = Time.now)
58
- @parser ||= CronParser.new(cron_line)
56
+ def next_occurrence_time(time = Time.zone.now)
57
+ @parser ||= CronParser.new(cron_line, Time.zone)
59
58
  @parser.next(time)
60
59
  end
61
60
 
62
- def generate_delay_options(time = Time.now, options = {})
61
+ def generate_delay_options(time = Time.zone.now, options = {})
63
62
  {
64
63
  :start_at => next_occurrence_time(time),
65
64
  :start_before => options['start_before'],
66
- :recurring_logic_id => self.id
65
+ :recurring_logic_id => id
67
66
  }
68
67
  end
69
68
 
70
69
  def valid?(*_)
71
- cron_line.present? && valid_cronline? && !self.state.nil? || can_start?
70
+ cron_line.present? && valid_cronline? && !state.nil? || can_start?
72
71
  end
73
72
 
74
73
  def valid_cronline?
@@ -77,21 +76,21 @@ module ForemanTasks
77
76
  false
78
77
  end
79
78
 
80
- def can_start?(time = Time.now)
79
+ def can_start?(time = Time.zone.now)
81
80
  (end_time.nil? || next_occurrence_time(time) < end_time) &&
82
81
  (max_iteration.nil? || iteration < max_iteration)
83
82
  end
84
83
 
85
- def can_continue?(time = Time.now)
86
- self.state == 'active' && can_start?(time)
84
+ def can_continue?(time = Time.zone.now)
85
+ state == 'active' && can_start?(time)
87
86
  end
88
87
 
89
88
  def finished?
90
- self.state == 'finished'
89
+ state == 'finished'
91
90
  end
92
91
 
93
92
  def humanized_state
94
- case self.state
93
+ case state
95
94
  when 'active'
96
95
  N_('Active')
97
96
  when 'cancelled'
@@ -104,13 +103,13 @@ module ForemanTasks
104
103
  end
105
104
 
106
105
  def self.assemble_cronline(hash)
107
- hash.values_at(*[:minutes, :hours, :days, :months, :days_of_week])
108
- .map { |value| (value.nil? || value.blank?) ? '*' : value }
106
+ hash.values_at(:minutes, :hours, :days, :months, :days_of_week)
107
+ .map { |value| value.nil? || value.blank? ? '*' : value }
109
108
  .join(' ')
110
109
  end
111
110
 
112
111
  def self.new_from_cronline(cronline)
113
- self.new.tap do |logic|
112
+ new.tap do |logic|
114
113
  logic.cron_line = cronline
115
114
  logic.task_group = ::ForemanTasks::TaskGroups::RecurringLogicTaskGroup.new
116
115
  end
@@ -120,7 +119,7 @@ module ForemanTasks
120
119
  cronline = if triggering.input_type == :cronline
121
120
  triggering.cronline
122
121
  else
123
- ::ForemanTasks::RecurringLogic.assemble_cronline(cronline_hash triggering.input_type, triggering.time, triggering.days_of_week)
122
+ ::ForemanTasks::RecurringLogic.assemble_cronline(cronline_hash(triggering.input_type, triggering.time, triggering.days_of_week))
124
123
  end
125
124
  ::ForemanTasks::RecurringLogic.new_from_cronline(cronline).tap do |manager|
126
125
  manager.end_time = triggering.end_time unless triggering.end_time_limited.blank?
@@ -132,8 +131,8 @@ module ForemanTasks
132
131
  def self.cronline_hash(recurring_type, time_hash, days_of_week_hash)
133
132
  hash = Hash[[:years, :months, :days, :hours, :minutes].zip(time_hash.values)]
134
133
  hash.update :days_of_week => days_of_week_hash
135
- .select { |key, value| value == "1" }
136
- .keys.join(',')
134
+ .select { |_key, value| value == '1' }
135
+ .keys.join(',')
137
136
  allowed_keys = case recurring_type
138
137
  when :monthly
139
138
  [:minutes, :hours, :days]
@@ -4,7 +4,7 @@ module ForemanTasks
4
4
  class Task < ActiveRecord::Base
5
5
  include Authorizable
6
6
 
7
- # TODO missing validation of states
7
+ # TODO: missing validation of states
8
8
 
9
9
  self.primary_key = :id
10
10
  before_create :generate_id
@@ -18,17 +18,16 @@ module ForemanTasks
18
18
  if Rails::VERSION::MAJOR < 4
19
19
  has_many :recurring_logic_task_groups, :through => :task_group_members, :conditions => { :type => 'ForemanTasks::TaskGroups::RecurringLogicTaskGroup' }, :source => :task_group
20
20
  has_many :owners, :through => :locks, :source => :resource, :source_type => 'User',
21
- :conditions => ["foreman_tasks_locks.name = ?", Lock::OWNER_LOCK_NAME]
21
+ :conditions => ['foreman_tasks_locks.name = ?', Lock::OWNER_LOCK_NAME]
22
22
  else
23
23
  has_many :recurring_logic_task_groups, -> { where :type => 'ForemanTasks::TaskGroups::RecurringLogicTaskGroup' },
24
24
  :through => :task_group_members, :source => :task_group
25
25
  # in fact, the task has only one owner but Rails don't let you to
26
26
  # specify has_one relation though has_many relation
27
- has_many :owners, lambda {where(["foreman_tasks_locks.name = ?", Lock::OWNER_LOCK_NAME])},
27
+ has_many :owners, -> { where(['foreman_tasks_locks.name = ?', Lock::OWNER_LOCK_NAME]) },
28
28
  :through => :locks, :source => :resource, :source_type => 'User'
29
29
  end
30
30
 
31
-
32
31
  scoped_search :on => :id, :complete_value => false
33
32
  scoped_search :on => :label, :complete_value => true
34
33
  scoped_search :on => :state, :complete_value => true
@@ -37,21 +36,26 @@ module ForemanTasks
37
36
  scoped_search :on => :start_at, :complete_value => false
38
37
  scoped_search :on => :ended_at, :complete_value => false
39
38
  scoped_search :on => :parent_task_id, :complete_value => true
40
- scoped_search :in => :locks, :on => :resource_type, :complete_value => true, :rename => "resource_type", :ext_method => :search_by_generic_resource
41
- scoped_search :in => :locks, :on => :resource_id, :complete_value => false, :rename => "resource_id", :ext_method => :search_by_generic_resource
42
- scoped_search :in => :owners, :on => :id, :complete_value => true, :rename => "owner.id", :ext_method => :search_by_owner
43
- scoped_search :in => :owners, :on => :login, :complete_value => true, :rename => "owner.login", :ext_method => :search_by_owner
44
- scoped_search :in => :owners, :on => :firstname, :complete_value => true, :rename => "owner.firstname", :ext_method => :search_by_owner
45
- scoped_search :in => :task_groups, :on => :id, :complete_value => true, :rename => "task_group.id"
39
+ scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => 'resource_type', :ext_method => :search_by_generic_resource
40
+ scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'resource_id', :ext_method => :search_by_generic_resource
41
+ scoped_search :relation => :owners,
42
+ :on => :id,
43
+ :complete_value => true,
44
+ :rename => 'owner.id',
45
+ :ext_method => :search_by_owner,
46
+ :validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) || value == 'current_user' }
47
+ scoped_search :relation => :owners, :on => :login, :complete_value => true, :rename => 'owner.login', :ext_method => :search_by_owner
48
+ scoped_search :relation => :owners, :on => :firstname, :complete_value => true, :rename => 'owner.firstname', :ext_method => :search_by_owner
49
+ scoped_search :relation => :task_groups, :on => :id, :complete_value => true, :rename => 'task_group.id', :validator => ScopedSearch::Validators::INTEGER
46
50
 
47
51
  scope :active, -> { where('foreman_tasks_tasks.state != ?', :stopped) }
48
- scope :running, -> { where("foreman_tasks_tasks.state NOT IN ('stopped', 'paused')") }
52
+ scope :running, -> { where("foreman_tasks_tasks.state NOT IN ('stopped', 'paused')") }
49
53
  scope :for_resource,
50
- (lambda do |resource|
51
- joins(:locks).where(:"foreman_tasks_locks.resource_id" => resource.id,
52
- :"foreman_tasks_locks.resource_type" => resource.class.name)
53
- end)
54
- scope :for_action_types, (lambda { |action_types| where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
54
+ (lambda do |resource|
55
+ joins(:locks).where(:"foreman_tasks_locks.resource_id" => resource.id,
56
+ :"foreman_tasks_locks.resource_type" => resource.class.name)
57
+ end)
58
+ scope :for_action_types, (->(action_types) { where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
55
59
 
56
60
  def input
57
61
  {}
@@ -62,61 +66,67 @@ module ForemanTasks
62
66
  end
63
67
 
64
68
  def owner
65
- self.owners.first
69
+ owners.first
66
70
  end
67
71
 
68
72
  def username
69
- self.owner.try(:login)
73
+ owner.try(:login)
70
74
  end
71
75
 
72
76
  def execution_type
73
- self.start_at.to_i == self.started_at.to_i ? N_('Immediate') : N_('Delayed')
77
+ delayed? ? N_('Delayed') : N_('Immediate')
74
78
  end
75
79
 
76
80
  def humanized
77
81
  { action: label,
78
- input: "",
79
- output: "" }
82
+ input: '',
83
+ output: '' }
80
84
  end
81
85
 
82
86
  def cli_example
83
- ""
87
+ ''
84
88
  end
85
89
 
86
90
  # returns true if the task is running or waiting to be run
87
91
  def pending?
88
- self.state != 'stopped'
92
+ state != 'stopped'
89
93
  end
90
- alias_method :pending, :pending?
94
+ alias pending pending?
91
95
 
92
96
  def resumable?
93
97
  false
94
98
  end
95
99
 
96
100
  def paused?
97
- self.state == 'paused'
101
+ state == 'paused'
102
+ end
103
+
104
+ def recurring?
105
+ !recurring_logic_task_group_ids.empty?
106
+ end
107
+
108
+ def delayed?
109
+ start_at.to_i != started_at.to_i
98
110
  end
99
111
 
100
112
  def self_and_parents
101
113
  [self].tap do |ret|
102
- if parent_task
103
- ret.concat(parent_task.self_and_parents)
104
- end
114
+ ret.concat(parent_task.self_and_parents) if parent_task
105
115
  end
106
116
  end
107
117
 
108
118
  def self.search_by_generic_resource(key, operator, value)
109
- key = "resource_type" if key.blank?
110
- key_name = self.connection.quote_column_name(key.sub(/^.*\./,''))
119
+ key = 'resource_type' if key.blank?
120
+ key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
111
121
  condition = sanitize_sql_for_conditions(["foreman_tasks_locks.#{key_name} #{operator} ?", value])
112
122
 
113
- return {:conditions => condition, :joins => :locks }
123
+ { :conditions => condition, :joins => :locks }
114
124
  end
115
125
 
116
126
  def self.search_by_owner(key, operator, value)
117
127
  return { :conditions => '0 = 1' } if value == 'current_user' && User.current.nil?
118
128
 
119
- key_name = self.connection.quote_column_name(key.sub(/^.*\./,''))
129
+ key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
120
130
  joins = <<-SQL
121
131
  INNER JOIN foreman_tasks_locks AS foreman_tasks_locks_owner
122
132
  ON (foreman_tasks_locks_owner.task_id = foreman_tasks_tasks.id AND
@@ -132,21 +142,19 @@ module ForemanTasks
132
142
  condition = if key.blank?
133
143
  sanitize_sql_for_conditions(["users.login #{operator} ? or users.firstname #{operator} ? ", value, value])
134
144
  elsif key =~ /\.id\Z/
135
- if value == 'current_user'
136
- value = User.current.id
137
- end
145
+ value = User.current.id if value == 'current_user'
138
146
  sanitize_sql_for_conditions(["foreman_tasks_locks_owner.resource_id #{operator} ?", value])
139
147
  else
140
148
  sanitize_sql_for_conditions(["users.#{key_name} #{operator} ?", value])
141
149
  end
142
- return {:conditions => condition, :joins => joins }
150
+ { :conditions => condition, :joins => joins }
143
151
  end
144
152
 
145
153
  def progress
146
- case self.state.to_s
147
- when "running", "paused"
154
+ case state.to_s
155
+ when 'running', 'paused'
148
156
  0.5
149
- when "stopped"
157
+ when 'stopped'
150
158
  1
151
159
  else
152
160
  0
@@ -163,6 +171,24 @@ module ForemanTasks
163
171
  (groups - task_groups).each { |group| task_groups << group }
164
172
  end
165
173
 
174
+ def sub_tasks_counts
175
+ result = %w(cancelled error pending success warning).zip([0].cycle).to_h
176
+ result.update sub_tasks.group(:result).count
177
+ sum = result.values.reduce(:+)
178
+ if respond_to?(:main_action) && main_action.respond_to?(:total_count)
179
+ result[:total] = main_action.total_count
180
+ # In case of batch planning there might be some plans still unplanned (not present in database).
181
+ # To get correct counts we need to add them to either:
182
+ # cancelled when the parent is stopped
183
+ # pending when the parent is still running.
184
+ key = state == 'stopped' ? 'cancelled' : 'pending'
185
+ result[key] += result[:total] - sum
186
+ else
187
+ result[:total] = sum
188
+ end
189
+ result.symbolize_keys
190
+ end
191
+
166
192
  protected
167
193
 
168
194
  def generate_id