foreman_remote_execution 16.0.3 → 16.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40f170b56effb3a3f7f415ac040d45242641bc3210266bf723d957a4337e8aa2
4
- data.tar.gz: c6a8e5a792629049aa8e40fdc2a3b6b20cc182af3e0e3d1578e5e3614525e7d0
3
+ metadata.gz: 263ac53880d0a4d4673659013457388598c58706f452a8f6db7c94df017ac634
4
+ data.tar.gz: c7806ab71384b036d0ce50e2f8b55c0f9c13095fcfc455dd8057d28fabc74682
5
5
  SHA512:
6
- metadata.gz: 7b47defe2e23b8ce3ac1ebc08164749d7c2a03a133f78dfcb702c76cd4f434786b0dbeceb72c2d7ca47cedbd5d1f688175b9e0680245eac4e1064f9739d2b68c
7
- data.tar.gz: c0d069ef4dde5b1a07cb38b60f0dcc7218487cc58a5a2961039f2603517a2fdc5060b603b4a13b5ff4c659a13b9b25eb836b59e5e3d6efbe1e69202364cc0f87
6
+ metadata.gz: 8c20ef4f8c84e5b57e3b1562dba4191d3552269d327de0ce51e7dd9d737db87a3e15a390abb60003568035e168adf2e3f3053fdbaf3f36a3f0e3282941081008
7
+ data.tar.gz: 33c31c42d97a26a67331097c545e731c3b44a4430e6c8a2ce26f516b4296db78908639ab410b5959128f182d58d05681d4fc46e495c95c6cce120b2a2ff25e80
@@ -108,8 +108,9 @@ module Api
108
108
  param :id, :identifier, :required => true
109
109
  param_group :job_template_clone, :as => :create
110
110
  def clone
111
+ original = @job_template
111
112
  @job_template = @job_template.clone
112
- load_vars_from_template
113
+ @job_template.cloned_from = original
113
114
  @job_template.name = job_template_params[:name]
114
115
  process_response @job_template.save
115
116
  end
@@ -2,7 +2,7 @@ class CockpitController < ApplicationController
2
2
  before_action :find_resource, :only => [:host_ssh_params]
3
3
 
4
4
  def host_ssh_params
5
- render :json => SSHExecutionProvider.ssh_params(@host)
5
+ render :json => ScriptExecutionProvider.ssh_params(@host)
6
6
  end
7
7
 
8
8
  def redirect
@@ -10,7 +10,7 @@ class CockpitController < ApplicationController
10
10
 
11
11
  redir_url = URI.parse(params[:redirect_uri])
12
12
 
13
- cockpit_url = SSHExecutionProvider.cockpit_url_for_host('')
13
+ cockpit_url = ScriptExecutionProvider.cockpit_url_for_host('')
14
14
  redir_url.query = if redir_url.hostname == URI.join(Setting[:foreman_url], cockpit_url).hostname
15
15
  "access_token=#{request.session_options[:id]}"
16
16
  else
@@ -44,7 +44,7 @@ module HostsExtensionsHelper
44
44
  def web_console_button(host, *_args)
45
45
  return if !authorized_for(permission: 'cockpit_hosts', auth_object: host) || !can_execute_on_infrastructure_host?(host)
46
46
 
47
- url = SSHExecutionProvider.cockpit_url_for_host(host.name)
47
+ url = ScriptExecutionProvider.cockpit_url_for_host(host.name)
48
48
  url ? link_to(_('Web Console'), url, :class => 'btn btn-default', :id => :'web-console-button', :target => '_new') : nil
49
49
  end
50
50
 
@@ -70,7 +70,7 @@ module Actions
70
70
  .merge(additional_options)
71
71
 
72
72
  plan_delegated_action(proxy, provider.proxy_action_class, action_options, proxy_action_class: ::Actions::RemoteExecution::ProxyAction)
73
- plan_self :with_event_logging => true
73
+ plan_self
74
74
  end
75
75
 
76
76
  def finalize(*args)
@@ -127,32 +127,6 @@ module Actions
127
127
  end
128
128
 
129
129
  def fill_continuous_output(continuous_output)
130
- if input[:with_event_logging]
131
- continuous_output_from_template_invocation_events(continuous_output)
132
- return
133
- end
134
-
135
- delegated_output.fetch('result', []).each do |raw_output|
136
- continuous_output.add_raw_output(raw_output)
137
- end
138
-
139
- final_timestamp = (continuous_output.last_timestamp || task.ended_at).to_f + 1
140
-
141
- if task.state == 'stopped' && task.result == 'cancelled'
142
- continuous_output.add_output(_('Job cancelled by user'), 'debug', final_timestamp)
143
- else
144
- fill_planning_errors_to_continuous_output(continuous_output) unless exit_status
145
- end
146
- if exit_status
147
- continuous_output.add_output(_('Exit status: %s') % exit_status, 'stdout', final_timestamp)
148
- elsif run_step&.error
149
- continuous_output.add_output(_('Job finished with error') + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', final_timestamp)
150
- end
151
- rescue => e
152
- continuous_output.add_exception(_('Error loading data from proxy'), e)
153
- end
154
-
155
- def continuous_output_from_template_invocation_events(continuous_output)
156
130
  begin
157
131
  # Trigger reload
158
132
  delegated_output unless task.state == 'stopped'
@@ -160,6 +134,7 @@ module Actions
160
134
  # This is enough, the error will get shown using add_exception at the end of the method
161
135
  end
162
136
 
137
+ # Show the outputs which are already stored in the database
163
138
  task.template_invocation.template_invocation_events.order(:timestamp).find_each do |output|
164
139
  if output.event_type == 'exit'
165
140
  continuous_output.add_output(_('Exit status: %s') % output.event, 'stdout', output.timestamp)
@@ -167,11 +142,13 @@ module Actions
167
142
  continuous_output.add_raw_output(output.as_raw_continuous_output)
168
143
  end
169
144
  end
145
+
146
+ # Attach the exception at the end
170
147
  continuous_output.add_exception(_('Error loading data from proxy'), e) if e
171
148
  end
172
149
 
173
150
  def exit_status
174
- input[:with_event_logging] ? task.template_invocation.template_invocation_events.find_by(event_type: 'exit')&.event : delegated_output[:exit_status]
151
+ task.template_invocation.template_invocation_events.find_by(event_type: 'exit')&.event
175
152
  end
176
153
 
177
154
  def host_id
@@ -28,6 +28,8 @@ module ForemanRemoteExecution
28
28
  end
29
29
  }
30
30
 
31
+ after_build :reset_host_proxy_invocations
32
+
31
33
  def search_by_job_invocation(key, operator, value)
32
34
  if key == 'job_invocation.result'
33
35
  operator = operator == '=' ? 'IN' : 'NOT IN'
@@ -46,8 +48,12 @@ module ForemanRemoteExecution
46
48
  end
47
49
  end
48
50
 
51
+ def reset_host_proxy_invocations
52
+ HostProxyInvocation.where(host_id: self.id).delete_all
53
+ end
54
+
49
55
  def cockpit_url
50
- SSHExecutionProvider.cockpit_url_for_host(self.name)
56
+ ScriptExecutionProvider.cockpit_url_for_host(self.name)
51
57
  end
52
58
 
53
59
  def execution_status(options = {})
@@ -129,6 +129,11 @@ class JobTemplate < ::Template
129
129
  end
130
130
  end
131
131
 
132
+ def clone
133
+ deep_clone(:include => [:effective_user, :foreign_input_sets, :organizations, :locations],
134
+ :except => [:name, :locked])
135
+ end
136
+
132
137
  def provider
133
138
  RemoteExecutionProvider.provider_for(provider_type)
134
139
  end
@@ -40,7 +40,7 @@ class ScriptExecutionProvider < RemoteExecutionProvider
40
40
  proxy = proxy_for_cockpit(host)
41
41
  {
42
42
  :hostname => find_ip_or_hostname(host),
43
- :proxy => proxy.class == Symbol ? proxy : proxy.url,
43
+ :proxy => proxy.instance_of?(Symbol) ? proxy : proxy.url,
44
44
  :ssh_user => ssh_user(host),
45
45
  :ssh_port => ssh_port(host),
46
46
  :ssh_password => ssh_password(host),
@@ -50,7 +50,7 @@ class ScriptExecutionProvider < RemoteExecutionProvider
50
50
  end
51
51
 
52
52
  def cockpit_url_for_host(host)
53
- Setting[:remote_execution_cockpit_url] % { :host => host } if Setting[:remote_execution_cockpit_url].present?
53
+ format(Setting[:remote_execution_cockpit_url], host: host) if Setting[:remote_execution_cockpit_url].present?
54
54
  end
55
55
 
56
56
  def proxy_feature
@@ -76,5 +76,3 @@ class ScriptExecutionProvider < RemoteExecutionProvider
76
76
  end
77
77
  end
78
78
  end
79
-
80
- SSHExecutionProvider = ScriptExecutionProvider
@@ -1,6 +1,5 @@
1
1
  Rails.autoloaders.each do |autoloader|
2
2
  autoloader.inflector.inflect(
3
- 'ssh_execution_provider' => 'SSHExecutionProvider',
4
3
  'remote_execution_ssh' => 'RemoteExecutionSSH'
5
4
  )
6
5
  end
@@ -71,10 +71,10 @@ module ForemanRemoteExecution
71
71
  full_name: N_('Effective User')
72
72
  setting 'remote_execution_effective_user_method',
73
73
  type: :string,
74
- description: N_('What command should be used to switch to the effective user. One of %s') % ::SSHExecutionProvider::EFFECTIVE_USER_METHODS.inspect,
74
+ description: N_('What command should be used to switch to the effective user. One of %s') % ::ScriptExecutionProvider::EFFECTIVE_USER_METHODS.inspect,
75
75
  default: 'sudo',
76
76
  full_name: N_('Effective User Method'),
77
- collection: proc { Hash[::SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] }
77
+ collection: proc { Hash[::ScriptExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] }
78
78
  setting 'remote_execution_effective_user_password',
79
79
  type: :string,
80
80
  description: N_('Effective user password'),
@@ -328,7 +328,7 @@ module ForemanRemoteExecution
328
328
  require_dependency 'foreman_tasks/task'
329
329
  ForemanTasks::Task.include ForemanRemoteExecution::ForemanTasksTaskExtensions
330
330
  ForemanTasks::Cleaner.include ForemanRemoteExecution::ForemanTasksCleanerExtensions
331
- RemoteExecutionProvider.register(:SSH, ::SSHExecutionProvider)
331
+ RemoteExecutionProvider.register(:SSH, ::ScriptExecutionProvider)
332
332
  RemoteExecutionProvider.register(:script, ::ScriptExecutionProvider)
333
333
 
334
334
  ForemanRemoteExecution.register_rex_feature
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '16.0.3'.freeze
2
+ VERSION = '16.0.4'.freeze
3
3
  end
@@ -90,6 +90,35 @@ module Api
90
90
  assert_response :unprocessable_entity
91
91
  end
92
92
 
93
+ test 'should clone locked template' do
94
+ @template.locked = true
95
+ @template.save!
96
+ post :clone, params: { :id => @template.to_param, :job_template => {:name => 'MyClone'} }
97
+ assert_response :success
98
+
99
+ template = ActiveSupport::JSON.decode(@response.body)
100
+ assert_equal(template['locked'], false)
101
+ end
102
+
103
+ test 'should clone template with associations' do
104
+ template2 = FactoryBot.create(:job_template)
105
+ @template.organizations << FactoryBot.create(:organization)
106
+ @template.locations << FactoryBot.create(:location)
107
+ @template.foreign_input_sets << FactoryBot.build(:foreign_input_set).tap { |fis| fis.target_template_id = template2.id }
108
+ @template.effective_user.value = 'toor'
109
+ @template.save!
110
+
111
+ post :clone, params: { :id => @template.to_param, :job_template => {:name => 'MyClone'} }
112
+ template = ActiveSupport::JSON.decode(@response.body)
113
+
114
+ clone = JobTemplate.unscoped.find(template['id'])
115
+ assert_equal(clone.organizations.sort, @template.organizations.sort)
116
+ assert_equal(clone.locations.sort, @template.locations.sort)
117
+ assert_equal(clone.foreign_input_sets.map(&:target_template_id), [template2.id])
118
+ assert_equal(clone.effective_user.value, 'toor')
119
+ assert_equal(clone.cloned_from.id, @template.id)
120
+ end
121
+
93
122
  test 'should export template' do
94
123
  get :export, params: { :id => @template.to_param }
95
124
  User.current = users(:admin)
@@ -10,7 +10,7 @@ module ForemanRemoteExecution
10
10
  let(:job_invocation) { FactoryBot.create(:job_invocation, :with_task) }
11
11
  let(:host) { job_invocation.template_invocations.first.host }
12
12
  let(:provider) do
13
- provider = ::SSHExecutionProvider
13
+ provider = ::ScriptExecutionProvider
14
14
  provider.expects(:ssh_password).with(host).returns('sshpass')
15
15
  provider.expects(:effective_user_password).with(host).returns('sudopass')
16
16
  provider.expects(:ssh_key_passphrase).with(host).returns('keypass')
@@ -5,11 +5,11 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
5
5
  let(:providers) { RemoteExecutionProvider.providers }
6
6
  it { assert_kind_of HashWithIndifferentAccess, providers }
7
7
  it 'makes providers accessible using symbol' do
8
- assert_equal SSHExecutionProvider, providers[:SSH]
8
+ assert_equal ScriptExecutionProvider, providers[:SSH]
9
9
  assert_equal ScriptExecutionProvider, providers[:script]
10
10
  end
11
11
  it 'makes providers accessible using string' do
12
- assert_equal SSHExecutionProvider, providers['SSH']
12
+ assert_equal ScriptExecutionProvider, providers['SSH']
13
13
  assert_equal ScriptExecutionProvider, providers['script']
14
14
  end
15
15
  end
@@ -27,11 +27,11 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
27
27
 
28
28
  describe '.provider_for' do
29
29
  it 'accepts symbols' do
30
- assert_equal SSHExecutionProvider, RemoteExecutionProvider.provider_for(:SSH)
30
+ assert_equal ScriptExecutionProvider, RemoteExecutionProvider.provider_for(:SSH)
31
31
  end
32
32
 
33
33
  it 'accepts strings' do
34
- assert_equal SSHExecutionProvider, RemoteExecutionProvider.provider_for('SSH')
34
+ assert_equal ScriptExecutionProvider, RemoteExecutionProvider.provider_for('SSH')
35
35
  end
36
36
 
37
37
  it 'returns a default one if unknown value is provided' do
@@ -81,7 +81,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
81
81
  describe '.provider_proxy_features' do
82
82
  it 'returns correct values' do
83
83
  RemoteExecutionProvider.stubs(:providers).returns(
84
- :SSH => SSHExecutionProvider,
84
+ :SSH => ScriptExecutionProvider,
85
85
  :script => ScriptExecutionProvider
86
86
  )
87
87
 
@@ -112,15 +112,15 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
112
112
  end
113
113
  end
114
114
 
115
- describe SSHExecutionProvider do
115
+ describe ScriptExecutionProvider do
116
116
  before { User.current = FactoryBot.build(:user, :admin) }
117
117
  after { User.current = nil }
118
118
 
119
119
  let(:job_invocation) { FactoryBot.create(:job_invocation, :with_template) }
120
120
  let(:template_invocation) { job_invocation.pattern_template_invocations.first }
121
121
  let(:host) { FactoryBot.create(:host) }
122
- let(:proxy_options) { SSHExecutionProvider.proxy_command_options(template_invocation, host) }
123
- let(:secrets) { SSHExecutionProvider.secrets(host) }
122
+ let(:proxy_options) { ScriptExecutionProvider.proxy_command_options(template_invocation, host) }
123
+ let(:secrets) { ScriptExecutionProvider.secrets(host) }
124
124
 
125
125
  describe 'effective user' do
126
126
  it 'takes the effective user from value from the template invocation' do
@@ -153,11 +153,11 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
153
153
  assert_equal 'sudo', proxy_options[:effective_user_method]
154
154
  method_param.update!(:value => 'su')
155
155
  host.clear_host_parameters_cache!
156
- proxy_options = SSHExecutionProvider.proxy_command_options(template_invocation, host)
156
+ proxy_options = ScriptExecutionProvider.proxy_command_options(template_invocation, host)
157
157
  assert_equal 'su', proxy_options[:effective_user_method]
158
158
  method_param.update!(:value => 'dzdo')
159
159
  host.clear_host_parameters_cache!
160
- proxy_options = SSHExecutionProvider.proxy_command_options(template_invocation, host)
160
+ proxy_options = ScriptExecutionProvider.proxy_command_options(template_invocation, host)
161
161
  assert_equal 'dzdo', proxy_options[:effective_user_method]
162
162
  end
163
163
  end
@@ -219,7 +219,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
219
219
 
220
220
  it 'gets fqdn from flagged interfaces if not preferring ips' do
221
221
  # falling to primary interface
222
- assert_equal 'somehost.somedomain.org', SSHExecutionProvider.find_ip_or_hostname(host)
222
+ assert_equal 'somehost.somedomain.org', ScriptExecutionProvider.find_ip_or_hostname(host)
223
223
 
224
224
  # execution wins if present
225
225
  execution_interface = FactoryBot.build(:nic_managed,
@@ -228,13 +228,13 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
228
228
  host.primary_interface.update(:execution => false)
229
229
  host.interfaces.each(&:save)
230
230
  host.reload
231
- assert_equal execution_interface.fqdn, SSHExecutionProvider.find_ip_or_hostname(host)
231
+ assert_equal execution_interface.fqdn, ScriptExecutionProvider.find_ip_or_hostname(host)
232
232
  end
233
233
 
234
234
  it 'gets ip from flagged interfaces' do
235
235
  host.host_params['remote_execution_connect_by_ip'] = true
236
236
  # no ip address set on relevant interface - fallback to fqdn
237
- assert_equal 'somehost.somedomain.org', SSHExecutionProvider.find_ip_or_hostname(host)
237
+ assert_equal 'somehost.somedomain.org', ScriptExecutionProvider.find_ip_or_hostname(host)
238
238
 
239
239
  # provision interface with ip while primary without
240
240
  provision_interface = FactoryBot.build(:nic_managed,
@@ -243,12 +243,12 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
243
243
  host.primary_interface.update(:provision => false)
244
244
  host.interfaces.each(&:save)
245
245
  host.reload
246
- assert_equal provision_interface.ip, SSHExecutionProvider.find_ip_or_hostname(host)
246
+ assert_equal provision_interface.ip, ScriptExecutionProvider.find_ip_or_hostname(host)
247
247
 
248
248
  # both primary and provision interface have IPs: the primary wins
249
249
  host.primary_interface.update(:ip => '10.0.0.2', :execution => false)
250
250
  host.reload
251
- assert_equal host.primary_interface.ip, SSHExecutionProvider.find_ip_or_hostname(host)
251
+ assert_equal host.primary_interface.ip, ScriptExecutionProvider.find_ip_or_hostname(host)
252
252
 
253
253
  # there is an execution interface with IP: it wins
254
254
  execution_interface = FactoryBot.build(:nic_managed,
@@ -257,7 +257,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
257
257
  host.primary_interface.update(:execution => false)
258
258
  host.interfaces.each(&:save)
259
259
  host.reload
260
- assert_equal execution_interface.ip, SSHExecutionProvider.find_ip_or_hostname(host)
260
+ assert_equal execution_interface.ip, ScriptExecutionProvider.find_ip_or_hostname(host)
261
261
 
262
262
  # there is an execution interface with both IPv6 and IPv4: IPv4 is being preferred over IPv6 by default
263
263
  execution_interface = FactoryBot.build(:nic_managed,
@@ -265,7 +265,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
265
265
  host.interfaces = [execution_interface]
266
266
  host.interfaces.each(&:save)
267
267
  host.reload
268
- assert_equal execution_interface.ip, SSHExecutionProvider.find_ip_or_hostname(host)
268
+ assert_equal execution_interface.ip, ScriptExecutionProvider.find_ip_or_hostname(host)
269
269
  end
270
270
 
271
271
  it 'gets ipv6 from flagged interfaces with IPv6 preference' do
@@ -278,7 +278,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
278
278
  host.interfaces = [execution_interface]
279
279
  host.interfaces.each(&:save)
280
280
  host.reload
281
- assert_equal execution_interface.ip6, SSHExecutionProvider.find_ip_or_hostname(host)
281
+ assert_equal execution_interface.ip6, ScriptExecutionProvider.find_ip_or_hostname(host)
282
282
  end
283
283
 
284
284
  it 'gets ipv6 from flagged interfaces with IPv4 preference but without IPv4 address' do
@@ -291,7 +291,7 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
291
291
  host.interfaces = [execution_interface]
292
292
  host.interfaces.each(&:save)
293
293
  host.reload
294
- assert_equal execution_interface.ip6, SSHExecutionProvider.find_ip_or_hostname(host)
294
+ assert_equal execution_interface.ip6, ScriptExecutionProvider.find_ip_or_hostname(host)
295
295
  end
296
296
  end
297
297
  end
@@ -83,7 +83,7 @@ const Columns = () => {
83
83
 
84
84
  return {
85
85
  expand: {
86
- title: '',
86
+ title: ' ',
87
87
  weight: 0,
88
88
  wrapper: () => null,
89
89
  },
@@ -136,7 +136,7 @@ const Columns = () => {
136
136
  weight: 5,
137
137
  },
138
138
  actions: {
139
- title: '',
139
+ title: ' ',
140
140
  weight: 6,
141
141
  wrapper: () => null,
142
142
  },
@@ -7,6 +7,16 @@
7
7
  height: $chart_size;
8
8
  width: $chart_size;
9
9
  margin-bottom: 10px;
10
+
11
+ svg {
12
+ position: relative;
13
+ overflow: visible;
14
+ z-index: 999;
15
+ }
16
+
17
+ path {
18
+ cursor: pointer;
19
+ }
10
20
  }
11
21
 
12
22
  .chart-legend {
@@ -28,6 +38,10 @@
28
38
  font-weight: normal;
29
39
  }
30
40
  }
41
+
42
+ text, path {
43
+ cursor: pointer;
44
+ }
31
45
  }
32
46
 
33
47
  .pf-v5-c-divider {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  /* eslint-disable camelcase */
2
3
  import PropTypes from 'prop-types';
3
4
  import React, { useMemo, useEffect, useState } from 'react';
@@ -36,11 +37,20 @@ const JobInvocationHostTable = ({
36
37
  finished,
37
38
  autoRefresh,
38
39
  initialFilter,
40
+ onFilterUpdate,
39
41
  }) => {
40
42
  const columns = Columns();
41
43
  const columnNamesKeys = Object.keys(columns);
42
44
  const apiOptions = { key: JOB_INVOCATION_HOSTS };
43
- const [selectedFilter, setSelectedFilter] = useState(initialFilter || '');
45
+ const [selectedFilter, setSelectedFilter] = useState(initialFilter);
46
+
47
+ useEffect(() => {
48
+ if (initialFilter !== selectedFilter) {
49
+ wrapSetSelectedFilter(initialFilter);
50
+ }
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, [initialFilter]);
53
+
44
54
  const {
45
55
  searchParam: urlSearchQuery = '',
46
56
  page: urlPage,
@@ -111,6 +121,7 @@ const JobInvocationHostTable = ({
111
121
  return prevOptions;
112
122
  });
113
123
  setSelectedFilter(filter);
124
+ onFilterUpdate(filter);
114
125
  };
115
126
 
116
127
  useEffect(() => {
@@ -262,7 +273,7 @@ const JobInvocationHostTable = ({
262
273
  expandId: 'host-expandable',
263
274
  }}
264
275
  />
265
- {columnNamesKeys.slice(1).map(k => (
276
+ {columnNamesKeys.map(k => (
266
277
  <Td key={k}>{columns[k].wrapper(result)}</Td>
267
278
  ))}
268
279
  <Td isActionCell>
@@ -275,7 +286,7 @@ const JobInvocationHostTable = ({
275
286
  >
276
287
  <Td
277
288
  dataLabel={`${result.id}-expandable-content`}
278
- colSpan={columnNamesKeys.length + 1}
289
+ colSpan={columnNamesKeys.length}
279
290
  >
280
291
  <ExpandableRowContent>
281
292
  {result.job_status === 'cancelled' ||
@@ -303,8 +314,11 @@ JobInvocationHostTable.propTypes = {
303
314
  finished: PropTypes.bool.isRequired,
304
315
  autoRefresh: PropTypes.bool.isRequired,
305
316
  initialFilter: PropTypes.string.isRequired,
317
+ onFilterUpdate: PropTypes.func,
306
318
  };
307
319
 
308
- JobInvocationHostTable.defaultProps = {};
320
+ JobInvocationHostTable.defaultProps = {
321
+ onFilterUpdate: () => {},
322
+ };
309
323
 
310
324
  export default JobInvocationHostTable;
@@ -53,7 +53,6 @@ const JobInvocationSystemStatusChart = ({
53
53
  };
54
54
  const chartSize = 105;
55
55
  const [legendWidth, setLegendWidth] = useState(270);
56
- const [cursor, setCursor] = useState('default');
57
56
 
58
57
  // Calculates chart legend width based on its content
59
58
  useEffect(() => {
@@ -79,7 +78,7 @@ const JobInvocationSystemStatusChart = ({
79
78
 
80
79
  return (
81
80
  <>
82
- <FlexItem className="chart-donut" style={{ cursor }}>
81
+ <FlexItem className="chart-donut">
83
82
  <ChartDonut
84
83
  allowTooltip
85
84
  constrainToVisibleArea
@@ -96,12 +95,6 @@ const JobInvocationSystemStatusChart = ({
96
95
  target: 'data',
97
96
  eventHandlers: {
98
97
  onClick: onChartClick,
99
- onMouseOver: () => {
100
- setCursor('pointer');
101
- },
102
- onMouseOut: () => {
103
- setCursor('default');
104
- },
105
98
  },
106
99
  },
107
100
  ]}
@@ -109,7 +102,12 @@ const JobInvocationSystemStatusChart = ({
109
102
  total > 0 ? chartData.map(d => d.color) : [emptyChartDonut.value]
110
103
  }
111
104
  labelComponent={
112
- <ChartTooltip pointerLength={0} renderInPortal={false} />
105
+ <ChartTooltip
106
+ pointerLength={0}
107
+ renderInPortal={false}
108
+ constrainToVisibleArea
109
+ center={{ x: 15, y: 0 }}
110
+ />
113
111
  }
114
112
  title={chartDonutTitle}
115
113
  titleComponent={
@@ -133,7 +131,7 @@ const JobInvocationSystemStatusChart = ({
133
131
  height={chartSize}
134
132
  />
135
133
  </FlexItem>
136
- <FlexItem className="chart-legend" style={{ cursor }}>
134
+ <FlexItem className="chart-legend">
137
135
  <Text ouiaId="legend-title" className="legend-title">
138
136
  {__('System status')}
139
137
  </Text>
@@ -156,24 +154,12 @@ const JobInvocationSystemStatusChart = ({
156
154
  target: 'data',
157
155
  eventHandlers: {
158
156
  onClick: onChartClick,
159
- onMouseOver: () => {
160
- setCursor('pointer');
161
- },
162
- onMouseOut: () => {
163
- setCursor('default');
164
- },
165
157
  },
166
158
  },
167
159
  {
168
160
  target: 'labels',
169
161
  eventHandlers: {
170
162
  onClick: onChartClick,
171
- onMouseOver: () => {
172
- setCursor('pointer');
173
- },
174
- onMouseOut: () => {
175
- setCursor('default');
176
- },
177
163
  },
178
164
  },
179
165
  ]}
@@ -55,8 +55,8 @@ const JobInvocationDetailPage = ({
55
55
  );
56
56
  const [selectedFilter, setSelectedFilter] = useState('');
57
57
 
58
- const handleFilterChange = filter => {
59
- setSelectedFilter(filter);
58
+ const handleFilterChange = newFilter => {
59
+ setSelectedFilter(newFilter);
60
60
  };
61
61
 
62
62
  let isAlreadyStarted = false;
@@ -119,6 +119,7 @@ const JobInvocationDetailPage = ({
119
119
  data={items}
120
120
  isAlreadyStarted={isAlreadyStarted}
121
121
  formattedStartDate={formattedStartDate}
122
+ onFilterChange={handleFilterChange}
122
123
  />
123
124
  <Divider
124
125
  orientation={{
@@ -155,6 +156,7 @@ const JobInvocationDetailPage = ({
155
156
  finished={finished}
156
157
  autoRefresh={autoRefresh}
157
158
  initialFilter={selectedFilter}
159
+ onFilterUpdate={handleFilterChange}
158
160
  />
159
161
  )}
160
162
  </PageSection>
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.0.3
4
+ version: 16.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-13 00:00:00.000000000 Z
10
+ date: 2025-06-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: deface
@@ -187,7 +187,7 @@ files:
187
187
  - app/models/remote_execution_feature.rb
188
188
  - app/models/remote_execution_provider.rb
189
189
  - app/models/rex_mail_notification.rb
190
- - app/models/ssh_execution_provider.rb
190
+ - app/models/script_execution_provider.rb
191
191
  - app/models/target_remote_execution_proxy.rb
192
192
  - app/models/targeting.rb
193
193
  - app/models/targeting_host.rb