From e99f9d60aaa69b1a41cf8274994e42e54551358c Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Sun, 15 Jan 2017 20:24:43 -0500 Subject: [PATCH] Fixes #17148: Allow searching for hypervisors --- .../api/rhsm/candlepin_proxies_controller.rb | 1 + .../subscription_facet_host_extensions.rb | 16 +++++ app/models/katello/host/subscription_facet.rb | 25 ++++++++ ...5_add_hypervisor_to_subscription_facets.rb | 6 ++ db/seeds.d/108-subcription-bookmarks.rb | 17 ++++++ lib/katello/engine.rb | 2 + ...pdate_subscription_facet_backend_data.rake | 61 +++++++++++++++++++ .../tasks/upgrades/3.3/hypervisors.rake | 7 +++ test/lib/tasks/seeds_test.rb | 7 +++ test/models/host/subscription_facet_test.rb | 60 ++++++++++++++++++ 10 files changed, 202 insertions(+) create mode 100644 db/migrate/20170122204325_add_hypervisor_to_subscription_facets.rb create mode 100644 db/seeds.d/108-subcription-bookmarks.rb create mode 100644 lib/katello/tasks/update_subscription_facet_backend_data.rake create mode 100644 lib/katello/tasks/upgrades/3.3/hypervisors.rake diff --git a/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb b/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb index 31b1b5b78c5..b280239930a 100644 --- a/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +++ b/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb @@ -257,6 +257,7 @@ def server_status render :json => status end + #api :PUT, "/consumers/:id", N_("Update consumer information") def facts sync_task(::Actions::Katello::Host::Update, @host, rhsm_params) update_host_registered_through(@host, request.headers) diff --git a/app/models/katello/concerns/subscription_facet_host_extensions.rb b/app/models/katello/concerns/subscription_facet_host_extensions.rb index f96ce726d92..afc199dd29b 100644 --- a/app/models/katello/concerns/subscription_facet_host_extensions.rb +++ b/app/models/katello/concerns/subscription_facet_host_extensions.rb @@ -16,6 +16,8 @@ module SubscriptionFacetHostExtensions has_many :activation_keys, :through => :subscription_facet has_one :subscription_status_object, :class_name => 'Katello::SubscriptionStatus', :foreign_key => 'host_id' + has_one :hypervisor_host, :through => :subscription_facet + scoped_search :on => :status, :relation => :subscription_status_object, :rename => :subscription_status, :complete_value => SUBSCRIPTION_STATUS_MAP @@ -29,6 +31,8 @@ module SubscriptionFacetHostExtensions scoped_search :relation => :activation_keys, :on => :name, :rename => :activation_key, :complete_value => true, :ext_method => :find_by_activation_key scoped_search :relation => :activation_keys, :on => :id, :rename => :activation_key_id, :complete_value => true, :ext_method => :find_by_activation_key_id, :only_explicit => true, :validator => ScopedSearch::Validators::INTEGER + scoped_search :on => :hypervisor, :relation => :subscription_facet, :complete_value => true + scoped_search :on => :name, :relation => :hypervisor_host, :complete_value => true, :rename => :hypervisor_host, :ext_method => :find_by_hypervisor_host end def update_action @@ -57,6 +61,18 @@ def find_by_activation_key_id(_key, operator, value) { :conditions => "#{::Host::Managed.table_name}.id IN (#{hosts.pluck(:id).join(',')})" } end end + + def find_by_hypervisor_host(_key, operator, value) + conditions = sanitize_sql_for_conditions(["#{::Host.table_name}.name #{operator} ?", value_to_sql(operator, value)]) + hosts = ::Host.where(conditions) + hosts = ::Host.joins(:subscription_facet).where("#{Katello::Host::SubscriptionFacet.table_name}.hypervisor_host_id" => hosts) + + if hosts.empty? + {:conditions => "1=0"} + else + {:conditions => "#{::Host::Managed.table_name}.id IN (#{hosts.pluck(:id).join(',')})"} + end + end end end end diff --git a/app/models/katello/host/subscription_facet.rb b/app/models/katello/host/subscription_facet.rb index 45a02eb250c..f66a8856f79 100644 --- a/app/models/katello/host/subscription_facet.rb +++ b/app/models/katello/host/subscription_facet.rb @@ -5,6 +5,7 @@ class SubscriptionFacet < Katello::Model include Facets::Base belongs_to :user, :inverse_of => :subscription_facets, :class_name => "::User" + belongs_to :hypervisor_host, :class_name => "::Host::Managed", :foreign_key => "hypervisor_host_id" has_many :activation_keys, :through => :subscription_facet_activation_keys, :class_name => "Katello::ActivationKey" has_many :subscription_facet_activation_keys, :class_name => "Katello::SubscriptionFacetActivationKey", :dependent => :destroy, :inverse_of => :subscription_facet @@ -23,6 +24,9 @@ def update_from_consumer_attributes(consumer_params) end def import_database_attributes(consumer_params) + update_hypervisor(consumer_params) + update_guests + self.autoheal = consumer_params['autoheal'] unless consumer_params['autoheal'].blank? self.service_level = consumer_params['serviceLevel'] unless consumer_params['serviceLevel'].blank? self.registered_at = consumer_params['created'] unless consumer_params['created'].blank? @@ -35,6 +39,27 @@ def import_database_attributes(consumer_params) end end + def update_hypervisor(consumer_params) + if consumer_params.try(:[], 'type').try(:[], 'label') == 'hypervisor' + self.hypervisor = true + elsif !consumer_params.try(:[], 'guestIds').empty? + self.hypervisor = true + elsif !candlepin_consumer.virtual_guests.empty? + # Check by calling out to Candlepin last for efficiency + self.hypervisor = true + end + end + + def update_guests + if self.hypervisor + guests = self.candlepin_consumer.virtual_guests + subscription_facets = SubscriptionFacet.where(:host => guests) + subscription_facets.update_all(:hypervisor_host_id => self.host.id) + elsif (virtual_host = self.candlepin_consumer.virtual_host) + self.hypervisor_host = virtual_host + end + end + def consumer_attributes attrs = { :autoheal => autoheal, diff --git a/db/migrate/20170122204325_add_hypervisor_to_subscription_facets.rb b/db/migrate/20170122204325_add_hypervisor_to_subscription_facets.rb new file mode 100644 index 00000000000..0c4b125623d --- /dev/null +++ b/db/migrate/20170122204325_add_hypervisor_to_subscription_facets.rb @@ -0,0 +1,6 @@ +class AddHypervisorToSubscriptionFacets < ActiveRecord::Migration + def change + add_column :katello_subscription_facets, :hypervisor, :boolean, :default => false + add_column :katello_subscription_facets, :hypervisor_host_id, :integer + end +end diff --git a/db/seeds.d/108-subcription-bookmarks.rb b/db/seeds.d/108-subcription-bookmarks.rb new file mode 100644 index 00000000000..a20870d0b58 --- /dev/null +++ b/db/seeds.d/108-subcription-bookmarks.rb @@ -0,0 +1,17 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# !!! PLEASE KEEP THIS SCRIPT IDEMPOTENT !!! +# + +Bookmark.without_auditing do + bookmarks = [ + {:name => "list hypervisors", :query => 'hypervisor = true', :controller => "hosts"} + ] + + bookmarks.each do |input| + next if audit_modified? Bookmark, input[:name], :controller => input[:controller] + b = Bookmark.find_or_create_by({ :public => true }.merge(input)) + fail "Unable to create bookmark: #{format_errors b}" if b.nil? || b.errors.any? + end +end diff --git a/lib/katello/engine.rb b/lib/katello/engine.rb index f14cc66d6f1..7ff292d7463 100644 --- a/lib/katello/engine.rb +++ b/lib/katello/engine.rb @@ -267,6 +267,7 @@ def find_assets(args = {}) load "#{Katello::Engine.root}/lib/katello/tasks/import_applicability.rake" load "#{Katello::Engine.root}/lib/katello/tasks/clean_published_repo_directories.rake" load "#{Katello::Engine.root}/lib/katello/tasks/virt_who_report.rake" + load "#{Katello::Engine.root}/lib/katello/tasks/update_subscription_facet_backend_data.rake" load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/2.4/import_package_groups.rake" load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/2.4/import_rpms.rake" @@ -278,6 +279,7 @@ def find_assets(args = {}) load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/3.0/update_puppet_repository_distributors.rake" load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/3.0/update_subscription_facet_backend_data.rake" load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/3.3/import_subscriptions.rake" + load "#{Katello::Engine.root}/lib/katello/tasks/upgrades/3.3/hypervisors.rake" end end diff --git a/lib/katello/tasks/update_subscription_facet_backend_data.rake b/lib/katello/tasks/update_subscription_facet_backend_data.rake new file mode 100644 index 00000000000..b3c39d06c71 --- /dev/null +++ b/lib/katello/tasks/update_subscription_facet_backend_data.rake @@ -0,0 +1,61 @@ +namespace :katello do + task :update_subscription_facet_backend_data => ["environment"] do + def error(message) + @errors << message + @errors << "\n" + end + + def report_errors + if @errors.any? + filename = "subscription_facet_upgrade-#{Time.now.to_i}.log" + path = "/var/log/foreman/#{filename}" + path = "/tmp/#{filename}" unless File.writable?(path) + + file = File.open(path, 'w') + @errors.each { |error| file.write(error) } + file.close + $stderr.print "***********************************\n" + $stderr.print "*************WARNING***************\n" + $stderr.print "Errors detected during upgrade step.\n" + $stderr.print "Details saved to: #{file.path}\n" + $stderr.print "This step can be rerun with:\n" + $stderr.print " foreman-rake katello:update_subscription_facet_backend_data\n" + $stderr.print "You are likely encountering a bug.\n" + $stderr.print "***********************************\n" + end + end + + @errors ||= [] + User.current = User.anonymous_api_admin + puts _("Updating backend data for subscription facets") + + #there may be some invalid hosts, if there are create a primary interface + ::Host.includes(:interfaces).find_each do |host| + if host.primary_interface.nil? + host.interfaces.create!(:primary => true) + end + end + + Katello::Host::SubscriptionFacet.find_each do |subscription_facet| + begin + candlepin_attrs = subscription_facet.candlepin_consumer.consumer_attributes + subscription_facet.import_database_attributes(candlepin_attrs) + subscription_facet.host = ::Host::Managed.find(subscription_facet.host_id) + subscription_facet.save! + + host = subscription_facet.host + host.name = ::Katello::Host::SubscriptionFacet.sanitize_name(host.name) + host.save! if host.name_changed? + + Katello::Host::SubscriptionFacet.update_facts(subscription_facet.host, candlepin_attrs[:facts]) + rescue StandardError => exception + error("Error: #{subscription_facet.host.name} - #{subscription_facet.host.id}") + error(candlepin_attrs) + error(exception.message) + error(exception.backtrace.join("\n")) + error("\n") + end + end + report_errors + end +end diff --git a/lib/katello/tasks/upgrades/3.3/hypervisors.rake b/lib/katello/tasks/upgrades/3.3/hypervisors.rake new file mode 100644 index 00000000000..46e62a146c1 --- /dev/null +++ b/lib/katello/tasks/upgrades/3.3/hypervisors.rake @@ -0,0 +1,7 @@ +namespace :katello do + namespace :upgrades do + namespace '3.3' do + task :hypervisors => ["katello:update_subscription_facet_backend_data"] + end + end +end diff --git a/test/lib/tasks/seeds_test.rb b/test/lib/tasks/seeds_test.rb index 3e22dbdec10..7ada49cf494 100644 --- a/test/lib/tasks/seeds_test.rb +++ b/test/lib/tasks/seeds_test.rb @@ -78,4 +78,11 @@ class MailNotificationsTest < SeedsTest assert MailNotification[:sync_errata] end end + + class SubscriptionBookmarkstest < SeedsTest + test "Ensure hypervisor bookmark is created" do + seed + refute Bookmark.where(:name => "list hypervisors").empty? + end + end end diff --git a/test/models/host/subscription_facet_test.rb b/test/models/host/subscription_facet_test.rb index 80e0a017178..aadd9b68b42 100644 --- a/test/models/host/subscription_facet_test.rb +++ b/test/models/host/subscription_facet_test.rb @@ -210,5 +210,65 @@ def test_propose_existing_hostname_fqdn_exists facts = {'network.hostname' => 'foo', 'network.hostname-override' => 'baz.com'} assert_equal 'foo', Host::SubscriptionFacet.propose_existing_hostname(facts) end + + def test_search_hypervisor + subscription_facet.hypervisor = "true" + subscription_facet.save! + + assert_includes ::Host.search_for("hypervisor = true"), host + end + + def test_search_hypervisor_host + subscription_facet.hypervisor = "true" + subscription_facet.save! + guest_host = FactoryGirl.create(:host, :with_content, :with_subscription, :content_view => view, + :lifecycle_environment => library, :organization => org) + subscription_facet.candlepin_consumer.expects(:virtual_guests).returns([guest_host]) + subscription_facet.update_guests + + assert_includes ::Host.search_for("hypervisor_host = #{host.name}"), guest_host + end + + def test_update_hypervisor_using_candlepin_type + consumer_params = {'type' => {'label' => 'hypervisor'}} + subscription_facet.update_hypervisor(consumer_params) + + assert subscription_facet.hypervisor + end + + def test_update_hypervisor_using_guest_ids + consumer_params = {'guestIds' => ['1']} + subscription_facet.update_hypervisor(consumer_params) + + assert subscription_facet.hypervisor + end + + def test_update_hypervisor_via_candlepin_api + consumer_params = {} + subscription_facet.candlepin_consumer.expects(:virtual_guests).returns(['1']) + subscription_facet.update_hypervisor(consumer_params) + + assert subscription_facet.hypervisor + end + + def test_update_guests_for_hypervisor + guest_host = FactoryGirl.create(:host, :with_content, :with_subscription, :content_view => view, + :lifecycle_environment => library, :organization => org) + subscription_facet.hypervisor = true + subscription_facet.candlepin_consumer.expects(:virtual_guests).returns([guest_host]) + subscription_facet.update_guests + + assert_equal host, guest_host.subscription_facet.reload.hypervisor_host + end + + def test_update_guests_for_guest + virt_host = FactoryGirl.create(:host, :with_content, :with_subscription, :content_view => view, + :lifecycle_environment => library, :organization => org) + subscription_facet.hypervisor = false + subscription_facet.candlepin_consumer.expects(:virtual_host).returns(virt_host) + subscription_facet.update_guests + + assert_equal virt_host, subscription_facet.hypervisor_host + end end end