foreman_rh_cloud 14.1.1 → 14.1.3
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/api/machine_telemetries_controller.rb +4 -8
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +19 -5
- data/lib/foreman_inventory_upload/async/destroy_organization_hbi_hosts_job.rb +49 -0
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +26 -4
- data/lib/foreman_inventory_upload.rb +12 -1
- data/lib/foreman_rh_cloud/engine.rb +1 -0
- data/lib/foreman_rh_cloud/organization_destroy_extensions.rb +10 -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_self_host_sync.rb +12 -2
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +16 -2
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/destroy_organization_hbi_hosts_job_test.rb +63 -0
- data/test/jobs/insights_generate_notifications_test.rb +26 -0
- data/test/jobs/inventory_self_host_sync_test.rb +9 -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/organization_destroy_extensions_test.rb +50 -0
- data/test/unit/services/foreman_rh_cloud/url_remediations_retriever_test.rb +82 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/EmptyResults.test.js +10 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/EmptyState.test.js +13 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/ErrorState.test.js +20 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/ListItem.test.js +31 -8
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/ListItemStatus.test.js +26 -10
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountList.test.js +33 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +55 -35
- data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/FileDownload.test.js +13 -9
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilter.test.js +12 -15
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js +32 -12
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +14 -7
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorButton.test.js +47 -18
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.test.js +58 -15
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButton.test.js +23 -9
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonSelectors.test.js +19 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +25 -37
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/ScheduledRun.test.js +28 -8
- data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/StatusChart.test.js +25 -8
- data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/TabContainer.test.js +11 -9
- data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/TabFooter.test.js +11 -9
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/InventoryAutoUpload.test.js +33 -12
- data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +21 -8
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsActions.test.js +61 -47
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +48 -4
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js +126 -35
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableSelectors.test.js +90 -24
- data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +79 -21
- data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncActions.test.js +31 -6
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js +42 -9
- data/webpack/__tests__/ForemanRhCloudHelpers.test.js +91 -53
- data/webpack/common/Switcher/__tests__/HelpLabel.test.js +25 -10
- data/webpack/common/Switcher/__tests__/SwitcherPF4.test.js +41 -10
- metadata +9 -95
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/__snapshots__/EmptyResults.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/__snapshots__/EmptyState.test.js.snap +0 -25
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/__snapshots__/ErrorState.test.js.snap +0 -20
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +0 -47
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +0 -59
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListActions.test.js +0 -34
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListIntegration.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListSelectors.test.js +0 -25
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +0 -49
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +0 -86
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +0 -75
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +0 -46
- data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/__snapshots__/FileDownload.test.js.snap +0 -26
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterActions.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterReducer.test.js +0 -28
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterSelectors.test.js +0 -21
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilter.test.js.snap +0 -21
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterActions.test.js.snap +0 -17
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterReducer.test.js.snap +0 -19
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterSelectors.test.js.snap +0 -9
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -43
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/AdvancedSettingActions.test.js +0 -9
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/__snapshots__/AdvancedSettingActions.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/InventorySettingsActions.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/__snapshots__/InventorySettingsActions.test.js.snap +0 -26
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +0 -68
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorActions.test.js +0 -9
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorActions.test.js.snap +0 -11
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorButton.test.js.snap +0 -59
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/__snapshots__/SettingsWarning.test.js.snap +0 -32
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButton.test.js.snap +0 -15
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonSelectors.test.js.snap +0 -3
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +0 -58
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/__snapshots__/ScheduledRun.test.js.snap +0 -23
- data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/__snapshots__/StatusChart.test.js.snap +0 -74
- data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/__snapshots__/TabContainer.test.js.snap +0 -18
- data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/__snapshots__/TabFooter.test.js.snap +0 -12
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/__snapshots__/InventoryAutoUpload.test.js.snap +0 -96
- data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryUpload.test.js +0 -10
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +0 -5
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryUpload.test.js.snap +0 -14
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsReducer.test.js +0 -33
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsSelectors.test.js +0 -21
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsActions.test.js.snap +0 -65
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsReducer.test.js.snap +0 -19
- data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsSelectors.test.js.snap +0 -9
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap +0 -131
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableSelectors.test.js.snap +0 -87
- data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +0 -10
- data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncHelpers.test.js +0 -9
- data/webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncActions.test.js.snap +0 -15
- data/webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncHelpers.test.js.snap +0 -3
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js +0 -19
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js +0 -26
- data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js +0 -13
- data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap +0 -34
- data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap +0 -56
- data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap +0 -32
- data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabSelectors.test.js.snap +0 -18
- data/webpack/__tests__/ForemanRhCloudSelectors.test.js +0 -22
- data/webpack/__tests__/ForemanRhCloudTestHelpers.test.js +0 -20
- data/webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap +0 -19
- data/webpack/__tests__/__snapshots__/ForemanRhCloudSelectors.test.js.snap +0 -25
- data/webpack/__tests__/__snapshots__/ForemanRhCloudTestHelpers.test.js.snap +0 -39
- data/webpack/common/Switcher/__tests__/__snapshots__/HelpLabel.test.js.snap +0 -16
- data/webpack/common/Switcher/__tests__/__snapshots__/SwitcherPF4.test.js.snap +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89c6b782cc34fb3ffc4f13ad66c435c5729028df995e7bdba1c15e668a72fe89
|
|
4
|
+
data.tar.gz: 356fb55567950b341b4509f6ee39e30b24127c695b3924765070167800595f44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8bdb9ff78ed0950bd67f5bcc1483a0f2568af9a3462633ef6fc25d061bf942712d416d717f43e3dcef448dddb11cc6f7b9ad1fe62d6530ad2dbf44af3a3eb4a6
|
|
7
|
+
data.tar.gz: f55334cb175a624ae8aeb21242b609ce8af3c49061c7e09b460b46fef3d3239cab42232c0c52eb043ed345a661b331409545344f24e89e46e3fb9a782666e1bc
|
|
@@ -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}")
|
|
@@ -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}")
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
module ForemanRhCloud
|
|
2
2
|
class URLRemediationsRetriever < RemediationsRetriever
|
|
3
|
-
attr_reader :url, :
|
|
3
|
+
attr_reader :url, :headers
|
|
4
4
|
|
|
5
5
|
def initialize(url:, organization_id:, payload: '', headers: {}, logger: Logger.new(IO::NULL))
|
|
6
6
|
super(logger: logger)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
parsed_url = URI.parse(url)
|
|
9
|
+
query_params = parsed_url.query ? CGI.parse(parsed_url.query) : {}
|
|
10
|
+
hosts_param = query_params.delete('hosts')
|
|
11
|
+
|
|
12
|
+
if hosts_param.present?
|
|
13
|
+
@host_uuids = hosts_param.flat_map { |v| v.split(',') }.map(&:strip).reject(&:blank?)
|
|
14
|
+
@host_uuids = nil if @host_uuids.empty?
|
|
15
|
+
parsed_url.query = query_params.any? ? URI.encode_www_form(query_params) : nil
|
|
16
|
+
@url = parsed_url.to_s
|
|
17
|
+
else
|
|
18
|
+
@host_uuids = nil
|
|
19
|
+
@url = url
|
|
20
|
+
end
|
|
21
|
+
|
|
9
22
|
@payload = payload
|
|
10
23
|
@headers = headers
|
|
11
24
|
@organization_id = organization_id
|
|
@@ -14,7 +27,7 @@ module ForemanRhCloud
|
|
|
14
27
|
private
|
|
15
28
|
|
|
16
29
|
def query_playbook
|
|
17
|
-
logger.debug("Querying playbook at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
30
|
+
logger.debug("Querying playbook via #{method.to_s.upcase} at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
18
31
|
|
|
19
32
|
super
|
|
20
33
|
end
|
|
@@ -28,11 +41,12 @@ module ForemanRhCloud
|
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
def payload
|
|
31
|
-
|
|
44
|
+
return @host_uuids.to_json if @host_uuids.present?
|
|
45
|
+
@payload.present? ? @payload.to_json : @payload
|
|
32
46
|
end
|
|
33
47
|
|
|
34
48
|
def method
|
|
35
|
-
:get
|
|
49
|
+
@host_uuids.present? ? :post : :get
|
|
36
50
|
end
|
|
37
51
|
|
|
38
52
|
def organization
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ForemanInventoryUpload
|
|
2
|
+
module Async
|
|
3
|
+
class DestroyOrganizationHbiHostsJob < ::Actions::EntryAction
|
|
4
|
+
include ForemanRhCloud::CertAuth
|
|
5
|
+
|
|
6
|
+
def plan(organization_id)
|
|
7
|
+
plan_self(organization_id: organization_id)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
org = Organization.find_by(id: input[:organization_id])
|
|
12
|
+
unless org
|
|
13
|
+
output[:result] = _("Organization not found")
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
logger.info("Destroying all HBI hosts for organization #{org.label} (id: #{org.id})")
|
|
18
|
+
|
|
19
|
+
execute_cloud_request(
|
|
20
|
+
organization: org,
|
|
21
|
+
method: :delete,
|
|
22
|
+
url: ForemanInventoryUpload.hosts_delete_all_url,
|
|
23
|
+
headers: {
|
|
24
|
+
content_type: :json,
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
output[:result] = format(_("Successfully deleted all HBI hosts for organization %s"), org.label)
|
|
29
|
+
rescue RestClient::NotFound
|
|
30
|
+
output[:result] = format(_("No HBI hosts found for organization %s"), org&.label)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
logger.error(format(_("Failed to destroy HBI hosts for organization %s: %s"), org&.label, e.message))
|
|
33
|
+
raise
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def logger
|
|
37
|
+
Foreman::Logging.logger('background')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def rescue_strategy
|
|
41
|
+
Dynflow::Action::Rescue::Skip
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def humanized_name
|
|
45
|
+
_("Destroy HBI hosts for organization")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -106,6 +106,7 @@ module ForemanInventoryUpload
|
|
|
106
106
|
|
|
107
107
|
def host_ips(host)
|
|
108
108
|
# Determines and returns the IP addresses associated with a host, applying obfuscation if enabled.
|
|
109
|
+
return {} if host.nil?
|
|
109
110
|
|
|
110
111
|
# If IP obfuscation is enabled for the host return a representation of obfuscated IP addresses.
|
|
111
112
|
return obfuscated_ips(host) if obfuscate_ips?(host)
|
|
@@ -155,10 +156,29 @@ module ForemanInventoryUpload
|
|
|
155
156
|
|
|
156
157
|
def hostname_match
|
|
157
158
|
bash_hostname = `uname -n`.chomp
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
foreman_host = ForemanRhCloud.foreman_host
|
|
160
|
+
|
|
161
|
+
# If bash hostname matches foreman_host, use fqdn
|
|
162
|
+
if foreman_host && bash_hostname == foreman_host.name
|
|
163
|
+
return fqdn(foreman_host)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# If no foreman_host, try foreman_host_name from Setting[:foreman_url]
|
|
167
|
+
unless foreman_host
|
|
168
|
+
foreman_hostname_from_setting = ForemanRhCloud.foreman_host_name
|
|
169
|
+
if foreman_hostname_from_setting
|
|
170
|
+
# Apply obfuscation if enabled
|
|
171
|
+
return obfuscate_fqdn(foreman_hostname_from_setting) if Setting[:obfuscate_inventory_hostnames]
|
|
172
|
+
|
|
173
|
+
return foreman_hostname_from_setting
|
|
174
|
+
end
|
|
175
|
+
# Otherwise fall through to bash_hostname below
|
|
176
|
+
# NOTE: Containerized foremanctl setups must configure Setting[:foreman_url]
|
|
177
|
+
# as bash hostname may not be available or meaningful in containers
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Fallback to bash hostname (with obfuscation if enabled)
|
|
181
|
+
if Setting[:obfuscate_inventory_hostnames]
|
|
162
182
|
obfuscate_fqdn(bash_hostname)
|
|
163
183
|
else
|
|
164
184
|
bash_hostname
|
|
@@ -166,6 +186,8 @@ module ForemanInventoryUpload
|
|
|
166
186
|
end
|
|
167
187
|
|
|
168
188
|
def bios_uuid(host)
|
|
189
|
+
return nil if host.nil?
|
|
190
|
+
|
|
169
191
|
value = fact_value(host, 'dmi::system::uuid') || ''
|
|
170
192
|
uuid_value(value)
|
|
171
193
|
end
|
|
@@ -96,13 +96,24 @@ 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)
|
|
103
110
|
"#{inventory_base_url}/#{host_uuid}"
|
|
104
111
|
end
|
|
105
112
|
|
|
113
|
+
def self.hosts_delete_all_url
|
|
114
|
+
"#{inventory_base_url}/all?confirm_delete_all=true"
|
|
115
|
+
end
|
|
116
|
+
|
|
106
117
|
def self.hosts_by_ids_url(host_uuids)
|
|
107
118
|
host_ids_string = host_uuids.join(',')
|
|
108
119
|
"#{inventory_base_url}/#{host_ids_string}"
|
|
@@ -42,6 +42,7 @@ module ForemanRhCloud
|
|
|
42
42
|
::Katello::Api::Rhsm::CandlepinDynflowProxyController.include InsightsCloud::PackageProfileUploadExtensions
|
|
43
43
|
::Katello::Api::Rhsm::CandlepinProxiesController.include InsightsCloud::CandlepinProxiesExtensions
|
|
44
44
|
::Katello::RegistrationManager.singleton_class.prepend ::ForemanRhCloud::RegistrationManagerExtensions
|
|
45
|
+
::Actions::Katello::Organization::Destroy.prepend ::ForemanRhCloud::OrganizationDestroyExtensions
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module ForemanRhCloud
|
|
2
|
+
module OrganizationDestroyExtensions
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
def remove_consumers(organization)
|
|
6
|
+
plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, organization.id) if ForemanRhCloud.with_iop_smart_proxy?
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
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
|
|
@@ -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
|
@@ -154,8 +154,22 @@ module InsightsCloud::Api
|
|
|
154
154
|
|
|
155
155
|
get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
|
|
156
156
|
assert_equal 500, @response.status
|
|
157
|
-
assert_equal
|
|
158
|
-
|
|
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']
|
|
159
173
|
end
|
|
160
174
|
|
|
161
175
|
test "should create insights facet" do
|
|
@@ -177,8 +177,22 @@ module InsightsCloud
|
|
|
177
177
|
|
|
178
178
|
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
|
179
179
|
assert_equal 500, @response.status
|
|
180
|
-
assert_equal
|
|
181
|
-
|
|
180
|
+
assert_equal @body, @response.body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
test "should forward JSON error responses without double-escaping" do
|
|
184
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
185
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
186
|
+
net_http_resp['content-type'] = 'application/json'
|
|
187
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
188
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
189
|
+
|
|
190
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }, session: set_session
|
|
191
|
+
assert_equal 404, @response.status
|
|
192
|
+
assert_includes @response.content_type, 'application/json'
|
|
193
|
+
assert_equal json_error, @response.body
|
|
194
|
+
parsed = JSON.parse(@response.body)
|
|
195
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
182
196
|
end
|
|
183
197
|
|
|
184
198
|
test "should allow forward_request with nil location (Any location)" do
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class DestroyOrganizationHbiHostsJobTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing::Factories
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
|
|
10
|
+
Organization.any_instance.stubs(:manifest_expired?).returns(false)
|
|
11
|
+
@org = FactoryBot.create(:organization)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test 'Deletes all HBI hosts for organization' do
|
|
15
|
+
expected_url = ForemanInventoryUpload.hosts_delete_all_url
|
|
16
|
+
|
|
17
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).with do |params|
|
|
18
|
+
params[:organization] == @org &&
|
|
19
|
+
params[:method] == :delete &&
|
|
20
|
+
params[:url] == expected_url &&
|
|
21
|
+
params[:headers][:content_type] == :json
|
|
22
|
+
end.returns(mock_response)
|
|
23
|
+
|
|
24
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
25
|
+
action = run_action(action)
|
|
26
|
+
|
|
27
|
+
assert_match(/Successfully deleted/, action.output[:result])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test 'Handles RestClient::NotFound gracefully' do
|
|
31
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).raises(RestClient::NotFound)
|
|
32
|
+
|
|
33
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
34
|
+
action = run_action(action)
|
|
35
|
+
|
|
36
|
+
assert_match(/No HBI hosts found/, action.output[:result])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test 'Raises on other errors' do
|
|
40
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).raises(
|
|
41
|
+
RestClient::InternalServerError.new
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
45
|
+
|
|
46
|
+
assert_raises(RestClient::InternalServerError) do
|
|
47
|
+
run_action(action)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'hosts_delete_all_url returns correct format' do
|
|
52
|
+
url = ForemanInventoryUpload.hosts_delete_all_url
|
|
53
|
+
|
|
54
|
+
assert_match %r{/hosts/all\?confirm_delete_all=true$}, url
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def mock_response(code: 200, body: '')
|
|
58
|
+
response = mock('response')
|
|
59
|
+
response.stubs(:code).returns(code)
|
|
60
|
+
response.stubs(:body).returns(body)
|
|
61
|
+
response
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class InsightsGenerateNotificationsTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing::Factories
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
test 'skips notifications when foreman_host is nil' do
|
|
12
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
13
|
+
|
|
14
|
+
# Ensure blueprint exists or create it
|
|
15
|
+
NotificationBlueprint.find_or_create_by(name: 'insights_satellite_hits') do |bp|
|
|
16
|
+
bp.message = 'Test message'
|
|
17
|
+
bp.level = 'info'
|
|
18
|
+
bp.expires_in = 7.days
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
plan = ForemanTasks.sync_task(InsightsCloud::Async::InsightsGenerateNotifications)
|
|
22
|
+
|
|
23
|
+
# Should not raise an error, task completes successfully
|
|
24
|
+
assert_includes ['success', 'stopped'], plan.state, "Task should complete without error"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -106,4 +106,13 @@ class InventorySelfHostSyncTest < ActiveSupport::TestCase
|
|
|
106
106
|
|
|
107
107
|
assert_equal @host1_inventory_id, @host1.insights.uuid
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
test 'skips sync when foreman_host is nil' do
|
|
111
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
112
|
+
|
|
113
|
+
plan = ForemanTasks.sync_task(InventorySync::Async::InventorySelfHostSync)
|
|
114
|
+
|
|
115
|
+
# Task should be stopped (not executed) when host is nil, not failed
|
|
116
|
+
assert_equal 'stopped', plan.state
|
|
117
|
+
end
|
|
109
118
|
end
|
|
@@ -2,8 +2,9 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
# reset cached value
|
|
6
|
-
ForemanRhCloud.
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
7
|
+
ENV.delete('SATELLITE_RH_CLOUD_FOREMAN_HOST')
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
test 'finds host by fullname' do
|
|
@@ -32,4 +33,51 @@ class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
|
|
|
32
33
|
|
|
33
34
|
assert_equal @host, actual
|
|
34
35
|
end
|
|
36
|
+
|
|
37
|
+
test 'returns nil when host does not exist' do
|
|
38
|
+
ForemanRhCloud.expects(:foreman_host_name).returns('nonexistent.example.com')
|
|
39
|
+
|
|
40
|
+
actual = ForemanRhCloud.foreman_host
|
|
41
|
+
|
|
42
|
+
assert_nil actual
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
test 'returns nil and does not query Host when foreman_host_name is nil' do
|
|
46
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns(nil)
|
|
47
|
+
::Host.unscoped.friendly.expects(:where).never
|
|
48
|
+
|
|
49
|
+
assert_nil ForemanRhCloud.foreman_host
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test 'caches nil value to avoid repeated lookups' do
|
|
53
|
+
ForemanRhCloud.expects(:foreman_host_name).once.returns('nonexistent.example.com')
|
|
54
|
+
|
|
55
|
+
2.times { ForemanRhCloud.foreman_host }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test 'extracts hostname from foreman_url setting' do
|
|
59
|
+
Setting[:foreman_url] = 'https://satellite.example.com'
|
|
60
|
+
|
|
61
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
62
|
+
|
|
63
|
+
assert_equal 'satellite.example.com', actual
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test 'handles invalid foreman_url gracefully' do
|
|
67
|
+
# Stub Setting to return invalid URL without validation
|
|
68
|
+
Setting.stubs(:[]).with(:foreman_url).returns('not a valid url')
|
|
69
|
+
|
|
70
|
+
actual = ForemanRhCloud.foreman_url_hostname
|
|
71
|
+
|
|
72
|
+
assert_nil actual
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test 'foreman_host_name uses foreman_url when marked_foreman_host is nil' do
|
|
76
|
+
ForemanRhCloud.expects(:marked_foreman_host).returns(nil)
|
|
77
|
+
ForemanRhCloud.expects(:foreman_url_hostname).returns('satellite.example.com')
|
|
78
|
+
|
|
79
|
+
actual = ForemanRhCloud.foreman_host_name
|
|
80
|
+
|
|
81
|
+
assert_equal 'satellite.example.com', actual
|
|
82
|
+
end
|
|
35
83
|
end
|
|
@@ -2,7 +2,8 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
4
4
|
setup do
|
|
5
|
-
|
|
5
|
+
# reset cached value - must remove the variable entirely, not just set to nil
|
|
6
|
+
ForemanRhCloud.remove_instance_variable(:@foreman_host) if ForemanRhCloud.instance_variable_defined?(:@foreman_host)
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
test 'generates an empty report' do
|
|
@@ -64,4 +65,26 @@ class MetadataGeneratorTest < ActiveSupport::TestCase
|
|
|
64
65
|
assert_not_nil(slice = slices['test_12345'])
|
|
65
66
|
assert_equal 3, slice['number_hosts']
|
|
66
67
|
end
|
|
68
|
+
|
|
69
|
+
test 'generates metadata when foreman_host is nil' do
|
|
70
|
+
ForemanRhCloud.stubs(:foreman_host).returns(nil)
|
|
71
|
+
ForemanRhCloud.stubs(:foreman_host_name).returns('satellite.example.com')
|
|
72
|
+
|
|
73
|
+
generator = ForemanInventoryUpload::Generators::Metadata.new
|
|
74
|
+
|
|
75
|
+
# Should not raise an error
|
|
76
|
+
json_str = nil
|
|
77
|
+
assert_nothing_raised do
|
|
78
|
+
json_str = generator.render do
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Verify hostname is from foreman_host_name
|
|
83
|
+
actual = JSON.parse(json_str.join("\n"))
|
|
84
|
+
assert_equal 'satellite.example.com', actual['reporting_host_name']
|
|
85
|
+
# Verify IP and BIOS UUID fields are nil when host is nil
|
|
86
|
+
# This is acceptable per SAT-25889 - cloud services don't rely on these fields
|
|
87
|
+
assert_nil actual['reporting_host_ips']
|
|
88
|
+
assert_nil actual['reporting_host_bios_uuid']
|
|
89
|
+
end
|
|
67
90
|
end
|