foreman_rh_cloud 5.0.28 → 5.0.29
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/controllers/insights_cloud/hits_controller.rb +1 -1
- data/app/models/insights_hit.rb +4 -0
- data/lib/foreman_inventory_upload/generators/queries.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/inventory_sync/async/host_result.rb +0 -5
- data/lib/inventory_sync/async/inventory_full_sync.rb +14 -9
- data/lib/inventory_sync/async/inventory_hosts_sync.rb +2 -6
- data/package.json +1 -1
- data/test/controllers/insights_sync/settings_controller_test.rb +2 -2
- data/test/controllers/uploads_settings_controller_test.rb +2 -2
- data/test/jobs/insights_full_sync_test.rb +2 -2
- data/test/jobs/insights_resolutions_sync_test.rb +1 -1
- data/test/jobs/insights_rules_sync_test.rb +2 -2
- data/test/jobs/inventory_full_sync_test.rb +31 -5
- data/test/jobs/inventory_hosts_sync_test.rb +16 -1
- data/test/jobs/inventory_scheduled_sync_test.rb +2 -2
- data/test/jobs/inventory_self_host_sync_test.rb +1 -1
- data/test/jobs/upload_report_job_test.rb +1 -1
- data/test/test_plugin_helper.rb +0 -2
- data/test/unit/rh_cloud_http_proxy_test.rb +1 -1
- data/test/unit/services/foreman_rh_cloud/branch_info_test.rb +1 -1
- data/test/unit/services/foreman_rh_cloud/template_renderer_helper_test.rb +1 -1
- data/test/unit/slice_generator_test.rb +15 -3
- data/test/unit/tags_generator_test.rb +2 -2
- data/webpack/ForemanRhCloudFills.js +7 -0
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +22 -6
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableActions.js +23 -16
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js +49 -2
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableHelpers.js +31 -14
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +15 -1
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap +0 -1
- data/webpack/InsightsCloudSync/Components/InsightsTable/table.scss +11 -13
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.scss +0 -14
- data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +13 -0
- data/webpack/InsightsCloudSync/InsightsCloudSyncHelpers.js +8 -0
- data/webpack/InsightsHostDetailsTab/InsightsTab.scss +4 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +104 -0
- data/webpack/common/DropdownToggle.js +24 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a77b9dd6b10d3bf354a1ea4888ac4c829a66de66da4f432860e76ffe1b2a887
|
|
4
|
+
data.tar.gz: 81913ea4f9745d2c2e08d2c6b5117395c3b360b1879acdd21c8b83fabeccb4a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 026bf10d2031f225aa64539059ed9fea96254a04e4dfb305db0ffc3ca7c0c3c9676b5d331a78cdf98320c3175765a1d862e7be707f4f69ef0f95a8149977119f
|
|
7
|
+
data.tar.gz: c8b159a4c8f86bfcf710094b3b6e49b06b00d0d8448f8d853ee0777bf356cd6a3165c497b98f9bba3106f1c1ac234f49f297ff3cf58b548fcb2d462c04eb4f33
|
|
@@ -7,7 +7,7 @@ module InsightsCloud
|
|
|
7
7
|
|
|
8
8
|
render json: {
|
|
9
9
|
hasToken: !Setting[:rh_cloud_token].empty?,
|
|
10
|
-
hits: hits.map { |hit| hit.attributes.merge(hostname: hit.host&.name, has_playbook: hit.has_playbook
|
|
10
|
+
hits: hits.map { |hit| hit.attributes.merge(hostname: hit.host&.name, has_playbook: hit.has_playbook?, host_uuid: hit.host_uuid) },
|
|
11
11
|
itemCount: hits.count,
|
|
12
12
|
}, status: :ok
|
|
13
13
|
end
|
data/app/models/insights_hit.rb
CHANGED
|
@@ -17,7 +17,6 @@ module InventorySync
|
|
|
17
17
|
@sub_ids.map do |sub_id|
|
|
18
18
|
host_id = host_id(sub_id)
|
|
19
19
|
if host_id
|
|
20
|
-
touched << host_id
|
|
21
20
|
{
|
|
22
21
|
host_id: host_id,
|
|
23
22
|
status: InventorySync::InventoryStatus::SYNC,
|
|
@@ -28,10 +27,6 @@ module InventorySync
|
|
|
28
27
|
end.compact
|
|
29
28
|
end
|
|
30
29
|
|
|
31
|
-
def touched
|
|
32
|
-
@touched ||= []
|
|
33
|
-
end
|
|
34
|
-
|
|
35
30
|
def host_id(sub_id)
|
|
36
31
|
hosts[sub_id]
|
|
37
32
|
end
|
|
@@ -14,11 +14,7 @@ module InventorySync
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def setup_statuses
|
|
17
|
-
@subscribed_hosts_ids = Set.new(
|
|
18
|
-
ForemanInventoryUpload::Generators::Queries.for_slice(
|
|
19
|
-
Host.unscoped.where(organization: input[:organization_id])
|
|
20
|
-
).pluck(:id)
|
|
21
|
-
)
|
|
17
|
+
@subscribed_hosts_ids = Set.new(affected_host_ids)
|
|
22
18
|
|
|
23
19
|
InventorySync::InventoryStatus.transaction do
|
|
24
20
|
InventorySync::InventoryStatus.where(host_id: @subscribed_hosts_ids).delete_all
|
|
@@ -35,8 +31,10 @@ module InventorySync
|
|
|
35
31
|
def update_statuses_batch
|
|
36
32
|
results = yield
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
existing_hosts = results.status_hashes.select { |hash| @subscribed_hosts_ids.include?(hash[:host_id]) }
|
|
35
|
+
|
|
36
|
+
update_hosts_status(existing_hosts)
|
|
37
|
+
host_statuses[:sync] += existing_hosts.size
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def rescue_strategy_for_self
|
|
@@ -45,9 +43,10 @@ module InventorySync
|
|
|
45
43
|
|
|
46
44
|
private
|
|
47
45
|
|
|
48
|
-
def update_hosts_status(status_hashes
|
|
46
|
+
def update_hosts_status(status_hashes)
|
|
49
47
|
InventorySync::InventoryStatus.create(status_hashes)
|
|
50
|
-
|
|
48
|
+
updated_ids = status_hashes.map { |hash| hash[:host_id] }
|
|
49
|
+
@subscribed_hosts_ids.subtract(updated_ids)
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
def add_missing_hosts_statuses(hosts_ids)
|
|
@@ -68,6 +67,12 @@ module InventorySync
|
|
|
68
67
|
disconnect: 0,
|
|
69
68
|
}
|
|
70
69
|
end
|
|
70
|
+
|
|
71
|
+
def affected_host_ids
|
|
72
|
+
ForemanInventoryUpload::Generators::Queries.for_slice(
|
|
73
|
+
Host.unscoped.where(organization: input[:organization_id])
|
|
74
|
+
).pluck(:id)
|
|
75
|
+
end
|
|
71
76
|
end
|
|
72
77
|
end
|
|
73
78
|
end
|
|
@@ -35,18 +35,14 @@ module InventorySync
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
37
|
def add_missing_insights_facets(uuids_hash)
|
|
38
|
-
|
|
39
|
-
missing_facets = uuids_hash.except(*existing_facets.map(&:first)).map do |host_id, uuid|
|
|
38
|
+
all_facets = uuids_hash.map do |host_id, uuid|
|
|
40
39
|
{
|
|
41
40
|
host_id: host_id,
|
|
42
41
|
uuid: uuid,
|
|
43
42
|
}
|
|
44
43
|
end
|
|
45
|
-
InsightsFacet.create(missing_facets)
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
InsightsFacet.where(host_id: host_id).update_all(uuid: uuids_hash[host_id])
|
|
49
|
-
end
|
|
45
|
+
InsightsFacet.upsert_all(all_facets, unique_by: :host_id) unless all_facets.empty?
|
|
50
46
|
end
|
|
51
47
|
|
|
52
48
|
def plan_self_host_sync
|
data/package.json
CHANGED
|
@@ -4,7 +4,7 @@ class SettingsControllerTest < ActionController::TestCase
|
|
|
4
4
|
tests InsightsCloud::SettingsController
|
|
5
5
|
|
|
6
6
|
test 'should return allow_auto_insights_sync setting' do
|
|
7
|
-
|
|
7
|
+
Setting[:allow_auto_insights_sync] = false
|
|
8
8
|
|
|
9
9
|
assert_equal false, Setting[:allow_auto_insights_sync]
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ class SettingsControllerTest < ActionController::TestCase
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
test 'should update allow_auto_insights_sync setting' do
|
|
19
|
-
|
|
19
|
+
Setting[:allow_auto_insights_sync] = false
|
|
20
20
|
|
|
21
21
|
assert_equal false, Setting[:allow_auto_insights_sync]
|
|
22
22
|
|
|
@@ -4,7 +4,7 @@ class UploadsSettingsControllerTest < ActionController::TestCase
|
|
|
4
4
|
tests ForemanInventoryUpload::UploadsSettingsController
|
|
5
5
|
|
|
6
6
|
test 'should get upload inventory settings' do
|
|
7
|
-
|
|
7
|
+
Setting[:allow_auto_inventory_upload] = true
|
|
8
8
|
|
|
9
9
|
assert_equal true, Setting[:allow_auto_inventory_upload]
|
|
10
10
|
|
|
@@ -17,7 +17,7 @@ class UploadsSettingsControllerTest < ActionController::TestCase
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
test 'should update allow_auto_inventory_upload setting' do
|
|
20
|
-
|
|
20
|
+
Setting[:allow_auto_inventory_upload] = false
|
|
21
21
|
|
|
22
22
|
assert_equal false, Setting[:allow_auto_inventory_upload]
|
|
23
23
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
2
|
require 'foreman_tasks/test_helpers'
|
|
3
3
|
|
|
4
4
|
class InsightsFullSyncTest < ActiveSupport::TestCase
|
|
@@ -7,7 +7,7 @@ class InsightsFullSyncTest < ActiveSupport::TestCase
|
|
|
7
7
|
setup do
|
|
8
8
|
InsightsCloud::Async::InsightsFullSync.any_instance.stubs(:plan_rules_sync)
|
|
9
9
|
InsightsCloud::Async::InsightsFullSync.any_instance.stubs(:plan_notifications)
|
|
10
|
-
|
|
10
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
11
11
|
|
|
12
12
|
uuid1 = 'accdf444-5628-451d-bf3e-cf909ad72756'
|
|
13
13
|
@host1 = FactoryBot.create(:host, :managed, name: 'host1')
|
|
@@ -63,7 +63,7 @@ class InsightsResolutionsSyncTest < ActiveSupport::TestCase
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
@rule = FactoryBot.create(:insights_rule, rule_id: 'network_tcp_connection_hang|NETWORK_TCP_CONNECTION_HANG_WARN')
|
|
66
|
-
|
|
66
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
test 'Resolutions data is replaced with data from cloud' do
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
2
|
require 'foreman_tasks/test_helpers'
|
|
3
3
|
|
|
4
4
|
class InsightsRulesSyncTest < ActiveSupport::TestCase
|
|
@@ -112,7 +112,7 @@ class InsightsRulesSyncTest < ActiveSupport::TestCase
|
|
|
112
112
|
@hit = FactoryBot.create(:insights_hit, host_id: @host.id)
|
|
113
113
|
|
|
114
114
|
InsightsCloud::Async::InsightsRulesSync.any_instance.stubs(:plan_resolutions)
|
|
115
|
-
|
|
115
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
test 'Hits data is replaced with data from cloud' do
|
|
@@ -37,6 +37,18 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
37
37
|
@host2.subscription_facet.pools << pool
|
|
38
38
|
@host2_inventory_id = '4536bf5c-ff03-4154-a8c9-32ff4b40e40c'
|
|
39
39
|
|
|
40
|
+
# this host would pass our plugin queries, so it could be uploaded to the cloud.
|
|
41
|
+
@host3 = FactoryBot.create(
|
|
42
|
+
:host,
|
|
43
|
+
:with_subscription,
|
|
44
|
+
:with_content,
|
|
45
|
+
content_view: cv.first,
|
|
46
|
+
lifecycle_environment: env,
|
|
47
|
+
organization: env.organization
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@host3.subscription_facet.pools << pool
|
|
51
|
+
|
|
40
52
|
ForemanInventoryUpload::Generators::Queries.instance_variable_set(:@fact_names, nil)
|
|
41
53
|
|
|
42
54
|
inventory_json = <<-INVENTORY_JSON
|
|
@@ -151,7 +163,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
151
163
|
{
|
|
152
164
|
"insights_id": "b533848e-465f-4f1a-9b2b-b71cb2d5239d",
|
|
153
165
|
"rhel_machine_id": null,
|
|
154
|
-
"subscription_manager_id": "
|
|
166
|
+
"subscription_manager_id": "#{@host3.subscription_facet.uuid}",
|
|
155
167
|
"satellite_id": "d29bde40-348e-437c-8acf-8fa98320fc1b",
|
|
156
168
|
"bios_uuid": "3cd5d972-cfb5-451a-8314-fd2f56629d7c",
|
|
157
169
|
"ip_addresses": [
|
|
@@ -159,7 +171,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
159
171
|
"fd6e:2298:736e::857",
|
|
160
172
|
"fd6e:2298:736e:0:2c66:6101:9cc6:2b23"
|
|
161
173
|
],
|
|
162
|
-
"fqdn": "
|
|
174
|
+
"fqdn": "#{@host3.fqdn}",
|
|
163
175
|
"mac_addresses": [
|
|
164
176
|
"6e:66:a6:fe:fc:07",
|
|
165
177
|
"00:00:00:00:00:00"
|
|
@@ -242,7 +254,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
242
254
|
end
|
|
243
255
|
|
|
244
256
|
test 'Host status should be SYNC for inventory hosts' do
|
|
245
|
-
|
|
257
|
+
Setting[:rh_cloud_token] = 'TEST TOKEN'
|
|
246
258
|
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
247
259
|
|
|
248
260
|
ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host2.organization)
|
|
@@ -254,7 +266,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
254
266
|
end
|
|
255
267
|
|
|
256
268
|
test 'Host status should be DISCONNECT for hosts that are not returned from cloud' do
|
|
257
|
-
|
|
269
|
+
Setting[:rh_cloud_token] = 'TEST TOKEN'
|
|
258
270
|
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
259
271
|
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
260
272
|
|
|
@@ -265,10 +277,24 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
|
|
|
265
277
|
end
|
|
266
278
|
|
|
267
279
|
test 'Task should be aborted if token is not present' do
|
|
268
|
-
|
|
280
|
+
Setting[:rh_cloud_token] = ''
|
|
269
281
|
|
|
270
282
|
InventorySync::Async::InventoryFullSync.any_instance.expects(:plan_self).never
|
|
271
283
|
|
|
272
284
|
ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
273
285
|
end
|
|
286
|
+
|
|
287
|
+
test 'Should skip hosts that are not returned in query' do
|
|
288
|
+
assert_nil InventorySync::InventoryStatus.where(host_id: @host3.id).first
|
|
289
|
+
|
|
290
|
+
Setting[:rh_cloud_token] = 'TEST TOKEN'
|
|
291
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
|
|
292
|
+
InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id])
|
|
293
|
+
FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
|
|
294
|
+
|
|
295
|
+
ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host1.organization)
|
|
296
|
+
@host2.reload
|
|
297
|
+
|
|
298
|
+
assert_nil InventorySync::InventoryStatus.where(host_id: @host3.id).first
|
|
299
|
+
end
|
|
274
300
|
end
|
|
@@ -6,7 +6,7 @@ class InventoryHostsSyncTest < ActiveSupport::TestCase
|
|
|
6
6
|
|
|
7
7
|
setup do
|
|
8
8
|
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
-
|
|
9
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
10
10
|
|
|
11
11
|
env = FactoryBot.create(:katello_k_t_environment)
|
|
12
12
|
cv = env.content_views << FactoryBot.create(:katello_content_view, organization: env.organization)
|
|
@@ -265,4 +265,19 @@ class InventoryHostsSyncTest < ActiveSupport::TestCase
|
|
|
265
265
|
|
|
266
266
|
assert_equal @host2_inventory_id, @host2.insights.uuid
|
|
267
267
|
end
|
|
268
|
+
|
|
269
|
+
test 'Inventory should sync empty facets list' do
|
|
270
|
+
empty_inventory = @inventory.deep_clone
|
|
271
|
+
empty_inventory['results'] = []
|
|
272
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:query_inventory).returns(empty_inventory)
|
|
273
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:plan_self_host_sync)
|
|
274
|
+
|
|
275
|
+
assert_nil @host2.insights
|
|
276
|
+
|
|
277
|
+
ForemanTasks.sync_task(InventorySync::Async::InventoryHostsSync)
|
|
278
|
+
|
|
279
|
+
@host2.reload
|
|
280
|
+
|
|
281
|
+
assert_nil @host2.insights
|
|
282
|
+
end
|
|
268
283
|
end
|
|
@@ -5,7 +5,7 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
|
|
|
5
5
|
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
|
6
6
|
|
|
7
7
|
test 'Schedules an execution if auto upload is enabled' do
|
|
8
|
-
|
|
8
|
+
Setting[:allow_auto_inventory_upload] = true
|
|
9
9
|
|
|
10
10
|
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
test 'Skips execution if auto upload is disabled' do
|
|
16
|
-
|
|
16
|
+
Setting[:allow_auto_inventory_upload] = false
|
|
17
17
|
|
|
18
18
|
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).never
|
|
19
19
|
|
|
@@ -6,7 +6,7 @@ class InventorySelfHostSyncTest < ActiveSupport::TestCase
|
|
|
6
6
|
|
|
7
7
|
setup do
|
|
8
8
|
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
-
|
|
9
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
10
10
|
|
|
11
11
|
# this host would pass our plugin queries, so it could be uploaded to the cloud.
|
|
12
12
|
@host1 = FactoryBot.create(:host)
|
|
@@ -12,7 +12,7 @@ class UploadReportJobTest < ActiveSupport::TestCase
|
|
|
12
12
|
'idCert' => 'TEST_CERT',
|
|
13
13
|
}
|
|
14
14
|
)
|
|
15
|
-
|
|
15
|
+
Setting[:content_disconnected] = true
|
|
16
16
|
|
|
17
17
|
ForemanTasks.sync_task(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
|
|
18
18
|
|
data/test/test_plugin_helper.rb
CHANGED
|
@@ -32,8 +32,6 @@ module KatelloLocationFix
|
|
|
32
32
|
|
|
33
33
|
included do
|
|
34
34
|
setup do
|
|
35
|
-
FactoryBot.create(:setting, name: 'default_location_subscribed_hosts')
|
|
36
|
-
FactoryBot.create(:setting, name: 'default_location_puppet_content')
|
|
37
35
|
Setting[:default_location_subscribed_hosts] = Location.first.title
|
|
38
36
|
end
|
|
39
37
|
end
|
|
@@ -41,7 +41,7 @@ class RhCloudHttpProxyTest < ActiveSupport::TestCase
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def setup_global_foreman_proxy
|
|
44
|
-
|
|
44
|
+
Setting[:http_proxy] = @global_foreman_proxy_mock
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def setup_cdn_proxy
|
|
@@ -38,7 +38,7 @@ class BranchInfoTest < ActiveSupport::TestCase
|
|
|
38
38
|
test 'should generate appropriate labels' do
|
|
39
39
|
2.times { FactoryBot.create(:katello_host_collection, :organization_id => @host.organization.id) }
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Setting[:include_parameter_tags] = true
|
|
42
42
|
|
|
43
43
|
Katello::HostCollection.all.map do |collection|
|
|
44
44
|
FactoryBot.create(:katello_host_collection_host, :host_id => @host.id, :host_collection_id => collection.id)
|
|
@@ -8,7 +8,7 @@ class TemplateRendererHelperTest < ActiveSupport::TestCase
|
|
|
8
8
|
response.stubs(:body).returns('TEST PLAYBOOK')
|
|
9
9
|
ForemanRhCloud::RemediationsRetriever.any_instance.stubs(:query_playbook).returns(response)
|
|
10
10
|
@host1 = FactoryBot.create(:host)
|
|
11
|
-
|
|
11
|
+
Setting[:rh_cloud_token] = 'MOCK_TOKEN'
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
test 'Generates a playbook for hit and remediation' do
|
|
@@ -189,7 +189,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
|
189
189
|
end
|
|
190
190
|
|
|
191
191
|
test 'generates obfuscated ip_address fields without inisghts-client' do
|
|
192
|
-
|
|
192
|
+
Setting[:obfuscate_inventory_ips] = true
|
|
193
193
|
|
|
194
194
|
@host.interfaces << FactoryBot.build(:nic_managed)
|
|
195
195
|
batch = Host.where(id: @host.id).in_batches.first
|
|
@@ -260,7 +260,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
|
260
260
|
end
|
|
261
261
|
|
|
262
262
|
test 'obfuscates fqdn when setting set' do
|
|
263
|
-
|
|
263
|
+
Setting[:obfuscate_inventory_hostnames] = true
|
|
264
264
|
|
|
265
265
|
batch = Host.where(id: @host.id).in_batches.first
|
|
266
266
|
generator = create_generator(batch)
|
|
@@ -447,6 +447,18 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
|
447
447
|
assert_equal 1, generator.hosts_count
|
|
448
448
|
end
|
|
449
449
|
|
|
450
|
+
test 'excludes hosts with host_registration_insights set to false' do
|
|
451
|
+
@host.host_parameters << HostParameter.create(
|
|
452
|
+
name: 'host_registration_insights',
|
|
453
|
+
value: "false",
|
|
454
|
+
parameter_type: 'boolean'
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
count = ForemanInventoryUpload::Generators::Queries.for_org(@host.organization_id).count
|
|
458
|
+
|
|
459
|
+
assert_equal 0, count
|
|
460
|
+
end
|
|
461
|
+
|
|
450
462
|
test 'shows system_memory_bytes in bytes' do
|
|
451
463
|
FactoryBot.create(:fact_value, fact_name: fact_names['memory::memtotal'], value: '1', host: @host)
|
|
452
464
|
|
|
@@ -625,7 +637,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
|
625
637
|
end
|
|
626
638
|
|
|
627
639
|
test 'include packages installed in the report' do
|
|
628
|
-
|
|
640
|
+
Setting[:exclude_installed_packages] = false
|
|
629
641
|
installed_package = ::Katello::InstalledPackage.create(name: 'test-package', nvrea: 'test-package-1.0.x86_64', nvra: 'test-package-1.0.x86_64')
|
|
630
642
|
|
|
631
643
|
another_host = FactoryBot.create(
|
|
@@ -63,7 +63,7 @@ class TagsGeneratorTest < ActiveSupport::TestCase
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
test 'generates parameter tags' do
|
|
66
|
-
|
|
66
|
+
Setting[:include_parameter_tags] = true
|
|
67
67
|
|
|
68
68
|
@host.stubs(:host_params).returns(
|
|
69
69
|
{
|
|
@@ -85,7 +85,7 @@ class TagsGeneratorTest < ActiveSupport::TestCase
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
test 'skips parameter tags if include_parameter_tags setting is off' do
|
|
88
|
-
|
|
88
|
+
Setting[:include_parameter_tags] = false
|
|
89
89
|
|
|
90
90
|
@host.stubs(:host_params).returns(
|
|
91
91
|
{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
|
3
3
|
import InventoryAutoUploadSwitcher from './ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload';
|
|
4
|
+
import NewHostDetailsTab from './InsightsHostDetailsTab/NewHostDetailsTab';
|
|
4
5
|
|
|
5
6
|
const fills = [
|
|
6
7
|
{
|
|
@@ -9,6 +10,12 @@ const fills = [
|
|
|
9
10
|
component: () => <InventoryAutoUploadSwitcher />,
|
|
10
11
|
weight: 50,
|
|
11
12
|
},
|
|
13
|
+
{
|
|
14
|
+
slot: 'host-details-page-tabs',
|
|
15
|
+
name: 'Insights',
|
|
16
|
+
component: props => <NewHostDetailsTab {...props} />,
|
|
17
|
+
weight: 700,
|
|
18
|
+
},
|
|
12
19
|
];
|
|
13
20
|
|
|
14
21
|
export const registerFills = () => {
|
|
@@ -4,7 +4,10 @@ import PropTypes from 'prop-types';
|
|
|
4
4
|
import { Table, TableHeader, TableBody } from '@patternfly/react-table';
|
|
5
5
|
import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
|
|
6
6
|
import SelectAllAlert from './SelectAllAlert';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
columns as defaultColumns,
|
|
9
|
+
getColumnsWithoutHostname,
|
|
10
|
+
} from './InsightsTableConstants';
|
|
8
11
|
import TableEmptyState from '../../../common/table/EmptyState';
|
|
9
12
|
import { modifySelectedRows, getSortColumnIndex } from './InsightsTableHelpers';
|
|
10
13
|
import Pagination from './Pagination';
|
|
@@ -27,10 +30,12 @@ const InsightsTable = ({
|
|
|
27
30
|
clearAllSelection,
|
|
28
31
|
error,
|
|
29
32
|
isAllSelected,
|
|
33
|
+
hideHost,
|
|
30
34
|
}) => {
|
|
31
35
|
const { perPage: appPerPage } = useForemanSettings();
|
|
32
36
|
const perPage = urlPerPage || appPerPage;
|
|
33
37
|
const [rows, setRows] = React.useState([]);
|
|
38
|
+
const [columns, setColumns] = React.useState(defaultColumns);
|
|
34
39
|
|
|
35
40
|
// acts as componentDidMount
|
|
36
41
|
useEffect(() => {
|
|
@@ -38,8 +43,12 @@ const InsightsTable = ({
|
|
|
38
43
|
}, []);
|
|
39
44
|
|
|
40
45
|
useEffect(() => {
|
|
41
|
-
setRows(
|
|
42
|
-
|
|
46
|
+
setRows(
|
|
47
|
+
modifySelectedRows(hits, selectedIds, showSelectAllAlert, hideHost)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (hideHost) setColumns(getColumnsWithoutHostname());
|
|
51
|
+
}, [hits, selectedIds, hideHost]);
|
|
43
52
|
|
|
44
53
|
return (
|
|
45
54
|
<React.Fragment>
|
|
@@ -51,14 +60,19 @@ const InsightsTable = ({
|
|
|
51
60
|
isAllSelected={isAllSelected}
|
|
52
61
|
/>
|
|
53
62
|
<Table
|
|
54
|
-
className="recommendations-table"
|
|
63
|
+
className="rh-cloud-recommendations-table"
|
|
55
64
|
aria-label="Recommendations Table"
|
|
56
65
|
onSelect={(_event, isSelected, rowId) =>
|
|
57
66
|
onTableSelect(isSelected, rowId, rows, selectedIds)
|
|
58
67
|
}
|
|
59
68
|
canSelectAll
|
|
60
|
-
sortBy={{
|
|
61
|
-
|
|
69
|
+
sortBy={{
|
|
70
|
+
index: getSortColumnIndex(columns, sortBy),
|
|
71
|
+
direction: sortOrder,
|
|
72
|
+
}}
|
|
73
|
+
onSort={(_event, index, direction) =>
|
|
74
|
+
onTableSort(columns, index, direction)
|
|
75
|
+
}
|
|
62
76
|
cells={columns}
|
|
63
77
|
rows={rows}
|
|
64
78
|
variant="compact"
|
|
@@ -89,6 +103,7 @@ InsightsTable.propTypes = {
|
|
|
89
103
|
query: PropTypes.string,
|
|
90
104
|
error: PropTypes.string,
|
|
91
105
|
isAllSelected: PropTypes.bool,
|
|
106
|
+
hideHost: PropTypes.bool,
|
|
92
107
|
};
|
|
93
108
|
|
|
94
109
|
InsightsTable.defaultProps = {
|
|
@@ -102,6 +117,7 @@ InsightsTable.defaultProps = {
|
|
|
102
117
|
query: '',
|
|
103
118
|
error: '',
|
|
104
119
|
isAllSelected: false,
|
|
120
|
+
hideHost: false,
|
|
105
121
|
};
|
|
106
122
|
|
|
107
123
|
export default InsightsTable;
|
|
@@ -2,14 +2,13 @@ import URI from 'urijs';
|
|
|
2
2
|
import { push } from 'connected-react-router';
|
|
3
3
|
import { get } from 'foremanReact/redux/API';
|
|
4
4
|
import { selectQueryParams } from './InsightsTableSelectors';
|
|
5
|
-
import { INSIGHTS_PATH } from '../../InsightsCloudSyncConstants';
|
|
6
5
|
import {
|
|
7
|
-
columns,
|
|
8
6
|
INSIGHTS_HITS_API_KEY,
|
|
9
7
|
INSIGHTS_HITS_PATH,
|
|
10
8
|
INSIGHTS_SET_SELECTED_IDS,
|
|
11
9
|
INSIGHTS_SET_SELECT_ALL_ALERT,
|
|
12
10
|
INSIGHTS_SET_SELECT_ALL,
|
|
11
|
+
NEW_HOST_PATH,
|
|
13
12
|
} from './InsightsTableConstants';
|
|
14
13
|
|
|
15
14
|
export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
|
|
@@ -29,17 +28,20 @@ export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
|
|
|
29
28
|
select_all: isSelectAll,
|
|
30
29
|
});
|
|
31
30
|
|
|
32
|
-
dispatch
|
|
33
|
-
push({
|
|
34
|
-
pathname: INSIGHTS_PATH,
|
|
35
|
-
search: uri.search(),
|
|
36
|
-
})
|
|
37
|
-
);
|
|
31
|
+
updateUrl(uri, dispatch);
|
|
38
32
|
|
|
39
33
|
if (!isSelectAll) {
|
|
40
34
|
dispatch(setSelectAllAlert(false));
|
|
41
35
|
}
|
|
42
36
|
|
|
37
|
+
let search = query;
|
|
38
|
+
if (isNewHostPage(uri)) {
|
|
39
|
+
const hostname = uri.pathname().split('/new/hosts/')[1];
|
|
40
|
+
const hostQuery = `hostname = ${hostname}`;
|
|
41
|
+
const q = query?.trim();
|
|
42
|
+
search = q ? `${hostQuery} AND (${q})` : hostQuery;
|
|
43
|
+
}
|
|
44
|
+
|
|
43
45
|
return dispatch(
|
|
44
46
|
get({
|
|
45
47
|
key: INSIGHTS_HITS_API_KEY,
|
|
@@ -47,7 +49,7 @@ export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
|
|
|
47
49
|
params: {
|
|
48
50
|
page,
|
|
49
51
|
per_page: perPage,
|
|
50
|
-
search
|
|
52
|
+
search,
|
|
51
53
|
order: `${sortBy} ${sortOrder}`,
|
|
52
54
|
},
|
|
53
55
|
handleSuccess: response => {
|
|
@@ -96,7 +98,7 @@ export const clearAllSelection = () => dispatch => {
|
|
|
96
98
|
dispatch(setSelectAll(false));
|
|
97
99
|
};
|
|
98
100
|
|
|
99
|
-
export const onTableSort = (
|
|
101
|
+
export const onTableSort = (columns, index, direction) => {
|
|
100
102
|
// The checkbox column shifts the data columns by 1;
|
|
101
103
|
const { sortKey } = columns[index - 1];
|
|
102
104
|
return fetchInsights({
|
|
@@ -144,11 +146,16 @@ export const onTableSelect = (
|
|
|
144
146
|
const setSelectAllUrl = selectAllValue => dispatch => {
|
|
145
147
|
const uri = new URI();
|
|
146
148
|
uri.setSearch({ select_all: selectAllValue });
|
|
149
|
+
updateUrl(uri, dispatch);
|
|
150
|
+
};
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const updateUrl = (uri, dispatch) => {
|
|
153
|
+
const nextUrlParams = { search: uri.search() };
|
|
154
|
+
if (isNewHostPage(uri)) {
|
|
155
|
+
// we need to keep the hash so the insights tab will remain selected in the new host details page.
|
|
156
|
+
nextUrlParams.hash = '/Insights';
|
|
157
|
+
}
|
|
158
|
+
dispatch(push(nextUrlParams));
|
|
154
159
|
};
|
|
160
|
+
|
|
161
|
+
const isNewHostPage = uri => uri.pathname().includes(NEW_HOST_PATH);
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
InsightsLabel,
|
|
5
5
|
Section,
|
|
6
6
|
} from '@redhat-cloud-services/frontend-components';
|
|
7
|
+
import { DropdownItem } from '@patternfly/react-core';
|
|
7
8
|
import { sortable, cellWidth } from '@patternfly/react-table';
|
|
8
|
-
import { AnsibeTowerIcon } from '@patternfly/react-icons';
|
|
9
|
+
import { AnsibeTowerIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';
|
|
9
10
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
10
11
|
import { foremanUrl } from '../../../ForemanRhCloudHelpers';
|
|
12
|
+
import DropdownToggle from '../../../common/DropdownToggle';
|
|
11
13
|
|
|
12
14
|
export const totalRiskFormatter = ({ title: totalRisk }) => ({
|
|
13
15
|
children: (
|
|
@@ -28,30 +30,73 @@ export const hasPlaybookFormatter = ({ title: hasPlaybook }) => ({
|
|
|
28
30
|
),
|
|
29
31
|
});
|
|
30
32
|
|
|
33
|
+
export const actionsFormatter = (props, { rowData = {} }) => {
|
|
34
|
+
const { recommendationUrl, accessRHUrl } = rowData;
|
|
35
|
+
const dropdownItems = [];
|
|
36
|
+
|
|
37
|
+
recommendationUrl &&
|
|
38
|
+
dropdownItems.push(
|
|
39
|
+
<DropdownItem key="recommendation-url">
|
|
40
|
+
<a href={recommendationUrl} target="_blank" rel="noopener noreferrer">
|
|
41
|
+
{__('View in Red Hat Insights')} <ExternalLinkAltIcon />
|
|
42
|
+
</a>
|
|
43
|
+
</DropdownItem>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
accessRHUrl &&
|
|
47
|
+
dropdownItems.push(
|
|
48
|
+
<DropdownItem key="access-url">
|
|
49
|
+
<a href={accessRHUrl} target="_blank" rel="noopener noreferrer">
|
|
50
|
+
{__('Knowledgebase article')} <ExternalLinkAltIcon />
|
|
51
|
+
</a>
|
|
52
|
+
</DropdownItem>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
children: <DropdownToggle items={dropdownItems} />,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
31
60
|
export const columns = [
|
|
32
61
|
{
|
|
62
|
+
id: 'hostname',
|
|
33
63
|
sortKey: 'hostname',
|
|
34
64
|
title: __('Hostname'),
|
|
35
65
|
transforms: [cellWidth(20), sortable],
|
|
36
66
|
},
|
|
37
67
|
{
|
|
68
|
+
id: 'recommendation',
|
|
38
69
|
sortKey: 'title',
|
|
39
70
|
title: __('Recommendation'),
|
|
40
71
|
transforms: [cellWidth(50), sortable],
|
|
41
72
|
},
|
|
42
73
|
{
|
|
74
|
+
id: 'total risk',
|
|
43
75
|
sortKey: 'total_risk',
|
|
44
76
|
title: __('Total risk'),
|
|
45
77
|
transforms: [cellWidth(15), sortable],
|
|
46
78
|
cellTransforms: [totalRiskFormatter],
|
|
47
79
|
},
|
|
48
80
|
{
|
|
81
|
+
id: 'remediate',
|
|
49
82
|
title: __('Remediate'),
|
|
50
|
-
transforms: [cellWidth(
|
|
83
|
+
transforms: [cellWidth(10)],
|
|
51
84
|
cellTransforms: [hasPlaybookFormatter],
|
|
52
85
|
},
|
|
86
|
+
{
|
|
87
|
+
id: 'actions',
|
|
88
|
+
title: '',
|
|
89
|
+
transforms: [cellWidth(5)],
|
|
90
|
+
cellTransforms: [actionsFormatter],
|
|
91
|
+
},
|
|
53
92
|
];
|
|
54
93
|
|
|
94
|
+
export const getColumnsWithoutHostname = () => {
|
|
95
|
+
const nextCols = columns.slice(1);
|
|
96
|
+
nextCols[0].transforms = [cellWidth(70), sortable];
|
|
97
|
+
return nextCols;
|
|
98
|
+
};
|
|
99
|
+
|
|
55
100
|
export const paginationTitles = {
|
|
56
101
|
items: __('items'),
|
|
57
102
|
page: __('page'),
|
|
@@ -75,3 +120,5 @@ export const INSIGHTS_SET_SELECTED_IDS = 'INSIGHTS_SET_SELECTED_IDS';
|
|
|
75
120
|
export const INSIGHTS_SET_SELECT_ALL_ALERT = 'INSIGHTS_SET_SELECT_ALL_ALERT';
|
|
76
121
|
|
|
77
122
|
export const INSIGHTS_SET_SELECT_ALL = 'INSIGHTS_SET_SELECT_ALL';
|
|
123
|
+
|
|
124
|
+
export const NEW_HOST_PATH = '/new/hosts/';
|
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
export const modifySelectedRows = (
|
|
3
|
+
hits,
|
|
4
|
+
selectedIds,
|
|
5
|
+
showSelectAllAlert,
|
|
6
|
+
hideHost
|
|
7
|
+
) => {
|
|
5
8
|
if (hits.length === 0) return [];
|
|
6
9
|
|
|
7
10
|
return hits
|
|
8
11
|
.asMutable()
|
|
9
|
-
.map(
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
cells: [hostname, title, total_risk, has_playbook],
|
|
13
|
-
disableCheckbox,
|
|
12
|
+
.map(
|
|
13
|
+
({
|
|
14
14
|
id,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
hostname,
|
|
16
|
+
title,
|
|
17
|
+
total_risk,
|
|
18
|
+
has_playbook,
|
|
19
|
+
results_url,
|
|
20
|
+
solution_url,
|
|
21
|
+
}) => {
|
|
22
|
+
const disableCheckbox = !has_playbook;
|
|
23
|
+
const cells = [hostname, title, total_risk, has_playbook, results_url];
|
|
24
|
+
if (hideHost) cells.shift();
|
|
25
|
+
return {
|
|
26
|
+
cells,
|
|
27
|
+
disableCheckbox,
|
|
28
|
+
id,
|
|
29
|
+
/** The main table checkbox will be seen as selected only if all rows are selected,
|
|
30
|
+
* in this case we need to select also the disabled once and hide it with css */
|
|
31
|
+
selected: selectedIds[id] || (disableCheckbox && showSelectAllAlert),
|
|
32
|
+
recommendationUrl: results_url,
|
|
33
|
+
accessRHUrl: solution_url,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
);
|
|
20
37
|
};
|
|
21
38
|
|
|
22
|
-
export const getSortColumnIndex = sortBy => {
|
|
39
|
+
export const getSortColumnIndex = (columns, sortBy) => {
|
|
23
40
|
let colIndex = 0;
|
|
24
41
|
columns.forEach((col, index) => {
|
|
25
42
|
if (col.sortKey === sortBy) {
|
|
@@ -17,6 +17,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
|
|
|
17
17
|
cells={
|
|
18
18
|
Array [
|
|
19
19
|
Object {
|
|
20
|
+
"id": "hostname",
|
|
20
21
|
"sortKey": "hostname",
|
|
21
22
|
"title": "Hostname",
|
|
22
23
|
"transforms": Array [
|
|
@@ -25,6 +26,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
|
|
|
25
26
|
],
|
|
26
27
|
},
|
|
27
28
|
Object {
|
|
29
|
+
"id": "recommendation",
|
|
28
30
|
"sortKey": "title",
|
|
29
31
|
"title": "Recommendation",
|
|
30
32
|
"transforms": Array [
|
|
@@ -36,6 +38,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
|
|
|
36
38
|
"cellTransforms": Array [
|
|
37
39
|
[Function],
|
|
38
40
|
],
|
|
41
|
+
"id": "total risk",
|
|
39
42
|
"sortKey": "total_risk",
|
|
40
43
|
"title": "Total risk",
|
|
41
44
|
"transforms": Array [
|
|
@@ -47,14 +50,25 @@ exports[`InsightsTable rendering render with Props 1`] = `
|
|
|
47
50
|
"cellTransforms": Array [
|
|
48
51
|
[Function],
|
|
49
52
|
],
|
|
53
|
+
"id": "remediate",
|
|
50
54
|
"title": "Remediate",
|
|
51
55
|
"transforms": Array [
|
|
52
56
|
[Function],
|
|
53
57
|
],
|
|
54
58
|
},
|
|
59
|
+
Object {
|
|
60
|
+
"cellTransforms": Array [
|
|
61
|
+
[Function],
|
|
62
|
+
],
|
|
63
|
+
"id": "actions",
|
|
64
|
+
"title": "",
|
|
65
|
+
"transforms": Array [
|
|
66
|
+
[Function],
|
|
67
|
+
],
|
|
68
|
+
},
|
|
55
69
|
]
|
|
56
70
|
}
|
|
57
|
-
className="recommendations-table"
|
|
71
|
+
className="rh-cloud-recommendations-table"
|
|
58
72
|
contentId="expanded-content"
|
|
59
73
|
dropdownDirection="down"
|
|
60
74
|
dropdownPosition="right"
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
@import '~@redhat-cloud-services/frontend-components/index.css';
|
|
2
2
|
|
|
3
|
-
.rh-cloud-
|
|
4
|
-
.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
display: none;
|
|
8
|
-
}
|
|
3
|
+
.rh-cloud-recommendations-table {
|
|
4
|
+
.pf-c-table__check {
|
|
5
|
+
input:disabled {
|
|
6
|
+
display: none;
|
|
9
7
|
}
|
|
8
|
+
}
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
10
|
+
.td-insights-remediate-playbook {
|
|
11
|
+
svg {
|
|
12
|
+
margin-right: 5px;
|
|
15
13
|
}
|
|
14
|
+
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
16
|
+
.td-insights-remediate-manual {
|
|
17
|
+
padding-left: 18px;
|
|
20
18
|
}
|
|
21
19
|
}
|
|
@@ -6,18 +6,4 @@
|
|
|
6
6
|
margin-top: 5px;
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
// applies to the backdrop parent of the modal
|
|
11
|
-
@at-root .pf-c-backdrop {
|
|
12
|
-
width: calc(100% - 200px);
|
|
13
|
-
left: 200px;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// where the vertical nav breaks: https://github.com/theforeman/foreman/blob/3347fa49d500964f0209122d8d36c920d1feafcc/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.scss#L26
|
|
17
|
-
@media (max-width: 768px) {
|
|
18
|
-
@at-root .pf-c-backdrop {
|
|
19
|
-
width: 100%;
|
|
20
|
-
left: 0;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
9
|
}
|
|
@@ -2,6 +2,8 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
4
|
import { Dropdown, DropdownItem, KebabToggle } from '@patternfly/react-core';
|
|
5
|
+
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
|
6
|
+
import { redHatAdvisorSystems } from '../InsightsCloudSyncHelpers';
|
|
5
7
|
|
|
6
8
|
const ToolbarDropdown = ({ onRecommendationSync }) => {
|
|
7
9
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
@@ -12,6 +14,17 @@ const ToolbarDropdown = ({ onRecommendationSync }) => {
|
|
|
12
14
|
>
|
|
13
15
|
{__('Sync recommendations')}
|
|
14
16
|
</DropdownItem>,
|
|
17
|
+
<DropdownItem key="cloud-advisor-systems-link">
|
|
18
|
+
<a
|
|
19
|
+
href={redHatAdvisorSystems()}
|
|
20
|
+
target="_blank"
|
|
21
|
+
rel="noopener noreferrer"
|
|
22
|
+
>
|
|
23
|
+
{__('View in Red Hat Insights')}
|
|
24
|
+
{' '}
|
|
25
|
+
<ExternalLinkAltIcon />
|
|
26
|
+
</a>
|
|
27
|
+
</DropdownItem>,
|
|
15
28
|
];
|
|
16
29
|
return (
|
|
17
30
|
<Dropdown
|
|
@@ -8,3 +8,11 @@ export const cloudTokenSettingUrl = () => {
|
|
|
8
8
|
settingsUrl.setSearch({ search: 'name = rh_cloud_token' });
|
|
9
9
|
return settingsUrl.toString();
|
|
10
10
|
};
|
|
11
|
+
|
|
12
|
+
export const redHatConsole = path => `https://console.redhat.com/${path || ''}`;
|
|
13
|
+
export const redHatInsights = path => redHatConsole(`insights/${path || ''}`);
|
|
14
|
+
export const redHatInventory = path =>
|
|
15
|
+
redHatInsights(`inventory/${path || ''}`);
|
|
16
|
+
export const redHatAdvisor = path => redHatInsights(`advisor/${path || ''}`);
|
|
17
|
+
export const redHatAdvisorSystems = path =>
|
|
18
|
+
redHatAdvisor(`systems/${path || ''}`);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
4
|
+
import SearchBar from 'foremanReact/components/SearchBar';
|
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
|
+
import {
|
|
7
|
+
Grid,
|
|
8
|
+
GridItem,
|
|
9
|
+
Dropdown,
|
|
10
|
+
DropdownItem,
|
|
11
|
+
KebabToggle,
|
|
12
|
+
} from '@patternfly/react-core';
|
|
13
|
+
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
|
14
|
+
import InsightsTable from '../InsightsCloudSync/Components/InsightsTable';
|
|
15
|
+
import RemediationModal from '../InsightsCloudSync/Components/RemediationModal';
|
|
16
|
+
import Pagination from '../InsightsCloudSync/Components/InsightsTable/Pagination';
|
|
17
|
+
import { INSIGHTS_SEARCH_PROPS } from '../InsightsCloudSync/InsightsCloudSyncConstants';
|
|
18
|
+
import { fetchInsights } from '../InsightsCloudSync/Components/InsightsTable/InsightsTableActions';
|
|
19
|
+
import {
|
|
20
|
+
selectSearch,
|
|
21
|
+
selectHits,
|
|
22
|
+
} from '../InsightsCloudSync/Components/InsightsTable/InsightsTableSelectors';
|
|
23
|
+
import './InsightsTab.scss';
|
|
24
|
+
import { redHatAdvisorSystems } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
|
|
25
|
+
|
|
26
|
+
const NewHostDetailsTab = ({ hostName, router }) => {
|
|
27
|
+
const dispatch = useDispatch();
|
|
28
|
+
const query = useSelector(selectSearch);
|
|
29
|
+
const hits = useSelector(selectHits);
|
|
30
|
+
|
|
31
|
+
useEffect(() => () => router.replace({ search: null }), [router]);
|
|
32
|
+
|
|
33
|
+
const onSearch = q => dispatch(fetchInsights({ query: q, page: 1 }));
|
|
34
|
+
|
|
35
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
36
|
+
const onSatInsightsClick = () =>
|
|
37
|
+
router.push({ pathname: '/foreman_rh_cloud/insights_cloud' });
|
|
38
|
+
|
|
39
|
+
const dropdownItems = [
|
|
40
|
+
<DropdownItem key="insights-link">
|
|
41
|
+
<a onClick={onSatInsightsClick}>{__('Go to Satellite Insights page')}</a>
|
|
42
|
+
</DropdownItem>,
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
if (hits.length) {
|
|
46
|
+
const { host_uuid: uuid } = hits[0];
|
|
47
|
+
dropdownItems.push(
|
|
48
|
+
<DropdownItem key="insights-advisor-link">
|
|
49
|
+
<a
|
|
50
|
+
href={redHatAdvisorSystems(uuid)}
|
|
51
|
+
target="_blank"
|
|
52
|
+
rel="noopener noreferrer"
|
|
53
|
+
>
|
|
54
|
+
{__('View in Red Hat Insights')}
|
|
55
|
+
{' '}
|
|
56
|
+
<ExternalLinkAltIcon />
|
|
57
|
+
</a>
|
|
58
|
+
</DropdownItem>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Grid id="new_host_details_insights_tab" hasGutter>
|
|
64
|
+
<GridItem span={5}>
|
|
65
|
+
<SearchBar
|
|
66
|
+
data={INSIGHTS_SEARCH_PROPS}
|
|
67
|
+
onSearch={onSearch}
|
|
68
|
+
initialQuery={query}
|
|
69
|
+
/>
|
|
70
|
+
</GridItem>
|
|
71
|
+
<GridItem span={4}>
|
|
72
|
+
<RemediationModal />
|
|
73
|
+
<Dropdown
|
|
74
|
+
className="insights-dropdown"
|
|
75
|
+
onSelect={() => setIsDropdownOpen(false)}
|
|
76
|
+
toggle={
|
|
77
|
+
<KebabToggle onToggle={isOpen => setIsDropdownOpen(isOpen)} />
|
|
78
|
+
}
|
|
79
|
+
isOpen={isDropdownOpen}
|
|
80
|
+
isPlain
|
|
81
|
+
dropdownItems={dropdownItems}
|
|
82
|
+
/>
|
|
83
|
+
</GridItem>
|
|
84
|
+
<GridItem span={3}>
|
|
85
|
+
<Pagination variant="top" isCompact />
|
|
86
|
+
</GridItem>
|
|
87
|
+
<GridItem>
|
|
88
|
+
<InsightsTable hideHost hostname={hostName} />
|
|
89
|
+
</GridItem>
|
|
90
|
+
</Grid>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
NewHostDetailsTab.propTypes = {
|
|
95
|
+
hostName: PropTypes.string,
|
|
96
|
+
router: PropTypes.object,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
NewHostDetailsTab.defaultProps = {
|
|
100
|
+
hostName: '',
|
|
101
|
+
router: {},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default NewHostDetailsTab;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Dropdown, KebabToggle } from '@patternfly/react-core';
|
|
4
|
+
|
|
5
|
+
const DropdownToggle = ({ items, ...props }) => {
|
|
6
|
+
const [isOpen, setOpen] = useState(false);
|
|
7
|
+
return (
|
|
8
|
+
<Dropdown
|
|
9
|
+
onSelect={() => setOpen(false)}
|
|
10
|
+
toggle={<KebabToggle onToggle={value => setOpen(value)} />}
|
|
11
|
+
isOpen={isOpen}
|
|
12
|
+
isPlain
|
|
13
|
+
dropdownItems={items}
|
|
14
|
+
position="right"
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
DropdownToggle.propTypes = {
|
|
21
|
+
items: PropTypes.array.isRequired,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default DropdownToggle;
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_rh_cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Red Hat Cloud team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-11-
|
|
11
|
+
date: 2021-11-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: katello
|
|
@@ -595,6 +595,7 @@ files:
|
|
|
595
595
|
- webpack/InsightsHostDetailsTab/InsightsTabConstants.js
|
|
596
596
|
- webpack/InsightsHostDetailsTab/InsightsTabReducer.js
|
|
597
597
|
- webpack/InsightsHostDetailsTab/InsightsTabSelectors.js
|
|
598
|
+
- webpack/InsightsHostDetailsTab/NewHostDetailsTab.js
|
|
598
599
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTab.fixtures.js
|
|
599
600
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js
|
|
600
601
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js
|
|
@@ -628,6 +629,7 @@ files:
|
|
|
628
629
|
- webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap
|
|
629
630
|
- webpack/__tests__/__snapshots__/ForemanRhCloudSelectors.test.js.snap
|
|
630
631
|
- webpack/__tests__/__snapshots__/ForemanRhCloudTestHelpers.test.js.snap
|
|
632
|
+
- webpack/common/DropdownToggle.js
|
|
631
633
|
- webpack/common/ForemanTasks/ForemanTasksActions.js
|
|
632
634
|
- webpack/common/ForemanTasks/ForemanTasksHelpers.js
|
|
633
635
|
- webpack/common/ForemanTasks/index.js
|