module Katello module Applicability class ApplicableContentHelper attr_accessor :content_facet, :content_unit_class, :bound_library_instance_repos def initialize(content_facet, content_unit_class, bound_library_instance_repos) self.content_facet = content_facet self.content_unit_class = content_unit_class self.bound_library_instance_repos = bound_library_instance_repos end def calculate_and_import if self.bound_library_instance_repos.any? to_add, to_remove = applicable_differences ActiveRecord::Base.transaction do insert(to_add) unless to_add.blank? remove(to_remove) unless to_remove.blank? end end end def fetch_content_ids if self.content_unit_class == ::Katello::Erratum fetch_errata_content_ids elsif self.content_unit_class == ::Katello::Deb fetch_deb_content_ids elsif self.content_unit_class == ::Katello::ModuleStream fetch_module_stream_content_ids else fetch_rpm_content_ids end end def fetch_errata_content_ids # Query for all Errata ids that are attached to the host's applicable packages query = 'SELECT DISTINCT katello_repository_errata.erratum_id AS id FROM katello_repository_errata INNER JOIN katello_erratum_packages ON katello_repository_errata.erratum_id = katello_erratum_packages.erratum_id INNER JOIN katello_rpms ON katello_rpms.nvra = katello_erratum_packages.nvrea INNER JOIN katello_content_facet_applicable_rpms ON katello_content_facet_applicable_rpms.rpm_id = katello_rpms.id WHERE katello_content_facet_applicable_rpms.content_facet_id = :content_facet_id AND katello_repository_errata.repository_id IN (:repo_ids)' return Katello::Erratum.find_by_sql([query, { content_facet_id: content_facet.id, repo_ids: self.bound_library_instance_repos }]).map(&:id) end def fetch_module_stream_content_ids # Query for all applicable module stream ids query = 'SELECT DISTINCT katello_repository_module_streams.module_stream_id AS id FROM katello_repository_module_streams INNER JOIN katello_module_stream_rpms ON katello_repository_module_streams.module_stream_id = katello_module_stream_rpms.module_stream_id INNER JOIN katello_rpms ON katello_rpms.id = katello_module_stream_rpms.rpm_id INNER JOIN katello_content_facet_applicable_rpms ON katello_content_facet_applicable_rpms.rpm_id = katello_rpms.id WHERE katello_content_facet_applicable_rpms.content_facet_id = :content_facet_id AND katello_repository_module_streams.repository_id IN (:repo_ids)' return Katello::ModuleStream.find_by_sql([query, { content_facet_id: content_facet.id, repo_ids: self.bound_library_instance_repos }]).map(&:id) end def fetch_deb_content_ids repo_deb = ::Katello::RepositoryDeb.arel_table deb = ::Katello::Deb.arel_table installed_deb = Katello::InstalledDeb.arel_table host_installed_deb = Katello::HostInstalledDeb.arel_table deb_version_compare = Arel::Nodes::NamedFunction.new('deb_version_cmp', [deb[:version], installed_deb[:version]]).gt(0) content = deb.join(repo_deb).on(repo_deb[:deb_id].eq(deb[:id])) .join(installed_deb).on(installed_deb[:name].eq(deb[:name])) .join(host_installed_deb).on(host_installed_deb[:installed_deb_id].eq(installed_deb[:id])) .where(deb_version_compare) .where(host_installed_deb[:host_id].eq(self.content_facet.host.id)) .distinct .project(deb[Arel.star]) ::Katello::Deb.find_by_sql(content.to_sql).pluck(:id) end def fetch_rpm_content_ids enabled_module_stream_ids = fetch_enabled_module_stream_ids ::Katello::Rpm. joins("INNER JOIN katello_repository_rpms ON katello_rpms.id = katello_repository_rpms.rpm_id"). joins("INNER JOIN katello_installed_packages ON katello_rpms.name = katello_installed_packages.name AND katello_rpms.arch = katello_installed_packages.arch AND katello_rpms.evr > katello_installed_packages.evr AND katello_installed_packages.id in (#{newest_distinct_installed_packages_query})"). joins("LEFT JOIN katello_module_stream_rpms ON katello_rpms.id = katello_module_stream_rpms.rpm_id"). joins("INNER JOIN katello_host_installed_packages ON katello_installed_packages.id = katello_host_installed_packages.installed_package_id"). where("katello_repository_rpms.repository_id in (:bound_library_repos)", :bound_library_repos => self.bound_library_instance_repos). where("katello_host_installed_packages.host_id = :content_facet_id", :content_facet_id => self.content_facet.host.id). where("(katello_module_stream_rpms.module_stream_id IS NULL AND katello_installed_packages.id NOT IN (:locked_modular_installed_packages)) OR (katello_module_stream_rpms.module_stream_id IN (:enabled_module_streams) AND katello_installed_packages.id IN (:locked_modular_installed_packages))", :enabled_module_streams => enabled_module_stream_ids, :locked_modular_installed_packages => locked_modular_installed_packages(enabled_module_stream_ids)).pluck(:id).uniq end def fetch_enabled_module_stream_ids # Query for applicable RPM ids # -> Include all non-modular rpms or rpms that exist within installed module streams ::Katello::ModuleStream. joins("inner join katello_available_module_streams on katello_module_streams.name = katello_available_module_streams.name and katello_module_streams.stream = katello_available_module_streams.stream and katello_module_streams.context = katello_available_module_streams.context"). joins("inner join katello_host_available_module_streams on katello_available_module_streams.id = katello_host_available_module_streams.available_module_stream_id"). where("katello_host_available_module_streams.host_id = :content_facet_id and katello_host_available_module_streams.status = 'enabled'", :content_facet_id => self.content_facet.host.id).select(:id) end # Installed packages that are locked for the host due to enabled module stream membership def locked_modular_installed_packages(enabled_module_streams) rpms_in_enabled_module_streams = ::Katello::Rpm. joins("INNER JOIN katello_module_stream_rpms ON katello_rpms.id = katello_module_stream_rpms.rpm_id"). where("katello_module_stream_rpms.module_stream_id IN (:enabled_module_streams)", :enabled_module_streams => enabled_module_streams).select(:nvra, :epoch) ::Katello::InstalledPackage.where(nvra: rpms_in_enabled_module_streams.map(&:nvra), epoch: rpms_in_enabled_module_streams.map(&:epoch)).select(:id) end def newest_distinct_installed_packages_query "SELECT DISTINCT ON (katello_installed_packages.name, katello_installed_packages.arch) katello_installed_packages.id " \ "FROM katello_installed_packages INNER JOIN " \ "katello_host_installed_packages ON " \ "katello_installed_packages.id = " \ "katello_host_installed_packages.installed_package_id " \ "WHERE katello_host_installed_packages.host_id = " \ "#{content_facet.host.id} ORDER BY katello_installed_packages.name, katello_installed_packages.arch, katello_installed_packages.evr DESC" end def applicable_differences ActiveRecord::Base.connection.uncached do consumer_ids = content_facet.send(applicable_units).pluck("#{content_unit_class.table_name}.id") content_ids = fetch_content_ids to_remove = consumer_ids - content_ids to_add = content_ids - consumer_ids [to_add, to_remove] end end def insert(applicable_ids) unless applicable_ids.empty? upserts = applicable_ids.collect do |applicable_id| { content_unit_association_id => applicable_id, :content_facet_id => content_facet.id } end content_facet_association_class.upsert_all(upserts, unique_by: [content_unit_association_id, :content_facet_id]) end end def remove(applicable_ids) content_facet_association_class.where(:content_facet_id => content_facet.id, content_unit_association_id => applicable_ids).delete_all end def content_unit_association_id "#{content_unit_class.name.demodulize.underscore}_id".to_sym end def content_facet_association_class # Example: ContentFacetErratum self.content_unit_class.content_facet_association_class end def content_units content_unit_class.name.demodulize.pluralize.underscore.to_sym end def applicable_units "applicable_#{content_units}".to_sym end end end end