From 20141af8fb8db2f324169626b0f036b1f330a554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ne=C4=8Das?= Date: Mon, 18 Jan 2016 12:36:17 +0100 Subject: [PATCH 1/3] fixes #10756 - developer api for registering features and templates Any plugin (or core) and define a feature at initialization: ``` RemoteExecutionFeature.register(:reprovision, N_("Reprovision"), :description => "Reprovision the host via script", :provided_inputs => ["script"]) ``` The mapping between the feature and templates can be made via 'Administer -> Remote Execution Features'. ``` composer = JobInvocationComposer.for_feature(:reprovision, host, :script => "grubby...") composer.save! composer.trigger ``` When seeding a template, there is a new meta `feature`, which allows to assign some template as default to the specified feature. The template mapping is now limited to just one template, but in future, we can extend that to allow different mappings through organizations, but lets keep it simple first. --- .../api/v2/job_invocations_controller.rb | 6 +- app/controllers/job_invocations_controller.rb | 8 +-- .../remote_execution_features_controller.rb | 19 +++++ app/models/input_template_renderer.rb | 1 + app/models/job_invocation_composer.rb | 71 ++++++++++++++++++- app/models/job_template.rb | 9 ++- app/models/remote_execution_feature.rb | 34 +++++++++ app/models/targeting.rb | 4 +- .../remote_execution_features/_form.html.erb | 18 +++++ .../remote_execution_features/index.html.erb | 21 ++++++ .../remote_execution_features/show.html.erb | 3 + config/routes.rb | 2 + ...124600_create_remote_execution_features.rb | 14 ++++ ..._template_invocation_input_values_value.rb | 14 ++++ lib/foreman_remote_execution/engine.rb | 13 +++- test/unit/remote_execution_feature_test.rb | 42 +++++++++++ test/unit/targeting_test.rb | 6 +- 17 files changed, 268 insertions(+), 17 deletions(-) create mode 100644 app/controllers/remote_execution_features_controller.rb create mode 100644 app/models/remote_execution_feature.rb create mode 100644 app/views/remote_execution_features/_form.html.erb create mode 100644 app/views/remote_execution_features/index.html.erb create mode 100644 app/views/remote_execution_features/show.html.erb create mode 100644 db/migrate/20160118124600_create_remote_execution_features.rb create mode 100644 db/migrate/20160201154900_change_template_invocation_input_values_value.rb create mode 100644 test/unit/remote_execution_feature_test.rb diff --git a/app/controllers/api/v2/job_invocations_controller.rb b/app/controllers/api/v2/job_invocations_controller.rb index 9460277d4..5ee8c5842 100644 --- a/app/controllers/api/v2/job_invocations_controller.rb +++ b/app/controllers/api/v2/job_invocations_controller.rb @@ -57,10 +57,8 @@ def show def create composer = JobInvocationComposer.from_api_params(job_invocation_params) composer.save! - @job_invocation = composer.job_invocation - @job_invocation.generate_description! if @job_invocation.description.blank? - composer.triggering.trigger(::Actions::RemoteExecution::RunHostsJob, @job_invocation) - process_response @job_invocation + composer.trigger + process_response composer.job_invocation end api :GET, '/job_invocations/:id/hosts/:host_id', N_('Get output for a host') diff --git a/app/controllers/job_invocations_controller.rb b/app/controllers/job_invocations_controller.rb index de1c7a5b8..84ae25f36 100644 --- a/app/controllers/job_invocations_controller.rb +++ b/app/controllers/job_invocations_controller.rb @@ -30,7 +30,7 @@ def rerun if params[:failed_only] host_ids = job_invocation.failed_host_ids - @composer.search_query = @composer.targeting.build_query_from_hosts(host_ids) + @composer.search_query = Targeting.build_query_from_hosts(host_ids) end render :action => 'new' @@ -39,10 +39,8 @@ def rerun def create @composer = JobInvocationComposer.from_ui_params(params) if @composer.save - job_invocation = @composer.job_invocation - job_invocation.generate_description! if job_invocation.description.blank? - @composer.triggering.trigger(::Actions::RemoteExecution::RunHostsJob, job_invocation) - redirect_to job_invocation_path(job_invocation) + @composer.trigger + redirect_to job_invocation_path(@composer.job_invocation) else @composer.job_invocation.description_format = nil if params[:job_invocation].key?(:description_override) render :action => 'new' diff --git a/app/controllers/remote_execution_features_controller.rb b/app/controllers/remote_execution_features_controller.rb new file mode 100644 index 000000000..77241792e --- /dev/null +++ b/app/controllers/remote_execution_features_controller.rb @@ -0,0 +1,19 @@ +class RemoteExecutionFeaturesController < ::ApplicationController + before_filter :find_resource, :only => [:show, :update] + + def index + @remote_execution_features = resource_base.all + end + + def show + end + + def update + if @remote_execution_feature.update_attributes(params[:remote_execution_feature]) + process_success :object => @remote_execution_feature + else + process_error :object => @remote_execution_feature + end + end + +end diff --git a/app/models/input_template_renderer.rb b/app/models/input_template_renderer.rb index abaefb30b..d02a02e42 100644 --- a/app/models/input_template_renderer.rb +++ b/app/models/input_template_renderer.rb @@ -2,6 +2,7 @@ class InputTemplateRenderer class UndefinedInput < ::Foreman::Exception end + include Rails.application.routes.url_helpers include UnattendedHelper attr_accessor :template, :host, :invocation, :input_values, :error_message diff --git a/app/models/job_invocation_composer.rb b/app/models/job_invocation_composer.rb index 65daad24c..5d03dc5fd 100644 --- a/app/models/job_invocation_composer.rb +++ b/app/models/job_invocation_composer.rb @@ -164,6 +164,66 @@ def triggering_params end end + class ParamsForFeature + attr_reader :feature_label, :feature, :provided_inputs + + def initialize(feature_label, hosts, provided_inputs = {}) + @feature = RemoteExecutionFeature.feature(feature_label) + @provided_inputs = provided_inputs + if hosts.is_a? Bookmark + @host_bookmark = hosts + elsif hosts.is_a? Host::Base + @host_objects = [hosts] + elsif hosts.is_a? String + @host_scoped_search = hosts + else + @host_objects = hosts + end + end + + def params + { :job_category => template.job_category, + :targeting => targeting_params, + :triggering => {}, + :template_invocations => template_invocations_params, + :description_format => template.generate_description_format }.with_indifferent_access + end + + private + + def targeting_params + ret = {} + ret['targeting_type'] = Targeting::STATIC_TYPE + ret['search_query'] = @host_scoped_search if @host_scoped_search + ret['search_query'] = Targeting.build_query_from_hosts(@host_objects) if @host_objects + ret['bookmark_id'] = @host_bookmark.id if @host_bookmark + ret['user_id'] = User.current.id + ret + end + + def template_invocations_params + [ { 'template_id' => template.id, + 'input_values' => input_values_params } ] + end + + def input_values_params + @provided_inputs.map do |key, value| + input = template.template_inputs.find_by_name!(key) + { 'template_input_id' => input.id, 'value' => value } + end + end + + def template + template = JobTemplate.authorized(:view_job_templates).find_by_id(feature.template_id) + unless template + raise Foreman::Exception.new(N_('The template %{template_name} mapped to feature %{feature_name} is not accessible by the user'), + :template_name => mapping.template.name, + :feature_name => feature.name) + end + template + end + end + attr_accessor :params, :job_invocation, :host_ids, :search_query delegate :job_category, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation @@ -190,6 +250,10 @@ def self.from_api_params(api_params) self.new(ApiParams.new(api_params).params) end + def self.for_feature(feature_label, hosts, provided_inputs = {}) + self.new(ParamsForFeature.new(feature_label, hosts, provided_inputs).params) + end + def compose job_invocation.job_category = validate_job_category(params[:job_category]) job_invocation.job_category ||= available_job_categories.first if @set_defaults @@ -201,6 +265,11 @@ def compose self end + def trigger + job_invocation.generate_description! if job_invocation.description.blank? + triggering.trigger(::Actions::RemoteExecution::RunHostsJob, job_invocation) + end + def valid? targeting.valid? & job_invocation.valid? & !pattern_template_invocations.map(&:valid?).include?(false) end @@ -262,7 +331,7 @@ def displayed_search_query if @search_query.present? @search_query elsif host_ids.present? - targeting.build_query_from_hosts(host_ids) + Targeting.build_query_from_hosts(host_ids) elsif targeting.bookmark_id if (bookmark = available_bookmarks.find_by(:id => targeting.bookmark_id)) bookmark.query diff --git a/app/models/job_template.rb b/app/models/job_template.rb index b52d861a1..fae553216 100644 --- a/app/models/job_template.rb +++ b/app/models/job_template.rb @@ -21,6 +21,8 @@ class NonUniqueInputsError < Foreman::Exception has_many :pattern_template_invocations, :conditions => 'host_id IS NULL', :foreign_key => 'template_id', :class_name => 'TemplateInvocation' end + has_many :remote_execution_features, :dependent => :nullify, :foreign_key => 'template_id' + # these can't be shared in parent class, scoped search can't handle STI properly # tested with scoped_search 3.2.0 include Taxonomix @@ -70,13 +72,18 @@ def self.import(template, options = {}) inputs = metadata.delete('template_inputs') - template = self.create(metadata.merge(:template => template.gsub(/<%\#.+?.-?%>\n?/m, '')).merge(options)) + template = self.create(metadata.merge(:template => template.gsub(/<%\#.+?.-?%>\n?/m, '')).except('feature').merge(options)) template.assign_taxonomies inputs.each do |input| template.template_inputs << TemplateInput.create(input) end + if metadata['feature'] && feature = RemoteExecutionFeature.feature(metadata['feature']) + feature.template_id ||= template.id + feature.save! + end + template end diff --git a/app/models/remote_execution_feature.rb b/app/models/remote_execution_feature.rb new file mode 100644 index 000000000..9a44e6ce5 --- /dev/null +++ b/app/models/remote_execution_feature.rb @@ -0,0 +1,34 @@ +class RemoteExecutionFeature < ActiveRecord::Base + attr_accessible :label, :name, :provided_input_names, :description, :template_id + + validate :label, :name, :presence => true, :unique => true + + extend FriendlyId + friendly_id :label + + def provided_input_names + self.provided_inputs.to_s.split(',').map(&:chomp) + end + + def provided_input_names=(values) + self.provided_inputs = Array(values).join(',') + end + + class << self + def feature(label) + self.find_by_label(label) || raise(::Foreman::Exception.new(N_("Unknown remote execution feature %s"), label)) + end + + def register(label, name, options = {}) + return false unless RemoteExecutionFeature.table_exists? + options.assert_valid_keys(:provided_inputs, :description) + feature = self.find_by_label(label) + if feature.nil? + feature = self.create!(:label => label, :name => name, :provided_input_names => options[:provided_inputs], :description => options[:description]) + else + feature.update_attributes!(:name => name, :provided_input_names => options[:provided_inputs], :description => options[:description]) + end + return feature + end + end +end diff --git a/app/models/targeting.rb b/app/models/targeting.rb index ad3f03f42..cb2b2fe68 100644 --- a/app/models/targeting.rb +++ b/app/models/targeting.rb @@ -51,9 +51,9 @@ def static? targeting_type == STATIC_TYPE end - def build_query_from_hosts(ids) + def self.build_query_from_hosts(ids) hosts = Host.where(:id => ids).all.group_by(&:id) - ids.map { |id| "name = #{hosts[id].first.name}" }.join(' or ') + hosts.map { |id, h| "name = #{h.first.name}" }.join(' or ') end def resolved? diff --git a/app/views/remote_execution_features/_form.html.erb b/app/views/remote_execution_features/_form.html.erb new file mode 100644 index 000000000..0dea52e33 --- /dev/null +++ b/app/views/remote_execution_features/_form.html.erb @@ -0,0 +1,18 @@ +<%= form_for @remote_execution_feature do |f| %> +
+ <%= field(f, :name) { @remote_execution_feature.name } %> +
+
+ <%= field(f, :label) { @remote_execution_feature.label } %> +
+
+ <%= field(f, :description) { @remote_execution_feature.description } %> +
+
+ <%= field(f, :provided_inputs) { @remote_execution_feature.provided_inputs } %> +
+
+ <%= selectable_f f, :template_id, JobTemplate.all.map { |t| [ t.name, t.id ] }, { :include_blank => true }, :class => 'input_type_selector' %> +
+<%= submit_or_cancel f %> +<% end %> diff --git a/app/views/remote_execution_features/index.html.erb b/app/views/remote_execution_features/index.html.erb new file mode 100644 index 000000000..0b2730fab --- /dev/null +++ b/app/views/remote_execution_features/index.html.erb @@ -0,0 +1,21 @@ +<% title _('Remote Execution Features') %> + + + + + + + + + + + + <% @remote_execution_features.each do |feature| %> + + + + + + <% end %> + +
<%= sort :label, :as => _('Label') %><%= sort :name, :as => _('Name') %><%= sort :description, :as => _('Description') %>
<%= link_to_if_authorized feature.label, hash_for_remote_execution_feature_path(feature).merge(:auth_object => feature, :permission => :view_remote_execution_feature) %><%= feature.name %><%= feature.description %>
diff --git a/app/views/remote_execution_features/show.html.erb b/app/views/remote_execution_features/show.html.erb new file mode 100644 index 000000000..1260de570 --- /dev/null +++ b/app/views/remote_execution_features/show.html.erb @@ -0,0 +1,3 @@ +<% title _("Edit Remote Execution Feature") %> + +<%= render :partial => 'form' %> diff --git a/config/routes.rb b/config/routes.rb index 2fbe3330c..750260f23 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,6 +25,8 @@ end end + resources :remote_execution_features, :only => [:show, :index, :update] + # index is needed so the auto_complete_search can be constructed, otherwise autocompletion in filter does not work resources :template_invocations, :only => [:show, :index] do collection do diff --git a/db/migrate/20160118124600_create_remote_execution_features.rb b/db/migrate/20160118124600_create_remote_execution_features.rb new file mode 100644 index 000000000..4a9c3441d --- /dev/null +++ b/db/migrate/20160118124600_create_remote_execution_features.rb @@ -0,0 +1,14 @@ +class CreateRemoteExecutionFeatures < ActiveRecord::Migration + def change + create_table :remote_execution_features do |t| + t.string :label, :index => true, :null => false + t.string :name + t.string :description + t.text :provided_inputs, :null => true + t.integer :template_id, :null => true + end + add_index :remote_execution_features, :label + add_index :remote_execution_features, :template_id + add_foreign_key :remote_execution_features, :templates + end +end diff --git a/db/migrate/20160201154900_change_template_invocation_input_values_value.rb b/db/migrate/20160201154900_change_template_invocation_input_values_value.rb new file mode 100644 index 000000000..4a9c3441d --- /dev/null +++ b/db/migrate/20160201154900_change_template_invocation_input_values_value.rb @@ -0,0 +1,14 @@ +class CreateRemoteExecutionFeatures < ActiveRecord::Migration + def change + create_table :remote_execution_features do |t| + t.string :label, :index => true, :null => false + t.string :name + t.string :description + t.text :provided_inputs, :null => true + t.integer :template_id, :null => true + end + add_index :remote_execution_features, :label + add_index :remote_execution_features, :template_id + add_foreign_key :remote_execution_features, :templates + end +end diff --git a/lib/foreman_remote_execution/engine.rb b/lib/foreman_remote_execution/engine.rb index f55a9bcc6..f2594bcbf 100644 --- a/lib/foreman_remote_execution/engine.rb +++ b/lib/foreman_remote_execution/engine.rb @@ -45,6 +45,8 @@ class Engine < ::Rails::Engine :'api/v2/job_templates' => [:update], :'api/v2/template_inputs' => [:create, :update, :destroy], :'api/v2/foreign_input_sets' => [:create, :update, :destroy]}, :resource_type => 'JobTemplate' + permission :edit_remote_execution_features, { :remote_execution_features => [:index, :show, :update], + :'api/v2/remote_execution_features' => [:index, :show, :update]}, :resource_type => 'RemoteExecutionFeature' permission :destroy_job_templates, { :job_templates => [:destroy], :'api/v2/job_templates' => [:destroy] }, :resource_type => 'JobTemplate' permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate' @@ -72,7 +74,8 @@ class Engine < ::Rails::Engine :create_job_templates, :lock_job_templates, :view_audit_logs, - :filter_autocompletion_for_template_invocation + :filter_autocompletion_for_template_invocation, + :edit_remote_execution_features ] # Add a new role called 'Remote Execution User ' if it doesn't exist @@ -85,6 +88,11 @@ class Engine < ::Rails::Engine caption: N_('Job templates'), parent: :hosts_menu, after: :provisioning_templates + menu :admin_menu, :remote_execution_features, + url_hash: { controller: :remote_execution_features, action: :index }, + caption: N_('Remote Execution Features'), + parent: :administer_menu, + after: :bookmarks menu :top_menu, :job_invocations, url_hash: { controller: :job_invocations, action: :index }, @@ -95,6 +103,9 @@ class Engine < ::Rails::Engine register_custom_status HostStatus::ExecutionStatus # add dashboard widget # widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1 + + RemoteExecutionFeature.register(:reprovision, N_("Reprovision"), + :description => "Reprovision the host via a script") end end diff --git a/test/unit/remote_execution_feature_test.rb b/test/unit/remote_execution_feature_test.rb new file mode 100644 index 000000000..6bb142149 --- /dev/null +++ b/test/unit/remote_execution_feature_test.rb @@ -0,0 +1,42 @@ +require 'test_plugin_helper' + +describe RemoteExecutionFeature do + + let(:install_feature) do + RemoteExecutionFeature.register(:katello_install_package, N_("Katello: Install package"), + :description => "Install package via Katello user interface", + :provided_inputs => ["package"]) + end + + let(:package_template) do + FactoryGirl.create(:job_template).tap do |job_template| + job_template.job_category = 'Package Action' + job_template.name = 'Package Action - SSH Default' + job_template.template_inputs.create(:name => "package", :input_type => "user") + end + end + + let(:host) { FactoryGirl.create(:host) } + + before do + User.current = users :admin + install_feature.update_attributes!(:template_id => package_template.id) + end + + describe 'composer' do + it 'prepares composer for given feature based on the mapping' do + composer = JobInvocationComposer.for_feature(:katello_install_package, host, :package => "zsh") + assert composer.valid? + composer.pattern_template_invocations.size.must_equal 1 + template_invocation = composer.pattern_template_invocations.first + template_invocation.template.must_equal package_template + template_invocation.input_values.size.must_equal 1 + + input_value = template_invocation.input_values.first + input_value.value.must_equal 'zsh' + input_value.template_input.name.must_equal 'package' + + composer.targeting.search_query.must_equal "name = #{host.name}" + end + end +end diff --git a/test/unit/targeting_test.rb b/test/unit/targeting_test.rb index d557aea61..33571da6a 100644 --- a/test/unit/targeting_test.rb +++ b/test/unit/targeting_test.rb @@ -86,7 +86,7 @@ end context 'for two hosts' do - let(:query) { targeting.build_query_from_hosts([ host.id, second_host.id ]) } + let(:query) { Targeting.build_query_from_hosts([ host.id, second_host.id ]) } it 'builds query using host names joining with or' do query.must_include "name = #{host.name}" @@ -99,7 +99,7 @@ end context 'for one host' do - let(:query) { targeting.build_query_from_hosts([ host.id ]) } + let(:query) { Targeting.build_query_from_hosts([ host.id ]) } it 'builds query using host name' do query.must_equal "name = #{host.name}" @@ -109,7 +109,7 @@ end context 'for no id' do - let(:query) { targeting.build_query_from_hosts([]) } + let(:query) { Targeting.build_query_from_hosts([]) } it 'builds query to find all hosts' do Host.search_for(query).must_include host From 9ec393c1c04a744fa0627ed5731a5bce4925f0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ne=C4=8Das?= Date: Mon, 1 Feb 2016 14:59:08 +0100 Subject: [PATCH 2/3] refs #13491 - seed power action templates --- app/views/templates/power_action.erb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app/views/templates/power_action.erb diff --git a/app/views/templates/power_action.erb b/app/views/templates/power_action.erb new file mode 100644 index 000000000..36fd857bd --- /dev/null +++ b/app/views/templates/power_action.erb @@ -0,0 +1,22 @@ +<%# +kind: job_template +name: Power Action - SSH Default +job_category: Power +description_format: '%{action} host' +provider_type: SSH +template_inputs: +- name: action + description: Action to perform on the service + input_type: user + options: "restart\nshutdown" + required: true +%> + +echo <%= input('action') %> host && sleep 3 +<%= case input('action') + when 'restart' + 'reboot' + else + 'shutdown -h now' + end %> + From f2b275f44c482ceaebb3545ee99e6e53d00df26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ne=C4=8Das?= Date: Mon, 1 Feb 2016 16:33:34 +0100 Subject: [PATCH 3/3] fixes #13500 - Pxe-less reprovision with remote execution This commit adds ability to run the script provision template via remote execution and reboot the host, which effectively brings PXEless provisioning into Foreman. --- .../hosts_controller_extensions.rb | 36 +++++++++++++++++++ .../hosts_helper_extensions.rb | 10 ++++-- .../host_extensions.rb | 2 +- app/models/remote_execution_feature.rb | 2 ++ app/views/templates/reprovision.erb | 16 +++++++++ config/routes.rb | 6 ++++ ..._template_invocation_input_values_value.rb | 13 ++----- lib/foreman_remote_execution/engine.rb | 6 +++- 8 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 app/controllers/concerns/foreman_remote_execution/hosts_controller_extensions.rb create mode 100644 app/views/templates/reprovision.erb diff --git a/app/controllers/concerns/foreman_remote_execution/hosts_controller_extensions.rb b/app/controllers/concerns/foreman_remote_execution/hosts_controller_extensions.rb new file mode 100644 index 000000000..807224538 --- /dev/null +++ b/app/controllers/concerns/foreman_remote_execution/hosts_controller_extensions.rb @@ -0,0 +1,36 @@ +module ForemanRemoteExecution + module HostsControllerExtensions + extend ActiveSupport::Concern + include Foreman::Renderer + + included do + alias_method_chain(:action_permission, :remote_execution) + end + + def reprovision + find_resource + script_template = @host.provisioning_template(:kind => 'script') + if script_template.nil? + process_error :redirect => :back, :error_msg => _("No script provisioning template available") + return + end + @host.setBuild + script = unattended_render(script_template.template, @template_name) + composer = JobInvocationComposer.for_feature(:reprovision, @host, :script => script) + composer.save! + composer.trigger + process_success :success_msg => _("Reprovision job started. The host should reboot soon."), :success_redirect => :back + end + + private + + def action_permission_with_remote_execution + case params[:action] + when 'reprovision' + :edit_host + else + action_permission_without_remote_execution + end + end + end +end diff --git a/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb b/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb index 18b9db161..4e2e8a58e 100644 --- a/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +++ b/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb @@ -11,9 +11,13 @@ def multiple_actions_with_remote_execution multiple_actions_without_remote_execution + [[_('Run Job'), new_job_invocation_path, false]] end - def host_title_actions_with_run_button(*args) - title_actions(button_group(link_to(_('Run Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button))) - host_title_actions_without_run_button(*args) + def host_title_actions_with_run_button(host) + links = [link_to(_('Run Job'), new_job_invocation_path(:host_ids => [host.id]), :id => :run_button)] + if RemoteExecutionFeature.feature(:reprovision).template && @host.provisioning_template(:kind => 'script') + links << link_to(_('Reprovision'), reprovision_host_path(host.id), { :method => :post, :disabled => @host.build }) + end + title_actions(button_group(*links)) + host_title_actions_without_run_button(host) end end end diff --git a/app/models/concerns/foreman_remote_execution/host_extensions.rb b/app/models/concerns/foreman_remote_execution/host_extensions.rb index f31767c41..ae6d8c1c8 100644 --- a/app/models/concerns/foreman_remote_execution/host_extensions.rb +++ b/app/models/concerns/foreman_remote_execution/host_extensions.rb @@ -50,7 +50,7 @@ def remote_execution_proxies(provider) proxy_scope = ::SmartProxy end - proxies[:global] = proxy_scope.authorized.with_features(provider) + proxies[:global] = proxy_scope.with_features(provider) end proxies diff --git a/app/models/remote_execution_feature.rb b/app/models/remote_execution_feature.rb index 9a44e6ce5..c00446aa8 100644 --- a/app/models/remote_execution_feature.rb +++ b/app/models/remote_execution_feature.rb @@ -3,6 +3,8 @@ class RemoteExecutionFeature < ActiveRecord::Base validate :label, :name, :presence => true, :unique => true + belongs_to :template + extend FriendlyId friendly_id :label diff --git a/app/views/templates/reprovision.erb b/app/views/templates/reprovision.erb new file mode 100644 index 000000000..f2a463639 --- /dev/null +++ b/app/views/templates/reprovision.erb @@ -0,0 +1,16 @@ +<%# +kind: job_template +name: Reprovision - SSH Default +job_category: Power +description_format: 'Reprovision host' +feature: reprovision +provider_type: SSH +template_inputs: +- name: script + description: script to configure the bootloader to provision on next boot + input_type: user + required: true +%> + +<%= input('script') %> +<%= render_template("Power Action - SSH Default", :action => "restart") %> diff --git a/config/routes.rb b/config/routes.rb index 750260f23..7f59f3ca7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,10 @@ Rails.application.routes.draw do + resources :hosts, :only => [] do + member do + post 'reprovision', :method => :post + end + end + resources :job_templates, :except => [:show] do member do get 'clone_template' diff --git a/db/migrate/20160201154900_change_template_invocation_input_values_value.rb b/db/migrate/20160201154900_change_template_invocation_input_values_value.rb index 4a9c3441d..a4db68792 100644 --- a/db/migrate/20160201154900_change_template_invocation_input_values_value.rb +++ b/db/migrate/20160201154900_change_template_invocation_input_values_value.rb @@ -1,14 +1,5 @@ -class CreateRemoteExecutionFeatures < ActiveRecord::Migration +class ChangeTemplateInvocationInputValuesValue < ActiveRecord::Migration def change - create_table :remote_execution_features do |t| - t.string :label, :index => true, :null => false - t.string :name - t.string :description - t.text :provided_inputs, :null => true - t.integer :template_id, :null => true - end - add_index :remote_execution_features, :label - add_index :remote_execution_features, :template_id - add_foreign_key :remote_execution_features, :templates + change_column :template_invocation_input_values, :value, :text end end diff --git a/lib/foreman_remote_execution/engine.rb b/lib/foreman_remote_execution/engine.rb index f2594bcbf..119bd14da 100644 --- a/lib/foreman_remote_execution/engine.rb +++ b/lib/foreman_remote_execution/engine.rb @@ -105,7 +105,8 @@ class Engine < ::Rails::Engine # widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1 RemoteExecutionFeature.register(:reprovision, N_("Reprovision"), - :description => "Reprovision the host via a script") + :description => "Reprovision the host via a script", + :provided_inputs => ['script']) end end @@ -136,6 +137,9 @@ class Engine < ::Rails::Engine # Template.reflect_on_association :template_inputs # => <#Association... # ProvisioningTemplate.reflect_on_association :template_inputs # => nil require_dependency 'job_template' + + HostsController.send(:include, ForemanRemoteExecution::HostsControllerExtensions) + (Template.descendants + [Template]).each { |klass| klass.send(:include, ForemanRemoteExecution::TemplateExtensions) } (Taxonomy.descendants + [Taxonomy]).each { |klass| klass.send(:include, ForemanRemoteExecution::TaxonomyExtensions) }