foreman_remote_execution 0.0.6 → 0.0.7
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/app/assets/javascripts/execution_interface.js +6 -0
- data/app/assets/javascripts/template_invocation.js +18 -0
- data/app/assets/stylesheets/template_invocation.css.scss +53 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +64 -0
- data/app/controllers/job_invocations_controller.rb +11 -2
- data/app/controllers/template_invocations_controller.rb +16 -0
- data/app/helpers/remote_execution_helper.rb +78 -15
- data/app/lib/actions/middleware/bind_job_invocation.rb +29 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +20 -53
- data/app/lib/actions/remote_execution/run_hosts_job.rb +30 -9
- data/app/lib/actions/remote_execution/run_proxy_command.rb +64 -3
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +43 -5
- data/app/models/concerns/foreman_remote_execution/nic_extensions.rb +25 -0
- data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +10 -0
- data/app/models/concerns/foreman_remote_execution/taxonomy_extensions.rb +16 -0
- data/app/models/input_template_renderer.rb +5 -1
- data/app/models/job_invocation.rb +74 -7
- data/app/models/job_invocation_api_composer.rb +62 -0
- data/app/models/job_invocation_composer.rb +4 -1
- data/app/models/job_template.rb +2 -4
- data/app/models/setting/remote_execution.rb +6 -2
- data/app/models/target_remote_execution_proxy.rb +4 -0
- data/app/models/template_input.rb +14 -2
- data/app/models/template_invocation.rb +12 -1
- data/app/models/template_invocation_input_value.rb +1 -1
- data/app/overrides/execution_interface.rb +9 -0
- data/app/overrides/foreman/nics/_execution_interface.html.erb +1 -0
- data/app/overrides/foreman/subnets/_rex_tab.html.erb +1 -0
- data/app/overrides/foreman/subnets/_rex_tab_pane.html.erb +5 -0
- data/app/overrides/subnet_proxies.rb +9 -0
- data/app/services/proxy_load_balancer.rb +30 -0
- data/app/views/api/v2/job_invocations/base.json.rabl +3 -0
- data/app/views/api/v2/job_invocations/create.json.rabl +3 -0
- data/app/views/api/v2/job_invocations/index.json.rabl +3 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +3 -0
- data/app/views/api/v2/job_invocations/show.json.rabl +18 -0
- data/app/views/api/v2/job_templates/base.json.rabl +1 -1
- data/app/views/job_invocations/_form.html.erb +9 -0
- data/app/views/job_invocations/_tab_hosts.html.erb +10 -14
- data/app/views/job_invocations/_tab_overview.html.erb +2 -9
- data/app/views/job_invocations/show.html.erb +8 -1
- data/app/views/job_invocations/show.js.erb +6 -0
- data/app/views/template_invocations/_output_line_set.html.erb +7 -0
- data/app/views/template_invocations/_refresh.js.erb +7 -0
- data/app/views/template_invocations/show.html.erb +38 -0
- data/app/views/template_invocations/show.js.erb +16 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20150826191632_create_target_remote_execution_proxies.rb +14 -0
- data/db/migrate/20150903192731_add_execution_to_interface.rb +22 -0
- data/doc/source/design/index.md +5 -0
- data/doc/source/design/wireframes.pdf +0 -0
- data/lib/foreman_remote_execution.rb +1 -1
- data/lib/foreman_remote_execution/engine.rb +13 -4
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/factories/foreman_remote_execution_factories.rb +46 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +45 -0
- data/test/test_plugin_helper.rb +18 -0
- data/test/unit/actions/run_hosts_job_test.rb +13 -8
- data/test/unit/actions/run_proxy_command_test.rb +109 -5
- data/test/unit/concerns/host_extensions_test.rb +90 -0
- data/test/unit/concerns/nic_extensions_test.rb +9 -0
- data/test/unit/input_template_renderer_test.rb +24 -10
- data/test/unit/job_invocation_api_composer_test.rb +117 -0
- data/test/unit/job_invocation_composer_test.rb +16 -1
- data/test/unit/job_invocation_test.rb +48 -3
- data/test/unit/proxy_load_balancer_test.rb +25 -0
- metadata +57 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7b17c729ed77837816fc91f385aec89be98be24
|
|
4
|
+
data.tar.gz: 32d6a582df1ffb8e3d20163481176e651070127e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 733f0235a691db6d71bc9ad72bcf1d3c45db6c95530ed9a57d39203d59401b92858ffb9954c2dccba622ae5a3819147fae35415a1073e1cb920fb138ef89ef46
|
|
7
|
+
data.tar.gz: 35cfdf9e050b3ebe132c955c0f21c41fbd97e33a3dee871f52e556b446a6d64625b66b0c558bd5d01a23465695b0a1cb233d2e39d5ac3f43c48e5aa6793e3d62
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
$(document).on('click', '.interface_execution', function () {
|
|
2
|
+
confirm_flag_change(this, '.interface_execution', __("Another interface is already set as execution. Are you sure you want to use this one instead?"));
|
|
3
|
+
|
|
4
|
+
var modal_form = $('#interfaceModal').find('.modal-body').contents();
|
|
5
|
+
$('#interfaceForms .interface_execution:checked').attr("checked", false);
|
|
6
|
+
});
|
|
@@ -31,6 +31,11 @@ function job_invocation_form_binds() {
|
|
|
31
31
|
$('#job_template_' + $(this).val()).show();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
$('input.trigger_mode_selector').on('click', function () {
|
|
35
|
+
$("#trigger_mode_future").hide();
|
|
36
|
+
$('#trigger_mode_' + $(this).val()).show();
|
|
37
|
+
});
|
|
38
|
+
|
|
34
39
|
$('select#job_invocation_job_name').on('change', refresh_execution_form);
|
|
35
40
|
|
|
36
41
|
$('button#refresh_execution_form').on('click', refresh_execution_form);
|
|
@@ -39,3 +44,16 @@ function job_invocation_form_binds() {
|
|
|
39
44
|
|
|
40
45
|
$('select#targeting_bookmark_id').on('change', refresh_search_query);
|
|
41
46
|
}
|
|
47
|
+
|
|
48
|
+
function delayed_refresh(url, data){
|
|
49
|
+
setTimeout(function () {
|
|
50
|
+
$.ajax(
|
|
51
|
+
{
|
|
52
|
+
url: url,
|
|
53
|
+
data: data,
|
|
54
|
+
dataType: "script",
|
|
55
|
+
error: function() { $("div.terminal div.printable").append(__('<div class="line error">There was an error while updating the status, try <a href="javascript:window.location.href=window.location.href">refreshing</a> the page</div>')) }
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
}, 1000);
|
|
59
|
+
}
|
|
@@ -14,3 +14,56 @@ fieldset.provider_form {
|
|
|
14
14
|
margin-left: 90px;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
div.terminal {
|
|
19
|
+
display: block;
|
|
20
|
+
padding: 9.5px;
|
|
21
|
+
margin: 0 0 10px;
|
|
22
|
+
font-size: 13px;
|
|
23
|
+
line-height: 1.428571429;
|
|
24
|
+
word-break: break-all;
|
|
25
|
+
word-wrap: break-word;
|
|
26
|
+
color: #B2B2B2;
|
|
27
|
+
background-color: #000000;
|
|
28
|
+
border: 1px solid #000000;
|
|
29
|
+
border-radius: 4px;
|
|
30
|
+
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
31
|
+
|
|
32
|
+
div.printable {
|
|
33
|
+
min-height: 50px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
div.line.error, div.line.debug {
|
|
37
|
+
color: #a9302a;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
div.line.stderr {
|
|
41
|
+
background-color: rgba(205, 209, 207, 0.21);
|
|
42
|
+
color: #FFFFFF;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
div.line span.counter {
|
|
46
|
+
float: left;
|
|
47
|
+
clear: left;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
div.line div.content {
|
|
51
|
+
position: relative;
|
|
52
|
+
margin-left: 50px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
a {
|
|
56
|
+
color: #FFFFFF;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
a.scroll-link-top {
|
|
60
|
+
position: relative;
|
|
61
|
+
bottom: 10px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
a.scroll-link-bottom {
|
|
65
|
+
position: relative;
|
|
66
|
+
z-index: 5;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Api
|
|
2
|
+
module V2
|
|
3
|
+
class JobInvocationsController < ::Api::V2::BaseController
|
|
4
|
+
include ::Api::Version2
|
|
5
|
+
include ::Api::TaxonomyScope
|
|
6
|
+
include ::Foreman::Renderer
|
|
7
|
+
|
|
8
|
+
before_filter :find_optional_nested_object
|
|
9
|
+
before_filter :find_resource, :only => %w{show update destroy clone}
|
|
10
|
+
before_filter :validate_templates, :only => :create
|
|
11
|
+
|
|
12
|
+
api :GET, "/job_invocations/", N_("List job invocations")
|
|
13
|
+
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
14
|
+
def index
|
|
15
|
+
@job_invocations = resource_scope_for_index
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
api :GET, "/job_invocations/:id", N_("Show job invocation")
|
|
19
|
+
param :id, :identifier, :required => true
|
|
20
|
+
def show
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def_param_group :job_invocation do
|
|
24
|
+
param :job_invocation, Hash, :required => true, :action_aware => true do
|
|
25
|
+
param :job_name, String, :required => true, :desc => N_("Job name")
|
|
26
|
+
param :template_id, String, :required => false, :desc => N_("If using a specific template, the id of that template.")
|
|
27
|
+
param :targeting_type, String, :required => true, :desc => N_("Invocation type, one of %s") % Targeting::TYPES
|
|
28
|
+
param :inputs, Array, :required => false, :desc => N_("Inputs to use") do
|
|
29
|
+
param :name, String, :required => true
|
|
30
|
+
param :value, String, :required => true
|
|
31
|
+
end
|
|
32
|
+
param :bookmark_id, Integer, :required => false
|
|
33
|
+
param :search_query, Integer, :required => false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
api :POST, "/job_invocations/", N_("Create a job template")
|
|
38
|
+
param_group :job_invocation, :as => :create
|
|
39
|
+
def create
|
|
40
|
+
composer = JobInvocationApiComposer.new(JobInvocation.new, User.current, params[:job_invocation])
|
|
41
|
+
composer.save!
|
|
42
|
+
ForemanTasks.async_task(::Actions::RemoteExecution::RunHostsJob, composer.job_invocation)
|
|
43
|
+
@job_invocation = composer.job_invocation
|
|
44
|
+
process_response @job_invocation
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def validate_templates
|
|
50
|
+
templates = []
|
|
51
|
+
if params[:job_invocation][:template_id]
|
|
52
|
+
templates << JobTemplate.find(params[:job_invocation][:template_id])
|
|
53
|
+
else
|
|
54
|
+
templates = JobTemplate.where(:job_name => params[:job_invocation][:job_name])
|
|
55
|
+
if templates.pluck(:provider_type).uniq.length != templates.length
|
|
56
|
+
raise Foreman::Exception, _("Duplicate remote execution providers found for specified Job, please specify a single template_id.")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
raise Foreman::Exception, _("No templates associated with specified Job Name") if templates.empty?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -24,9 +24,17 @@ class JobInvocationsController < ApplicationController
|
|
|
24
24
|
|
|
25
25
|
def create
|
|
26
26
|
@composer = JobInvocationComposer.new.compose_from_params(params)
|
|
27
|
+
action = ::Actions::RemoteExecution::RunHostsJob
|
|
27
28
|
if @composer.save
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
job_invocation = @composer.job_invocation
|
|
30
|
+
if job_invocation.trigger_mode == :future
|
|
31
|
+
ForemanTasks.delay action,
|
|
32
|
+
job_invocation.delay_options,
|
|
33
|
+
job_invocation
|
|
34
|
+
else
|
|
35
|
+
ForemanTasks.async_task(action, job_invocation)
|
|
36
|
+
end
|
|
37
|
+
redirect_to job_invocation_path(job_invocation)
|
|
30
38
|
else
|
|
31
39
|
render :action => 'new'
|
|
32
40
|
end
|
|
@@ -34,6 +42,7 @@ class JobInvocationsController < ApplicationController
|
|
|
34
42
|
|
|
35
43
|
def show
|
|
36
44
|
@job_invocation = resource_base.find(params[:id])
|
|
45
|
+
@auto_refresh = @job_invocation.last_task.try(:pending?)
|
|
37
46
|
end
|
|
38
47
|
|
|
39
48
|
def index
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class TemplateInvocationsController < ApplicationController
|
|
2
|
+
def controller_permission
|
|
3
|
+
'job_invocations'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def show
|
|
7
|
+
@template_invocation_task = ForemanTasks::Task.find(params[:id])
|
|
8
|
+
@template_invocation = @template_invocation_task.locks.where(:resource_type => 'TemplateInvocation').first.try(:resource)
|
|
9
|
+
@host = @template_invocation_task.locks.where(:resource_type => 'Host::Managed').first.try(:resource)
|
|
10
|
+
@auto_refresh = @template_invocation_task.pending?
|
|
11
|
+
@since = params[:since].to_f if params[:since].present?
|
|
12
|
+
@line_sets = @template_invocation_task.main_action.live_output
|
|
13
|
+
@line_sets = @line_sets.drop_while { |o| o['timestamp'].to_f <= @since } if @since
|
|
14
|
+
@line_counter = params[:line_counter].to_i
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -11,14 +11,18 @@ module RemoteExecutionHelper
|
|
|
11
11
|
options = { :class => 'statistics-pie small', :expandable => true, :border => 0, :show_title => true }
|
|
12
12
|
|
|
13
13
|
if (bulk_task = invocation.last_task)
|
|
14
|
+
failed_tasks = bulk_task.sub_tasks.select { |sub_task| %w(warning error).include? sub_task.result }
|
|
15
|
+
cancelled_tasks, failed_tasks = failed_tasks.partition { |task| task_cancelled? task }
|
|
14
16
|
success = bulk_task.output['success_count'] || 0
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
cancelled = cancelled_tasks.length
|
|
18
|
+
failed = failed_tasks.length
|
|
19
|
+
pending = (bulk_task.output['pending_count'] || bulk_task.sub_tasks.count)
|
|
17
20
|
|
|
18
21
|
flot_pie_chart('status', job_invocation_status(invocation),
|
|
19
22
|
[{:label => _('Success'), :data => success, :color => '#5CB85C'},
|
|
20
23
|
{:label => _('Failed'), :data => failed, :color => '#D9534F'},
|
|
21
|
-
{:label => _('Pending'), :data => pending, :color => '#DEDEDE'}
|
|
24
|
+
{:label => _('Pending'), :data => pending, :color => '#DEDEDE'},
|
|
25
|
+
{:label => _('Cancelled'), :data => cancelled, :color => '#B7312D'}],
|
|
22
26
|
options)
|
|
23
27
|
else
|
|
24
28
|
content_tag(:h4, job_invocation_status(invocation))
|
|
@@ -28,11 +32,18 @@ module RemoteExecutionHelper
|
|
|
28
32
|
def job_invocation_status(invocation)
|
|
29
33
|
if invocation.last_task.blank?
|
|
30
34
|
_('Job not started yet 0%')
|
|
35
|
+
elsif invocation.last_task.state == 'scheduled'
|
|
36
|
+
_('Job set to execute at %s') % invocation.last_task.start_at
|
|
37
|
+
elsif invocation.last_task.state == 'stopped' && invocation.last_task.result == 'error'
|
|
38
|
+
invocation.last_task.execution_plan.errors.map(&:message).join("\n")
|
|
31
39
|
else
|
|
32
40
|
label = invocation.last_task.pending ? _('Running') : _('Finished')
|
|
33
41
|
label + ' ' + (invocation.last_task.progress * 100).to_i.to_s + '%'
|
|
34
42
|
end
|
|
43
|
+
end
|
|
35
44
|
|
|
45
|
+
def task_cancelled?(task)
|
|
46
|
+
task.execution_plan.errors.map(&:exception).any? { |exception| exception.class == ::ForemanTasks::Task::TaskCancelledException }
|
|
36
47
|
end
|
|
37
48
|
|
|
38
49
|
def host_counter(label, count)
|
|
@@ -42,18 +53,39 @@ module RemoteExecutionHelper
|
|
|
42
53
|
end
|
|
43
54
|
|
|
44
55
|
def template_invocation_status(task)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
if task.nil?
|
|
57
|
+
content_tag(:i, ' '.html_safe, :class => 'glyphicon glyphicon-question-sign') + content_tag(:span, _('N/A'))
|
|
58
|
+
else
|
|
59
|
+
case task.result
|
|
60
|
+
when 'warning', 'error'
|
|
61
|
+
if task_cancelled?(task)
|
|
62
|
+
content_tag(:i, ' '.html_safe, :class => 'glyphicon glyphicon-warning-sign') + content_tag(:span, _('cancelled'), :class => 'status-error')
|
|
63
|
+
else
|
|
64
|
+
content_tag(:i, ' '.html_safe, :class => 'glyphicon glyphicon-exclamation-sign') + content_tag(:span, _('failed'), :class => 'status-error')
|
|
65
|
+
end
|
|
66
|
+
when 'success'
|
|
67
|
+
content_tag(:i, ' '.html_safe, :class => 'glyphicon glyphicon-ok-sign') + content_tag(:span, _('success'), :class => 'status-ok')
|
|
68
|
+
when 'pending'
|
|
69
|
+
content_tag(:i, ' '.html_safe, :class => 'glyphicon glyphicon-question-sign') + content_tag(:span, _('pending'))
|
|
70
|
+
else
|
|
71
|
+
task.result
|
|
72
|
+
end
|
|
54
73
|
end
|
|
55
74
|
end
|
|
56
75
|
|
|
76
|
+
def template_invocation_actions(task, host)
|
|
77
|
+
if task.nil?
|
|
78
|
+
[]
|
|
79
|
+
else
|
|
80
|
+
[display_link_if_authorized(_("Details"), hash_for_template_invocation_path(:id => task).merge(:auth_object => host, :permission => :view_foreman_tasks))]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def remote_execution_provider_for(task)
|
|
85
|
+
template_invocation = task.locks.where(:resource_type => 'TemplateInvocation').first.try(:resource) unless task.nil?
|
|
86
|
+
template_invocation.nil? ? _('N/A') : _(RemoteExecutionProvider.provider_for(template_invocation.template.provider_type))
|
|
87
|
+
end
|
|
88
|
+
|
|
57
89
|
def job_invocation_task_buttons(task)
|
|
58
90
|
buttons = []
|
|
59
91
|
buttons << link_to(_('Refresh'), {}, :class => 'btn btn-default', :title => _('Refresh this page'))
|
|
@@ -79,11 +111,28 @@ module RemoteExecutionHelper
|
|
|
79
111
|
:disabled => !task.cancellable?,
|
|
80
112
|
:method => :post)
|
|
81
113
|
end
|
|
82
|
-
return
|
|
114
|
+
return buttons
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def template_invocation_task_buttons(task)
|
|
118
|
+
buttons = []
|
|
119
|
+
if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
|
|
120
|
+
buttons << link_to(_("Task Details"), foreman_tasks_task_path(task),
|
|
121
|
+
:class => "btn btn-default",
|
|
122
|
+
:title => _('See the task details'))
|
|
123
|
+
end
|
|
124
|
+
if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task)
|
|
125
|
+
buttons << link_to(_("Cancel Job"), cancel_foreman_tasks_task_path(task),
|
|
126
|
+
:class => "btn btn-danger",
|
|
127
|
+
:title => _('Try to cancel the job on a host'),
|
|
128
|
+
:disabled => !task.cancellable?,
|
|
129
|
+
:method => :post)
|
|
130
|
+
end
|
|
131
|
+
return buttons
|
|
83
132
|
end
|
|
84
133
|
|
|
85
134
|
def link_to_invocation_task_if_authorized(invocation)
|
|
86
|
-
if invocation.last_task.present?
|
|
135
|
+
if invocation.last_task.present? && invocation.last_task.state != 'scheduled'
|
|
87
136
|
link_to_if_authorized job_invocation_status(invocation),
|
|
88
137
|
hash_for_foreman_tasks_task_path(invocation.last_task).merge(:auth_object => invocation.last_task, :permission => :view_foreman_tasks)
|
|
89
138
|
else
|
|
@@ -93,6 +142,20 @@ module RemoteExecutionHelper
|
|
|
93
142
|
|
|
94
143
|
def invocation_count(invocation, options = {})
|
|
95
144
|
options = { :unknown_string => 'N/A' }.merge(options)
|
|
96
|
-
|
|
145
|
+
if invocation.last_task.nil? || invocation.last_task.state != 'scheduled'
|
|
146
|
+
(invocation.last_task.try(:output) || {}).fetch(options[:output_key], options[:unknown_string])
|
|
147
|
+
else
|
|
148
|
+
options[:unknown_string]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def preview_box(template_invocation, target)
|
|
153
|
+
renderer = InputTemplateRenderer.new(template_invocation.template, target, template_invocation)
|
|
154
|
+
if (preview = renderer.preview)
|
|
155
|
+
content_tag :pre, preview
|
|
156
|
+
else
|
|
157
|
+
alert :class => "alert-block alert-danger base in fade has-error",
|
|
158
|
+
:text => renderer.error_message.html_safe
|
|
159
|
+
end
|
|
97
160
|
end
|
|
98
161
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Actions
|
|
2
|
+
module Middleware
|
|
3
|
+
|
|
4
|
+
class BindJobInvocation < ::Dynflow::Middleware
|
|
5
|
+
|
|
6
|
+
def delay(*args)
|
|
7
|
+
_schedule_options, job_invocation = args
|
|
8
|
+
pass(*args).tap { bind(job_invocation) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def plan(*args)
|
|
12
|
+
job_invocation = args.first
|
|
13
|
+
pass(*args).tap { bind(job_invocation) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def task
|
|
19
|
+
@task ||= ForemanTasks::Task::DynflowTask.find_by_external_id!(action.execution_plan_id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def bind(job_invocation)
|
|
23
|
+
job_invocation.update_attribute :last_task_id, task.id if job_invocation.last_task_id != task.id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -8,12 +8,17 @@ module Actions
|
|
|
8
8
|
|
|
9
9
|
include ::Dynflow::Action::Cancellable
|
|
10
10
|
|
|
11
|
-
def plan(job_invocation, host)
|
|
11
|
+
def plan(job_invocation, host, template_invocation, proxy, connection_options = {})
|
|
12
12
|
action_subject(host, :job_name => job_invocation.job_name)
|
|
13
|
-
|
|
14
|
-
template_invocation = find_template_invocation(job_invocation, host)
|
|
15
13
|
hostname = find_ip_or_hostname(host)
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
raise _("Could not use any template used in the job invocation") if template_invocation.blank?
|
|
16
|
+
|
|
17
|
+
settings = { :global_proxy => 'remote_execution_global_proxy',
|
|
18
|
+
:fallback_proxy => 'remote_execution_fallback_proxy' }
|
|
19
|
+
|
|
20
|
+
raise _("Could not use any proxy. Consider configuring %{global_proxy} " +
|
|
21
|
+
"or %{fallback_proxy} in settings") % settings if proxy.blank?
|
|
17
22
|
|
|
18
23
|
renderer = InputTemplateRenderer.new(template_invocation.template, host, template_invocation)
|
|
19
24
|
script = renderer.render
|
|
@@ -22,71 +27,33 @@ module Actions
|
|
|
22
27
|
link!(job_invocation)
|
|
23
28
|
link!(template_invocation)
|
|
24
29
|
|
|
25
|
-
plan_action(RunProxyCommand, proxy, hostname, script)
|
|
30
|
+
plan_action(RunProxyCommand, proxy, hostname, script, { :connection_options => connection_options })
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
def humanized_output
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
output << proxy_output[:result].map { |o| o[:output] }.join("")
|
|
35
|
-
end
|
|
36
|
-
output << "Exit status: #{host_run_action.exit_status}" if host_run_action.exit_status
|
|
37
|
-
return output.join("\n")
|
|
34
|
+
live_output.map { |line| line['output'].chomp }.join("\n")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def live_output
|
|
38
|
+
planned_actions(RunProxyCommand).first.live_output
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def humanized_name
|
|
41
42
|
_('Run %{job_name} on %{host}') % { :job_name => input[:job_name], :host => input[:host][:name] }
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if template_invocation.template.provider_type == provider
|
|
49
|
-
return template_invocation
|
|
50
|
-
end
|
|
45
|
+
def find_ip_or_hostname(host)
|
|
46
|
+
%w(execution primary provision).each do |flag|
|
|
47
|
+
if host.send("#{flag}_interface") && host.send("#{flag}_interface").ip.present?
|
|
48
|
+
return host.execution_interface.ip
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
|
|
54
|
-
raise _("Could not use any template used in the job invocation")
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def find_ip_or_hostname(host)
|
|
58
52
|
host.interfaces.each do |interface|
|
|
59
53
|
return interface.ip unless interface.ip.blank?
|
|
60
54
|
end
|
|
61
|
-
return host.name
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def available_providers(job_invocation, host)
|
|
65
|
-
# TODO: determine from the host and job_invocation details
|
|
66
|
-
return ['Ssh']
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def find_proxy(template_invocation, host)
|
|
70
|
-
provider = template_invocation.template.provider_type.to_s
|
|
71
|
-
all_host_proxies(host).each do |proxies|
|
|
72
|
-
if proxy = proxies.joins(:features).where("features.name = ?", provider).first
|
|
73
|
-
return proxy
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
raise _("Could not use any proxy: assign a proxy with provider '%{provider}' to the host or set '%{global_proxy_setting}' in settings") %\
|
|
77
|
-
{ :provider => provider, :global_proxy_setting => 'remote_execution_global_proxy' }
|
|
78
|
-
end
|
|
79
55
|
|
|
80
|
-
|
|
81
|
-
Enumerator.new do |e|
|
|
82
|
-
host.interfaces.each do |interface|
|
|
83
|
-
if interface.subnet
|
|
84
|
-
e << ::SmartProxy.where(:id => interface.subnet.proxies.map(&:id))
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
e << host.smart_proxies
|
|
88
|
-
e << ::SmartProxy.authorized if Setting[:remote_execution_global_proxy]
|
|
89
|
-
end
|
|
56
|
+
return host.fqdn
|
|
90
57
|
end
|
|
91
58
|
end
|
|
92
59
|
end
|