Class: Katello::Host::ContentFacet

Inherits:
Model
  • Object
show all
Includes:
Facets::Base
Defined in:
app/models/katello/host/content_facet.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: Jail

Constant Summary collapse

HOST_TOOLS_PACKAGE_NAME =
'katello-host-tools'.freeze
HOST_TOOLS_TRACER_PACKAGE_NAME =
'katello-host-tools-tracer'.freeze
SUBSCRIPTION_MANAGER_PACKAGE_NAME =
'subscription-manager'.freeze
ALL_HOST_TOOLS_PACKAGE_NAMES =
[ "python-#{HOST_TOOLS_PACKAGE_NAME}",
"python3-#{HOST_TOOLS_PACKAGE_NAME}",
HOST_TOOLS_PACKAGE_NAME ].freeze
ALL_TRACER_PACKAGE_NAMES =
[ "python-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
"python3-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
HOST_TOOLS_TRACER_PACKAGE_NAME ].freeze
BOOTC_FIELD_FACT_NAMES =
[
  "bootc.booted.image",
  "bootc.booted.digest",
  "bootc.staged.image",
  "bootc.staged.digest",
  "bootc.rollback.image",
  "bootc.rollback.digest",
  "bootc.available.image",
  "bootc.available.digest",
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Model

#destroy!

Constructor Details

#initialize(*args) ⇒ ContentFacet

Returns a new instance of ContentFacet.



87
88
89
90
91
92
93
94
95
96
# File 'app/models/katello/host/content_facet.rb', line 87

def initialize(*args)
  init_args = args.first || {}
  cvenv_ids = init_args.delete(:content_view_environment_ids)&.reject(&:blank?)
  super(*args)
  if cvenv_ids.present?
    # find() preserves input order and raises on missing IDs, unlike where()
    self.content_view_environments = ContentViewEnvironment.find(cvenv_ids)
  end
  self.cvenvs_changed = false
end

Instance Attribute Details

#cvenvs_changedObject

Returns the value of attribute cvenvs_changed.



85
86
87
# File 'app/models/katello/host/content_facet.rb', line 85

def cvenvs_changed
  @cvenvs_changed
end

Class Method Details

.find_manifest_entity(digest:) ⇒ Object



348
349
350
# File 'app/models/katello/host/content_facet.rb', line 348

def self.find_manifest_entity(digest:)
  ::Katello::DockerManifestList.find_by(digest: digest) || ::Katello::DockerManifest.find_by(digest: digest)
end

.in_content_view_version_environments(version_environments) ⇒ Object



289
290
291
292
293
294
295
296
297
298
# File 'app/models/katello/host/content_facet.rb', line 289

def self.in_content_view_version_environments(version_environments)
  # takes a structure of [{:content_view_version => ContentViewVersion, :environments => [KTEnvironment]}]
  relation = self.joins(:content_view_environment_content_facets => :content_view_environment)
  queries = version_environments.map do |version_environment|
    version = version_environment[:content_view_version]
    env_ids = version_environment[:environments].map(&:id)
    "(#{::Katello::ContentViewEnvironment.table_name}.content_view_version_id = #{version.id} AND #{::Katello::ContentViewEnvironment.table_name}.environment_id IN (#{env_ids.join(',')}))"
  end
  relation.where(queries.join(" OR "))
end

.inherited_attributes(hostgroup, facet_attributes) ⇒ Object



430
431
432
433
434
435
436
437
438
439
# File 'app/models/katello/host/content_facet.rb', line 430

def self.inherited_attributes(hostgroup, facet_attributes)
  facet_attributes[:kickstart_repository_id] ||= hostgroup.inherited_kickstart_repository_id
  facet_attributes[:content_source_id] ||= hostgroup.inherited_content_source_id
  unless facet_attributes.key?(:content_view_environment_ids)
    cvenv_id = hostgroup.content_facet&.content_view_environment_id
    cvenv_id ||= hostgroup.inherited_content_view_environment_id if cvenv_id.nil? && hostgroup.ancestry.present?
    facet_attributes[:content_view_environment_ids] = [cvenv_id] if cvenv_id
  end
  facet_attributes
end

.joins_installable_debsObject



364
365
366
# File 'app/models/katello/host/content_facet.rb', line 364

def self.joins_installable_debs
  joins_installable_relation(Katello::Deb, Katello::ContentFacetApplicableDeb)
end

.joins_installable_errataObject



360
361
362
# File 'app/models/katello/host/content_facet.rb', line 360

def self.joins_installable_errata
  joins_installable_relation(Katello::Erratum, Katello::ContentFacetErratum)
end

.joins_installable_relation(content_model, facet_join_model) ⇒ Object



417
418
419
420
421
422
423
424
425
426
427
428
# File 'app/models/katello/host/content_facet.rb', line 417

def self.joins_installable_relation(content_model, facet_join_model)
  facet_repository = Katello::ContentFacetRepository.table_name
  content_table = content_model.table_name
  facet_join_table = facet_join_model.table_name
  repo_join_table = content_model.repository_association_class.table_name

  self.joins("INNER JOIN #{facet_repository} on #{facet_repository}.content_facet_id = #{table_name}.id",
             "INNER JOIN #{repo_join_table} on #{repo_join_table}.repository_id = #{facet_repository}.repository_id",
             "INNER JOIN #{content_table} on #{content_table}.id = #{repo_join_table}.#{content_model.unit_id_field}",
             "INNER JOIN #{facet_join_table} on #{facet_join_table}.#{content_model.unit_id_field} = #{content_table}.id").
       where("#{facet_join_table}.content_facet_id = #{self.table_name}.id")
end

.joins_installable_rpmsObject



368
369
370
# File 'app/models/katello/host/content_facet.rb', line 368

def self.joins_installable_rpms
  joins_installable_relation(Katello::Rpm, Katello::ContentFacetApplicableRpm)
end

.joins_repositoriesObject



372
373
374
375
376
377
378
379
380
381
382
# File 'app/models/katello/host/content_facet.rb', line 372

def self.joins_repositories
  facet_repository = Katello::ContentFacetRepository.table_name
  root_repository = Katello::RootRepository.table_name
  repository = Katello::Repository.table_name

  self.joins("INNER JOIN #{facet_repository} on #{facet_repository}.content_facet_id = #{table_name}.id",
             "INNER JOIN #{repository} on #{repository}.id = #{facet_repository}.repository_id",
             "INNER JOIN #{root_repository} on #{root_repository}.id = #{repository}.root_id",
             "INNER JOIN #{Katello::Content.table_name} on #{Katello::Content.table_name}.cp_content_id = #{root_repository}.content_id").
       where("#{facet_repository}.content_facet_id = #{self.table_name}.id")
end

.populate_fields_from_facts(host, parser, _type, _source_proxy) ⇒ Object



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'app/models/katello/host/content_facet.rb', line 323

def self.populate_fields_from_facts(host, parser, _type, _source_proxy)
  return if host.content_facet.blank?
  facet = host.content_facet || host.build_content_facet
  attrs_to_add = {}
  BOOTC_FIELD_FACT_NAMES.each do |fact_name|
    next unless parser.facts.key?(fact_name)
    fact_value = parser.facts[fact_name]
    field_name = fact_name.tr(".", "_")
    attrs_to_add[field_name] = fact_value
  end
  if attrs_to_add['bootc_booted_digest'].present?
    manifest_entity = find_manifest_entity(digest: attrs_to_add['bootc_booted_digest'])
    if manifest_entity.present?
      attrs_to_add['manifest_entity_type'] = manifest_entity.model_name.name
      attrs_to_add['manifest_entity_id'] = manifest_entity.id
    else
      # remove the association if the manifest entity is not found
      attrs_to_add['manifest_entity_type'] = nil
      attrs_to_add['manifest_entity_id'] = nil
    end
  end
  facet.assign_attributes(attrs_to_add)
  facet.save unless facet.new_record?
end

.trigger_applicability_generation(host_ids) ⇒ Object



254
255
256
257
258
# File 'app/models/katello/host/content_facet.rb', line 254

def self.trigger_applicability_generation(host_ids)
  host_ids = [host_ids] unless host_ids.is_a?(Array)
  ::Katello::ApplicableHostQueue.push_hosts(host_ids)
  ::Katello::EventQueue.push_event(::Katello::Events::GenerateHostApplicability::EVENT_TYPE, 0)
end

.with_applicable_errata(errata) ⇒ Object



352
353
354
# File 'app/models/katello/host/content_facet.rb', line 352

def self.with_applicable_errata(errata)
  self.joins(:applicable_errata).where("#{Katello::Erratum.table_name}.id" => errata)
end

.with_installable_errata(errata) ⇒ Object



356
357
358
# File 'app/models/katello/host/content_facet.rb', line 356

def self.with_installable_errata(errata)
  joins_installable_errata.where("#{Katello::Erratum.table_name}.id" => errata)
end

.with_non_installable_errata(errata, hosts = nil) ⇒ Object



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'app/models/katello/host/content_facet.rb', line 300

def self.with_non_installable_errata(errata, hosts = nil)
  content_facets = Katello::Host::ContentFacet.select(:id).where(:host_id => hosts)
  reachable_repos = ::Katello::ContentFacetRepository.where(content_facet_id: content_facets).distinct.pluck(:repository_id)
  installable_errata = ::Katello::ContentFacetErratum.select(:id).
    where(content_facet_id: content_facets).
    joins(
    "inner join #{::Katello::RepositoryErratum.table_name} ON #{Katello::ContentFacetErratum.table_name}.erratum_id = #{Katello::RepositoryErratum.table_name}.erratum_id",
    "inner JOIN #{Katello::ContentFacetRepository.table_name} "\
      "ON #{Katello::ContentFacetErratum.table_name}.content_facet_id = #{Katello::ContentFacetRepository.table_name}.content_facet_id "\
      "AND #{Katello::RepositoryErratum.table_name}.repository_id = #{Katello::ContentFacetRepository.table_name}.repository_id"
    ).
    where("#{Katello::RepositoryErratum.table_name}.repository_id" => reachable_repos).
    where("#{Katello::RepositoryErratum.table_name}.erratum_id" => errata).
    where("#{Katello::ContentFacetRepository.table_name}.repository_id" => reachable_repos).
    where("#{Katello::ContentFacetRepository.table_name}.content_facet_id" => content_facets)

  non_installable_errata = ::Katello::ContentFacetErratum.select(:content_facet_id).
    where.not(id: installable_errata).
    where(content_facet_id: content_facets, erratum_id: errata)

  Katello::Host::ContentFacet.where(id: non_installable_errata)
end

Instance Method Details

#available_releasesObject



384
385
386
387
388
# File 'app/models/katello/host/content_facet.rb', line 384

def available_releases
  self.content_view_environments.flat_map do |cvenv|
    cvenv.content_view.version(cvenv.lifecycle_environment).available_releases
  end
end

#calculate_and_import_applicabilityObject

Katello applicability



261
262
263
264
265
266
267
268
269
270
271
272
# File 'app/models/katello/host/content_facet.rb', line 261

def calculate_and_import_applicability
  bound_repos = bound_repositories.collect do |repo|
    repo.library_instance_id.nil? ? repo.id : repo.library_instance_id
  end

  ::Katello::Applicability::ApplicableContentHelper.new(self, ::Katello::Deb, bound_repos).calculate_and_import
  ::Katello::Applicability::ApplicableContentHelper.new(self, ::Katello::Rpm, bound_repos).calculate_and_import
  ::Katello::Applicability::ApplicableContentHelper.new(self, ::Katello::Erratum, bound_repos).calculate_and_import
  ::Katello::Applicability::ApplicableContentHelper.new(self, ::Katello::ModuleStream, bound_repos).calculate_and_import
  update_applicability_counts
  self.host&.refresh_statuses([::Katello::ErrataStatus, ::Katello::RhelLifecycleStatus])
end

#content_view_environment_labelsObject



161
162
163
# File 'app/models/katello/host/content_facet.rb', line 161

def content_view_environment_labels
  content_view_environments.map(&:label).join(',')
end

#content_view_environments=(new_cvenvs) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/models/katello/host/content_facet.rb', line 147

def content_view_environments=(new_cvenvs)
  if new_cvenvs.length > 1 && !Setting['allow_multiple_content_views']
    fail ::Katello::Errors::MultiEnvironmentNotSupportedError,
    _("Assigning a host to multiple content view environments is not enabled. To enable, set the allow_multiple_content_views setting.")
  end
  super(new_cvenvs)
  Katello::ContentViewEnvironmentContentFacet.reprioritize_for_content_facet(self, new_cvenvs)
  self.content_view_environments.reload unless self.new_record?
  # Mark as changed to ensure the before_update callback triggers Candlepin update.
  # Don't call update_candlepin_associations directly - the callback handles it and
  # prevents duplicate requests when called during host.save!
  self.cvenvs_changed = true unless self.new_record?
end

#content_view_environments_all_default_or_rolling?Boolean

Returns:

  • (Boolean)


172
173
174
175
176
177
178
179
180
181
182
183
# File 'app/models/katello/host/content_facet.rb', line 172

def content_view_environments_all_default_or_rolling?
  return if content_view_environments.blank?
  # Returns true if all applicable errata are installable, meaning:
  # - First CVEnv is Library (Default Org View + Library environment), OR
  # - All CVEnvs are rolling CVs or Library hosts
  # This determines whether the Applicable/Installable toggle should be hidden.
  return true if content_view_environments.first.default_environment?

  content_view_environments.all? do |cvenv|
    cvenv.content_view&.rolling? || cvenv.default_environment?
  end
end

#cvenvs_changed?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'app/models/katello/host/content_facet.rb', line 119

def cvenvs_changed?
  cvenvs_changed
end

#default_environment?Boolean

Returns:

  • (Boolean)


165
166
167
168
169
170
# File 'app/models/katello/host/content_facet.rb', line 165

def default_environment?
  return if content_view_environments.blank?
  # if default content view environment is first, this is equivalent to default being the only one.
  # if default content view environment is not first, candlepin will prioritize CV repos over library repos in case of conflicts.
  content_view_environments.first.default_environment?
end

#errata_countsObject



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'app/models/katello/host/content_facet.rb', line 232

def errata_counts
  installable_hash = {
    :security => installable_security_errata_count,
    :bugfix => installable_bugfix_errata_count,
    :enhancement => installable_enhancement_errata_count,
  }
  installable_hash[:total] = installable_hash.values.inject(:+)
  # same for applicable, but we need to get the counts from the db
  applicable_errata_counts = applicable_errata.pluck(:errata_type).tally
  applicable_hash = {
    :bugfix => applicable_errata_counts.values_at(*Katello::Erratum::BUGZILLA).compact.sum,
    :security => applicable_errata_counts.values_at(*Katello::Erratum::SECURITY).compact.sum,
    :enhancement => applicable_errata_counts.values_at(*Katello::Erratum::ENHANCEMENT).compact.sum,
  }
  applicable_hash[:total] = applicable_errata_counts.values.sum

  # keeping installable at the top level for backward compatibility
  installable_hash.merge({
                           :applicable => applicable_hash,
                         })
end

#host_tools_installed?(force_update_cache: false) ⇒ Boolean

Returns:

  • (Boolean)


405
406
407
408
409
410
# File 'app/models/katello/host/content_facet.rb', line 405

def host_tools_installed?(force_update_cache: false)
  Rails.cache.fetch("#{self.host.id}/host_tools_installed", expires_in: 7.days, force: force_update_cache) do
    self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => ALL_HOST_TOOLS_PACKAGE_NAMES).any? ||
      self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => ALL_HOST_TOOLS_PACKAGE_NAMES).any?
  end
end

#image_mode_host?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'app/models/katello/host/content_facet.rb', line 107

def image_mode_host?
  bootc_booted_image.present?
end

#installable_debs(env = nil, content_view = nil) ⇒ Object



220
221
222
# File 'app/models/katello/host/content_facet.rb', line 220

def installable_debs(env = nil, content_view = nil)
  Deb.installable_for_content_facet(self, env, content_view)
end

#installable_errata(env = nil, content_view = nil) ⇒ Object



216
217
218
# File 'app/models/katello/host/content_facet.rb', line 216

def installable_errata(env = nil, content_view = nil)
  Erratum.installable_for_content_facet(self, env, content_view)
end

#installable_module_streams(env = nil, content_view = nil) ⇒ Object



228
229
230
# File 'app/models/katello/host/content_facet.rb', line 228

def installable_module_streams(env = nil, content_view = nil)
  ModuleStream.installable_for_content_facet(self, env, content_view)
end

#installable_rpms(env = nil, content_view = nil) ⇒ Object



224
225
226
# File 'app/models/katello/host/content_facet.rb', line 224

def installable_rpms(env = nil, content_view = nil)
  Rpm.installable_for_content_facet(self, env, content_view)
end

#mark_cvenvs_changed(_cvenv) ⇒ Object



98
99
100
101
# File 'app/models/katello/host/content_facet.rb', line 98

def mark_cvenvs_changed(_cvenv)
  Rails.logger.debug("ContentFacet: Marking content view environments changed for host #{host&.to_label}")
  self.cvenvs_changed = true
end

#mark_cvenvs_unchangedObject



103
104
105
# File 'app/models/katello/host/content_facet.rb', line 103

def mark_cvenvs_unchanged
  self.cvenvs_changed = false
end

#multi_content_view_environment?Boolean

Returns:

  • (Boolean)


123
124
125
126
# File 'app/models/katello/host/content_facet.rb', line 123

def multi_content_view_environment?
  # returns false if there are no content view environments
  content_view_environments.size > 1
end

#single_content_viewObject



133
134
135
136
137
138
# File 'app/models/katello/host/content_facet.rb', line 133

def single_content_view
  if multi_content_view_environment?
    Rails.logger.warn _("Content facet for host %s has more than one content view. Use #content_views instead.") % host.name
  end
  content_view_environments&.first&.content_view
end

#single_content_view_environment?Boolean

Returns:

  • (Boolean)


128
129
130
131
# File 'app/models/katello/host/content_facet.rb', line 128

def single_content_view_environment?
  # also returns false if there are no content view environments
  content_view_environments.size == 1
end

#single_lifecycle_environmentObject



140
141
142
143
144
145
# File 'app/models/katello/host/content_facet.rb', line 140

def single_lifecycle_environment
  if multi_content_view_environment?
    Rails.logger.warn _("Content facet for host %s has more than one lifecycle environment. Use #lifecycle_environments instead.") % host.name
  end
  content_view_environments&.first&.lifecycle_environment
end

#tracer_deb_available?Boolean

Returns:

  • (Boolean)


401
402
403
# File 'app/models/katello/host/content_facet.rb', line 401

def tracer_deb_available?
  ::Katello::Deb.deb_installable_for_host(self.host).where(name: HOST_TOOLS_TRACER_PACKAGE_NAME).any?
end

#tracer_installed?(force_update_cache: false) ⇒ Boolean

Returns:

  • (Boolean)


390
391
392
393
394
395
# File 'app/models/katello/host/content_facet.rb', line 390

def tracer_installed?(force_update_cache: false)
  Rails.cache.fetch("#{self.host.id}/tracer_installed", expires_in: 7.days, force: force_update_cache) do
    self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => ALL_TRACER_PACKAGE_NAMES).any? ||
      self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => ALL_TRACER_PACKAGE_NAMES).any?
  end
end

#tracer_rpm_available?Boolean

Returns:

  • (Boolean)


397
398
399
# File 'app/models/katello/host/content_facet.rb', line 397

def tracer_rpm_available?
  ::Katello::Rpm.yum_installable_for_host(self.host).where(name: ALL_TRACER_PACKAGE_NAMES).any?
end

#update_applicability_countsObject



274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/models/katello/host/content_facet.rb', line 274

def update_applicability_counts
  self.assign_attributes(
      :installable_security_errata_count => self.installable_errata.security.count,
      :installable_bugfix_errata_count => self.installable_errata.bugfix.count,
      :installable_enhancement_errata_count => self.installable_errata.enhancement.count,
      :applicable_deb_count => self.content_facet_applicable_debs.count,
      :upgradable_deb_count => self.installable_debs.count,
      :applicable_rpm_count => self.content_facet_applicable_rpms.count,
      :upgradable_rpm_count => self.installable_rpms.count,
      :applicable_module_stream_count => self.content_facet_applicable_module_streams.count,
      :upgradable_module_stream_count => self.installable_module_streams.count
  )
  self.save!(:validate => false)
end

#update_errata_statusObject



412
413
414
415
# File 'app/models/katello/host/content_facet.rb', line 412

def update_errata_status
  host.get_status(::Katello::ErrataStatus).refresh!
  host.refresh_global_status!
end

#update_repositories_by_paths(paths) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'app/models/katello/host/content_facet.rb', line 185

def update_repositories_by_paths(paths)
  prefixes = %w(/pulp/deb/ /pulp/repos/ /pulp/content/)
  relative_paths = []

  # paths == ["/pulp/content/Default_Organization/Library/custom/Test_product/test2",
  #           "/pulp/content/Org/Library/custom/Test_product/test2/%3Fcomp%3Dmain%26rel%3Dstable"]
  paths.each do |path|
    if (prefix = prefixes.find { |pre| path.start_with?(pre) })
      # strip prefix and deb? content postfix before adding to relative_paths
      relative_paths << path.sub(prefix, '').sub(%r{/?(%3F|\?).*}, '')
    else
      Rails.logger.warn("System #{self.host.name} (#{self.host.id}) requested binding to repo with unknown prefix. #{path}")
    end
  end

  repos = Repository.where(relative_path: relative_paths)
  relative_paths -= repos.pluck(:relative_path) # remove relative paths that match our repos

  # Any leftover relative paths do not match the repos we've just retrieved from the db,
  # so we should log warnings about them.
  relative_paths.each do |repo_path|
    Rails.logger.warn("System #{self.host.name} (#{self.host.id}) requested binding to unknown repo #{repo_path}")
  end

  unless self.bound_repositories.sort == repos.sort
    self.bound_repositories = repos
    self.save!
  end
  self.bound_repositories.pluck(:relative_path)
end

#uuidObject

Delegate uuid to subscription_facet since UUID is a subscription/consumer concept The uuid column was removed from content_facets table



11
12
13
# File 'app/models/katello/host/content_facet.rb', line 11

def uuid
  host&.subscription_facet&.uuid
end

#yum_or_yum_transientObject



111
112
113
114
115
116
117
# File 'app/models/katello/host/content_facet.rb', line 111

def yum_or_yum_transient
  if image_mode_host?
    'dnf --transient'
  else
    'yum'
  end
end