diff --git a/app/controllers/katello/api/v2/capsule_content_controller.rb b/app/controllers/katello/api/v2/capsule_content_controller.rb index 106d4c272b7..c6de230b9be 100644 --- a/app/controllers/katello/api/v2/capsule_content_controller.rb +++ b/app/controllers/katello/api/v2/capsule_content_controller.rb @@ -26,13 +26,6 @@ def counts render json: @capsule.content_counts.to_json end - api :POST, '/capsules/:id/content/update_counts', N_('Update content counts for the smart proxy') - param :id, Integer, :desc => N_('Id of the smart proxy'), :required => true - def update_counts - task = async_task(::Actions::Katello::CapsuleContent::UpdateContentCounts, @capsule) - respond_for_async :resource => task - end - api :GET, '/capsules/:id/content/lifecycle_environments', N_('List the lifecycle environments attached to the smart proxy') param_group :lifecycle_environments def lifecycle_environments @@ -86,6 +79,24 @@ def sync respond_for_async :resource => task end + api :POST, '/capsules/:id/content/update_counts', N_('Update content counts for the smart proxy') + param :id, Integer, :desc => N_('Id of the smart proxy'), :required => true + param :environment_id, Integer, :desc => N_('Id of the environment to limit the content counting on') + param :content_view_id, Integer, :desc => N_('Id of the content view to limit the content counting on') + param :repository_id, Integer, :desc => N_('Id of the repository to limit the content counting on') + def update_counts + find_environment if params[:environment_id] + find_content_view if params[:content_view_id] + find_repository if params[:repository_id] + count_options = { + :environment_id => @environment.try(:id), + :content_view_id => @content_view.try(:id), + :repository_id => @repository.try(:id) + } + task = async_task(::Actions::Katello::CapsuleContent::UpdateContentCounts, @capsule, count_options) + respond_for_async :resource => task + end + api :GET, '/capsules/:id/content/sync', N_('Get current smart proxy synchronization status') param :id, Integer, :desc => N_('Id of the smart proxy'), :required => true param :organization_id, Integer, :desc => N_('Id of the organization to get the status for'), :required => false diff --git a/app/lib/actions/katello/capsule_content/sync_capsule.rb b/app/lib/actions/katello/capsule_content/sync_capsule.rb index b01d0e2d898..20e6ce432d2 100644 --- a/app/lib/actions/katello/capsule_content/sync_capsule.rb +++ b/app/lib/actions/katello/capsule_content/sync_capsule.rb @@ -5,7 +5,11 @@ class SyncCapsule < ::Actions::EntryAction # rubocop:disable Metrics/MethodLength execution_plan_hooks.use :update_content_counts, :on => :success def plan(smart_proxy, options = {}) - plan_self(:smart_proxy_id => smart_proxy.id) + plan_self(:smart_proxy_id => smart_proxy.id, + :environment_id => options[:environment_id], + :content_view_id => options[:content_view_id], + :repository_id => options[:repository_id], + :skip_content_counts_update => options[:skip_content_counts_update]) action_subject(smart_proxy) environment = options[:environment] content_view = options[:content_view] @@ -69,9 +73,10 @@ def repos_to_sync(smart_proxy, environment, content_view, repository, skip_metat end def update_content_counts(_execution_plan) - if Setting[:automatic_content_count_updates] + if Setting[:automatic_content_count_updates] && !input[:skip_content_counts_update] smart_proxy = ::SmartProxy.unscoped.find(input[:smart_proxy_id]) - ::ForemanTasks.async_task(::Actions::Katello::CapsuleContent::UpdateContentCounts, smart_proxy) + options = {environment_id: input[:environment_id], content_view_id: input[:content_view_id], repository_id: input[:repository_id]} + ::ForemanTasks.async_task(::Actions::Katello::CapsuleContent::UpdateContentCounts, smart_proxy, options) else Rails.logger.info "Skipping content counts update as automatic content count updates are disabled. To enable automatic content count updates, set the 'automatic_content_count_updates' setting to true. To update content counts manually, run the 'Update Content Counts' action." diff --git a/app/lib/actions/katello/capsule_content/update_content_counts.rb b/app/lib/actions/katello/capsule_content/update_content_counts.rb index 4bf846bac3f..dab47ca66e2 100644 --- a/app/lib/actions/katello/capsule_content/update_content_counts.rb +++ b/app/lib/actions/katello/capsule_content/update_content_counts.rb @@ -2,8 +2,9 @@ module Actions module Katello module CapsuleContent class UpdateContentCounts < Actions::EntryAction - def plan(smart_proxy) - plan_self(:smart_proxy_id => smart_proxy.id) + def plan(smart_proxy, options = {}) + input[:options] = options + plan_self(:smart_proxy_id => smart_proxy.id, environment_id: options[:environment_id], content_view_id: options[:content_view_id], repository_id: options[:repository_id]) end def humanized_name @@ -12,7 +13,22 @@ def humanized_name def run smart_proxy = ::SmartProxy.unscoped.find(input[:smart_proxy_id]) - smart_proxy.update_content_counts! + env = find_env(input[:environment_id]) + content_view = find_content_view(input[:content_view_id]) + repository = find_repository(input[:repository_id]) + smart_proxy.update_content_counts! env, content_view, repository + end + + def find_env(environment_id) + ::Katello::KTEnvironment.find(environment_id) if environment_id + end + + def find_content_view(content_view_id) + ::Katello::ContentView.find(content_view_id) if content_view_id + end + + def find_repository(repository_id) + ::Katello::Repository.find(repository_id) if repository_id end def rescue_strategy diff --git a/app/lib/actions/katello/content_view/capsule_sync.rb b/app/lib/actions/katello/content_view/capsule_sync.rb index e83d6c0b33e..efc9174ad4c 100644 --- a/app/lib/actions/katello/content_view/capsule_sync.rb +++ b/app/lib/actions/katello/content_view/capsule_sync.rb @@ -12,9 +12,25 @@ def plan(content_view, environment) smart_proxies = SmartProxy.unscoped.with_environment(environment).select { |sp| sp.authorized?(:manage_capsule_content) && sp.authorized?(:view_capsule_content) } unless smart_proxies.blank? plan_action(::Actions::BulkAction, ::Actions::Katello::CapsuleContent::Sync, smart_proxies.sort, - :content_view_id => content_view.id, :environment_id => environment.id) + :content_view_id => content_view.id, :environment_id => environment.id, :skip_content_counts_update => true) end end + #For Content view triggered capsule sync, we need to update content counts in one action in finalize, instead of one action per CV, per env, per smart proxy + plan_self(:content_view_id => content_view.id, :environment_id => environment.id) + end + end + + def finalize + if Setting[:automatic_content_count_updates] + environment = ::Katello::KTEnvironment.find(input[:environment_id]) + smart_proxies = SmartProxy.unscoped.with_environment(environment).select { |sp| sp.authorized?(:manage_capsule_content) && sp.authorized?(:view_capsule_content) } + options = {environment_id: input[:environment_id], content_view_id: input[:content_view_id]} + smart_proxies.each do |smart_proxy| + ::ForemanTasks.async_task(::Actions::Katello::CapsuleContent::UpdateContentCounts, smart_proxy, options) + end + else + Rails.logger.info "Skipping content counts update as automatic content count updates are disabled. To enable automatic content count updates, set the 'automatic_content_count_updates' setting to true. +To update content counts manually, run the 'Update Content Counts' action." end end end diff --git a/app/models/katello/concerns/smart_proxy_extensions.rb b/app/models/katello/concerns/smart_proxy_extensions.rb index f16f2f3c259..aa748ff21f3 100644 --- a/app/models/katello/concerns/smart_proxy_extensions.rb +++ b/app/models/katello/concerns/smart_proxy_extensions.rb @@ -140,8 +140,80 @@ def load_balanced? URI.parse(self.url).host != self.registration_host end - def update_content_counts! + def update_content_counts!(environment = nil, content_view = nil, repository = nil) # {:content_view_versions=>{87=>{:repositories=>{1=>{:metadata=>{},:counts=>{:rpms=>98, :module_streams=>9898}}}}} + if environment.nil? && content_view.nil? && repository.nil? + global_content_counts + else + smart_proxy_helper = ::Katello::SmartProxyHelper.new(self) + repos = repository ? [repository] : smart_proxy_helper.repositories_available_to_capsule(environment, content_view) + self.with_lock do + repos_content_count(repos) + end + end + end + + #{"content_view_versions"=> + # {"5"=> + # {"repositories"=> + # {"20"=>{"counts"=>{"rpm"=>32, "erratum"=>4}, "metadata"=>{"env_id"=>2, "product_id"=>1, "content_type"=>"yum", "library_instance_id"=>14}}, + # "21"=>{"counts"=>{"file"=>3}, "metadata"=>{"env_id"=>2, "product_id"=>1, "content_type"=>"file", "library_instance_id"=>15}}, + # "22"=>{"counts"=>{"file"=>3}, "metadata"=>{"env_id"=>3, "product_id"=>1, "content_type"=>"file", "library_instance_id"=>15}}, + # "23"=>{"counts"=>{"rpm"=>32, "erratum"=>4}, "metadata"=>{"env_id"=>3, "product_id"=>1, "content_type"=>"yum", "library_instance_id"=>14}}}}}} + # + + def repos_content_count(repos) + new_content_counts = initialize_content_counts + repos.each do |repo| + process_repository(repo, new_content_counts) + end + remove_unavailable_versions(new_content_counts) + update(content_counts: new_content_counts) + end + + def initialize_content_counts + (content_counts.deep_dup || { content_view_versions: {} }).with_indifferent_access + end + + def process_repository(repo, content_counts) + repo_mirror_service = repo.backend_service(self).with_mirror_adapter + repo_content_counts = repo_mirror_service.latest_content_counts + translated_counts = translate_counts(repo, repo_mirror_service, repo_content_counts) + content_counts[:content_view_versions][repo.content_view_version_id.to_s] ||= { repositories: {}} + content_counts[:content_view_versions][repo.content_view_version_id.to_s][:repositories][repo.id.to_s] = translated_counts + end + + def translate_counts(repo, repo_mirror_service, repo_content_counts) + translated_counts = {metadata: {}, counts: {}} + translated_counts[:metadata] = { + env_id: repo.environment_id, + library_instance_id: repo.library_instance_or_self.id, + product_id: repo.product_id, + content_type: repo.content_type + } + repo_content_counts&.each do |name, count| + count = count[:count] + if name == 'rpm.package' && repo.content_counts['srpm'] > 0 + translated_counts[:counts]['srpm'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::Srpm) + translated_counts[:counts]['rpm'] = count - translated_counts[:counts]['srpm'] + elsif name == 'container.manifest' && repo.content_counts['docker_manifest_list'] > 0 + translated_counts[:counts]['docker_manifest_list'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::DockerManifestList) + translated_counts[:counts]['docker_manifest'] = count - translated_counts[:counts]['docker_manifest_list'] + else + translated_counts[:counts][::Katello::Pulp3::PulpContentUnit.katello_name_from_pulpcore_name(name, repo)] = count + end + end + translated_counts + end + + def remove_unavailable_versions(content_counts) + version_ids_available_to_proxy = Katello::ContentViewVersion.in_environment(lifecycle_environments)&.pluck(:id)&.uniq + version_ids_in_count_map = content_counts[:content_view_versions].keys&.map(&:to_i) + version_ids_to_remove = version_ids_in_count_map - version_ids_available_to_proxy + version_ids_to_remove.each { |id| content_counts[:content_view_versions].delete(id.to_s) } + end + + def global_content_counts new_content_counts = { content_view_versions: {} } smart_proxy_helper = ::Katello::SmartProxyHelper.new(self) repos = smart_proxy_helper.repositories_available_to_capsule diff --git a/app/services/katello/smart_proxy_helper.rb b/app/services/katello/smart_proxy_helper.rb index 14edb3d4e98..79d45d530fd 100644 --- a/app/services/katello/smart_proxy_helper.rb +++ b/app/services/katello/smart_proxy_helper.rb @@ -47,9 +47,9 @@ def combined_repos_available_to_capsule(environment = nil, content_view = nil, r def repositories_available_to_capsule(environments = nil, content_view = nil) environments = @smart_proxy.lifecycle_environments if environments.nil? - yum_repos = Katello::Repository.in_environment(environments) - yum_repos = yum_repos.in_content_views([content_view]) if content_view - yum_repos.smart_proxy_syncable + repos = Katello::Repository.in_environment(environments) + repos = repos.in_content_views([content_view]) if content_view + repos.smart_proxy_syncable end def unsyncable_content_types diff --git a/test/actions/katello/content_view_test.rb b/test/actions/katello/content_view_test.rb index 42b9f18377f..124e5f2d915 100644 --- a/test/actions/katello/content_view_test.rb +++ b/test/actions/katello/content_view_test.rb @@ -442,7 +442,9 @@ class CapsuleSyncTest < TestBase plan_action(action, content_view, library) assert_action_planned_with(action, ::Actions::BulkAction, ::Actions::Katello::CapsuleContent::Sync, [smart_proxy_service_1.smart_proxy, smart_proxy_service_2.smart_proxy].sort, - :content_view_id => content_view.id, :environment_id => library.id) + :content_view_id => content_view.id, + :environment_id => library.id, + :skip_content_counts_update => true) end end diff --git a/webpack/scenes/SmartProxy/ExpandedSmartProxyRepositories.js b/webpack/scenes/SmartProxy/ExpandedSmartProxyRepositories.js index 8a2dd087063..9d8a84409bc 100644 --- a/webpack/scenes/SmartProxy/ExpandedSmartProxyRepositories.js +++ b/webpack/scenes/SmartProxy/ExpandedSmartProxyRepositories.js @@ -19,7 +19,6 @@ const ExpandedSmartProxyRepositories = ({ filteredData[key] = entry; } }); - return filteredData; }; const envContentCounts = filterDataByEnvId(); @@ -48,6 +47,43 @@ const ExpandedSmartProxyRepositories = ({ /* eslint-enable max-len */ return cellList; }; + + const getDataListItems = () => { + if (Object.keys(envContentCounts).length) { + return Object.keys(envContentCounts).map((repo, index) => ( + + + + + + )); + } + + if (repositories?.length) { + return repositories.map((repo, index) => ( + + + + + + )); + } + + return ( + + + ]} + /> + + + ); + }; + if (syncedToCapsule) { return ( @@ -64,24 +100,7 @@ const ExpandedSmartProxyRepositories = ({ /> - {Object.keys(envContentCounts).length ? - Object.keys(envContentCounts).map((repo, index) => ( - - - - - - )) : - - - ]} - /> - - - } + {getDataListItems()} ); } @@ -112,7 +131,7 @@ const ExpandedSmartProxyRepositories = ({ ]} + dataListCells={[]} />