foreman_remote_execution 1.3.3 → 1.3.4
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 +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
|