foreman-tasks 0.9.1 → 0.9.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fae15bedc8d294cc8486d2d99b0843d53840d1e6
4
- data.tar.gz: af614f0e462ca233cc5615f2f47793caa184f380
3
+ metadata.gz: ffd1f0c3a1276172b47fc40f58b34d92641fe2b1
4
+ data.tar.gz: e9faaecd202c22b32c13de8de2ba8bea5ddaee49
5
5
  SHA512:
6
- metadata.gz: bd813948f1ca5b2305e06dd090e631bd1c598f8b83e52a37ae7e50071e9cfd31a73ae52cec3ed0822033089ead74109d222adc35b718d4e29021536264ef2cce
7
- data.tar.gz: 9ce2326641602979ada0e7c56dc957923eb579791ceee0c74b0e06346d2007a792472e5cc9d216ec59f65576427adcfaab276b3023995e045410016554dbcf39
6
+ metadata.gz: 5f057fff909d5f4cfc7a107f91dcba2654618dca2615fa23b819e0cbe6867f6ad7cdaae9b4de8bbbde832c9783d8c0b1d729151a9a7dacefdb42d76c15f5e597
7
+ data.tar.gz: 3f306778e7796e788f5f251bd2c4be7ef31ae94094a6b29d9ab7e1972650eae9c0c9c85590ae10d173be2b46101cb2c7d77da43cdaed09de038db478246f00b0
data/Gemfile CHANGED
@@ -3,7 +3,8 @@ source "https://rubygems.org"
3
3
  # Declare your gem's dependencies in tasks.gemspec.
4
4
  # Bundler will treat runtime dependencies like base dependencies, and
5
5
  # development dependencies will be added by default to the :development group.
6
- gemspec
6
+
7
+ gemspec :name => 'foreman-tasks'
7
8
 
8
9
  # Declare any dependencies that are still in development here instead of in
9
10
  # your gemspec. These might include edge Rails or gems from your path or
@@ -116,7 +116,7 @@ module ForemanTasks
116
116
  tags = []
117
117
  tags << text_f(f, :start_at_raw, :label => _('Start at'), :placeholder => 'YYYY-mm-dd HH:MM')
118
118
  tags << text_f(f, :start_before_raw, :label => _('Start before'), :placeholder => 'YYYY-mm-dd HH:MM',
119
- :label_help => _('Indicates that the action should be cancelled if it cannot be started before this time.'))
119
+ :label_help => _('Indicates that the action should be cancelled if it cannot be started before this time.'))
120
120
  content_tag(:fieldset, nil, :id => 'trigger_mode_future', :class => "trigger_mode_form #{'hidden' unless triggering.future?}") do
121
121
  tags.join.html_safe
122
122
  end
@@ -1,6 +1,5 @@
1
1
  module Actions
2
2
  class BulkAction < Actions::ActionWithSubPlans
3
-
4
3
  include Dynflow::Action::WithBulkSubPlans
5
4
 
6
5
  # == Parameters:
@@ -47,7 +47,7 @@ module Actions
47
47
  end
48
48
 
49
49
  def restore_curent_user
50
- User.current = User.find(action.input[:current_user_id])
50
+ User.current = User.unscoped.find(action.input[:current_user_id])
51
51
  yield
52
52
  ensure
53
53
  User.current = nil
@@ -108,13 +108,13 @@ module ForemanTasks
108
108
  @_dynflow_task_wrapped = true
109
109
 
110
110
  @_dynflow_hook_action = case method
111
- when :save
112
- new_record? ? create_action : update_action
113
- when :destroy
114
- destroy_action
115
- else
116
- raise 'unexpected method'
117
- end
111
+ when :save
112
+ new_record? ? create_action : update_action
113
+ when :destroy
114
+ destroy_action
115
+ else
116
+ raise 'unexpected method'
117
+ end
118
118
  ensure_not_in_transaction! if @_dynflow_hook_action
119
119
  yield.tap do |result|
120
120
  execute_planned_action if result
@@ -38,10 +38,10 @@ module ForemanTasks
38
38
  scoped_search :on => :parent_task_id, :complete_value => true
39
39
  scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => 'resource_type', :ext_method => :search_by_generic_resource
40
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',
41
+ scoped_search :relation => :owners,
42
+ :on => :id,
43
+ :complete_value => true,
44
+ :rename => 'owner.id',
45
45
  :ext_method => :search_by_owner,
46
46
  :validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) || value == 'current_user' }
47
47
  scoped_search :relation => :owners, :on => :login, :complete_value => true, :rename => 'owner.login', :ext_method => :search_by_owner
@@ -172,7 +172,9 @@ module ForemanTasks
172
172
  end
173
173
 
174
174
  def sub_tasks_counts
175
- result = %w(cancelled error pending success warning).zip([0].cycle).to_h
175
+ result = %w(cancelled error pending success warning).inject({}) do |hash, state|
176
+ hash.update(state => 0)
177
+ end
176
178
  result.update sub_tasks.group(:result).count
177
179
  sum = result.values.reduce(:+)
178
180
  if respond_to?(:main_action) && main_action.respond_to?(:total_count)
@@ -2,7 +2,6 @@ module ForemanTasks
2
2
  class Task::DynflowTask < ForemanTasks::Task
3
3
  include Algebrick::TypeCheck
4
4
 
5
- delegate :cancellable?, :progress, to: :execution_plan
6
5
  scope :for_action, ->(action_class) { where(label: action_class.name) }
7
6
 
8
7
  def update_from_dynflow(data)
@@ -19,26 +18,50 @@ module ForemanTasks
19
18
  DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
20
19
  end
21
20
  end
22
- self.label ||= main_action.class.name
21
+ self.label ||= main_action && main_action.class.name
23
22
  changes = self.changes
24
23
  save!
25
24
  changes
26
25
  end
27
26
 
27
+ def cancellable?
28
+ execution_plan.try(:cancellable?)
29
+ end
30
+
28
31
  def cancel
29
- execution_plan.cancel.any?
32
+ execution_plan!.cancel.any?
30
33
  end
31
34
 
32
35
  def resumable?
33
- execution_plan.state == :paused
36
+ execution_plan.try(:state) == :paused
34
37
  end
35
38
 
36
39
  def cancellable_action?(action)
37
40
  action.is_a?(::Dynflow::Action::Cancellable)
38
41
  end
39
42
 
40
- def execution_plan
41
- @execution_plan ||= ForemanTasks.dynflow.world.persistence.load_execution_plan(external_id)
43
+ def progress
44
+ execution_plan.try(:progress) || 0
45
+ end
46
+
47
+ def execution_plan(silence_exception = true)
48
+ return @execution_plan if defined?(@execution_plan)
49
+ execution_plan = ForemanTasks.dynflow.world.persistence.load_execution_plan(external_id)
50
+ # don't use invalid execution plans for our purposes
51
+ if execution_plan.respond_to?(:valid?) && !execution_plan.valid?
52
+ raise execution_plan.exception
53
+ else
54
+ @execution_plan = execution_plan
55
+ end
56
+ @execution_plan
57
+ rescue => e
58
+ Foreman::Logging.exception("Could not load execution plan #{external_id} for task #{id}", e, :logger => 'foreman-tasks')
59
+ raise e unless silence_exception
60
+ nil
61
+ end
62
+
63
+ def execution_plan!
64
+ execution_plan(false)
42
65
  end
43
66
 
44
67
  def input
@@ -50,11 +73,11 @@ module ForemanTasks
50
73
  end
51
74
 
52
75
  def failed_steps
53
- execution_plan.steps_in_state(:skipped, :skipping, :error)
76
+ execution_plan.try(:steps_in_state, :skipped, :skipping, :error) || []
54
77
  end
55
78
 
56
79
  def running_steps
57
- execution_plan.steps_in_state(:running, :suspended)
80
+ execution_plan.try(:steps_in_state, :running, :suspended) || []
58
81
  end
59
82
 
60
83
  def humanized
@@ -69,8 +92,8 @@ module ForemanTasks
69
92
  end
70
93
 
71
94
  def main_action
72
- return @main_action if @main_action
73
- execution_plan.root_plan_step.action execution_plan
95
+ return @main_action if defined?(@main_action)
96
+ @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
74
97
  end
75
98
 
76
99
  def get_humanized(method)
@@ -79,7 +102,8 @@ module ForemanTasks
79
102
  method = "humanized_#{method}".to_sym
80
103
  end
81
104
  Match! method, :humanized_name, :humanized_input, :humanized_output, :humanized_errors
82
- return N_('N/A') if method != :humanized_name && main_action.execution_plan.state == :scheduled
105
+ return N_('N/A') if method != :humanized_name && (main_action.nil? || main_action.execution_plan.state == :scheduled)
106
+ return N_(label) if method == :humanized_name && main_action.nil?
83
107
  @humanized_cache[method] ||= begin
84
108
  if main_action.respond_to? method
85
109
  begin
@@ -102,6 +126,10 @@ module ForemanTasks
102
126
  logger.warn('Task %s updated at consistency check: %s' % [task.id, changes.inspect])
103
127
  end
104
128
  rescue => e
129
+ # if we fail updating the data from dynflow, it usually means there is something
130
+ # odd with the data consistency and at this point it is not possible to resume, switching
131
+ # the task to stopped/error
132
+ task.update_attributes(:state => 'stopped', :result => 'error')
105
133
  Foreman::Logging.exception("Failed at consistency check for task #{task.id}", e, :logger => 'foreman-tasks')
106
134
  end
107
135
  end
@@ -126,7 +154,7 @@ module ForemanTasks
126
154
  end
127
155
 
128
156
  def cancelled?
129
- execution_plan.errors.map(&:exception).any? { |exception| exception.class == ::ForemanTasks::Task::TaskCancelledException }
157
+ execution_plan.errors.any? { |error| error.exception_class == ::ForemanTasks::Task::TaskCancelledException }
130
158
  end
131
159
  end
132
160
  end
@@ -1,7 +1,11 @@
1
1
  <% if @task.is_a? ForemanTasks::Task::DynflowTask %>
2
2
  <div>
3
3
  <% failed_steps = @task.failed_steps %>
4
- <% if failed_steps.empty? %>
4
+ <% if @task.execution_plan.nil? %>
5
+ <div class="alert alert-danger">
6
+ <%= _("Execution plan data not available ") %>
7
+ </div>
8
+ <% elsif failed_steps.empty? %>
5
9
  <div class="alert alert-success">
6
10
  <%= _("No errors") %>
7
11
  </div>
data/bin/dynflow-executor CHANGED
@@ -2,45 +2,70 @@
2
2
 
3
3
  require 'optparse'
4
4
 
5
- options = { foreman_root: Dir.pwd }
5
+ class ArgvParser
6
+ attr_reader :options, :command
6
7
 
7
- opts = OptionParser.new do |opts|
8
- opts.banner = <<BANNER
8
+ def initialize(argv, file)
9
+ @options = { foreman_root: Dir.pwd }
10
+
11
+ opts = OptionParser.new do |opts|
12
+ opts.banner = banner(file)
13
+
14
+ opts.on('-h', '--help', 'Show this message') do
15
+ puts opts
16
+ exit 1
17
+ end
18
+ opts.on('-f', '--foreman-root=PATH', "Path to Foreman Rails root path. By default '#{@options[:foreman_root]}'") do |path|
19
+ @options[:foreman_root] = path
20
+ end
21
+ opts.on('-c', '--executors-count=COUNT', 'Number of parallel executors to spawn. Overrides EXECUTORS_COUNT environment varaible.') do |count|
22
+ @options[:executors_count] = count.to_i
23
+ end
24
+ opts.on('-m', '--memory-limit=SIZE', 'Limits the amount of memory an executor can consume. Overrides EXECUTOR_MEMORY_LIMIT environment varaible. You can use kb, mb, gb') do |size|
25
+ @options[:memory_limit] = size
26
+ end
27
+ opts.on('--executor-memory-init-delay=SECONDS', 'Start memory polling after SECONDS. Overrides EXECUTOR_MEMORY_MONITOR_DELAY environment varaible.') do |seconds|
28
+ @options[:memory_init_delay] = seconds.to_i
29
+ end
30
+ opts.on('--executor-memory-polling-interval=SECONDS', 'Check for memory useage every SECONDS sec. Overrides EXECUTOR_MEMORY_MONITOR_INTERVAL environment varaible.') do |seconds|
31
+ @options[:memory_polling_interval] = seconds.to_i
32
+ end
33
+ end
34
+
35
+ args = opts.parse!(argv)
36
+ @command = args.first || 'run'
37
+ end
38
+
39
+ def banner(file)
40
+ banner = <<BANNER
9
41
  Run Dynflow executor for Foreman tasks.
10
42
 
11
- Usage: #{File.basename($PROGRAM_NAME)} [options] ACTION"
43
+ Usage: #{File.basename(file)} [options] ACTION"
12
44
 
13
45
  ACTION can be one of:
14
46
 
15
- * start - start the executor on background. It creates these files
16
- in tmp/pid directory:
47
+ * start - start the executor on background. It creates these files
48
+ in tmp/pid directory:
17
49
 
18
- * dynflow_executor_monitor.pid - pid of monitor ensuring
19
- the executor keeps running
20
- * dynflow_executor.pid - pid of the executor itself
21
- * dynflow_executor.output - stdout of the executor
22
- * stop - stops the running executor
23
- * restart - restarts the running executor
24
- * run - run the executor in foreground
50
+ * dynflow_executor_monitor.pid - pid of monitor ensuring
51
+ the executor keeps running
52
+ * dynflow_executor.pid - pid of the executor itself
53
+ * dynflow_executor.output - stdout of the executor
54
+ * stop - stops the running executor
55
+ * restart - restarts the running executor
56
+ * run - run the executor in foreground
25
57
 
26
58
  BANNER
27
-
28
- opts.on('-h', '--help', 'Show this message') do
29
- puts opts
30
- exit 1
31
- end
32
- opts.on('-f', '--foreman-root=PATH', "Path to Foreman Rails root path. By default '#{options[:foreman_root]}'") do |path|
33
- options[:foreman_root] = path
34
- end
35
- opts.on('-c', '--executors-count=COUNT', 'Number of parallel executors to spawn. Overrides EXECUTORS_COUNT environment varaible.') do |count|
36
- options[:executors_count] = count.to_i
59
+ banner
37
60
  end
38
61
  end
39
62
 
40
- args = opts.parse!(ARGV)
41
- command = args.first || 'run'
63
+ # run the script if it's executed explicitly
64
+ if $PROGRAM_NAME == __FILE__
65
+ parser = ArgvParser.new(ARGV, $PROGRAM_NAME)
42
66
 
43
- app_file = File.expand_path('./config/application', options[:foreman_root])
44
- require app_file
67
+ app_file = File.expand_path('./config/application', parser.options[:foreman_root])
68
+ require app_file
45
69
 
46
- ForemanTasks::Dynflow::Daemon.new.run_background(command, options)
70
+ ForemanTasks::Dynflow::Daemon.new.run_background(parser.command, parser.options)
71
+ end
@@ -12,3 +12,15 @@ RUBY_GC_MALLOC_LIMIT_MAX=16000100
12
12
  RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.1
13
13
  RUBY_GC_OLDMALLOC_LIMIT=16000100
14
14
  RUBY_GC_OLDMALLOC_LIMIT_MAX=16000100
15
+
16
+ #Set the number of executors you want to run
17
+ #EXECUTORS_COUNT=1
18
+
19
+ #Set memory limit for executor process, before it's restarted automatically
20
+ #EXECUTOR_MEMORY_LIMIT=2gb
21
+
22
+ #Set delay before first memory polling to let executor initialize (in sec)
23
+ #EXECUTOR_MEMORY_MONITOR_DELAY=7200 #default: 2 hours
24
+
25
+ #Set memory polling interval, process memory will be checked every N seconds.
26
+ #EXECUTOR_MEMORY_MONITOR_INTERVAL=60
@@ -29,9 +29,10 @@ DESC
29
29
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
30
30
 
31
31
  s.add_dependency "foreman-tasks-core"
32
- s.add_dependency "dynflow", '~> 0.8.22'
32
+ s.add_dependency "dynflow", '~> 0.8.24'
33
33
  s.add_dependency "sequel" # for Dynflow process persistence
34
34
  s.add_dependency "sinatra" # for Dynflow web console
35
35
  s.add_dependency "daemons" # for running remote executor
36
36
  s.add_dependency "parse-cron", '~> 0.1.4'
37
+ s.add_dependency "get_process_mem" # for memory polling
37
38
  end
@@ -9,8 +9,9 @@ module ForemanTasks
9
9
  require 'foreman_tasks/dynflow/daemon'
10
10
  require 'foreman_tasks/dynflow/console_authorizer'
11
11
 
12
- def initialize
12
+ def initialize(world_class = nil)
13
13
  @required = false
14
+ @world_class = world_class
14
15
  end
15
16
 
16
17
  def config
@@ -37,7 +38,7 @@ module ForemanTasks
37
38
  if config.lazy_initialization && defined?(PhusionPassenger)
38
39
  config.dynflow_logger.warn('ForemanTasks: lazy loading with PhusionPassenger might lead to unexpected results')
39
40
  end
40
- config.initialize_world.tap do |world|
41
+ init_world.tap do |world|
41
42
  @world = world
42
43
 
43
44
  unless config.remote?
@@ -119,5 +120,12 @@ module ForemanTasks
119
120
  def loaded_paths
120
121
  @loaded_paths ||= Set.new
121
122
  end
123
+
124
+ private
125
+
126
+ def init_world
127
+ return config.initialize_world(@world_class) if @world_class
128
+ config.initialize_world
129
+ end
122
130
  end
123
131
  end
@@ -1,18 +1,47 @@
1
1
  require 'fileutils'
2
+ require 'daemons'
3
+ require 'get_process_mem'
4
+ require 'dynflow/watchers/memory_consumption_watcher'
2
5
 
3
6
  module ForemanTasks
4
7
  class Dynflow::Daemon
8
+ attr_reader :dynflow_memory_watcher_class,
9
+ :daemons_class
10
+
11
+ # make Daemon dependency injection ready for testing purposes
12
+ def initialize(
13
+ dynflow_memory_watcher_class = ::Dynflow::Watchers::MemoryConsumptionWatcher,
14
+ daemons_class = ::Daemons
15
+ )
16
+ @dynflow_memory_watcher_class = dynflow_memory_watcher_class
17
+ @daemons_class = daemons_class
18
+ end
19
+
5
20
  # load the Rails environment and initialize the executor
6
21
  # in this thread.
7
- def run(foreman_root = Dir.pwd)
22
+ def run(foreman_root = Dir.pwd, options = {})
8
23
  STDERR.puts('Starting Rails environment')
9
24
  foreman_env_file = File.expand_path('./config/environment.rb', foreman_root)
10
25
  unless File.exist?(foreman_env_file)
11
26
  raise "#{foreman_root} doesn't seem to be a foreman root directory"
12
27
  end
28
+
29
+ STDERR.puts("Starting dynflow with the following options: #{options}")
30
+
13
31
  ForemanTasks.dynflow.executor!
32
+
33
+ if options[:memory_limit] > 0
34
+ ForemanTasks.dynflow.config.on_init do |world|
35
+ memory_watcher = initialize_memory_watcher(world, options[:memory_limit], options)
36
+ world.terminated.on_completion do
37
+ STDERR.puts("World has been terminated")
38
+ memory_watcher = nil # the object can be disposed
39
+ end
40
+ end
41
+ end
42
+
14
43
  require foreman_env_file
15
- STDERR.puts('Everything ready')
44
+ STDERR.puts("Everything ready for world: #{(ForemanTasks.dynflow.initialized? ? ForemanTasks.dynflow.world.id : nil)}")
16
45
  sleep
17
46
  ensure
18
47
  STDERR.puts('Exiting')
@@ -20,13 +49,6 @@ module ForemanTasks
20
49
 
21
50
  # run the executor as a daemon
22
51
  def run_background(command = 'start', options = {})
23
- default_options = { foreman_root: Dir.pwd,
24
- process_name: 'dynflow_executor',
25
- pid_dir: "#{Rails.root}/tmp/pids",
26
- log_dir: File.join(Rails.root, 'log'),
27
- wait_attempts: 300,
28
- wait_sleep: 1,
29
- executors_count: (ENV['EXECUTORS_COUNT'] || 1).to_i }
30
52
  options = default_options.merge(options)
31
53
  FileUtils.mkdir_p(options[:pid_dir])
32
54
  begin
@@ -42,18 +64,13 @@ module ForemanTasks
42
64
  STDERR.puts("Dynflow Executor: #{command} in progress")
43
65
 
44
66
  options[:executors_count].times do
45
- Daemons.run_proc(options[:process_name],
46
- :multiple => true,
47
- :dir => options[:pid_dir],
48
- :log_dir => options[:log_dir],
49
- :dir_mode => :normal,
50
- :monitor => true,
51
- :log_output => true,
52
- :log_output_syslog => true,
53
- :ARGV => [command]) do |*_args|
67
+ daemons_class.run_proc(
68
+ options[:process_name],
69
+ daemons_options(command, options)
70
+ ) do |*_args|
54
71
  begin
55
72
  ::Logging.reopen
56
- run(options[:foreman_root])
73
+ run(options[:foreman_root], options)
57
74
  rescue => e
58
75
  STDERR.puts e.message
59
76
  Foreman::Logging.exception('Failed running foreman-tasks daemon', e)
@@ -68,5 +85,59 @@ module ForemanTasks
68
85
  def world
69
86
  ForemanTasks.dynflow.world
70
87
  end
88
+
89
+ private
90
+
91
+ def daemons_options(command, options)
92
+ {
93
+ :multiple => true,
94
+ :dir => options[:pid_dir],
95
+ :log_dir => options[:log_dir],
96
+ :dir_mode => :normal,
97
+ :monitor => true,
98
+ :log_output => true,
99
+ :log_output_syslog => true,
100
+ :monitor_interval => [options[:memory_polling_interval] / 2, 30].min,
101
+ :ARGV => [command]
102
+ }
103
+ end
104
+
105
+ def default_options
106
+ {
107
+ foreman_root: Dir.pwd,
108
+ process_name: 'dynflow_executor',
109
+ pid_dir: "#{Rails.root}/tmp/pids",
110
+ log_dir: File.join(Rails.root, 'log'),
111
+ wait_attempts: 300,
112
+ wait_sleep: 1,
113
+ executors_count: (ENV['EXECUTORS_COUNT'] || 1).to_i,
114
+ memory_limit: begin
115
+ (ENV['EXECUTOR_MEMORY_LIMIT'] || '').to_gb.gigabytes
116
+ rescue RuntimeError
117
+ ENV['EXECUTOR_MEMORY_LIMIT'].to_i
118
+ end,
119
+ memory_init_delay: (ENV['EXECUTOR_MEMORY_MONITOR_DELAY'] || 7200).to_i, # 2 hours
120
+ memory_polling_interval: (ENV['EXECUTOR_MEMORY_MONITOR_INTERVAL'] || 60).to_i
121
+ }
122
+ end
123
+
124
+ def initialize_memory_watcher(world, memory_limit, options)
125
+ watcher_options = {}
126
+ watcher_options[:polling_interval] = options[:memory_polling_interval]
127
+ watcher_options[:initial_wait] = options[:memory_init_delay]
128
+ watcher_options[:memory_checked_callback] = ->(current_memory, memory_limit) { log_memory_within_limit(current_memory, memory_limit) }
129
+ watcher_options[:memory_limit_exceeded_callback] = ->(current_memory, memory_limit) { log_memory_limit_exceeded(current_memory, memory_limit) }
130
+ dynflow_memory_watcher_class.new(world, memory_limit, watcher_options)
131
+ end
132
+
133
+ def log_memory_limit_exceeded(current_memory, memory_limit)
134
+ message = "Memory level exceeded, registered #{current_memory} bytes, which is greater than #{memory_limit} limit."
135
+ Foreman::Logging.logger('foreman-tasks').error(message)
136
+ end
137
+
138
+ def log_memory_within_limit(current_memory, memory_limit)
139
+ message = "Memory level OK, registered #{current_memory} bytes, which is less than #{memory_limit} limit."
140
+ Foreman::Logging.logger('foreman-tasks').debug(message)
141
+ end
71
142
  end
72
143
  end