foreman_rh_cloud 7.0.46 → 8.0.47
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/foreman_inventory_upload/uploads_settings_controller.rb +1 -0
- data/app/models/insights_missing_host.rb +5 -0
- data/db/migrate/20230515140211_add_missing_hosts_table.foreman_rh_cloud.rb +13 -0
- data/lib/foreman_inventory_upload/async/remove_insights_hosts_job.rb +68 -0
- data/lib/foreman_inventory_upload/generators/tags.rb +3 -5
- data/lib/foreman_inventory_upload.rb +6 -1
- data/lib/foreman_rh_cloud/engine.rb +2 -1
- data/lib/foreman_rh_cloud/settings.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/inventory_sync/async/host_result.rb +5 -0
- data/lib/inventory_sync/async/inventory_hosts_sync.rb +30 -1
- data/lib/inventory_sync/async/inventory_scheduled_sync.rb +13 -2
- data/lib/tasks/hybrid_cloud.rake +6 -5
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +11 -3
- data/test/factories/insights_factories.rb +8 -0
- data/test/factories/inventory_upload_factories.rb +15 -2
- data/test/jobs/inventory_hosts_sync_test.rb +32 -0
- data/test/jobs/inventory_scheduled_sync_test.rb +12 -0
- data/test/jobs/remove_insights_hosts_job_test.rb +90 -0
- data/test/test_plugin_helper.rb +17 -0
- data/test/unit/slice_generator_test.rb +15 -9
- data/test/unit/tags_generator_test.rb +22 -7
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/AdvancedSettingsConstants.js +7 -0
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettingsSelectors.js +3 -0
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/__snapshots__/InventorySettings.test.js.snap +4 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4c84356ce25f9683795e8adb7ab42d223cef0a4ba84a4a8d9f5836cc6b80986
|
4
|
+
data.tar.gz: 5f86cd9c8240aff463e25620a5e0bb47fbc15ab7318647f40918d98a85513ab8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a7765f56a4d7e695dd253b17fd87820e8455fbe95a23450e2577125f65f20acc813723a2ff036c81d6b41c4d75d1bcf3b3aea65fb1b4042b916a68a117340a5
|
7
|
+
data.tar.gz: d8e8db10a0fc49b9741d38f93e5d59917f85b7ac6fcc8e3007dcb84346ccd15c07bd644a91de1a5ea38942cbb5b27553d5a7c59315e317fe79d1b13b542151bd
|
@@ -6,6 +6,7 @@ module ForemanInventoryUpload
|
|
6
6
|
hostObfuscationEnabled: Setting[:obfuscate_inventory_hostnames],
|
7
7
|
ipsObfuscationEnabled: Setting[:obfuscate_inventory_ips],
|
8
8
|
excludePackagesEnabled: Setting[:exclude_installed_packages],
|
9
|
+
allowAutoInsightsMismatchDelete: Setting[:allow_auto_insights_mismatch_delete],
|
9
10
|
CloudConnectorStatus: ForemanInventoryUpload::UploadsSettingsController.cloud_connector_status,
|
10
11
|
lastSyncTask: last_successful_inventory_sync_task,
|
11
12
|
}, status: :ok
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddMissingHostsTable < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :insights_missing_hosts do |t|
|
6
|
+
t.integer :organization_id
|
7
|
+
t.string :name
|
8
|
+
t.string :insights_id
|
9
|
+
t.string :rhsm_id
|
10
|
+
t.string :ip_address
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Async
|
3
|
+
class RemoveInsightsHostsJob < ::Actions::EntryAction
|
4
|
+
include ForemanRhCloud::CertAuth
|
5
|
+
|
6
|
+
def plan(search_term, organization_id)
|
7
|
+
plan_self(search_term: search_term, organization_id: organization_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
logger.debug("Attempting to remove hosts by search term: #{search_term}")
|
12
|
+
|
13
|
+
host_uuids = InsightsMissingHost.search_for(search_term).pluck(:insights_id)
|
14
|
+
|
15
|
+
page_number = 1
|
16
|
+
while (current_page = host_uuids.paginate(page: page_number, per_page: page_size)).present?
|
17
|
+
logger.debug("Removing #{(page_number - 1) * page_size} - #{page_number * page_size}/#{current_page.total_entries} hosts: #{current_page.join(',')}")
|
18
|
+
response = delete_page(current_page, organization)
|
19
|
+
# write the response in case we want to track it later
|
20
|
+
output["response_page#{page_number}"] = response.body
|
21
|
+
|
22
|
+
# remove host records that reported success after deletion
|
23
|
+
if response.code >= 200 && response.code < 300
|
24
|
+
remove_host_records(current_page)
|
25
|
+
else
|
26
|
+
error! "Cloud responded with code: #{response.code}"
|
27
|
+
end
|
28
|
+
|
29
|
+
page_number += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def logger
|
34
|
+
Foreman::Logging.logger('background')
|
35
|
+
end
|
36
|
+
|
37
|
+
def search_term
|
38
|
+
input[:search_term]
|
39
|
+
end
|
40
|
+
|
41
|
+
def organization
|
42
|
+
@organization ||= Organization.find_by(id: input[:organization_id])
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete_page(host_uuids, organization)
|
46
|
+
execute_cloud_request(
|
47
|
+
organization: organization,
|
48
|
+
method: :delete,
|
49
|
+
url: ForemanInventoryUpload.hosts_by_ids_url(host_uuids),
|
50
|
+
headers: {
|
51
|
+
content_type: :json,
|
52
|
+
}
|
53
|
+
)
|
54
|
+
rescue RestClient::ExceptionWithResponse => error_response
|
55
|
+
error_response.response
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_host_records(uuids)
|
59
|
+
InsightsMissingHost.where(insights_id: uuids).delete_all
|
60
|
+
end
|
61
|
+
|
62
|
+
def page_size
|
63
|
+
# the_most_conservative_url_size_limit(2083) / uri_size(36) with some spares for the domain name
|
64
|
+
40
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -45,11 +45,9 @@ module ForemanInventoryUpload
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def content_data
|
48
|
-
[
|
49
|
-
|
50
|
-
|
51
|
-
] +
|
52
|
-
(@host.activation_keys || []).map { |item| ['activation_key', item.name] }
|
48
|
+
(@host.lifecycle_environments.uniq || []).map { |item| ['lifecycle_environment', item.name] } +
|
49
|
+
(@host.activation_keys || []).map { |item| ['activation_key', item.name] } +
|
50
|
+
(@host.content_views || []).map { |item| ['content_view', item.name] }
|
53
51
|
end
|
54
52
|
|
55
53
|
def satellite_server_data
|
@@ -71,7 +71,7 @@ module ForemanInventoryUpload
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def self.inventory_base_url
|
74
|
-
ForemanRhCloud.cert_base_url
|
74
|
+
"#{ForemanRhCloud.cert_base_url}/api/inventory/v1/hosts"
|
75
75
|
end
|
76
76
|
|
77
77
|
def self.inventory_export_url
|
@@ -82,4 +82,9 @@ module ForemanInventoryUpload
|
|
82
82
|
def self.inventory_self_url
|
83
83
|
inventory_base_url + "?hostname_or_id=#{ForemanRhCloud.foreman_host.fqdn}"
|
84
84
|
end
|
85
|
+
|
86
|
+
def self.hosts_by_ids_url(host_ids)
|
87
|
+
host_ids_string = host_ids.join(',')
|
88
|
+
"#{inventory_base_url}/#{host_ids_string}"
|
89
|
+
end
|
85
90
|
end
|
@@ -127,7 +127,8 @@ module ForemanRhCloud
|
|
127
127
|
|
128
128
|
extend_page 'hosts/_list' do |context|
|
129
129
|
context.with_profile :cloud, _('RH Cloud'), default: true do
|
130
|
-
add_pagelet :hosts_table_column_header, key: :insights_recommendations_count, label: _('Recommendations'), sortable: true, width: '12%', class: 'hidden-xs ellipsis', priority: 100
|
130
|
+
add_pagelet :hosts_table_column_header, key: :insights_recommendations_count, label: _('Recommendations'), sortable: true, width: '12%', class: 'hidden-xs ellipsis', priority: 100,
|
131
|
+
export_data: CsvExporter::ExportDefinition.new(:insights_recommendations_count, callback: ->(host) { host&.insights_hits&.count })
|
131
132
|
add_pagelet :hosts_table_column_content, key: :insights_recommendations_count, callback: ->(host) { hits_counts_cell(host) }, class: 'hidden-xs ellipsis text-center', priority: 100
|
132
133
|
end
|
133
134
|
end
|
@@ -2,6 +2,7 @@ Foreman::SettingManager.define(:foreman) do
|
|
2
2
|
category(:rh_cloud, N_('RHCloud')) do
|
3
3
|
setting('allow_auto_inventory_upload', type: :boolean, description: N_('Enable automatic upload of your host inventory to the Red Hat cloud'), default: true, full_name: N_('Automatic inventory upload'))
|
4
4
|
setting('allow_auto_insights_sync', type: :boolean, description: N_('Enable automatic synchronization of Insights recommendations from the Red Hat cloud'), default: false, full_name: N_('Synchronize recommendations Automatically'))
|
5
|
+
setting('allow_auto_insights_mismatch_delete', type: :boolean, description: N_('Enable automatic deletion of mismatched host records from the Red Hat cloud'), default: false, full_name: N_('Automatic mismatch deletion'))
|
5
6
|
setting('obfuscate_inventory_hostnames', type: :boolean, description: N_('Obfuscate host names sent to the Red Hat cloud'), default: false, full_name: N_('Obfuscate host names'))
|
6
7
|
setting('obfuscate_inventory_ips', type: :boolean, description: N_('Obfuscate ipv4 addresses sent to the Red Hat cloud'), default: false, full_name: N_('Obfuscate host ipv4 addresses'))
|
7
8
|
setting('exclude_installed_packages', type: :boolean, description: N_('Exclude installed packages from being uploaded to the Red Hat cloud'), default: false, full_name: N_("Exclude installed Packages"))
|
@@ -12,6 +12,7 @@ module InventorySync
|
|
12
12
|
@sub_ids = result["results"].map { |host| host['subscription_manager_id'] }
|
13
13
|
@uuid_by_sub_id = Hash[result["results"].map { |host| [host['subscription_manager_id'], host['id']] }]
|
14
14
|
@uuid_by_fqdn = Hash[result["results"].map { |host| [host['fqdn']&.downcase, host['id']] }]
|
15
|
+
@results = result["results"]
|
15
16
|
end
|
16
17
|
|
17
18
|
def status_hashes
|
@@ -42,6 +43,10 @@ module InventorySync
|
|
42
43
|
@host_uuids ||= Hash[@sub_ids.map { |sub_id| [host_id(sub_id), @uuid_by_sub_id[sub_id]] }].except(nil)
|
43
44
|
end
|
44
45
|
|
46
|
+
def missing_hosts
|
47
|
+
@results.select { |host| hosts[host['subscription_manager_id']].nil? }
|
48
|
+
end
|
49
|
+
|
45
50
|
def percentage
|
46
51
|
ratio = @per_page * @page * 1.0 / @total * 100
|
47
52
|
ratio > 100 ? 100 : ratio.truncate(2)
|
@@ -1,8 +1,11 @@
|
|
1
1
|
module InventorySync
|
2
2
|
module Async
|
3
3
|
class InventoryHostsSync < QueryInventoryJob
|
4
|
+
MAX_IP_STRING_SIZE = 254
|
5
|
+
|
4
6
|
set_callback :iteration, :around, :setup_facet_transaction
|
5
7
|
set_callback :step, :around, :create_facets
|
8
|
+
set_callback :step, :around, :create_missing_hosts
|
6
9
|
|
7
10
|
def plan(organizations)
|
8
11
|
# by default the tasks will be executed concurrently
|
@@ -11,7 +14,7 @@ module InventorySync
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def setup_facet_transaction
|
14
|
-
|
17
|
+
ActiveRecord::Base.transaction do
|
15
18
|
yield
|
16
19
|
end
|
17
20
|
end
|
@@ -23,6 +26,17 @@ module InventorySync
|
|
23
26
|
results
|
24
27
|
end
|
25
28
|
|
29
|
+
def create_missing_hosts
|
30
|
+
results = yield
|
31
|
+
missing_hosts = results.missing_hosts.map { |host| to_missing_host_record(host, results.organization) }
|
32
|
+
# remove records that are no longer in the query results
|
33
|
+
InsightsMissingHost.
|
34
|
+
where.not(insights_id: missing_hosts.map { |host_hash| host_hash[:insights_id] }).
|
35
|
+
where(organization_id: results.organization.id).delete_all
|
36
|
+
# readd new hosts that appear in the results, but the subscription_id is missing from the DB.
|
37
|
+
InsightsMissingHost.upsert_all(missing_hosts) if missing_hosts.present?
|
38
|
+
end
|
39
|
+
|
26
40
|
def rescue_strategy_for_self
|
27
41
|
Dynflow::Action::Rescue::Fail
|
28
42
|
end
|
@@ -42,6 +56,21 @@ module InventorySync
|
|
42
56
|
InsightsFacet.upsert_all(all_facets, unique_by: :host_id) unless all_facets.empty?
|
43
57
|
end
|
44
58
|
|
59
|
+
def to_missing_host_record(host_result, organization)
|
60
|
+
{
|
61
|
+
name: host_result['fqdn'],
|
62
|
+
insights_id: host_result['id'],
|
63
|
+
rhsm_id: host_result['subscription_manager_id'],
|
64
|
+
ip_address: to_ip_address_string(host_result['ip_addresses']),
|
65
|
+
organization_id: organization.id,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_ip_address_string(ip_addresses)
|
70
|
+
string_size = 0
|
71
|
+
ip_addresses.take_while { |address| (string_size += address.length) <= MAX_IP_STRING_SIZE }
|
72
|
+
end
|
73
|
+
|
45
74
|
def plan_self_host_sync
|
46
75
|
plan_action InventorySync::Async::InventorySelfHostSync
|
47
76
|
end
|
@@ -14,8 +14,14 @@ module InventorySync
|
|
14
14
|
end
|
15
15
|
|
16
16
|
after_delay do
|
17
|
-
|
18
|
-
|
17
|
+
# perform a sequence of sync then delete in parallel for all organizations
|
18
|
+
concurrence do
|
19
|
+
Organization.unscoped.each do |org|
|
20
|
+
sequence do
|
21
|
+
plan_org_sync(org)
|
22
|
+
plan_remove_insights_hosts(org) if Setting[:allow_auto_insights_mismatch_delete]
|
23
|
+
end
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -24,6 +30,11 @@ module InventorySync
|
|
24
30
|
plan_action InventoryFullSync, org
|
25
31
|
end
|
26
32
|
|
33
|
+
def plan_remove_insights_hosts
|
34
|
+
# plan a remove hosts action with search set to empty (all records)
|
35
|
+
plan_action(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', org.id)
|
36
|
+
end
|
37
|
+
|
27
38
|
def logger
|
28
39
|
action_logger
|
29
40
|
end
|
data/lib/tasks/hybrid_cloud.rake
CHANGED
@@ -22,9 +22,9 @@ namespace :rh_cloud do |args|
|
|
22
22
|
exit(1)
|
23
23
|
end
|
24
24
|
|
25
|
-
organization = Organization.find_by(id: ENV['org_id'].to_i) # saw this coming in as a string, so making sure it gets passed as an integer.
|
26
|
-
|
27
|
-
@
|
25
|
+
@organization = Organization.find_by(id: ENV['org_id'].to_i) # saw this coming in as a string, so making sure it gets passed as an integer.
|
26
|
+
@uid = cp_owner_id(@organization)
|
27
|
+
@hostname = ForemanRhCloud.foreman_host_name
|
28
28
|
logger.error('Organization provided does not have a manifest imported.') + exit(1) if @uid.nil?
|
29
29
|
|
30
30
|
puts 'Paste your token, output will be hidden.'
|
@@ -39,7 +39,8 @@ namespace :rh_cloud do |args|
|
|
39
39
|
|
40
40
|
def payload
|
41
41
|
{
|
42
|
-
"uid": @uid
|
42
|
+
"uid": @uid,
|
43
|
+
"display_name": "#{@hostname}+#{@organization.label}"
|
43
44
|
}
|
44
45
|
end
|
45
46
|
|
@@ -49,7 +50,7 @@ namespace :rh_cloud do |args|
|
|
49
50
|
|
50
51
|
begin
|
51
52
|
response = execute_cloud_request(
|
52
|
-
organization: organization,
|
53
|
+
organization: @organization,
|
53
54
|
method: method,
|
54
55
|
url: registrations_url,
|
55
56
|
headers: headers,
|
data/package.json
CHANGED
@@ -110,7 +110,7 @@ module InsightsCloud::Api
|
|
110
110
|
User.current = User.find_by(login: 'secret_admin')
|
111
111
|
|
112
112
|
@env = FactoryBot.create(:katello_k_t_environment)
|
113
|
-
|
113
|
+
@env2 = FactoryBot.create(:katello_k_t_environment, organization: @env.organization)
|
114
114
|
|
115
115
|
@host = FactoryBot.create(
|
116
116
|
:host,
|
@@ -118,8 +118,16 @@ module InsightsCloud::Api
|
|
118
118
|
:with_content,
|
119
119
|
:with_hostgroup,
|
120
120
|
:with_parameter,
|
121
|
-
|
122
|
-
|
121
|
+
content_view_environments: [
|
122
|
+
FactoryBot.create(
|
123
|
+
:katello_content_view_environment,
|
124
|
+
content_view: FactoryBot.create(:katello_content_view, organization: @env.organization),
|
125
|
+
lifecycle_environment: @env),
|
126
|
+
FactoryBot.create(
|
127
|
+
:katello_content_view_environment,
|
128
|
+
content_view: FactoryBot.create(:katello_content_view, organization: @env.organization),
|
129
|
+
lifecycle_environment: @env2),
|
130
|
+
],
|
123
131
|
organization: @env.organization
|
124
132
|
)
|
125
133
|
|
@@ -41,6 +41,14 @@ FactoryBot.define do
|
|
41
41
|
resolution_risk { 1 }
|
42
42
|
resolution_type { 'fix' }
|
43
43
|
end
|
44
|
+
|
45
|
+
factory :insights_missing_host do
|
46
|
+
organization { association :organization }
|
47
|
+
sequence(:name) { |n| "removed.host#{n}.test" }
|
48
|
+
insights_id { Foreman.uuid }
|
49
|
+
rhsm_id { Foreman.uuid }
|
50
|
+
ip_address { "192.168.1.1,192.168.2.1" }
|
51
|
+
end
|
44
52
|
end
|
45
53
|
|
46
54
|
FactoryBot.modify do
|
@@ -33,6 +33,13 @@ FactoryBot.define do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
FactoryBot.define do
|
37
|
+
factory :katello_content_view_environment, :class => Katello::ContentViewEnvironment do
|
38
|
+
sequence(:name) { |n| "name#{n}" }
|
39
|
+
sequence(:label) { |n| "label#{n}" }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
36
43
|
FactoryBot.define do
|
37
44
|
factory :katello_k_t_environment, :aliases => [:katello_environment], :class => Katello::KTEnvironment do
|
38
45
|
sequence(:name) { |n| "Environment#{n}" }
|
@@ -93,6 +100,7 @@ FactoryBot.modify do
|
|
93
100
|
content_view { nil }
|
94
101
|
lifecycle_environment { nil }
|
95
102
|
content_source { nil }
|
103
|
+
content_view_environments { [] }
|
96
104
|
end
|
97
105
|
|
98
106
|
trait :with_content do
|
@@ -100,9 +108,14 @@ FactoryBot.modify do
|
|
100
108
|
|
101
109
|
after(:build) do |host, evaluator|
|
102
110
|
if host.content_facet
|
103
|
-
|
104
|
-
|
111
|
+
if evaluator.content_view && evaluator.lifecycle_environment
|
112
|
+
host.content_facet.assign_single_environment(
|
113
|
+
content_view_id: evaluator.content_view.id,
|
114
|
+
lifecycle_environment_id: evaluator.lifecycle_environment.id
|
115
|
+
)
|
116
|
+
end
|
105
117
|
host.content_facet.content_source = evaluator.content_source if evaluator.content_source
|
118
|
+
host.content_facet.content_view_environments = evaluator.content_view_environments unless evaluator.content_view_environments.empty?
|
106
119
|
end
|
107
120
|
end
|
108
121
|
end
|
@@ -294,4 +294,36 @@ class InventoryHostsSyncTest < ActiveSupport::TestCase
|
|
294
294
|
|
295
295
|
assert_nil @host2.insights
|
296
296
|
end
|
297
|
+
|
298
|
+
test 'Inventory should create new missing host records' do
|
299
|
+
org = FactoryBot.create(:organization)
|
300
|
+
|
301
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:query_inventory).returns(@inventory)
|
302
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:plan_self_host_sync)
|
303
|
+
|
304
|
+
setup_certs_expectation do
|
305
|
+
InventorySync::Async::InventoryHostsSync.any_instance.stubs(:candlepin_id_cert)
|
306
|
+
end
|
307
|
+
|
308
|
+
ForemanTasks.sync_task(InventorySync::Async::InventoryHostsSync, [org])
|
309
|
+
|
310
|
+
assert_equal 2, InsightsMissingHost.count
|
311
|
+
end
|
312
|
+
|
313
|
+
test 'Inventory should remove old missing host records' do
|
314
|
+
org = FactoryBot.create(:organization)
|
315
|
+
|
316
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:query_inventory).returns(@inventory)
|
317
|
+
InventorySync::Async::InventoryHostsSync.any_instance.expects(:plan_self_host_sync)
|
318
|
+
|
319
|
+
FactoryBot.create(:insights_missing_host, organization: org)
|
320
|
+
|
321
|
+
setup_certs_expectation do
|
322
|
+
InventorySync::Async::InventoryHostsSync.any_instance.stubs(:candlepin_id_cert)
|
323
|
+
end
|
324
|
+
|
325
|
+
ForemanTasks.sync_task(InventorySync::Async::InventoryHostsSync, [org])
|
326
|
+
|
327
|
+
assert_equal 2, InsightsMissingHost.count
|
328
|
+
end
|
297
329
|
end
|
@@ -6,8 +6,10 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
|
|
6
6
|
|
7
7
|
test 'Schedules an execution if auto upload is enabled' do
|
8
8
|
Setting[:allow_auto_inventory_upload] = true
|
9
|
+
Setting[:allow_auto_insights_mismatch_delete] = true
|
9
10
|
|
10
11
|
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
|
12
|
+
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_remove_insights_hosts).times(Organization.unscoped.count)
|
11
13
|
|
12
14
|
ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
|
13
15
|
end
|
@@ -19,4 +21,14 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
|
|
19
21
|
|
20
22
|
ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
|
21
23
|
end
|
24
|
+
|
25
|
+
test 'Skips mismatch deletion if the setting is disabled' do
|
26
|
+
Setting[:allow_auto_inventory_upload] = true
|
27
|
+
Setting[:allow_auto_insights_mismatch_delete] = false
|
28
|
+
|
29
|
+
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
|
30
|
+
InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_remove_insights_hosts).never
|
31
|
+
|
32
|
+
ForemanTasks.sync_task(InventorySync::Async::InventoryScheduledSync)
|
33
|
+
end
|
22
34
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
require 'foreman_tasks/test_helpers'
|
3
|
+
|
4
|
+
class RemoveInsightsHostJobTest < ActiveSupport::TestCase
|
5
|
+
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
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 host records on cloud success' do
|
15
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.expects(:execute_cloud_request).returns(
|
16
|
+
mock_response
|
17
|
+
)
|
18
|
+
|
19
|
+
FactoryBot.create(:insights_missing_host, organization: @org)
|
20
|
+
|
21
|
+
task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
|
22
|
+
|
23
|
+
assert_equal 0, InsightsMissingHost.count
|
24
|
+
assert_equal 'success', task.result
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'Does not delete hosts on cloud failure' do
|
28
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.expects(:execute_cloud_request).raises(
|
29
|
+
RestClient::Exceptions::EXCEPTIONS_MAP.fetch(500).new(mock_response(code: 500), 500)
|
30
|
+
)
|
31
|
+
|
32
|
+
FactoryBot.create(:insights_missing_host, organization: @org)
|
33
|
+
|
34
|
+
begin
|
35
|
+
ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
|
36
|
+
rescue ForemanTasks::TaskError => ex
|
37
|
+
task = ex.task
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_equal 1, InsightsMissingHost.count
|
41
|
+
assert_equal 'error', task.result
|
42
|
+
end
|
43
|
+
|
44
|
+
test 'Paginates the hosts list' do
|
45
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.stubs(:page_size).returns(1)
|
46
|
+
|
47
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.expects(:execute_cloud_request).returns(
|
48
|
+
mock_response(body: 'response2')
|
49
|
+
)
|
50
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.expects(:execute_cloud_request).returns(
|
51
|
+
mock_response(body: 'response1')
|
52
|
+
)
|
53
|
+
|
54
|
+
FactoryBot.create(:insights_missing_host, organization: @org)
|
55
|
+
FactoryBot.create(:insights_missing_host, organization: @org)
|
56
|
+
|
57
|
+
task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, '', @org.id)
|
58
|
+
|
59
|
+
assert_equal 0, InsightsMissingHost.count
|
60
|
+
assert_equal 'success', task.result
|
61
|
+
assert_equal 'response1', task.output[:response_page1]
|
62
|
+
assert_equal 'response2', task.output[:response_page2]
|
63
|
+
end
|
64
|
+
|
65
|
+
test 'Uses scoped_search to select hosts' do
|
66
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.stubs(:page_size).returns(1)
|
67
|
+
|
68
|
+
# Since the request is paginated per 1 host, I would expect only one call to execute_cloud_request
|
69
|
+
ForemanInventoryUpload::Async::RemoveInsightsHostsJob.any_instance.expects(:execute_cloud_request).returns(
|
70
|
+
mock_response(body: 'response1')
|
71
|
+
)
|
72
|
+
|
73
|
+
FactoryBot.create(:insights_missing_host, name: 'test a', organization: @org)
|
74
|
+
FactoryBot.create(:insights_missing_host, name: 'test b', organization: @org)
|
75
|
+
|
76
|
+
task = ForemanTasks.sync_task(ForemanInventoryUpload::Async::RemoveInsightsHostsJob, 'name ~ b', @org.id)
|
77
|
+
|
78
|
+
assert_equal 1, InsightsMissingHost.count
|
79
|
+
assert_equal 'test a', InsightsMissingHost.first.name
|
80
|
+
assert_equal 'success', task.result
|
81
|
+
assert_equal 'response1', task.output[:response_page1]
|
82
|
+
end
|
83
|
+
|
84
|
+
def mock_response(code: 200, body: '')
|
85
|
+
response = mock('response')
|
86
|
+
response.stubs(:code).returns(code)
|
87
|
+
response.stubs(:body).returns(body)
|
88
|
+
response
|
89
|
+
end
|
90
|
+
end
|
data/test/test_plugin_helper.rb
CHANGED
@@ -8,6 +8,13 @@ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories
|
|
8
8
|
# FactoryBot.definition_file_paths << "#{Katello::Engine.root}/test/factories"
|
9
9
|
FactoryBot.reload
|
10
10
|
|
11
|
+
begin
|
12
|
+
require 'pry-rescue/minitest'
|
13
|
+
require 'pry-byebug'
|
14
|
+
rescue LoadError
|
15
|
+
# if the extension is not loaded - please continue, no harm done.
|
16
|
+
end
|
17
|
+
|
11
18
|
module FolderIsolation
|
12
19
|
extend ActiveSupport::Concern
|
13
20
|
|
@@ -100,3 +107,13 @@ module MockForemanHostname
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
end
|
110
|
+
|
111
|
+
module CandlepinIsolation
|
112
|
+
extend ActiveSupport::Concern
|
113
|
+
|
114
|
+
included do
|
115
|
+
setup do
|
116
|
+
Katello::Host::SubscriptionFacet.any_instance.stubs(:backend_update_needed?).returns(false)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -2,6 +2,7 @@ require 'test_plugin_helper'
|
|
2
2
|
|
3
3
|
class SliceGeneratorTest < ActiveSupport::TestCase
|
4
4
|
include KatelloLocationFix
|
5
|
+
include CandlepinIsolation
|
5
6
|
|
6
7
|
setup do
|
7
8
|
User.current = User.find_by(login: 'secret_admin')
|
@@ -30,7 +31,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
30
31
|
subscription: FactoryBot.create(:katello_subscription, organization_id: env.organization.id)
|
31
32
|
)
|
32
33
|
@host.interfaces.first.identifier = 'test_nic1'
|
33
|
-
@host.save!
|
34
|
+
@host.interfaces.first.save!
|
34
35
|
|
35
36
|
ForemanInventoryUpload::Generators::Queries.instance_variable_set(:@fact_names, nil)
|
36
37
|
end
|
@@ -300,6 +301,11 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
300
301
|
|
301
302
|
test 'generates a report with satellite facts' do
|
302
303
|
hostgroup = FactoryBot.create(:hostgroup, name: 'Special"name')
|
304
|
+
# Don't try to update CP in tests
|
305
|
+
Katello::Resources::Candlepin::Consumer.stubs(:update)
|
306
|
+
# Don't try update facts for the host
|
307
|
+
Katello::Host::SubscriptionFacet.stubs(:update_facts)
|
308
|
+
|
303
309
|
@host.hostgroup = hostgroup
|
304
310
|
@host.save!
|
305
311
|
|
@@ -326,7 +332,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
326
332
|
actual_host = actual['hosts'].first
|
327
333
|
assert_tag('satellite-id', actual_host, 'satellite_instance_id')
|
328
334
|
assert_tag(@host.organization_id.to_s, actual_host, 'organization_id')
|
329
|
-
assert_tag(@host.
|
335
|
+
assert_tag(@host.content_views.first.name, actual_host, 'content_view')
|
330
336
|
assert_tag(@host.location.name, actual_host, 'location')
|
331
337
|
assert_tag(@host.organization.name, actual_host, 'organization')
|
332
338
|
assert_tag(@host.hostgroup.name, actual_host, 'hostgroup')
|
@@ -348,13 +354,13 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
348
354
|
:host,
|
349
355
|
:with_subscription,
|
350
356
|
:with_content,
|
351
|
-
content_view: @host.
|
352
|
-
lifecycle_environment: @host.
|
357
|
+
content_view: @host.content_views.first,
|
358
|
+
lifecycle_environment: @host.lifecycle_environments.first,
|
353
359
|
organization: @host.organization
|
354
360
|
)
|
355
361
|
|
356
362
|
@host.subscription_facet.hypervisor_host = hypervisor_host
|
357
|
-
@host.save!
|
363
|
+
@host.subscription_facet.save!
|
358
364
|
|
359
365
|
batch = Host.where(id: @host.id).in_batches.first
|
360
366
|
generator = create_generator(batch)
|
@@ -497,8 +503,8 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
497
503
|
:host,
|
498
504
|
:with_subscription,
|
499
505
|
:with_content,
|
500
|
-
content_view: @host.
|
501
|
-
lifecycle_environment: @host.
|
506
|
+
content_view: @host.content_views.first,
|
507
|
+
lifecycle_environment: @host.lifecycle_environments.first,
|
502
508
|
organization: new_org
|
503
509
|
)
|
504
510
|
|
@@ -644,8 +650,8 @@ class SliceGeneratorTest < ActiveSupport::TestCase
|
|
644
650
|
:host,
|
645
651
|
:with_subscription,
|
646
652
|
:with_content,
|
647
|
-
content_view: @host.
|
648
|
-
lifecycle_environment: @host.
|
653
|
+
content_view: @host.content_views.first,
|
654
|
+
lifecycle_environment: @host.lifecycle_environments.first,
|
649
655
|
organization: @host.organization,
|
650
656
|
installed_packages: [installed_package]
|
651
657
|
)
|
@@ -2,12 +2,13 @@ require 'test_plugin_helper'
|
|
2
2
|
|
3
3
|
class TagsGeneratorTest < ActiveSupport::TestCase
|
4
4
|
include KatelloLocationFix
|
5
|
+
include CandlepinIsolation
|
5
6
|
|
6
7
|
setup do
|
7
8
|
User.current = User.find_by(login: 'secret_admin')
|
8
9
|
|
9
10
|
env = FactoryBot.create(:katello_k_t_environment)
|
10
|
-
|
11
|
+
env2 = FactoryBot.create(:katello_k_t_environment, organization: env.organization)
|
11
12
|
|
12
13
|
@location1 = FactoryBot.create(:location)
|
13
14
|
@location2 = FactoryBot.create(:location, parent: @location1)
|
@@ -20,15 +21,27 @@ class TagsGeneratorTest < ActiveSupport::TestCase
|
|
20
21
|
:redhat,
|
21
22
|
:with_subscription,
|
22
23
|
:with_content,
|
23
|
-
content_view: cv.first,
|
24
|
-
lifecycle_environment: env,
|
25
24
|
organization: env.organization,
|
26
25
|
location: @location2,
|
27
|
-
hostgroup: @hostgroup2
|
26
|
+
hostgroup: @hostgroup2,
|
27
|
+
content_view_environments: [
|
28
|
+
FactoryBot.create(
|
29
|
+
:katello_content_view_environment,
|
30
|
+
content_view: FactoryBot.create(:katello_content_view, organization: env.organization),
|
31
|
+
lifecycle_environment: env),
|
32
|
+
FactoryBot.create(
|
33
|
+
:katello_content_view_environment,
|
34
|
+
content_view: FactoryBot.create(:katello_content_view, organization: env.organization),
|
35
|
+
lifecycle_environment: env2),
|
36
|
+
]
|
28
37
|
)
|
29
38
|
|
30
39
|
@host.organization.pools << FactoryBot.create(:katello_pool, account_number: '1234', cp_id: 1)
|
31
40
|
@host.interfaces.first.identifier = 'test_nic1'
|
41
|
+
# Don't try to update CP in tests
|
42
|
+
Katello::Resources::Candlepin::Consumer.stubs(:update)
|
43
|
+
# Don't try update facts for the host
|
44
|
+
Katello::Host::SubscriptionFacet.stubs(:update_facts)
|
32
45
|
@host.save!
|
33
46
|
end
|
34
47
|
|
@@ -46,8 +59,10 @@ class TagsGeneratorTest < ActiveSupport::TestCase
|
|
46
59
|
assert_nil actual['host collection']
|
47
60
|
|
48
61
|
assert_equal @host.organization.name, actual['organization'].first.last
|
49
|
-
assert_equal @host.
|
50
|
-
assert_equal @host.
|
62
|
+
assert_equal @host.lifecycle_environments.pluck(:name).min, actual['lifecycle_environment'].map(&:second).min
|
63
|
+
assert_equal @host.lifecycle_environments.pluck(:name).max, actual['lifecycle_environment'].map(&:second).max
|
64
|
+
assert_equal @host.content_views.pluck(:name).min, actual['content_view'].map(&:second).min
|
65
|
+
assert_equal @host.content_views.pluck(:name).max, actual['content_view'].map(&:second).max
|
51
66
|
assert_equal Foreman.instance_id, actual['satellite_instance_id'].first.last
|
52
67
|
assert_equal @host.organization_id.to_s, actual['organization_id'].first.last
|
53
68
|
end
|
@@ -55,7 +70,7 @@ class TagsGeneratorTest < ActiveSupport::TestCase
|
|
55
70
|
test 'filters tags with empty values' do
|
56
71
|
generator = create_generator
|
57
72
|
|
58
|
-
@host.stubs(:
|
73
|
+
@host.stubs(:content_views).returns([])
|
59
74
|
|
60
75
|
actual = generator.generate.group_by { |key, value| key }
|
61
76
|
|
@@ -25,4 +25,11 @@ export const settingsDict = {
|
|
25
25
|
'Exclude installed packages from being uploaded to the Red Hat cloud'
|
26
26
|
),
|
27
27
|
},
|
28
|
+
allowAutoInsightsMismatchDelete: {
|
29
|
+
name: 'allow_auto_insights_mismatch_delete',
|
30
|
+
label: __('Automatic mismatch deletion'),
|
31
|
+
tooltip: __(
|
32
|
+
'Enable automatic deletion of mismatched host records from the Red Hat cloud'
|
33
|
+
),
|
34
|
+
},
|
28
35
|
};
|
@@ -23,5 +23,9 @@ exports[`InventorySettings rendering render without Props 1`] = `
|
|
23
23
|
key="excludePackagesEnabled"
|
24
24
|
setting="excludePackagesEnabled"
|
25
25
|
/>
|
26
|
+
<AdvancedSetting
|
27
|
+
key="allowAutoInsightsMismatchDelete"
|
28
|
+
setting="allowAutoInsightsMismatchDelete"
|
29
|
+
/>
|
26
30
|
</div>
|
27
31
|
`;
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_rh_cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 8.0.47
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Red Hat Cloud team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: katello
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- app/models/insights_client_report_status.rb
|
142
142
|
- app/models/insights_facet.rb
|
143
143
|
- app/models/insights_hit.rb
|
144
|
+
- app/models/insights_missing_host.rb
|
144
145
|
- app/models/insights_resolution.rb
|
145
146
|
- app/models/insights_rule.rb
|
146
147
|
- app/models/inventory_sync/inventory_status.rb
|
@@ -183,6 +184,7 @@ files:
|
|
183
184
|
- db/migrate/20211027000001_create_task_output.foreman_rh_cloud.rb
|
184
185
|
- db/migrate/20220321000001_add_unique_to_insights_rules.foreman_rh_cloud.rb
|
185
186
|
- db/migrate/20221102110254_fix_rh_cloud_settings_category_to_dsl.rb
|
187
|
+
- db/migrate/20230515140211_add_missing_hosts_table.foreman_rh_cloud.rb
|
186
188
|
- db/seeds.d/179_ui_notifications.rb
|
187
189
|
- db/seeds.d/50_job_templates.rb
|
188
190
|
- lib/foreman_inventory_upload.rb
|
@@ -192,6 +194,7 @@ files:
|
|
192
194
|
- lib/foreman_inventory_upload/async/generate_report_job.rb
|
193
195
|
- lib/foreman_inventory_upload/async/progress_output.rb
|
194
196
|
- lib/foreman_inventory_upload/async/queue_for_upload_job.rb
|
197
|
+
- lib/foreman_inventory_upload/async/remove_insights_hosts_job.rb
|
195
198
|
- lib/foreman_inventory_upload/async/shell_process.rb
|
196
199
|
- lib/foreman_inventory_upload/async/upload_report_job.rb
|
197
200
|
- lib/foreman_inventory_upload/generators/archived_report.rb
|
@@ -256,6 +259,7 @@ files:
|
|
256
259
|
- test/jobs/inventory_hosts_sync_test.rb
|
257
260
|
- test/jobs/inventory_scheduled_sync_test.rb
|
258
261
|
- test/jobs/inventory_self_host_sync_test.rb
|
262
|
+
- test/jobs/remove_insights_hosts_job_test.rb
|
259
263
|
- test/jobs/upload_report_job_test.rb
|
260
264
|
- test/models/insights_client_report_status_test.rb
|
261
265
|
- test/test_plugin_helper.rb
|
@@ -692,6 +696,7 @@ test_files:
|
|
692
696
|
- test/jobs/inventory_hosts_sync_test.rb
|
693
697
|
- test/jobs/inventory_scheduled_sync_test.rb
|
694
698
|
- test/jobs/inventory_self_host_sync_test.rb
|
699
|
+
- test/jobs/remove_insights_hosts_job_test.rb
|
695
700
|
- test/jobs/upload_report_job_test.rb
|
696
701
|
- test/models/insights_client_report_status_test.rb
|
697
702
|
- test/test_plugin_helper.rb
|