foreman_rh_cloud 13.2.8 → 13.2.10
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/insights_cloud/api/machine_telemetries_controller.rb +13 -10
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/app/models/concerns/rh_cloud_host.rb +4 -2
- 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/generators/fact_helpers.rb +26 -4
- data/lib/foreman_inventory_upload.rb +8 -1
- data/lib/foreman_rh_cloud/engine.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/foreman_rh_cloud.rb +36 -9
- data/lib/insights_cloud/async/insights_generate_notifications.rb +10 -1
- data/lib/inventory_sync/async/inventory_full_sync.rb +39 -3
- data/lib/inventory_sync/async/inventory_self_host_sync.rb +12 -2
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +56 -2
- data/test/controllers/insights_cloud/candlepin_proxies_extensions_test.rb +70 -0
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/insights_client_status_aging_test.rb +40 -0
- data/test/jobs/insights_generate_notifications_test.rb +26 -0
- data/test/jobs/inventory_full_sync_test.rb +212 -0
- data/test/jobs/inventory_self_host_sync_test.rb +9 -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/foreman_rh_cloud_self_host_test.rb +50 -2
- data/test/unit/metadata_generator_test.rb +24 -1
- data/test/unit/rh_cloud_host_test.rb +60 -0
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +24 -2
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +73 -10
- 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/ForemanRhCloudHelpers.js +22 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +27 -1
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +134 -22
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4301f698357e719f6ba5d08677bdeaf3ccefb995835dcf86f3e253cb0c01d599
|
|
4
|
+
data.tar.gz: 77f3bc1ef3e85cc1d8988a53f2cd5fdcd65b99a9fb6d6da1cc5f366bbfebc6d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 000fad5efc5a8bd42e03045e33bc10795a5d658aa3ef1098e6f95fa2a0476630cc85fe6b7b40929e817a4ca0d166fd88c8073b4faa972b72a84d0a146eb7aa70
|
|
7
|
+
data.tar.gz: 06fe93496e585831f593b8d31223577eb236adf7d1e4d90342cd203ddc9fbf9a80e9b58636b033fa04adefc806885feba8326a4e34dc38844bbaacd083df9c91
|
|
@@ -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
|
|
@@ -32,14 +32,10 @@ module InsightsCloud::Api
|
|
|
32
32
|
rescue RestClient::ExceptionWithResponse => e
|
|
33
33
|
response_obj = e.response.presence || e.exception
|
|
34
34
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:error => response_obj.to_s,
|
|
40
|
-
:headers => {},
|
|
41
|
-
:response => response_obj,
|
|
42
|
-
}, status: code
|
|
35
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
36
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
37
|
+
|
|
38
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
43
39
|
rescue StandardError => e
|
|
44
40
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
45
41
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -88,9 +84,16 @@ module InsightsCloud::Api
|
|
|
88
84
|
private
|
|
89
85
|
|
|
90
86
|
def ensure_telemetry_enabled_for_consumer
|
|
91
|
-
|
|
87
|
+
config = telemetry_config(@host)
|
|
88
|
+
|
|
89
|
+
# Always update the status based on current parameter value
|
|
90
|
+
# This will set USER_OMITTED if parameter is false, or REPORTING/NO_REPORT if true
|
|
91
|
+
@host.get_status(InsightsClientReportStatus).refresh!
|
|
92
|
+
@host.refresh_global_status!
|
|
93
|
+
|
|
94
|
+
unless config
|
|
92
95
|
logger.debug("Rejected telemetry forwarding for host #{@host.name}, insights param is set to: #{config}")
|
|
93
|
-
render_message
|
|
96
|
+
return render_message('Telemetry is not enabled for this host', :status => 403)
|
|
94
97
|
end
|
|
95
98
|
config
|
|
96
99
|
end
|
|
@@ -36,14 +36,10 @@ module InsightsCloud
|
|
|
36
36
|
rescue RestClient::ExceptionWithResponse => e
|
|
37
37
|
response_obj = e.response.presence || e.exception
|
|
38
38
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
39
|
-
|
|
39
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
40
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
40
41
|
|
|
41
|
-
return render
|
|
42
|
-
:message => message,
|
|
43
|
-
:error => response_obj.to_s,
|
|
44
|
-
:headers => {},
|
|
45
|
-
:response => response_obj,
|
|
46
|
-
}, status: code
|
|
42
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
47
43
|
rescue StandardError => e
|
|
48
44
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
49
45
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -15,12 +15,14 @@ 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
|
+
:sync => ::InventorySync::InventoryStatus::SYNC,
|
|
25
|
+
:user_omitted => ::InventorySync::InventoryStatus::USER_OMITTED }
|
|
24
26
|
scoped_search :on => :id, :rename => :insights_uuid, :only_explicit => true,
|
|
25
27
|
:ext_method => :search_by_insights_uuid, :complete_value => false
|
|
26
28
|
|
|
@@ -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
|
|
@@ -114,6 +114,7 @@ module ForemanInventoryUpload
|
|
|
114
114
|
|
|
115
115
|
def host_ips(host)
|
|
116
116
|
# Determines and returns the IP addresses associated with a host, applying obfuscation if enabled.
|
|
117
|
+
return {} if host.nil?
|
|
117
118
|
|
|
118
119
|
# If IP obfuscation is enabled for the host return a representation of obfuscated IP addresses.
|
|
119
120
|
return obfuscated_ips(host) if obfuscate_ips?(host)
|
|
@@ -163,10 +164,29 @@ module ForemanInventoryUpload
|
|
|
163
164
|
|
|
164
165
|
def hostname_match
|
|
165
166
|
bash_hostname = `uname -n`.chomp
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
foreman_host = ForemanRhCloud.foreman_host
|
|
168
|
+
|
|
169
|
+
# If bash hostname matches foreman_host, use fqdn
|
|
170
|
+
if foreman_host && bash_hostname == foreman_host.name
|
|
171
|
+
return fqdn(foreman_host)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# If no foreman_host, try foreman_host_name from Setting[:foreman_url]
|
|
175
|
+
unless foreman_host
|
|
176
|
+
foreman_hostname_from_setting = ForemanRhCloud.foreman_host_name
|
|
177
|
+
if foreman_hostname_from_setting
|
|
178
|
+
# Apply obfuscation if enabled
|
|
179
|
+
return obfuscate_fqdn(foreman_hostname_from_setting) if Setting[:obfuscate_inventory_hostnames]
|
|
180
|
+
|
|
181
|
+
return foreman_hostname_from_setting
|
|
182
|
+
end
|
|
183
|
+
# Otherwise fall through to bash_hostname below
|
|
184
|
+
# NOTE: Containerized foremanctl setups must configure Setting[:foreman_url]
|
|
185
|
+
# as bash hostname may not be available or meaningful in containers
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Fallback to bash hostname (with obfuscation if enabled)
|
|
189
|
+
if Setting[:obfuscate_inventory_hostnames]
|
|
170
190
|
obfuscate_fqdn(bash_hostname)
|
|
171
191
|
else
|
|
172
192
|
bash_hostname
|
|
@@ -174,6 +194,8 @@ module ForemanInventoryUpload
|
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def bios_uuid(host)
|
|
197
|
+
return nil if host.nil?
|
|
198
|
+
|
|
177
199
|
value = fact_value(host, 'dmi::system::uuid') || ''
|
|
178
200
|
uuid_value(value)
|
|
179
201
|
end
|
|
@@ -96,7 +96,14 @@ module ForemanInventoryUpload
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def self.inventory_self_url
|
|
99
|
-
|
|
99
|
+
host = ForemanRhCloud.foreman_host
|
|
100
|
+
hostname = host ? host.fqdn : ForemanRhCloud.foreman_host_name
|
|
101
|
+
if hostname.nil?
|
|
102
|
+
Rails.logger.warn("Cannot determine Foreman hostname for inventory sync. " \
|
|
103
|
+
"Please configure Setting[:foreman_url]. " \
|
|
104
|
+
"Containerized setups must explicitly set this.")
|
|
105
|
+
end
|
|
106
|
+
inventory_base_url + "?hostname_or_id=#{hostname}"
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
def self.host_by_id_url(host_uuid)
|
|
@@ -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
|
data/lib/foreman_rh_cloud.rb
CHANGED
|
@@ -84,23 +84,50 @@ module ForemanRhCloud
|
|
|
84
84
|
|
|
85
85
|
# For testing purposes we can override the default hostname with an environment variable SATELLITE_RH_CLOUD_FOREMAN_HOST
|
|
86
86
|
def self.foreman_host
|
|
87
|
-
@foreman_host
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return @foreman_host if defined?(@foreman_host)
|
|
88
|
+
|
|
89
|
+
fullname = foreman_host_name
|
|
90
|
+
return @foreman_host = nil unless fullname
|
|
91
|
+
|
|
92
|
+
# Try fullname first
|
|
93
|
+
host = ::Host.unscoped.friendly.where(name: fullname).first
|
|
94
|
+
|
|
95
|
+
# If not found, try shortname
|
|
96
|
+
if host.nil?
|
|
92
97
|
shortname = /(?<shortname>[^\.]*)\.?.*/.match(fullname)[:shortname]
|
|
93
|
-
::Host.unscoped.friendly.
|
|
98
|
+
host = ::Host.unscoped.friendly.where(name: shortname).first
|
|
94
99
|
end
|
|
100
|
+
|
|
101
|
+
@foreman_host = host
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
def self.foreman_host_name
|
|
98
|
-
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name ||
|
|
105
|
+
ENV['SATELLITE_RH_CLOUD_FOREMAN_HOST'] || marked_foreman_host&.name || foreman_url_hostname
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.foreman_url_hostname
|
|
109
|
+
return nil unless Setting[:foreman_url]
|
|
110
|
+
|
|
111
|
+
begin
|
|
112
|
+
# Ensure setting is a string to avoid TypeError from URI.parse
|
|
113
|
+
url = Setting[:foreman_url].to_s
|
|
114
|
+
URI.parse(url).host
|
|
115
|
+
rescue URI::InvalidURIError, ArgumentError, TypeError => e
|
|
116
|
+
Rails.logger.warn("Invalid foreman_url setting: #{e.message}")
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
99
119
|
end
|
|
100
120
|
|
|
101
121
|
def self.marked_foreman_host
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
# Find host with infrastructure_facet.foreman_instance = true
|
|
123
|
+
# Facets use a special mechanism in Foreman, so we query the facet table directly
|
|
124
|
+
return nil unless defined?(HostFacets::InfrastructureFacet)
|
|
125
|
+
|
|
126
|
+
facet = HostFacets::InfrastructureFacet.find_by(foreman_instance: true)
|
|
127
|
+
facet&.host
|
|
128
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
129
|
+
# Table might not exist yet during migrations
|
|
130
|
+
Rails.logger.debug("Could not query marked foreman host: #{e.message}")
|
|
104
131
|
nil
|
|
105
132
|
end
|
|
106
133
|
|
|
@@ -13,7 +13,16 @@ module InsightsCloud
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def add_satellite_notifications
|
|
16
|
-
|
|
16
|
+
host = foreman_host
|
|
17
|
+
|
|
18
|
+
# Skip if no Foreman host record exists
|
|
19
|
+
unless host
|
|
20
|
+
logger.debug("Skipping Insights notifications: no Foreman host record found")
|
|
21
|
+
blueprint&.notifications&.destroy_all
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
hits_count = InsightsHit.where(host_id: host.id).count
|
|
17
26
|
|
|
18
27
|
# Remove stale notifications
|
|
19
28
|
blueprint.notifications.destroy_all
|
|
@@ -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
|
|
@@ -4,7 +4,14 @@ module InventorySync
|
|
|
4
4
|
set_callback :step, :around, :create_facets
|
|
5
5
|
|
|
6
6
|
def plan
|
|
7
|
-
|
|
7
|
+
host = ForemanRhCloud.foreman_host
|
|
8
|
+
|
|
9
|
+
if host.nil?
|
|
10
|
+
logger.warn("Skipping self-host inventory sync: no Foreman host record found.")
|
|
11
|
+
return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
super(host.organization)
|
|
8
15
|
end
|
|
9
16
|
|
|
10
17
|
def create_facets
|
|
@@ -22,7 +29,10 @@ module InventorySync
|
|
|
22
29
|
private
|
|
23
30
|
|
|
24
31
|
def add_missing_insights_facet(uuids_hash)
|
|
25
|
-
|
|
32
|
+
host = ForemanRhCloud.foreman_host
|
|
33
|
+
return unless host # Guard against nil
|
|
34
|
+
|
|
35
|
+
facet = InsightsFacet.find_or_create_by(host_id: host.id) do |facet|
|
|
26
36
|
facet.uuid = uuids_hash.values.first
|
|
27
37
|
end
|
|
28
38
|
|
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)
|
|
@@ -153,8 +154,22 @@ module InsightsCloud::Api
|
|
|
153
154
|
|
|
154
155
|
get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
|
|
155
156
|
assert_equal 500, @response.status
|
|
156
|
-
assert_equal
|
|
157
|
-
|
|
157
|
+
assert_equal @body, @response.body
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
test "should forward JSON error responses without double-escaping" do
|
|
161
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
162
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
163
|
+
net_http_resp['content-type'] = 'application/json'
|
|
164
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
165
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
166
|
+
|
|
167
|
+
get :forward_request, params: { "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }
|
|
168
|
+
assert_equal 404, @response.status
|
|
169
|
+
assert_includes @response.content_type, 'application/json'
|
|
170
|
+
assert_equal json_error, @response.body
|
|
171
|
+
parsed = JSON.parse(@response.body)
|
|
172
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
158
173
|
end
|
|
159
174
|
|
|
160
175
|
test "should create insights facet" do
|
|
@@ -170,6 +185,45 @@ module InsightsCloud::Api
|
|
|
170
185
|
|
|
171
186
|
assert_not_nil InsightsFacet.find_by(host_id: @host.id)
|
|
172
187
|
end
|
|
188
|
+
|
|
189
|
+
test "should update InsightsClientReportStatus when parameter is false and block request" do
|
|
190
|
+
# Remove the common parameter from setup and set it to false
|
|
191
|
+
CommonParameter.where(name: InsightsCloud.enable_client_param).delete_all
|
|
192
|
+
FactoryBot.create(:common_parameter, name: InsightsCloud.enable_client_param, key_type: 'boolean', value: false)
|
|
193
|
+
|
|
194
|
+
# Stub telemetry_config to return false (disabled)
|
|
195
|
+
InsightsCloud::Api::MachineTelemetriesController.any_instance.stubs(:telemetry_config).returns(false)
|
|
196
|
+
|
|
197
|
+
get :forward_request, params: { "path" => "platform/ingress/v1/upload" }
|
|
198
|
+
|
|
199
|
+
# Request should be blocked
|
|
200
|
+
assert_response 403
|
|
201
|
+
assert_equal 'Telemetry is not enabled for this host', JSON.parse(@response.body)['message']
|
|
202
|
+
|
|
203
|
+
# But status should be updated to USER_OMITTED
|
|
204
|
+
@host.reload
|
|
205
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
206
|
+
assert insights_status.persisted?, 'InsightsClientReportStatus should be created'
|
|
207
|
+
assert_equal InsightsClientReportStatus::USER_OMITTED, insights_status.status
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
test "should update InsightsClientReportStatus on successful upload" do
|
|
211
|
+
# Stub telemetry_config to return true (enabled) - common param from setup works for this
|
|
212
|
+
InsightsCloud::Api::MachineTelemetriesController.any_instance.stubs(:telemetry_config).returns(true)
|
|
213
|
+
|
|
214
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
|
215
|
+
res = RestClient::Response.create('response body', net_http_resp, @http_req)
|
|
216
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:forward_request).returns(res)
|
|
217
|
+
|
|
218
|
+
get :forward_request, params: { "path" => "/redhat_access/r/insights/uploads/" }
|
|
219
|
+
|
|
220
|
+
assert_response :success
|
|
221
|
+
|
|
222
|
+
# Status should have been refreshed during the request
|
|
223
|
+
@host.reload
|
|
224
|
+
insights_status = @host.get_status(InsightsClientReportStatus)
|
|
225
|
+
assert insights_status.persisted?, 'InsightsClientReportStatus should be created and persisted'
|
|
226
|
+
end
|
|
173
227
|
end
|
|
174
228
|
|
|
175
229
|
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
|