foreman_rh_cloud 14.0.3 → 14.1.0
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/concerns/insights_cloud/candlepin_proxies_extensions.rb +23 -0
- data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +9 -0
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +1 -1
- data/app/controllers/foreman_inventory_upload/api/tasks_controller.rb +2 -2
- data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +9 -2
- data/app/models/concerns/rh_cloud_host.rb +35 -3
- data/app/models/insights_client_report_status.rb +9 -1
- data/app/models/inventory_sync/inventory_status.rb +16 -4
- data/lib/foreman_inventory_upload/async/create_missing_insights_facets.rb +8 -2
- data/lib/foreman_rh_cloud/engine.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/insights_cloud/async/vmaas_reposcan_sync.rb +23 -8
- data/lib/inventory_sync/async/inventory_full_sync.rb +39 -3
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +40 -0
- data/test/controllers/insights_cloud/candlepin_proxies_extensions_test.rb +70 -0
- data/test/jobs/insights_client_status_aging_test.rb +40 -0
- data/test/jobs/inventory_full_sync_test.rb +212 -0
- data/test/models/insights_client_report_status_test.rb +109 -0
- data/test/models/inventory_sync/inventory_status_test.rb +85 -0
- data/test/unit/lib/insights_cloud/async/vmaas_reposcan_sync_test.rb +80 -25
- data/test/unit/rh_cloud_host_test.rb +214 -0
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +6 -1
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +45 -6
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonActions.js +8 -2
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +1 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/Toast.js +43 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/components/__tests__/Toast.test.js +82 -0
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +7 -4
- data/webpack/InsightsCloudSync/Components/InsightsTable/table.scss +9 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +26 -11
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecc86813dfba949a2d7125c96245b767f5ecc4edee058828bac97042ce5d4ee0
|
|
4
|
+
data.tar.gz: e6d920065bbe1a0a560c49050614abbad1c72d4f4c5e1494edf390db0495833e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e672658acda2399401392268c238361a81720e58d6d5693e70b1195c8aaef4d22ee35918b1ee329fae40d03efb09ad77c5562e9d81a905bd9e3b86945ed5a7f7
|
|
7
|
+
data.tar.gz: 1ff3460a85b2a26b64de9a472dcaf864bcb9cdd71043736a94f7f0784eb48ed91dcee94c84ae7ad4ae3c10bd31df8addd89175cf7c9ff6b221b8fadbe5035391
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module InsightsCloud
|
|
2
|
+
module CandlepinProxiesExtensions
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
# Update insights client status when hosts check in via subscription-manager facts upload
|
|
7
|
+
# rubocop:disable Rails/LexicallyScopedActionFilter
|
|
8
|
+
after_action :update_insights_client_status, only: [:facts]
|
|
9
|
+
# rubocop:enable Rails/LexicallyScopedActionFilter
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def update_insights_client_status
|
|
13
|
+
# Update InsightsClientReportStatus whenever host checks in via subscription-manager
|
|
14
|
+
# This ensures USER_OMITTED status gets set even when insights-client isn't installed
|
|
15
|
+
# (parameter=false means insights-client won't be installed, so it won't hit MachineTelemetriesController)
|
|
16
|
+
hoststatus = @host.get_status(InsightsClientReportStatus)
|
|
17
|
+
Rails.logger.debug "Current status: #{hoststatus.to_label}"
|
|
18
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
19
|
+
@host.refresh_global_status!
|
|
20
|
+
Rails.logger.debug "New status: #{hoststatus.to_label}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -6,6 +6,7 @@ module InsightsCloud
|
|
|
6
6
|
# This method explicitly listens on Katello actions
|
|
7
7
|
# rubocop:disable Rails/LexicallyScopedActionFilter
|
|
8
8
|
after_action :generate_host_report, only: [:upload_package_profile, :upload_profiles]
|
|
9
|
+
after_action :update_insights_client_status, only: [:upload_package_profile, :upload_profiles]
|
|
9
10
|
# rubocop:enable Rails/LexicallyScopedActionFilter
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -31,5 +32,13 @@ module InsightsCloud
|
|
|
31
32
|
insights_facet = @host.build_insights(uuid: @host.subscription_facet.uuid)
|
|
32
33
|
insights_facet.save
|
|
33
34
|
end
|
|
35
|
+
|
|
36
|
+
def update_insights_client_status
|
|
37
|
+
# Update InsightsClientReportStatus whenever host checks in via subscription-manager
|
|
38
|
+
# This ensures USER_OMITTED status gets set even when insights-client isn't installed
|
|
39
|
+
# (parameter=false means insights-client won't be installed, so it won't hit MachineTelemetriesController)
|
|
40
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
41
|
+
@host.refresh_global_status!
|
|
42
|
+
end
|
|
34
43
|
end
|
|
35
44
|
end
|
|
@@ -15,7 +15,7 @@ module ForemanInventoryUpload
|
|
|
15
15
|
tasks = ForemanTasks::Task
|
|
16
16
|
.active
|
|
17
17
|
.for_action_types(ACTION_TYPES)
|
|
18
|
-
.
|
|
18
|
+
.select_duration
|
|
19
19
|
|
|
20
20
|
if organization_id.present?
|
|
21
21
|
tasks = tasks.joins(:links)
|
|
@@ -40,7 +40,7 @@ module ForemanInventoryUpload
|
|
|
40
40
|
|
|
41
41
|
tasks = ForemanTasks::Task
|
|
42
42
|
.for_action_types(ACTION_TYPES)
|
|
43
|
-
.
|
|
43
|
+
.select_duration
|
|
44
44
|
.order('started_at DESC')
|
|
45
45
|
.limit(limit)
|
|
46
46
|
|
|
@@ -88,9 +88,16 @@ module InsightsCloud::Api
|
|
|
88
88
|
private
|
|
89
89
|
|
|
90
90
|
def ensure_telemetry_enabled_for_consumer
|
|
91
|
-
|
|
91
|
+
config = telemetry_config(@host)
|
|
92
|
+
|
|
93
|
+
# Always update the status based on current parameter value
|
|
94
|
+
# This will set USER_OMITTED if parameter is false, or REPORTING/NO_REPORT if true
|
|
95
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
96
|
+
@host.refresh_global_status!
|
|
97
|
+
|
|
98
|
+
unless config
|
|
92
99
|
logger.debug("Rejected telemetry forwarding for host #{@host.name}, insights param is set to: #{config}")
|
|
93
|
-
render_message
|
|
100
|
+
return render_message('Telemetry is not enabled for this host', :status => 403)
|
|
94
101
|
end
|
|
95
102
|
config
|
|
96
103
|
end
|
|
@@ -15,13 +15,16 @@ module RhCloudHost
|
|
|
15
15
|
has_one :insights_client_report_status_object, :class_name => '::InsightsClientReportStatus', :foreign_key => 'host_id'
|
|
16
16
|
scoped_search :relation => :insights_client_report_status_object, :on => :status, :rename => :insights_client_report_status,
|
|
17
17
|
:complete_value => { :reporting => ::InsightsClientReportStatus::REPORTING,
|
|
18
|
-
:no_report => ::InsightsClientReportStatus::NO_REPORT
|
|
18
|
+
:no_report => ::InsightsClientReportStatus::NO_REPORT,
|
|
19
|
+
:user_omitted => ::InsightsClientReportStatus::USER_OMITTED }
|
|
19
20
|
|
|
20
21
|
has_one :inventory_sync_status_object, :class_name => '::InventorySync::InventoryStatus', :foreign_key => 'host_id'
|
|
21
22
|
scoped_search :relation => :inventory_sync_status_object, :on => :status, :rename => :insights_inventory_sync_status,
|
|
22
23
|
:complete_value => { :disconnect => ::InventorySync::InventoryStatus::DISCONNECT,
|
|
23
|
-
:sync => ::InventorySync::InventoryStatus::SYNC
|
|
24
|
-
|
|
24
|
+
:sync => ::InventorySync::InventoryStatus::SYNC,
|
|
25
|
+
:user_omitted => ::InventorySync::InventoryStatus::USER_OMITTED }
|
|
26
|
+
scoped_search :on => :id, :rename => :insights_uuid, :only_explicit => true,
|
|
27
|
+
:ext_method => :search_by_insights_uuid, :complete_value => false
|
|
25
28
|
|
|
26
29
|
def insights_facet
|
|
27
30
|
insights
|
|
@@ -41,4 +44,33 @@ module RhCloudHost
|
|
|
41
44
|
insights_facet.update!(uuid: subscription_facet.uuid)
|
|
42
45
|
end
|
|
43
46
|
end
|
|
47
|
+
|
|
48
|
+
module ClassMethods
|
|
49
|
+
def search_by_insights_uuid(_key, operator, value)
|
|
50
|
+
# Determine which facet table to search based on IoP mode
|
|
51
|
+
facet_table = ForemanRhCloud.with_iop_smart_proxy? ? Katello::Host::SubscriptionFacet.table_name : InsightsFacet.table_name
|
|
52
|
+
|
|
53
|
+
# Build SQL condition
|
|
54
|
+
if ['IN', 'NOT IN'].include?(operator)
|
|
55
|
+
# For IN/NOT IN, value may be an array or comma-separated string
|
|
56
|
+
# Convert to array and build placeholders for each value
|
|
57
|
+
values = value.is_a?(Array) ? value : value.to_s.split(',').map(&:strip)
|
|
58
|
+
placeholders = (['?'] * values.size).join(',')
|
|
59
|
+
condition = sanitize_sql_for_conditions(
|
|
60
|
+
["#{facet_table}.uuid #{operator} (#{placeholders})", *values]
|
|
61
|
+
)
|
|
62
|
+
else
|
|
63
|
+
# For other operators (=, !=, LIKE, etc.), use value_to_sql for proper SQL formatting
|
|
64
|
+
condition = sanitize_sql_for_conditions(
|
|
65
|
+
["#{facet_table}.uuid #{operator} ?", value_to_sql(operator, value)]
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Return search parameters with LEFT JOIN to include hosts without facets
|
|
70
|
+
{
|
|
71
|
+
joins: "LEFT JOIN #{facet_table} ON #{facet_table}.host_id = #{Host::Managed.table_name}.id",
|
|
72
|
+
conditions: condition,
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
end
|
|
44
76
|
end
|
|
@@ -3,8 +3,9 @@ class InsightsClientReportStatus < HostStatus::Status
|
|
|
3
3
|
|
|
4
4
|
REPORTING = 0
|
|
5
5
|
NO_REPORT = 1
|
|
6
|
+
USER_OMITTED = 2
|
|
6
7
|
|
|
7
|
-
scope :stale, -> { where.not(reported_at: (Time.now - REPORT_INTERVAL)..Time.now) }
|
|
8
|
+
scope :stale, -> { where.not(status: USER_OMITTED).where.not(reported_at: (Time.now - REPORT_INTERVAL)..Time.now) }
|
|
8
9
|
scope :reporting, -> { where(status: REPORTING) }
|
|
9
10
|
|
|
10
11
|
def self.status_name
|
|
@@ -17,6 +18,8 @@ class InsightsClientReportStatus < HostStatus::Status
|
|
|
17
18
|
N_('Reporting')
|
|
18
19
|
when NO_REPORT
|
|
19
20
|
N_('Not reporting')
|
|
21
|
+
when USER_OMITTED
|
|
22
|
+
N_('Not reporting because host_registration_insights parameter value is false')
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
25
|
|
|
@@ -26,10 +29,15 @@ class InsightsClientReportStatus < HostStatus::Status
|
|
|
26
29
|
::HostStatus::Global::OK
|
|
27
30
|
when NO_REPORT
|
|
28
31
|
::HostStatus::Global::ERROR
|
|
32
|
+
when USER_OMITTED
|
|
33
|
+
::HostStatus::Global::OK
|
|
29
34
|
end
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
def to_status
|
|
38
|
+
excluded_by_host_param =
|
|
39
|
+
::Foreman::Cast.to_bool(host.host_param('host_registration_insights')) == false
|
|
40
|
+
return USER_OMITTED if excluded_by_host_param
|
|
33
41
|
in_interval? ? REPORTING : NO_REPORT
|
|
34
42
|
end
|
|
35
43
|
|
|
@@ -2,6 +2,7 @@ module InventorySync
|
|
|
2
2
|
class InventoryStatus < HostStatus::Status
|
|
3
3
|
DISCONNECT = 0
|
|
4
4
|
SYNC = 1
|
|
5
|
+
USER_OMITTED = 2
|
|
5
6
|
|
|
6
7
|
def self.status_name
|
|
7
8
|
N_('Inventory')
|
|
@@ -13,6 +14,8 @@ module InventorySync
|
|
|
13
14
|
::HostStatus::Global::WARN
|
|
14
15
|
when SYNC
|
|
15
16
|
::HostStatus::Global::OK
|
|
17
|
+
when USER_OMITTED
|
|
18
|
+
::HostStatus::Global::OK
|
|
16
19
|
else
|
|
17
20
|
::HostStatus::Global::WARN
|
|
18
21
|
end
|
|
@@ -21,16 +24,25 @@ module InventorySync
|
|
|
21
24
|
def to_label
|
|
22
25
|
case status
|
|
23
26
|
when DISCONNECT
|
|
24
|
-
N_('Host
|
|
27
|
+
N_('Host is not present on console.redhat.com Inventory service')
|
|
25
28
|
when SYNC
|
|
26
|
-
N_('
|
|
29
|
+
N_('Host is uploaded and present on console.redhat.com Inventory service')
|
|
30
|
+
when USER_OMITTED
|
|
31
|
+
N_('Host is excluded from upload to console.redhat.com Inventory service due to host parameter')
|
|
27
32
|
end
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
def to_status(options = {})
|
|
31
|
-
# this method used to calculate status.
|
|
32
|
-
#
|
|
36
|
+
# Normally, this method is used to calculate status.
|
|
37
|
+
# In foreman_rh_cloud 'we do things a bit differently around here.'
|
|
38
|
+
# Calculation is done externally in InventorySync::Async::InventoryFullSync, so we simply return the previously calculated status.
|
|
33
39
|
status
|
|
34
40
|
end
|
|
41
|
+
|
|
42
|
+
def relevant?(_options = {})
|
|
43
|
+
# Inventory status is not relevant in IoP mode since we use single-host reports
|
|
44
|
+
# that don't sync with the cloud inventory service
|
|
45
|
+
!ForemanRhCloud.with_iop_smart_proxy?
|
|
46
|
+
end
|
|
35
47
|
end
|
|
36
48
|
end
|
|
@@ -7,9 +7,15 @@ module ForemanInventoryUpload
|
|
|
7
7
|
|
|
8
8
|
def run
|
|
9
9
|
organization = ::Organization.find(input[:organization_id])
|
|
10
|
-
|
|
10
|
+
# Find hosts with subscription facets but without insights facets
|
|
11
|
+
# Note: We can't use scoped_search 'null? insights_uuid' because the null? operator
|
|
12
|
+
# doesn't work with ext_methods - it would check hosts.id IS NULL instead of the facet
|
|
13
|
+
hosts_without_facets = ::ForemanInventoryUpload::Generators::Queries.for_org(organization, use_batches: false)
|
|
14
|
+
.left_outer_joins(:insights)
|
|
15
|
+
.where(insights_facets: { id: nil })
|
|
16
|
+
|
|
11
17
|
facet_count = 0
|
|
12
|
-
hosts_without_facets.
|
|
18
|
+
hosts_without_facets.in_batches(of: ForemanInventoryUpload.slice_size) do |batch|
|
|
13
19
|
facets = batch.pluck(:id, 'katello_subscription_facets.uuid').map do |host_id, uuid|
|
|
14
20
|
{
|
|
15
21
|
host_id: host_id,
|
|
@@ -40,6 +40,7 @@ module ForemanRhCloud
|
|
|
40
40
|
::Host::Managed.include RhCloudHost
|
|
41
41
|
|
|
42
42
|
::Katello::Api::Rhsm::CandlepinDynflowProxyController.include InsightsCloud::PackageProfileUploadExtensions
|
|
43
|
+
::Katello::Api::Rhsm::CandlepinProxiesController.include InsightsCloud::CandlepinProxiesExtensions
|
|
43
44
|
::Katello::RegistrationManager.singleton_class.prepend ::ForemanRhCloud::RegistrationManagerExtensions
|
|
44
45
|
end
|
|
45
46
|
end
|
|
@@ -6,6 +6,8 @@ module InsightsCloud
|
|
|
6
6
|
class VmaasReposcanSync < ::Actions::EntryAction
|
|
7
7
|
include ::ForemanRhCloud::CertAuth
|
|
8
8
|
|
|
9
|
+
HTTP_TOO_MANY_REQUESTS = 429
|
|
10
|
+
|
|
9
11
|
# Subscribe to Katello repository sync hook action, if available
|
|
10
12
|
def self.subscribe
|
|
11
13
|
'Actions::Katello::Repository::SyncHook'.constantize
|
|
@@ -49,15 +51,9 @@ module InsightsCloud
|
|
|
49
51
|
|
|
50
52
|
response
|
|
51
53
|
rescue RestClient::ExceptionWithResponse => e
|
|
52
|
-
|
|
53
|
-
logger.error(message)
|
|
54
|
-
output[:message] = message
|
|
55
|
-
raise
|
|
54
|
+
handle_rest_client_error(e)
|
|
56
55
|
rescue StandardError => e
|
|
57
|
-
|
|
58
|
-
logger.error(message)
|
|
59
|
-
output[:message] = message
|
|
60
|
-
raise
|
|
56
|
+
handle_standard_error(e)
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
def rescue_strategy_for_self
|
|
@@ -70,6 +66,25 @@ module InsightsCloud
|
|
|
70
66
|
|
|
71
67
|
private
|
|
72
68
|
|
|
69
|
+
def handle_rest_client_error(exception)
|
|
70
|
+
if exception.response&.code == HTTP_TOO_MANY_REQUESTS
|
|
71
|
+
message = "VMaaS reposcan sync skipped: another sync already in progress (#{HTTP_TOO_MANY_REQUESTS})"
|
|
72
|
+
logger.warn(message)
|
|
73
|
+
else
|
|
74
|
+
message = "VMaaS reposcan sync failed: #{exception.response&.code} - #{exception.response&.body}"
|
|
75
|
+
logger.error(message)
|
|
76
|
+
end
|
|
77
|
+
output[:message] = message
|
|
78
|
+
# Do NOT raise - let rescue_strategy_for_self Skip handle this
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def handle_standard_error(exception)
|
|
82
|
+
message = "Error triggering VMaaS reposcan sync: #{exception.message}"
|
|
83
|
+
logger.error(message)
|
|
84
|
+
output[:message] = message
|
|
85
|
+
# Do NOT raise - let rescue_strategy_for_self Skip handle this
|
|
86
|
+
end
|
|
87
|
+
|
|
73
88
|
def logger
|
|
74
89
|
action_logger
|
|
75
90
|
end
|
|
@@ -15,16 +15,26 @@ module InventorySync
|
|
|
15
15
|
|
|
16
16
|
def setup_statuses
|
|
17
17
|
@subscribed_hosts_ids = Set.new(affected_host_ids)
|
|
18
|
+
@omitted_ids = Set.new(user_omitted_host_ids)
|
|
19
|
+
|
|
20
|
+
# Remove user-omitted hosts from subscribed set. In normal operation, affected_host_ids
|
|
21
|
+
# already excludes user-omitted hosts via for_slice, but this handles edge cases like
|
|
22
|
+
# a host transitioning from uploaded to user-omitted between syncs.
|
|
23
|
+
@subscribed_hosts_ids.subtract(@omitted_ids)
|
|
18
24
|
|
|
19
25
|
InventorySync::InventoryStatus.transaction do
|
|
20
26
|
InventorySync::InventoryStatus.where(host_id: @subscribed_hosts_ids).delete_all
|
|
27
|
+
InventorySync::InventoryStatus.where(host_id: @omitted_ids).delete_all
|
|
21
28
|
yield
|
|
22
|
-
add_missing_hosts_statuses(@subscribed_hosts_ids)
|
|
29
|
+
add_missing_hosts_statuses(@subscribed_hosts_ids) # any remaining hosts after yield are disconnected
|
|
30
|
+
add_user_omitted_host_statuses(@omitted_ids)
|
|
23
31
|
host_statuses[:disconnect] += @subscribed_hosts_ids.size
|
|
32
|
+
host_statuses[:user_omitted] += @omitted_ids.size
|
|
24
33
|
end
|
|
25
34
|
|
|
26
|
-
logger.debug("Synced hosts
|
|
27
|
-
logger.debug("Disconnected hosts
|
|
35
|
+
logger.debug("Synced hosts count: #{host_statuses[:sync]}")
|
|
36
|
+
logger.debug("Disconnected hosts count: #{host_statuses[:disconnect]}")
|
|
37
|
+
logger.debug("User-omitted hosts count: #{host_statuses[:user_omitted]}")
|
|
28
38
|
output[:host_statuses] = host_statuses
|
|
29
39
|
end
|
|
30
40
|
|
|
@@ -44,6 +54,7 @@ module InventorySync
|
|
|
44
54
|
private
|
|
45
55
|
|
|
46
56
|
def update_hosts_status(status_hashes)
|
|
57
|
+
# create Inventory statuses
|
|
47
58
|
InventorySync::InventoryStatus.create(status_hashes)
|
|
48
59
|
updated_ids = status_hashes.map { |hash| hash[:host_id] }
|
|
49
60
|
@subscribed_hosts_ids.subtract(updated_ids)
|
|
@@ -61,10 +72,23 @@ module InventorySync
|
|
|
61
72
|
)
|
|
62
73
|
end
|
|
63
74
|
|
|
75
|
+
def add_user_omitted_host_statuses(host_ids)
|
|
76
|
+
InventorySync::InventoryStatus.create(
|
|
77
|
+
host_ids.map do |host_id|
|
|
78
|
+
{
|
|
79
|
+
host_id: host_id,
|
|
80
|
+
status: InventorySync::InventoryStatus::USER_OMITTED,
|
|
81
|
+
reported_at: DateTime.current,
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
64
87
|
def host_statuses
|
|
65
88
|
@host_statuses ||= {
|
|
66
89
|
sync: 0,
|
|
67
90
|
disconnect: 0,
|
|
91
|
+
user_omitted: 0,
|
|
68
92
|
}
|
|
69
93
|
end
|
|
70
94
|
|
|
@@ -73,6 +97,18 @@ module InventorySync
|
|
|
73
97
|
Host.unscoped.where(organization: organizations)
|
|
74
98
|
).pluck(:id)
|
|
75
99
|
end
|
|
100
|
+
|
|
101
|
+
def user_omitted_host_ids
|
|
102
|
+
param_name = InsightsCloud.enable_client_param_inventory
|
|
103
|
+
|
|
104
|
+
# Use search_for to respect parameter inheritance (global, org, hostgroup, host)
|
|
105
|
+
# This matches the same logic used by for_slice, ensuring consistency
|
|
106
|
+
Host.unscoped
|
|
107
|
+
.where(organization: organizations)
|
|
108
|
+
.joins(:subscription_facet)
|
|
109
|
+
.search_for("params.#{param_name} = f")
|
|
110
|
+
.pluck(:id)
|
|
111
|
+
end
|
|
76
112
|
end
|
|
77
113
|
end
|
|
78
114
|
end
|
data/package.json
CHANGED
|
@@ -4,6 +4,7 @@ require 'rest-client'
|
|
|
4
4
|
module InsightsCloud::Api
|
|
5
5
|
class MachineTelemetriesControllerTest < ActionController::TestCase
|
|
6
6
|
include KatelloCVEHelper
|
|
7
|
+
include CandlepinIsolation
|
|
7
8
|
|
|
8
9
|
setup do
|
|
9
10
|
FactoryBot.create(:common_parameter, name: InsightsCloud.enable_client_param, key_type: 'boolean', value: true)
|
|
@@ -170,6 +171,45 @@ module InsightsCloud::Api
|
|
|
170
171
|
|
|
171
172
|
assert_not_nil InsightsFacet.find_by(host_id: @host.id)
|
|
172
173
|
end
|
|
174
|
+
|
|
175
|
+
test "should update InsightsClientReportStatus when parameter is false and block request" do
|
|
176
|
+
# Remove the common parameter from setup and set it to false
|
|
177
|
+
CommonParameter.where(name: InsightsCloud.enable_client_param).delete_all
|
|
178
|
+
FactoryBot.create(:common_parameter, name: InsightsCloud.enable_client_param, key_type: 'boolean', value: false)
|
|
179
|
+
|
|
180
|
+
# Stub telemetry_config to return false (disabled)
|
|
181
|
+
InsightsCloud::Api::MachineTelemetriesController.any_instance.stubs(:telemetry_config).returns(false)
|
|
182
|
+
|
|
183
|
+
get :forward_request, params: { "path" => "platform/ingress/v1/upload" }
|
|
184
|
+
|
|
185
|
+
# Request should be blocked
|
|
186
|
+
assert_response 403
|
|
187
|
+
assert_equal 'Telemetry is not enabled for this host', JSON.parse(@response.body)['message']
|
|
188
|
+
|
|
189
|
+
# But status should be updated to USER_OMITTED
|
|
190
|
+
@host.reload
|
|
191
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
192
|
+
assert insights_status.persisted?, 'InsightsClientReportStatus should be created'
|
|
193
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
test "should update InsightsClientReportStatus on successful upload" do
|
|
197
|
+
# Stub telemetry_config to return true (enabled) - common param from setup works for this
|
|
198
|
+
InsightsCloud::Api::MachineTelemetriesController.any_instance.stubs(:telemetry_config).returns(true)
|
|
199
|
+
|
|
200
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
|
201
|
+
res = RestClient::Response.create('response body', net_http_resp, @http_req)
|
|
202
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:forward_request).returns(res)
|
|
203
|
+
|
|
204
|
+
get :forward_request, params: { "path" => "/redhat_access/r/insights/uploads/" }
|
|
205
|
+
|
|
206
|
+
assert_response :success
|
|
207
|
+
|
|
208
|
+
# Status should have been refreshed during the request
|
|
209
|
+
@host.reload
|
|
210
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
211
|
+
assert insights_status.persisted?, 'InsightsClientReportStatus should be created and persisted'
|
|
212
|
+
end
|
|
173
213
|
end
|
|
174
214
|
|
|
175
215
|
context '#branch_info' do
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
module InsightsCloud
|
|
4
|
+
class CandlepinProxiesExtensionsTest < ActiveSupport::TestCase
|
|
5
|
+
setup do
|
|
6
|
+
@organization = FactoryBot.create(:organization)
|
|
7
|
+
@host = FactoryBot.create(:host, :with_subscription, :managed, organization: @organization)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
test 'updates InsightsClientReportStatus to USER_OMITTED when parameter is false' do
|
|
11
|
+
# Set parameter to false so status should be USER_OMITTED
|
|
12
|
+
FactoryBot.create(:common_parameter, name: 'host_registration_insights', key_type: 'boolean', value: false)
|
|
13
|
+
|
|
14
|
+
# Simulate what the callback does
|
|
15
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
16
|
+
@host.refresh_global_status!
|
|
17
|
+
|
|
18
|
+
# Verify the status was updated
|
|
19
|
+
@host.reload
|
|
20
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
21
|
+
assert insights_status.persisted?, 'InsightsClientReportStatus should be persisted'
|
|
22
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status,
|
|
23
|
+
'Status should be USER_OMITTED when host_registration_insights=false'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test 'sets USER_OMITTED status when parameter is inherited from hostgroup' do
|
|
27
|
+
# Create hostgroup with parameter
|
|
28
|
+
hostgroup = FactoryBot.create(:hostgroup)
|
|
29
|
+
hostgroup.group_parameters << GroupParameter.create(
|
|
30
|
+
name: 'host_registration_insights',
|
|
31
|
+
value: 'false',
|
|
32
|
+
key_type: 'boolean'
|
|
33
|
+
)
|
|
34
|
+
hostgroup.save!
|
|
35
|
+
|
|
36
|
+
@host.hostgroup = hostgroup
|
|
37
|
+
@host.save!
|
|
38
|
+
|
|
39
|
+
# Simulate what the callback does
|
|
40
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
41
|
+
@host.refresh_global_status!
|
|
42
|
+
|
|
43
|
+
# Verify status respects inherited parameter
|
|
44
|
+
@host.reload
|
|
45
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
46
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status,
|
|
47
|
+
'Status should be USER_OMITTED when host_registration_insights=false is inherited from hostgroup'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test 'refreshes global status' do
|
|
51
|
+
FactoryBot.create(:common_parameter, name: 'host_registration_insights', key_type: 'boolean', value: true)
|
|
52
|
+
|
|
53
|
+
# Simulate what the callback does
|
|
54
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
55
|
+
@host.refresh_global_status!
|
|
56
|
+
|
|
57
|
+
# Verify global status was updated
|
|
58
|
+
@host.reload
|
|
59
|
+
assert_not_nil @host.global_status, 'Global status should be set'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test 'CandlepinProxiesController includes the update_insights_client_status method' do
|
|
63
|
+
# Verify the concern is included and method is available
|
|
64
|
+
controller = Katello::Api::Rhsm::CandlepinProxiesController.new
|
|
65
|
+
|
|
66
|
+
assert_respond_to controller, :update_insights_client_status,
|
|
67
|
+
'CandlepinProxiesController should have update_insights_client_status method from concern'
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -31,4 +31,44 @@ class InsightsClientStatusAgingTest < ActiveSupport::TestCase
|
|
|
31
31
|
assert_equal InsightsClientReportStatus::NO_REPORT, @host3.get_status(InsightsClientReportStatus).status
|
|
32
32
|
assert_equal InsightsClientReportStatus::NO_REPORT, @host4.get_status(InsightsClientReportStatus).status
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
test 'aging job does not affect USER_OMITTED hosts' do
|
|
36
|
+
# Host 1: USER_OMITTED with old reported_at (should stay USER_OMITTED)
|
|
37
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host1.id).update(
|
|
38
|
+
status: InsightsClientReportStatus::USER_OMITTED,
|
|
39
|
+
reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Host 2: REPORTING with old reported_at (should change to NO_REPORT)
|
|
43
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host2.id).update(
|
|
44
|
+
status: InsightsClientReportStatus::REPORTING,
|
|
45
|
+
reported_at: Time.now - InsightsClientReportStatus::REPORT_INTERVAL - 1.day
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Host 3: USER_OMITTED with recent reported_at (should stay USER_OMITTED)
|
|
49
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host3.id).update(
|
|
50
|
+
status: InsightsClientReportStatus::USER_OMITTED,
|
|
51
|
+
reported_at: Time.now - 1.day
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Host 4: REPORTING with recent reported_at (should stay REPORTING)
|
|
55
|
+
InsightsClientReportStatus.find_or_initialize_by(host_id: @host4.id).update(
|
|
56
|
+
status: InsightsClientReportStatus::REPORTING,
|
|
57
|
+
reported_at: Time.now - 1.day
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
action = create_and_plan_action(InsightsCloud::Async::InsightsClientStatusAging)
|
|
61
|
+
run_action(action)
|
|
62
|
+
|
|
63
|
+
@hosts.each(&:reload)
|
|
64
|
+
|
|
65
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, @host1.get_status(InsightsClientReportStatus).status,
|
|
66
|
+
'USER_OMITTED host with old report should stay USER_OMITTED'
|
|
67
|
+
assert_equal InsightsClientReportStatus::NO_REPORT, @host2.get_status(InsightsClientReportStatus).status,
|
|
68
|
+
'REPORTING host with old report should change to NO_REPORT'
|
|
69
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, @host3.get_status(InsightsClientReportStatus).status,
|
|
70
|
+
'USER_OMITTED host with recent report should stay USER_OMITTED'
|
|
71
|
+
assert_equal InsightsClientReportStatus::REPORTING, @host4.get_status(InsightsClientReportStatus).status,
|
|
72
|
+
'REPORTING host with recent report should stay REPORTING'
|
|
73
|
+
end
|
|
34
74
|
end
|