foreman_remote_execution 4.1.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/js_ci.yml +29 -0
- data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
- data/.prettierrc +4 -0
- data/.rubocop.yml +13 -49
- data/.rubocop_todo.yml +326 -102
- data/Gemfile +1 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +21 -3
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_controller_extensions.rb +26 -0
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +18 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +38 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +9 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +38 -14
- data/app/models/foreign_input_set.rb +1 -1
- data/app/models/host_status/execution_status.rb +7 -0
- data/app/models/job_invocation.rb +2 -1
- data/app/models/job_invocation_composer.rb +1 -1
- data/app/models/remote_execution_feature.rb +5 -2
- data/app/models/remote_execution_provider.rb +6 -1
- data/app/services/remote_execution_proxy_selector.rb +3 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +1 -1
- data/app/views/api/v2/registration/_form.html.erb +12 -0
- data/app/views/template_invocations/_output_line_set.html.erb +1 -1
- data/app/views/template_invocations/show.html.erb +30 -23
- data/app/views/templates/ssh/package_action.erb +1 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20200820122057_add_proxy_selector_override_to_remote_execution_feature.rb +5 -0
- data/foreman_remote_execution.gemspec +1 -2
- data/lib/foreman_remote_execution/engine.rb +21 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +6 -6
- data/test/functional/api/v2/job_invocations_controller_test.rb +42 -3
- data/test/functional/api/v2/registration_controller_test.rb +73 -0
- data/test/functional/ui_job_wizard_controller_test.rb +16 -0
- data/test/unit/actions/run_hosts_job_test.rb +1 -0
- data/webpack/JobWizard/JobWizard.js +32 -0
- data/webpack/JobWizard/index.js +32 -0
- data/webpack/Routes/routes.js +12 -0
- data/webpack/__mocks__/foremanReact/history.js +1 -0
- data/webpack/global_index.js +4 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +5 -1
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +6 -2
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
- metadata +24 -23
@@ -98,6 +98,7 @@ handle_zypp_res_codes () {
|
|
98
98
|
end
|
99
99
|
-%>
|
100
100
|
[ -x "$(command -v subscription-manager)" ] && subscription-manager refresh
|
101
|
+
export DEBIAN_FRONTEND=noninteractive
|
101
102
|
apt-get -y update
|
102
103
|
apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= action %> <%= input("package") %>
|
103
104
|
<% elsif package_manager == 'zypper' -%>
|
data/config/routes.rb
CHANGED
@@ -43,6 +43,9 @@ Rails.application.routes.draw do
|
|
43
43
|
get 'cockpit/host_ssh_params/:id', to: 'cockpit#host_ssh_params'
|
44
44
|
end
|
45
45
|
get 'cockpit/redirect', to: 'cockpit#redirect'
|
46
|
+
get 'ui_job_wizard/categories', to: 'ui_job_wizard#categories'
|
47
|
+
|
48
|
+
match '/experimental/job_wizard', to: 'react#index', :via => [:get]
|
46
49
|
|
47
50
|
namespace :api, :defaults => {:format => 'json'} do
|
48
51
|
scope '(:apiv)', :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
|
@@ -55,6 +58,8 @@ Rails.application.routes.draw do
|
|
55
58
|
post 'cancel'
|
56
59
|
post 'rerun'
|
57
60
|
get 'template_invocations', :to => 'template_invocations#template_invocations'
|
61
|
+
get 'outputs'
|
62
|
+
post 'outputs'
|
58
63
|
end
|
59
64
|
end
|
60
65
|
|
@@ -26,9 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_dependency 'deface'
|
27
27
|
s.add_dependency 'dynflow', '>= 1.0.2', '< 2.0.0'
|
28
28
|
s.add_dependency 'foreman_remote_execution_core'
|
29
|
-
s.add_dependency 'foreman-tasks', '>= 0.
|
29
|
+
s.add_dependency 'foreman-tasks', '>= 4.0.0'
|
30
30
|
|
31
31
|
s.add_development_dependency 'factory_bot_rails', '~> 4.8.0'
|
32
|
-
s.add_development_dependency 'rubocop', '~> 0.80.0'
|
33
32
|
s.add_development_dependency 'rdoc'
|
34
33
|
end
|
@@ -53,6 +53,7 @@ module ForemanRemoteExecution
|
|
53
53
|
|
54
54
|
initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |_app|
|
55
55
|
Foreman::Plugin.register :foreman_remote_execution do
|
56
|
+
register_global_js_file 'global'
|
56
57
|
requires_foreman '>= 2.2'
|
57
58
|
|
58
59
|
apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
|
@@ -68,7 +69,8 @@ module ForemanRemoteExecution
|
|
68
69
|
permission :view_job_templates, { :job_templates => [:index, :show, :revision, :auto_complete_search, :auto_complete_job_category, :preview, :export],
|
69
70
|
:'api/v2/job_templates' => [:index, :show, :revision, :export],
|
70
71
|
:'api/v2/template_inputs' => [:index, :show],
|
71
|
-
:'api/v2/foreign_input_sets' => [:index, :show]
|
72
|
+
:'api/v2/foreign_input_sets' => [:index, :show],
|
73
|
+
:ui_job_wizard => [:categories]}, :resource_type => 'JobTemplate'
|
72
74
|
permission :create_job_templates, { :job_templates => [:new, :create, :clone_template, :import],
|
73
75
|
:'api/v2/job_templates' => [:create, :clone, :import] }, :resource_type => 'JobTemplate'
|
74
76
|
permission :edit_job_templates, { :job_templates => [:edit, :update],
|
@@ -83,7 +85,7 @@ module ForemanRemoteExecution
|
|
83
85
|
permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun, :preview_hosts],
|
84
86
|
'api/v2/job_invocations' => [:create, :rerun] }, :resource_type => 'JobInvocation'
|
85
87
|
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search], :template_invocations => [:show],
|
86
|
-
'api/v2/job_invocations' => [:index, :show, :output, :raw_output] }, :resource_type => 'JobInvocation'
|
88
|
+
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs] }, :resource_type => 'JobInvocation'
|
87
89
|
permission :view_template_invocations, { :template_invocations => [:show],
|
88
90
|
'api/v2/template_invocations' => [:template_invocations] }, :resource_type => 'TemplateInvocation'
|
89
91
|
permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
|
@@ -137,6 +139,13 @@ module ForemanRemoteExecution
|
|
137
139
|
parent: :monitor_menu,
|
138
140
|
after: :audits
|
139
141
|
|
142
|
+
menu :labs_menu, :job_wizard,
|
143
|
+
url_hash: { controller: 'job_wizard', action: :index },
|
144
|
+
caption: N_('Job wizard'),
|
145
|
+
parent: :lab_features_menu,
|
146
|
+
url: 'experimental/job_wizard',
|
147
|
+
after: :host_wizard
|
148
|
+
|
140
149
|
register_custom_status HostStatus::ExecutionStatus
|
141
150
|
# add dashboard widget
|
142
151
|
# widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
|
@@ -151,6 +160,14 @@ module ForemanRemoteExecution
|
|
151
160
|
extend_rabl_template 'api/v2/subnets/show', 'api/v2/subnets/remote_execution_proxies'
|
152
161
|
parameter_filter ::Subnet, :remote_execution_proxy_ids
|
153
162
|
describe_host { overview_buttons_provider :host_overview_buttons }
|
163
|
+
|
164
|
+
# Extend Registration module
|
165
|
+
extend_allowed_registration_vars :remote_execution_interface
|
166
|
+
extend_page 'registration/_form' do |cx|
|
167
|
+
cx.add_pagelet :global_registration, name: N_('Remote Execution'), partial: 'api/v2/registration/form', priority: 100, id: 'remote_execution_interface'
|
168
|
+
end
|
169
|
+
ForemanTasks.dynflow.eager_load_actions!
|
170
|
+
extend_observable_events(::Dynflow::Action.descendants.select { |klass| klass <= ::Actions::ObservableAction }.map(&:namespaced_event_names))
|
154
171
|
end
|
155
172
|
end
|
156
173
|
|
@@ -207,6 +224,8 @@ module ForemanRemoteExecution
|
|
207
224
|
ForemanRemoteExecution.register_rex_feature
|
208
225
|
|
209
226
|
::Api::V2::SubnetsController.include ::ForemanRemoteExecution::Concerns::Api::V2::SubnetsControllerExtensions
|
227
|
+
::Api::V2::RegistrationController.prepend ::ForemanRemoteExecution::Concerns::Api::V2::RegistrationControllerExtensions
|
228
|
+
::Api::V2::RegistrationController.include ::ForemanRemoteExecution::Concerns::Api::V2::RegistrationControllerExtensions::ApipieExtensions
|
210
229
|
end
|
211
230
|
|
212
231
|
initializer 'foreman_remote_execution.register_gettext', after: :load_config_initializers do |_app|
|
data/package.json
CHANGED
@@ -21,16 +21,16 @@
|
|
21
21
|
},
|
22
22
|
"devDependencies": {
|
23
23
|
"@babel/core": "^7.7.0",
|
24
|
-
"@theforeman/builder": "^4.
|
25
|
-
"@theforeman/eslint-plugin-foreman": "^4.
|
26
|
-
"@theforeman/stories": "^4.
|
27
|
-
"@theforeman/test": "^4.
|
28
|
-
"@theforeman/vendor-dev": "^4.
|
24
|
+
"@theforeman/builder": "^4.14.0",
|
25
|
+
"@theforeman/eslint-plugin-foreman": "^4.14.0",
|
26
|
+
"@theforeman/stories": "^4.14.0",
|
27
|
+
"@theforeman/test": "^4.14.0",
|
28
|
+
"@theforeman/vendor-dev": "^4.14.0",
|
29
29
|
"babel-eslint": "^10.0.0",
|
30
30
|
"eslint": "^6.8.0",
|
31
31
|
"prettier": "^1.19.1"
|
32
32
|
},
|
33
33
|
"peerDependencies": {
|
34
|
-
"@theforeman/vendor": ">= 4.
|
34
|
+
"@theforeman/vendor": ">= 4.14.0"
|
35
35
|
}
|
36
36
|
}
|
@@ -137,6 +137,7 @@ module Api
|
|
137
137
|
|
138
138
|
test 'should provide empty output for host which does not have a task yet' do
|
139
139
|
JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
|
140
|
+
JobInvocation.any_instance.expects(:finished?).returns(false)
|
140
141
|
get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
141
142
|
result = ActiveSupport::JSON.decode(@response.body)
|
142
143
|
assert_equal result['refresh'], true
|
@@ -144,6 +145,15 @@ module Api
|
|
144
145
|
assert_response :success
|
145
146
|
end
|
146
147
|
|
148
|
+
test 'should provide empty output marked as done for host which does not have a task when the job is finished' do
|
149
|
+
JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
|
150
|
+
get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
151
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
152
|
+
assert_equal result['refresh'], false
|
153
|
+
assert_equal result['output'], []
|
154
|
+
assert_response :success
|
155
|
+
end
|
156
|
+
|
147
157
|
test 'should fail with 404 for non-existing job invocation' do
|
148
158
|
invocation_id = @invocation.id + 1
|
149
159
|
assert_empty JobInvocation.where(:id => invocation_id)
|
@@ -160,6 +170,35 @@ module Api
|
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
173
|
+
describe '#outputs' do
|
174
|
+
test 'should provide outputs for hosts in the job' do
|
175
|
+
get :outputs, params: { :id => @invocation.id }
|
176
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
177
|
+
host_output = result['outputs'].first
|
178
|
+
assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
|
179
|
+
assert_equal host_output['refresh'], false
|
180
|
+
assert_equal host_output['output'], []
|
181
|
+
assert_response :success
|
182
|
+
end
|
183
|
+
|
184
|
+
test 'should provide outputs for selected hosts in the job' do
|
185
|
+
post :outputs, params: { :id => @invocation.id, :search_query => "id = #{@invocation.targeting.host_ids.first}" }, as: :json
|
186
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
187
|
+
host_output = result['outputs'].first
|
188
|
+
assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
|
189
|
+
assert_equal host_output['refresh'], false
|
190
|
+
assert_equal host_output['output'], []
|
191
|
+
assert_response :success
|
192
|
+
end
|
193
|
+
|
194
|
+
test 'should provide outputs for hosts in the job matching a search query' do
|
195
|
+
get :outputs, params: { :id => @invocation.id, :search_query => "name = definitely_not_in_the_job" }
|
196
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
197
|
+
assert_equal result['outputs'], []
|
198
|
+
assert_response :success
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
163
202
|
describe 'raw output' do
|
164
203
|
let(:fake_output) do
|
165
204
|
(1..5).map do |i|
|
@@ -172,7 +211,7 @@ module Api
|
|
172
211
|
let(:host) { @invocation.targeting.hosts.first }
|
173
212
|
|
174
213
|
test 'should provide raw output for a host' do
|
175
|
-
JobInvocation.any_instance.expects(:task).returns(OpenStruct.new(:scheduled? => false))
|
214
|
+
JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => false))
|
176
215
|
JobInvocation.any_instance.expects(:sub_task_for_host).returns(fake_task)
|
177
216
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
178
217
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -185,7 +224,7 @@ module Api
|
|
185
224
|
start_time = Time.now
|
186
225
|
JobInvocation.any_instance
|
187
226
|
.expects(:task).twice
|
188
|
-
.returns(OpenStruct.new(:scheduled? => true, :start_at => start_time))
|
227
|
+
.returns(OpenStruct.new(:scheduled? => true, :start_at => start_time, :pending? => true))
|
189
228
|
JobInvocation.any_instance.expects(:sub_task_for_host).never
|
190
229
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
191
230
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -197,7 +236,7 @@ module Api
|
|
197
236
|
end
|
198
237
|
|
199
238
|
test 'should provide raw output for host without task' do
|
200
|
-
JobInvocation.any_instance.expects(:task).returns(OpenStruct.new(:scheduled? => false))
|
239
|
+
JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => true))
|
201
240
|
JobInvocation.any_instance.expects(:sub_task_for_host)
|
202
241
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
203
242
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module V2
|
5
|
+
# Tests for the extra methods to play roles on a Host
|
6
|
+
class RegistrationControllerTest < ActionController::TestCase
|
7
|
+
describe 'host registration' do
|
8
|
+
let(:organization) { FactoryBot.create(:organization) }
|
9
|
+
let(:tax_location) { FactoryBot.create(:location) }
|
10
|
+
let(:template) do
|
11
|
+
FactoryBot.create(
|
12
|
+
:provisioning_template,
|
13
|
+
template_kind: template_kinds(:host_init_config),
|
14
|
+
template: 'template content <%= @host.name %>',
|
15
|
+
locations: [tax_location],
|
16
|
+
organizations: [organization]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
let(:os) do
|
20
|
+
FactoryBot.create(
|
21
|
+
:operatingsystem,
|
22
|
+
:with_associations,
|
23
|
+
family: 'Redhat',
|
24
|
+
provisioning_templates: [
|
25
|
+
template,
|
26
|
+
]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:host_params) do
|
31
|
+
{ host: { name: 'centos-test.example.com',
|
32
|
+
managed: false, build: false,
|
33
|
+
organization_id: organization.id,
|
34
|
+
location_id: tax_location.id,
|
35
|
+
operatingsystem_id: os.id } }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'remote_execution_interface' do
|
39
|
+
setup do
|
40
|
+
Setting[:default_host_init_config_template] = template.name
|
41
|
+
@host = Host.create(host_params[:host])
|
42
|
+
@interface0 = FactoryBot.create(:nic_managed, host: @host, identifier: 'dummy0', execution: false)
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'with existing interface' do
|
46
|
+
params = host_params.merge(remote_execution_interface: @interface0.identifier)
|
47
|
+
|
48
|
+
post :host, params: params, session: set_session_user
|
49
|
+
assert_response :success
|
50
|
+
assert @interface0.reload.execution
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'with not-existing interface' do
|
54
|
+
params = host_params.merge(remote_execution_interface: 'dummy999')
|
55
|
+
|
56
|
+
post :host, params: params, session: set_session_user
|
57
|
+
assert_response :not_found
|
58
|
+
end
|
59
|
+
|
60
|
+
test 'with multiple interfaces' do
|
61
|
+
interface1 = FactoryBot.create(:nic_managed, host: @host, identifier: 'dummy1', execution: false)
|
62
|
+
params = host_params.merge(remote_execution_interface: interface1.identifier)
|
63
|
+
|
64
|
+
post :host, params: params, session: set_session_user
|
65
|
+
assert_response :success
|
66
|
+
refute @interface0.reload.execution
|
67
|
+
assert interface1.reload.execution
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class UiJobWizardControllerTest < ActionController::TestCase
|
4
|
+
def setup
|
5
|
+
FactoryBot.create(:job_template, :job_category => 'cat1')
|
6
|
+
FactoryBot.create(:job_template, :job_category => 'cat2')
|
7
|
+
FactoryBot.create(:job_template, :job_category => 'cat2')
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'should respond with categories' do
|
11
|
+
get :categories, :params => {}, :session => set_session_user
|
12
|
+
assert_response :success
|
13
|
+
res = JSON.parse @response.body
|
14
|
+
assert_equal ['cat1','cat2'], res['job_categories']
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Wizard } from '@patternfly/react-core';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import history from 'foremanReact/history';
|
5
|
+
|
6
|
+
export const JobWizard = () => {
|
7
|
+
const steps = [
|
8
|
+
{
|
9
|
+
name: __('Category and template'),
|
10
|
+
component: <p>Category and template</p>,
|
11
|
+
},
|
12
|
+
{ name: __('Target hosts'), component: <p>TargetHosts </p> },
|
13
|
+
{ name: __('Advanced fields'), component: <p> AdvancedFields </p> },
|
14
|
+
{ name: __('Schedule'), component: <p>Schedule</p> },
|
15
|
+
{
|
16
|
+
name: __('Review details'),
|
17
|
+
component: <p>ReviewDetails</p>,
|
18
|
+
nextButtonText: 'Run',
|
19
|
+
},
|
20
|
+
];
|
21
|
+
const title = __('Run Job');
|
22
|
+
return (
|
23
|
+
<Wizard
|
24
|
+
onClose={() => history.goBack()}
|
25
|
+
navAriaLabel={`${title} steps`}
|
26
|
+
steps={steps}
|
27
|
+
height="70vh"
|
28
|
+
/>
|
29
|
+
);
|
30
|
+
};
|
31
|
+
|
32
|
+
export default JobWizard;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Title, Divider } from '@patternfly/react-core';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
5
|
+
import { JobWizard } from './JobWizard';
|
6
|
+
|
7
|
+
const JobWizardPage = () => {
|
8
|
+
const title = __('Run job');
|
9
|
+
const breadcrumbOptions = {
|
10
|
+
breadcrumbItems: [
|
11
|
+
{ caption: __('Jobs'), url: `/jobs` },
|
12
|
+
{ caption: title },
|
13
|
+
],
|
14
|
+
};
|
15
|
+
return (
|
16
|
+
<PageLayout
|
17
|
+
header={title}
|
18
|
+
breadcrumbOptions={breadcrumbOptions}
|
19
|
+
searchable={false}
|
20
|
+
>
|
21
|
+
<React.Fragment>
|
22
|
+
<Title headingLevel="h2" size="2xl">
|
23
|
+
{title}
|
24
|
+
</Title>
|
25
|
+
<Divider component="div" />
|
26
|
+
<JobWizard />
|
27
|
+
</React.Fragment>
|
28
|
+
</PageLayout>
|
29
|
+
);
|
30
|
+
};
|
31
|
+
|
32
|
+
export default JobWizardPage;
|
@@ -0,0 +1 @@
|
|
1
|
+
export default { goBack: () => null };
|
@@ -51,8 +51,12 @@ const TargetingHosts = ({ apiStatus, items }) => {
|
|
51
51
|
};
|
52
52
|
|
53
53
|
TargetingHosts.propTypes = {
|
54
|
-
apiStatus: PropTypes.string
|
54
|
+
apiStatus: PropTypes.string,
|
55
55
|
items: PropTypes.array.isRequired,
|
56
56
|
};
|
57
57
|
|
58
|
+
TargetingHosts.defaultProps = {
|
59
|
+
apiStatus: null,
|
60
|
+
};
|
61
|
+
|
58
62
|
export default TargetingHosts;
|
@@ -39,7 +39,7 @@ const TargetingHostsPage = ({
|
|
39
39
|
<br />
|
40
40
|
<TargetingHosts apiStatus={apiStatus} items={items} />
|
41
41
|
<Pagination
|
42
|
-
viewType="
|
42
|
+
viewType="table"
|
43
43
|
itemCount={totalHosts}
|
44
44
|
pagination={pagination}
|
45
45
|
onChange={args => handlePagination(args)}
|
@@ -52,11 +52,15 @@ const TargetingHostsPage = ({
|
|
52
52
|
TargetingHostsPage.propTypes = {
|
53
53
|
handleSearch: PropTypes.func.isRequired,
|
54
54
|
searchQuery: PropTypes.string.isRequired,
|
55
|
-
apiStatus: PropTypes.string
|
55
|
+
apiStatus: PropTypes.string,
|
56
56
|
items: PropTypes.array.isRequired,
|
57
57
|
totalHosts: PropTypes.number.isRequired,
|
58
58
|
pagination: PropTypes.object.isRequired,
|
59
59
|
handlePagination: PropTypes.func.isRequired,
|
60
60
|
};
|
61
61
|
|
62
|
+
TargetingHostsPage.defaultProps = {
|
63
|
+
apiStatus: null,
|
64
|
+
};
|
65
|
+
|
62
66
|
export default TargetingHostsPage;
|