foreman_remote_execution 1.3.3 → 1.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -4
- data/app/controllers/api/v2/foreign_input_sets_controller.rb +2 -2
- data/app/controllers/api/v2/job_invocations_controller.rb +5 -5
- data/app/controllers/api/v2/job_templates_controller.rb +3 -3
- data/app/controllers/api/v2/remote_execution_features_controller.rb +1 -1
- data/app/controllers/api/v2/template_inputs_controller.rb +3 -3
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/remote_execution_features_controller.rb +1 -1
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +4 -11
- data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +2 -8
- data/app/helpers/remote_execution_helper.rb +10 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +11 -2
- data/app/lib/actions/remote_execution/run_hosts_job.rb +8 -3
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +40 -46
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +2 -8
- data/app/models/concerns/foreman_remote_execution/taxonomy_extensions.rb +5 -4
- data/app/models/job_invocation.rb +13 -8
- data/app/models/job_invocation_composer.rb +1 -1
- data/app/models/template_invocation.rb +4 -4
- data/app/views/job_invocations/_tab_overview.html.erb +1 -1
- data/db/migrate/20150616080015_create_template_input.rb +1 -1
- data/db/migrate/20150708133241_add_targeting.rb +1 -1
- data/foreman_remote_execution.gemspec +2 -2
- data/lib/foreman_remote_execution/engine.rb +7 -5
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/unit/actions/run_hosts_job_test.rb +22 -17
- data/test/unit/job_invocation_composer_test.rb +11 -6
- data/test/unit/job_invocation_test.rb +13 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0511db7a2c04a4ddd08d45ebfe0d21e03bd8a919
|
4
|
+
data.tar.gz: 121d18818212309d66f9c20f347c6436b4773c0a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 947a9c319b0cfe8082cd6fc69da05a5d4ffae40b2e5501f5c5aaf8ab2bc4f9b2b354473d4bff53b7599fffeb182395ed4af5b2d39b2934866bccae9b5edc8f79
|
7
|
+
data.tar.gz: 3168906a4cc1bebf31465ef7b0a786ab093cfa29ef2f1fa6902564c1cfc273856663a6bcbacf3974317abb92f5bbfe574b95fb7d02a2a75f8050c0472c76ccae
|
data/.rubocop.yml
CHANGED
@@ -5,8 +5,8 @@ module Api
|
|
5
5
|
include ::Foreman::Renderer
|
6
6
|
include ::Foreman::Controller::Parameters::ForeignInputSet
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
before_action :find_required_nested_object
|
9
|
+
before_action :find_resource, :only => %w{show update destroy}
|
10
10
|
|
11
11
|
api :GET, '/templates/:template_id/foreign_input_sets', N_('List foreign input sets')
|
12
12
|
param :template_id, :identifier, :required => true
|
@@ -5,10 +5,10 @@ module Api
|
|
5
5
|
include ::Api::TaxonomyScope
|
6
6
|
include ::Foreman::Renderer
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
before_action :find_optional_nested_object
|
9
|
+
before_action :find_host, :only => %w{output}
|
10
|
+
before_action :find_resource, :only => %w{show update destroy clone}
|
11
|
+
before_action :validate_template, :only => :create
|
12
12
|
|
13
13
|
wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])
|
14
14
|
|
@@ -97,7 +97,7 @@ module Api
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def find_host
|
100
|
-
@host = Host
|
100
|
+
@host = Host.authorized(:view_hosts).find(params['host_id'])
|
101
101
|
rescue ActiveRecord::RecordNotFound
|
102
102
|
not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
|
103
103
|
end
|
@@ -7,10 +7,10 @@ module Api
|
|
7
7
|
include ::Foreman::Controller::ProvisioningTemplates
|
8
8
|
include ::Foreman::Controller::Parameters::JobTemplate
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
before_action :find_optional_nested_object
|
11
|
+
before_action :find_resource, :only => %w{show update destroy clone export}
|
12
12
|
|
13
|
-
|
13
|
+
before_action :handle_template_upload, :only => [:create, :update]
|
14
14
|
|
15
15
|
wrap_parameters JobTemplate, :include => (JobTemplate.attribute_names + [:ssh])
|
16
16
|
|
@@ -4,7 +4,7 @@ module Api
|
|
4
4
|
include ::Api::Version2
|
5
5
|
include ::Foreman::Controller::Parameters::RemoteExecutionFeature
|
6
6
|
|
7
|
-
|
7
|
+
before_action :find_resource, :only => %w{show update}
|
8
8
|
|
9
9
|
api :GET, '/remote_execution_features/', N_('List remote execution features')
|
10
10
|
def index
|
@@ -5,9 +5,9 @@ module Api
|
|
5
5
|
include ::Foreman::Renderer
|
6
6
|
include ::Foreman::Controller::Parameters::TemplateInput
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
before_action :find_required_nested_object
|
9
|
+
before_action :find_resource, :only => %w{show update destroy}
|
10
|
+
before_action :normalize_options, :only => %w{create update}
|
11
11
|
|
12
12
|
api :GET, '/templates/:template_id/template_inputs', N_('List template inputs')
|
13
13
|
param :template_id, :identifier, :required => true
|
@@ -20,7 +20,7 @@ class JobTemplatesController < ::TemplatesController
|
|
20
20
|
@template.template = params[:template]
|
21
21
|
renderer = InputTemplateRenderer.new(@template, host)
|
22
22
|
if (output = renderer.preview)
|
23
|
-
render :
|
23
|
+
render :plain => output
|
24
24
|
else
|
25
25
|
render :status => 406, :text => _('Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' % {:error => renderer.error_message})
|
26
26
|
end
|
@@ -1,14 +1,7 @@
|
|
1
1
|
module ForemanRemoteExecution
|
2
2
|
module HostsHelperExtensions
|
3
|
-
|
4
|
-
|
5
|
-
included do
|
6
|
-
alias_method_chain(:host_title_actions, :run_button)
|
7
|
-
alias_method_chain :multiple_actions, :remote_execution
|
8
|
-
end
|
9
|
-
|
10
|
-
def multiple_actions_with_remote_execution
|
11
|
-
multiple_actions_without_remote_execution + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
|
3
|
+
def multiple_actions
|
4
|
+
super + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
|
12
5
|
end
|
13
6
|
|
14
7
|
def schedule_job_multi_button(*args)
|
@@ -27,9 +20,9 @@ module ForemanRemoteExecution
|
|
27
20
|
link_to(_('Schedule Remote Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
23
|
+
def host_title_actions(*args)
|
31
24
|
title_actions(button_group(schedule_job_multi_button(*args)))
|
32
|
-
|
25
|
+
super(*args)
|
33
26
|
end
|
34
27
|
end
|
35
28
|
end
|
@@ -1,13 +1,7 @@
|
|
1
1
|
module ForemanRemoteExecution
|
2
2
|
module JobTemplatesExtensions
|
3
|
-
|
4
|
-
|
5
|
-
included do
|
6
|
-
alias_method_chain :permitted_actions, :run_button
|
7
|
-
end
|
8
|
-
|
9
|
-
def permitted_actions_with_run_button(template)
|
10
|
-
original = permitted_actions_without_run_button(template)
|
3
|
+
def permitted_actions(template)
|
4
|
+
original = super(template)
|
11
5
|
|
12
6
|
if template.is_a?(JobTemplate)
|
13
7
|
original.unshift(display_link_if_authorized(_('Run'), hash_for_new_job_invocation_path(:template_id => template.id).merge(:authorizer => authorizer))) unless template.snippet
|
@@ -122,6 +122,11 @@ module RemoteExecutionHelper
|
|
122
122
|
:title => _('Try to cancel the job'),
|
123
123
|
:disabled => !task.cancellable?,
|
124
124
|
:method => :post)
|
125
|
+
buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
|
126
|
+
:class => 'btn btn-danger',
|
127
|
+
:title => _('Try to abort the job without waiting for the results from the remote hosts'),
|
128
|
+
:disabled => !task.cancellable?,
|
129
|
+
:method => :post)
|
125
130
|
end
|
126
131
|
return buttons
|
127
132
|
end
|
@@ -140,6 +145,11 @@ module RemoteExecutionHelper
|
|
140
145
|
:title => _('Try to cancel the job on a host'),
|
141
146
|
:disabled => !task.cancellable?,
|
142
147
|
:method => :post)
|
148
|
+
buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
|
149
|
+
:class => 'btn btn-danger',
|
150
|
+
:title => _('Try to abort the job on a host without waiting for its result'),
|
151
|
+
:disabled => !task.cancellable?,
|
152
|
+
:method => :post)
|
143
153
|
end
|
144
154
|
return buttons
|
145
155
|
end
|
@@ -60,6 +60,7 @@ module Actions
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def check_exit_status
|
63
|
+
error! ForemanTasks::Task::TaskCancelledException.new(_('Task cancelled')) if delegated_action && delegated_action.output[:cancel_sent]
|
63
64
|
error! _('Job execution failed') if exit_status.to_s != '0'
|
64
65
|
end
|
65
66
|
|
@@ -89,20 +90,28 @@ module Actions
|
|
89
90
|
super << self
|
90
91
|
end
|
91
92
|
|
93
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
92
94
|
def fill_continuous_output(continuous_output)
|
93
|
-
fill_planning_errors_to_continuous_output(continuous_output) unless exit_status
|
94
95
|
delegated_output.fetch('result', []).each do |raw_output|
|
95
96
|
continuous_output.add_raw_output(raw_output)
|
96
97
|
end
|
98
|
+
|
97
99
|
final_timestamp = (continuous_output.last_timestamp || task.ended_at).to_f + 1
|
100
|
+
|
101
|
+
if task.state == 'stopped' && task.result == 'cancelled'
|
102
|
+
continuous_output.add_output(_('Job cancelled by user'), 'debug', final_timestamp)
|
103
|
+
else
|
104
|
+
fill_planning_errors_to_continuous_output(continuous_output) unless exit_status
|
105
|
+
end
|
98
106
|
if exit_status
|
99
107
|
continuous_output.add_output(_('Exit status: %s') % exit_status, 'stdout', final_timestamp)
|
100
108
|
elsif run_step && run_step.error
|
101
|
-
continuous_output.
|
109
|
+
continuous_output.add_output(_('Job finished with error') + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', final_timestamp)
|
102
110
|
end
|
103
111
|
rescue => e
|
104
112
|
continuous_output.add_exception(_('Error loading data from proxy'), e)
|
105
113
|
end
|
114
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
106
115
|
|
107
116
|
def exit_status
|
108
117
|
delegated_output[:exit_status]
|
@@ -3,6 +3,7 @@ module Actions
|
|
3
3
|
class RunHostsJob < Actions::ActionWithSubPlans
|
4
4
|
|
5
5
|
include Dynflow::Action::WithBulkSubPlans
|
6
|
+
include Dynflow::Action::WithPollingSubPlans
|
6
7
|
|
7
8
|
middleware.use Actions::Middleware::BindJobInvocation
|
8
9
|
middleware.use Actions::Middleware::RecurringLogic
|
@@ -10,15 +11,16 @@ module Actions
|
|
10
11
|
def delay(delay_options, job_invocation)
|
11
12
|
task.add_missing_task_groups(job_invocation.task_group)
|
12
13
|
job_invocation.targeting.resolve_hosts! if job_invocation.targeting.static? && !job_invocation.targeting.resolved?
|
14
|
+
input.update :job_invocation => job_invocation.to_action_input
|
13
15
|
super delay_options, job_invocation
|
14
16
|
end
|
15
17
|
|
16
18
|
def plan(job_invocation)
|
17
|
-
set_up_concurrency_control job_invocation
|
18
19
|
job_invocation.task_group.save! if job_invocation.task_group.try(:new_record?)
|
19
20
|
task.add_missing_task_groups(job_invocation.task_group) if job_invocation.task_group
|
20
21
|
action_subject(job_invocation)
|
21
22
|
job_invocation.targeting.resolve_hosts! if job_invocation.targeting.dynamic? || !job_invocation.targeting.resolved?
|
23
|
+
set_up_concurrency_control job_invocation
|
22
24
|
input.update(:job_category => job_invocation.job_category)
|
23
25
|
plan_self(:job_invocation_id => job_invocation.id)
|
24
26
|
end
|
@@ -50,7 +52,10 @@ module Actions
|
|
50
52
|
|
51
53
|
def set_up_concurrency_control(invocation)
|
52
54
|
limit_concurrency_level invocation.concurrency_level unless invocation.concurrency_level.nil?
|
53
|
-
|
55
|
+
unless invocation.time_span.nil?
|
56
|
+
distribute_over_time(invocation.time_span,
|
57
|
+
invocation.targeting.hosts.count)
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
def rescue_strategy
|
@@ -62,7 +67,7 @@ module Actions
|
|
62
67
|
end
|
63
68
|
|
64
69
|
def humanized_input
|
65
|
-
input
|
70
|
+
input.fetch(:job_invocation, {}).fetch(:description, '')
|
66
71
|
end
|
67
72
|
|
68
73
|
def humanized_name
|
@@ -1,47 +1,41 @@
|
|
1
1
|
module ForemanRemoteExecution
|
2
2
|
module HostExtensions
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
3
|
# rubocop:disable Metrics/BlockLength
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
4
|
+
def self.prepended(base)
|
5
|
+
base.instance_eval do
|
6
|
+
has_many :targeting_hosts, :dependent => :destroy, :foreign_key => 'host_id'
|
7
|
+
has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
|
8
|
+
has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id'
|
9
|
+
has_many :run_host_job_tasks, :through => :template_invocations
|
10
|
+
|
11
|
+
scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
|
12
|
+
:ext_method => :search_by_job_invocation,
|
13
|
+
:only_explicit => true,
|
14
|
+
:complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
|
15
|
+
|
16
|
+
scoped_search :relation => :template_invocations, :on => :job_invocation_id,
|
17
|
+
:rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
|
18
|
+
|
19
|
+
scoped_search :relation => :execution_status_object, :on => :status, :rename => :execution_status,
|
20
|
+
:complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
|
21
|
+
|
22
|
+
def search_by_job_invocation(key, operator, value)
|
23
|
+
if key == 'job_invocation.result'
|
24
|
+
operator = operator == '=' ? 'IN' : 'NOT IN'
|
25
|
+
value = TemplateInvocation::TaskResultMap.status_to_task_result(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
mapping = {
|
29
|
+
'job_invocation.id' => %(#{TemplateInvocation.table_name}.job_invocation_id #{operator} ?),
|
30
|
+
'job_invocation.result' => %(#{ForemanTasks::Task.table_name}.result #{operator} (?))
|
31
|
+
}
|
32
|
+
{
|
33
|
+
:conditions => sanitize_sql_for_conditions([mapping[key], value_to_sql(operator, value)]),
|
34
|
+
:joins => { :template_invocations => [:run_host_job_task] }
|
35
|
+
}
|
32
36
|
end
|
33
|
-
|
34
|
-
mapping = {
|
35
|
-
'job_invocation.id' => %(#{TemplateInvocation.table_name}.job_invocation_id #{operator} ?),
|
36
|
-
'job_invocation.result' => %(#{ForemanTasks::Task.table_name}.result #{operator} (?))
|
37
|
-
}
|
38
|
-
{
|
39
|
-
:conditions => sanitize_sql_for_conditions([mapping[key], value_to_sql(operator, value)]),
|
40
|
-
:joins => { :template_invocations => [:run_host_job_task] }
|
41
|
-
}
|
42
37
|
end
|
43
38
|
end
|
44
|
-
# rubocop:enable Metrics/BlockLength
|
45
39
|
|
46
40
|
def execution_status(options = {})
|
47
41
|
@execution_status ||= get_status(HostStatus::ExecutionStatus).to_status(options)
|
@@ -51,8 +45,8 @@ module ForemanRemoteExecution
|
|
51
45
|
@execution_status_label ||= get_status(HostStatus::ExecutionStatus).to_label(options)
|
52
46
|
end
|
53
47
|
|
54
|
-
def
|
55
|
-
params =
|
48
|
+
def host_params
|
49
|
+
params = super
|
56
50
|
keys = remote_execution_ssh_keys
|
57
51
|
params['remote_execution_ssh_keys'] = keys if keys.present?
|
58
52
|
[:remote_execution_ssh_user, :remote_execution_effective_user_method,
|
@@ -94,21 +88,21 @@ module ForemanRemoteExecution
|
|
94
88
|
@execution_interface = nil
|
95
89
|
end
|
96
90
|
|
97
|
-
def
|
98
|
-
became =
|
91
|
+
def becomes(*args)
|
92
|
+
became = super(*args)
|
99
93
|
became.drop_execution_interface_cache if became.respond_to? :drop_execution_interface_cache
|
100
94
|
became
|
101
95
|
end
|
102
96
|
|
103
|
-
def
|
97
|
+
def reload(*args)
|
104
98
|
drop_execution_interface_cache
|
105
|
-
|
99
|
+
super(*args)
|
106
100
|
end
|
107
101
|
|
108
102
|
private
|
109
103
|
|
110
|
-
def
|
111
|
-
|
104
|
+
def build_required_interfaces(attrs = {})
|
105
|
+
super(attrs)
|
112
106
|
self.primary_interface.execution = true if self.execution_interface.nil?
|
113
107
|
end
|
114
108
|
end
|
@@ -1,11 +1,5 @@
|
|
1
1
|
module ForemanRemoteExecution
|
2
2
|
module SmartProxyExtensions
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
alias_method_chain :refresh, :remote_execution
|
7
|
-
end
|
8
|
-
|
9
3
|
def pubkey
|
10
4
|
self[:pubkey] || update_pubkey
|
11
5
|
end
|
@@ -17,8 +11,8 @@ module ForemanRemoteExecution
|
|
17
11
|
key
|
18
12
|
end
|
19
13
|
|
20
|
-
def
|
21
|
-
errors =
|
14
|
+
def refresh
|
15
|
+
errors = super
|
22
16
|
update_pubkey
|
23
17
|
errors
|
24
18
|
end
|
@@ -5,11 +5,12 @@ module ForemanRemoteExecution
|
|
5
5
|
included do
|
6
6
|
has_many :job_templates, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'JobTemplate'
|
7
7
|
|
8
|
-
#
|
8
|
+
# TODO: on foreman_version_bump
|
9
|
+
# workaround for #11805 - use before_create for setting
|
9
10
|
# the default templates, remove after it's fixed in upstream
|
10
|
-
# (https://github.com/theforeman/foreman/pull/
|
11
|
-
#
|
12
|
-
skip_callback :create, :after, :assign_default_templates
|
11
|
+
# (https://github.com/theforeman/foreman/pull/4890) and gets
|
12
|
+
# into a 1.17 release
|
13
|
+
skip_callback :create, :after, :assign_default_templates, :raise => false
|
13
14
|
before_create :assign_default_templates
|
14
15
|
end
|
15
16
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class JobInvocation < ActiveRecord::Base
|
2
2
|
include Authorizable
|
3
|
+
audited :except => [ :task_id, :targeting_id, :task_group_id, :triggering_id ]
|
3
4
|
|
4
5
|
include ForemanRemoteExecution::ErrorsFlattener
|
5
6
|
FLATTENED_ERRORS_MAPPING = {
|
@@ -21,7 +22,7 @@ class JobInvocation < ActiveRecord::Base
|
|
21
22
|
scoped_search :on => :description # FIXME No auto complete because of https://github.com/wvanbergen/scoped_search/issues/138
|
22
23
|
|
23
24
|
has_many :template_invocations_hosts, :through => :template_invocations, :source => :host
|
24
|
-
scoped_search :
|
25
|
+
scoped_search :relation => :template_invocations_hosts, :on => :name, :rename => 'host', :complete_value => true
|
25
26
|
|
26
27
|
delegate :bookmark, :resolved?, :to => :targeting, :allow_nil => true
|
27
28
|
|
@@ -37,10 +38,10 @@ class JobInvocation < ActiveRecord::Base
|
|
37
38
|
:class_name => 'ForemanTasks::Task',
|
38
39
|
:source => 'run_host_job_task'
|
39
40
|
|
40
|
-
scoped_search :
|
41
|
-
scoped_search :
|
42
|
-
scoped_search :
|
43
|
-
scoped_search :
|
41
|
+
scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
|
42
|
+
scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
|
43
|
+
scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
|
44
|
+
scoped_search :relation => :task, :on => :state, :rename => 'status', :ext_method => :search_by_status,
|
44
45
|
:only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
|
45
46
|
|
46
47
|
belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
|
@@ -48,9 +49,9 @@ class JobInvocation < ActiveRecord::Base
|
|
48
49
|
|
49
50
|
scope :with_task, -> { references(:task) }
|
50
51
|
|
51
|
-
scoped_search :
|
52
|
+
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
52
53
|
|
53
|
-
scoped_search :
|
54
|
+
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
54
55
|
:ext_method => :search_by_recurring_logic, :only_explicit => true,
|
55
56
|
:complete_value => { :true => true, :false => false }
|
56
57
|
|
@@ -85,6 +86,10 @@ class JobInvocation < ActiveRecord::Base
|
|
85
86
|
HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.new(task).status_label
|
86
87
|
end
|
87
88
|
|
89
|
+
def to_label
|
90
|
+
description
|
91
|
+
end
|
92
|
+
|
88
93
|
# returns progress in percents
|
89
94
|
def progress(total = nil, done = nil)
|
90
95
|
if queued? || !targeting.resolved? || done == 0
|
@@ -183,7 +188,7 @@ class JobInvocation < ActiveRecord::Base
|
|
183
188
|
def progress_report
|
184
189
|
map = TemplateInvocation::TaskResultMap
|
185
190
|
all_keys = (map.results | map.statuses | [:progress, :total])
|
186
|
-
if queued? || !targeting.resolved?
|
191
|
+
if queued? || (task && task.started_at.nil?) || !targeting.resolved?
|
187
192
|
all_keys.reduce({}) do |acc, key|
|
188
193
|
acc.merge(key => 0)
|
189
194
|
end
|
@@ -186,7 +186,7 @@ class JobInvocationComposer
|
|
186
186
|
|
187
187
|
def targeting_params
|
188
188
|
if @host_ids
|
189
|
-
{ :search_query => Targeting.build_query_from_hosts(@host_ids) }
|
189
|
+
{ :search_query => Targeting.build_query_from_hosts(@host_ids), :targeting_type => job_invocation.targeting.targeting_type }
|
190
190
|
else
|
191
191
|
job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'user_id', 'targeting_type')
|
192
192
|
end
|
@@ -20,10 +20,10 @@ class TemplateInvocation < ActiveRecord::Base
|
|
20
20
|
validate :provides_required_input_values
|
21
21
|
before_validation :set_effective_user
|
22
22
|
|
23
|
-
scoped_search :
|
24
|
-
scoped_search :
|
25
|
-
scoped_search :
|
26
|
-
scoped_search :
|
23
|
+
scoped_search :relation => :host, :on => :name, :rename => 'host.name', :complete_value => true
|
24
|
+
scoped_search :relation => :host_group, :on => :name, :rename => 'host_group.name', :complete_value => true
|
25
|
+
scoped_search :relation => :template, :on => :job_category, :complete_value => true
|
26
|
+
scoped_search :relation => :template, :on => :name, :complete_value => true
|
27
27
|
|
28
28
|
class TaskResultMap
|
29
29
|
MAP = {
|
@@ -47,7 +47,7 @@
|
|
47
47
|
<b><%= _("Concurrency level limited to") %></b>: <%= job_invocation.concurrency_level %> <%= _('tasks at a time') %><br>
|
48
48
|
<% end %>
|
49
49
|
<% if job_invocation.time_span %>
|
50
|
-
<b><%= _("Set to distribute over") %></b>: <%= job_invocation.
|
50
|
+
<b><%= _("Set to distribute over") %></b>: <%= job_invocation.time_span %> <%= _('seconds') %><br>
|
51
51
|
<% end %>
|
52
52
|
<% if job_invocation.task && (job_invocation.task.delayed? || job_invocation.task.recurring?) %>
|
53
53
|
<b><%= _("Scheduled to start at") %></b>: <%= job_invocation.task.start_at.try(:in_time_zone) %><br>
|
@@ -11,7 +11,7 @@ class CreateTemplateInput < ActiveRecord::Migration
|
|
11
11
|
t.text :description
|
12
12
|
t.integer :template_id
|
13
13
|
|
14
|
-
t.timestamps
|
14
|
+
t.timestamps :null => true
|
15
15
|
end
|
16
16
|
|
17
17
|
add_foreign_key :template_inputs, :templates, :name => 'templates_template_id_fk', :column => 'template_id'
|
@@ -5,7 +5,7 @@ class AddTargeting < ActiveRecord::Migration
|
|
5
5
|
t.references :bookmark
|
6
6
|
t.references :user
|
7
7
|
t.string :targeting_type, :null => false, :limit => 255
|
8
|
-
t.timestamps
|
8
|
+
t.timestamps :null => true
|
9
9
|
end
|
10
10
|
|
11
11
|
add_index :targetings, [:bookmark_id], :name => 'targetings_bookmark_id'
|
@@ -24,9 +24,9 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.extra_rdoc_files = `git ls-files doc`.split("\n") + Dir['README*', 'LICENSE']
|
25
25
|
|
26
26
|
s.add_dependency 'deface'
|
27
|
-
s.add_dependency 'dynflow', '~> 0.8.
|
27
|
+
s.add_dependency 'dynflow', '~> 0.8.26'
|
28
28
|
s.add_dependency 'foreman_remote_execution_core'
|
29
|
-
s.add_dependency 'foreman-tasks', '>= 0.9'
|
29
|
+
s.add_dependency 'foreman-tasks', '>= 0.9.5'
|
30
30
|
|
31
31
|
s.add_development_dependency 'rubocop'
|
32
32
|
s.add_development_dependency 'rdoc'
|
@@ -109,7 +109,9 @@ module ForemanRemoteExecution
|
|
109
109
|
# widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
|
110
110
|
|
111
111
|
parameter_filter Subnet, :remote_execution_proxies, :remote_execution_proxy_ids => []
|
112
|
-
parameter_filter Nic
|
112
|
+
parameter_filter Nic::Interface do |ctx|
|
113
|
+
ctx.permit :execution
|
114
|
+
end
|
113
115
|
end
|
114
116
|
end
|
115
117
|
|
@@ -146,7 +148,7 @@ module ForemanRemoteExecution
|
|
146
148
|
|
147
149
|
User.send(:include, ForemanRemoteExecution::UserExtensions)
|
148
150
|
|
149
|
-
Host::Managed.send(:
|
151
|
+
Host::Managed.send(:prepend, ForemanRemoteExecution::HostExtensions)
|
150
152
|
Host::Managed.send(:include, ForemanTasks::Concerns::HostActionSubject)
|
151
153
|
|
152
154
|
(Nic::Base.descendants + [Nic::Base]).each do |klass|
|
@@ -154,10 +156,10 @@ module ForemanRemoteExecution
|
|
154
156
|
end
|
155
157
|
|
156
158
|
Bookmark.send(:include, ForemanRemoteExecution::BookmarkExtensions)
|
157
|
-
HostsHelper.send(:
|
158
|
-
ProvisioningTemplatesHelper.send(:
|
159
|
+
HostsHelper.send(:prepend, ForemanRemoteExecution::HostsHelperExtensions)
|
160
|
+
ProvisioningTemplatesHelper.send(:prepend, ForemanRemoteExecution::JobTemplatesExtensions)
|
159
161
|
|
160
|
-
SmartProxy.send(:
|
162
|
+
SmartProxy.send(:prepend, ForemanRemoteExecution::SmartProxyExtensions)
|
161
163
|
Subnet.send(:include, ForemanRemoteExecution::SubnetExtensions)
|
162
164
|
|
163
165
|
# We need to explicitly force to load the Task model due to Rails loader
|
@@ -11,6 +11,7 @@ module ForemanRemoteExecution
|
|
11
11
|
let(:job_invocation) do
|
12
12
|
FactoryGirl.build(:job_invocation, :with_template).tap do |invocation|
|
13
13
|
invocation.targeting = targeting
|
14
|
+
invocation.description = 'Some short description'
|
14
15
|
invocation.save
|
15
16
|
end
|
16
17
|
end
|
@@ -31,6 +32,11 @@ module ForemanRemoteExecution
|
|
31
32
|
plan_action action, job_invocation
|
32
33
|
end
|
33
34
|
|
35
|
+
let(:delayed) do
|
36
|
+
action.delay({ :start_at => Time.now.getlocal }, job_invocation)
|
37
|
+
action
|
38
|
+
end
|
39
|
+
|
34
40
|
before do
|
35
41
|
ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
|
36
42
|
User.current = users :admin
|
@@ -38,11 +44,6 @@ module ForemanRemoteExecution
|
|
38
44
|
end
|
39
45
|
|
40
46
|
context 'targeting resolving' do
|
41
|
-
let(:delayed) do
|
42
|
-
action.delay({ :start_at => Time.now.getlocal }, job_invocation)
|
43
|
-
action
|
44
|
-
end
|
45
|
-
|
46
47
|
it 'resolves dynamic targeting in plan' do
|
47
48
|
targeting.targeting_type = 'dynamic_query'
|
48
49
|
refute targeting.resolved?
|
@@ -78,6 +79,15 @@ module ForemanRemoteExecution
|
|
78
79
|
job_invocation.task_id.must_equal '123'
|
79
80
|
end
|
80
81
|
|
82
|
+
# In plan phase this is handled by #action_subject
|
83
|
+
# which is expected in tests
|
84
|
+
it 'sets input in delay phase when delayed' do
|
85
|
+
delayed.input[:job_invocation]['id'].must_equal job_invocation.id
|
86
|
+
delayed.input[:job_invocation]['name'].must_equal job_invocation.job_category
|
87
|
+
delayed.input[:job_invocation]['description'].must_equal job_invocation.description
|
88
|
+
planned # To make the expectations happy
|
89
|
+
end
|
90
|
+
|
81
91
|
describe 'concurrency control' do
|
82
92
|
let(:level) { 5 }
|
83
93
|
let(:span) { 60 }
|
@@ -85,33 +95,28 @@ module ForemanRemoteExecution
|
|
85
95
|
it 'can be disabled' do
|
86
96
|
job_invocation.expects(:concurrency_level)
|
87
97
|
job_invocation.expects(:time_span)
|
88
|
-
|
89
|
-
action.expects(:distribute_over_time).never
|
90
|
-
planned
|
98
|
+
planned.input.key?(:concurrency_control).must_equal false
|
91
99
|
end
|
92
100
|
|
93
101
|
it 'can limit concurrency level' do
|
94
102
|
job_invocation.expects(:concurrency_level).returns(level).twice
|
95
103
|
job_invocation.expects(:time_span)
|
96
|
-
|
97
|
-
|
98
|
-
planned
|
104
|
+
planned.input[:concurrency_control][:level].wont_be_empty
|
105
|
+
planned.input[:concurrency_control].key?(:time).must_equal false
|
99
106
|
end
|
100
107
|
|
101
108
|
it 'can distribute tasks over time' do
|
102
109
|
job_invocation.expects(:time_span).returns(span).twice
|
103
110
|
job_invocation.expects(:concurrency_level)
|
104
|
-
|
105
|
-
|
106
|
-
planned
|
111
|
+
planned.input[:concurrency_control][:time].wont_be_empty
|
112
|
+
planned.input[:concurrency_control].key?(:level).must_equal false
|
107
113
|
end
|
108
114
|
|
109
115
|
it 'can use both' do
|
110
116
|
job_invocation.expects(:time_span).returns(span).twice
|
111
|
-
action.expects(:distribute_over_time).with(span)
|
112
117
|
job_invocation.expects(:concurrency_level).returns(level).twice
|
113
|
-
|
114
|
-
planned
|
118
|
+
planned.input[:concurrency_control][:time].wont_be_empty
|
119
|
+
planned.input[:concurrency_control][:level].wont_be_empty
|
115
120
|
end
|
116
121
|
end
|
117
122
|
end
|
@@ -4,9 +4,15 @@ RemoteExecutionProvider.register(:Mcollective, OpenStruct)
|
|
4
4
|
|
5
5
|
class JobInvocationComposerTest < ActiveSupport::TestCase
|
6
6
|
before do
|
7
|
-
setup_user('view',
|
8
|
-
setup_user('
|
9
|
-
setup_user('view',
|
7
|
+
setup_user('view', 'job_templates', 'name ~ trying*')
|
8
|
+
setup_user('create', 'job_templates', 'name ~ trying*')
|
9
|
+
setup_user('view', 'job_invocations')
|
10
|
+
setup_user('create', 'job_invocations')
|
11
|
+
setup_user('view', 'bookmarks')
|
12
|
+
setup_user('create', 'bookmarks')
|
13
|
+
setup_user('edit', 'bookmarks')
|
14
|
+
setup_user('view', 'hosts')
|
15
|
+
setup_user('create', 'hosts')
|
10
16
|
end
|
11
17
|
|
12
18
|
let(:trying_job_template_1) { FactoryGirl.create(:job_template, :job_category => 'trying_job_template_1', :name => 'trying1', :provider_type => 'SSH') }
|
@@ -500,8 +506,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
500
506
|
}
|
501
507
|
}.with_indifferent_access
|
502
508
|
end
|
503
|
-
let(:existing) { composer.job_invocation
|
504
|
-
let(:new_job_invocation) { JobInvocation.new }
|
509
|
+
let(:existing) { composer.job_invocation }
|
505
510
|
let(:new_composer) { JobInvocationComposer.from_job_invocation(composer.job_invocation) }
|
506
511
|
|
507
512
|
before do
|
@@ -590,7 +595,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
590
595
|
|
591
596
|
it 'finds the inputs by name' do
|
592
597
|
assert composer.save!
|
593
|
-
assert_equal 1, composer.pattern_template_invocations.first.input_values.count
|
598
|
+
assert_equal 1, composer.pattern_template_invocations.first.input_values.collect.count
|
594
599
|
end
|
595
600
|
end
|
596
601
|
|
@@ -127,6 +127,19 @@ class JobInvocationTest < ActiveSupport::TestCase
|
|
127
127
|
specify { job_invocation.progress_report.must_equal progress_report_without_sub_tasks }
|
128
128
|
end
|
129
129
|
|
130
|
+
context 'with cancelled task' do
|
131
|
+
before do
|
132
|
+
task.state = 'stopped'
|
133
|
+
task.result = 'error'
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'calculates the progress correctly' do
|
137
|
+
job_invocation.targeting.stubs(:resolved?).returns(true)
|
138
|
+
task.expects(:sub_tasks_counts).never
|
139
|
+
job_invocation.progress_report.must_equal progress_report_without_sub_tasks
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
130
143
|
context 'with succeeded task' do
|
131
144
|
before do
|
132
145
|
task.state = 'stopped'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.8.
|
33
|
+
version: 0.8.26
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.8.
|
40
|
+
version: 0.8.26
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: foreman_remote_execution_core
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.9.5
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.9.5
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rubocop
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -431,7 +431,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
431
431
|
version: '0'
|
432
432
|
requirements: []
|
433
433
|
rubyforge_project:
|
434
|
-
rubygems_version: 2.
|
434
|
+
rubygems_version: 2.6.12
|
435
435
|
signing_key:
|
436
436
|
specification_version: 4
|
437
437
|
summary: A plugin bringing remote execution to the Foreman, completing the config
|