From 515a48bf0618c87c63666f945dcc7ae875c69b61 Mon Sep 17 00:00:00 2001 From: John Mitsch Date: Fri, 17 Jun 2016 09:21:26 -0700 Subject: [PATCH] Fixes #15347 - Filter hosts for activation key associations (#6113) All systems are being returned in the activation key associations page. This changes that page to use hosts endpoint instead of activation keys, which makes it possible to use scoped search to filter them. --- .../subscription_facet_host_extensions.rb | 25 +++++++++++++ app/models/katello/glue/candlepin/pool.rb | 6 ++++ .../api/v2/subscriptions/show.json.rabl | 36 ++++++++++--------- .../activation-key-associations.controller.js | 17 ++++++--- ...vation-key-associations-content-hosts.html | 16 ++++----- ...vation-key-associations.controller.test.js | 5 ++- test/models/host/subscription_facet_test.rb | 11 ++++++ test/models/pool_test.rb | 13 +++++++ 8 files changed, 99 insertions(+), 30 deletions(-) diff --git a/app/models/katello/concerns/subscription_facet_host_extensions.rb b/app/models/katello/concerns/subscription_facet_host_extensions.rb index db11bd6098f..3101ac23064 100644 --- a/app/models/katello/concerns/subscription_facet_host_extensions.rb +++ b/app/models/katello/concerns/subscription_facet_host_extensions.rb @@ -14,6 +14,7 @@ module SubscriptionFacetHostExtensions has_one :subscription_facet, :class_name => '::Katello::Host::SubscriptionFacet', :foreign_key => :host_id, :inverse_of => :host, :dependent => :destroy + has_many :activation_keys, :through => :subscription_facet has_one :subscription_status_object, :class_name => 'Katello::SubscriptionStatus', :foreign_key => 'host_id' scoped_search :on => :status, :in => :subscription_status_object, :rename => :subscription_status, :complete_value => SUBSCRIPTION_STATUS_MAP @@ -26,6 +27,8 @@ module SubscriptionFacetHostExtensions scoped_search :on => :last_checkin, :in => :subscription_facet, :complete_value => true scoped_search :on => :registered_at, :in => :subscription_facet, :rename => :registered_at scoped_search :on => :uuid, :in => :subscription_facet, :rename => :subscription_uuid + scoped_search :in => :activation_keys, :on => :name, :rename => :activation_key, :complete_value => true, :ext_method => :find_by_activation_key + scoped_search :in => :activation_keys, :on => :id, :rename => :activation_key_id, :complete_value => true, :ext_method => :find_by_activation_key_id end def update_action @@ -33,6 +36,28 @@ def update_action ::Actions::Katello::Host::Update end end + + module ClassMethods + def find_by_activation_key(_key, operator, value) + conditions = sanitize_sql_for_conditions(["#{Katello::ActivationKey.table_name}.name #{operator} ?", value_to_sql(operator, value)]) + hosts = ::Host::Managed.joins(:activation_keys).where(conditions) + if hosts.empty? + { :conditions => "1=0" } + else + { :conditions => "#{::Host::Managed.table_name}.id IN (#{hosts.pluck(:id).join(',')})" } + end + end + + def find_by_activation_key_id(_key, operator, value) + conditions = sanitize_sql_for_conditions(["#{Katello::ActivationKey.table_name}.id #{operator} ?", value_to_sql(operator, value)]) + hosts = ::Host::Managed.joins(:activation_keys).where(conditions) + if hosts.empty? + { :conditions => "1=0" } + else + { :conditions => "#{::Host::Managed.table_name}.id IN (#{hosts.pluck(:id).join(',')})" } + end + end + end end end end diff --git a/app/models/katello/glue/candlepin/pool.rb b/app/models/katello/glue/candlepin/pool.rb index 276b0455250..0408a5b8a92 100644 --- a/app/models/katello/glue/candlepin/pool.rb +++ b/app/models/katello/glue/candlepin/pool.rb @@ -135,6 +135,12 @@ def create_activation_key_associations def host System.find_by(:uuid => host_id) if host_id end + + def hosts + entitlements = Resources::Candlepin::Entitlement.get + uuids = entitlements.delete_if { |ent| ent["pool"]["id"] != cp_id }.map { |ent| ent["consumer"]["uuid"] } + ::Host.where(:id => Katello::Host::ContentFacet.where(:uuid => uuids).pluck(:host_id)) + end end end end diff --git a/app/views/katello/api/v2/subscriptions/show.json.rabl b/app/views/katello/api/v2/subscriptions/show.json.rabl index 0a86237042d..1d3147508ff 100644 --- a/app/views/katello/api/v2/subscriptions/show.json.rabl +++ b/app/views/katello/api/v2/subscriptions/show.json.rabl @@ -13,34 +13,36 @@ node :provided_products, :if => lambda { |sub| sub && !sub.products.blank? } do end node :systems do |subscription| - subscription.systems.readable.map do |sys| - facts = sys.facts + subscription.hosts.map do |host| + facts = host.facts { - uuid: sys.uuid, - host_id: sys.host_id, - name: sys.name, - environment: { id: sys.environment.id, name: sys.environment.name }, - content_view: { id: sys.content_view.id, name: sys.content_view.name }, - created: sys.created, - checkin_time: sys.checkin_time, - entitlement_status: sys.entitlementStatus, - service_level: sys.serviceLevel, - autoheal: sys.autoheal, + uuid: host.subscription_facet.try(:uuid), + host_id: host.id, + name: host.name, + environment: { id: host.content_facet.try(:lifecycle_environment).try(:id), + name: host.content_facet.try(:lifecycle_environment).try(:name) }, + content_view: { id: host.content_facet.try(:content_view).try(:id), + name: host.content_facet.try(:content_view).try(:name) }, + created: host.subscription_facet.try(:registered_at), + checkin_time: host.subscription_facet.try(:last_checkin), + entitlement_status: host.subscription_status, + service_level: host.subscription_facet.try(:service_level), + autoheal: host.subscription_facet.try(:autoheal), facts: { dmi: { memory: { - size: facts['dmi.memory.size'] + size: facts['dmi::memory::size'] } }, memory: { - memtotal: facts['memory.memtotal'] + memtotal: facts['memory::memtotal'] }, cpu: { - 'cpu_socket(s)' => facts['cpu.cpu_socket(s)'], - 'core(s)_per_socket' => facts['cpu.core(s)_per_socket'] + 'cpu_socket(s)' => facts['cpu::cpu_socket(s)'], + 'core(s)_per_socket' => facts['cpu::core(s)_per_socket'] }, virt: { - is_guest: facts['virt.is_guest'] + is_guest: facts['virt::is_guest'] } } } diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-associations.controller.js b/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-associations.controller.js index 038485d80d1..e7cdf4f974b 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-associations.controller.js +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-associations.controller.js @@ -14,8 +14,8 @@ * Provides the functionality for activation key associations. */ angular.module('Bastion.activation-keys').controller('ActivationKeyAssociationsController', - ['$scope', '$location', 'translate', 'Nutupane', 'ActivationKey', 'ContentHostsHelper', 'CurrentOrganization', - function ($scope, $location, translate, Nutupane, ActivationKey, ContentHostsHelper, CurrentOrganization) { + ['$scope', '$location', 'translate', 'Nutupane', 'ActivationKey', 'ContentHostsHelper', 'CurrentOrganization', 'Host', + function ($scope, $location, translate, Nutupane, ActivationKey, ContentHostsHelper, CurrentOrganization, Host) { var contentHostsNutupane, params = { 'organization_id': CurrentOrganization, 'search': $location.search().search || "", @@ -31,12 +31,21 @@ angular.module('Bastion.activation-keys').controller('ActivationKeyAssociationsC $scope.table.working = false; } - contentHostsNutupane = new Nutupane(ActivationKey, params, 'contentHosts'); + contentHostsNutupane = new Nutupane(Host, params); + contentHostsNutupane.searchTransform = function (term) { + var searchQuery, addition = "activation_key_id=" + $scope.$stateParams.activationKeyId; + if (term === "" || angular.isUndefined(term)) { + searchQuery = addition; + } else { + searchQuery = term + " and " + addition; + } + return searchQuery; + }; + contentHostsNutupane.masterOnly = true; $scope.detailsTable = contentHostsNutupane.table; $scope.activationKey.$promise.then(function () { - params.id = $scope.activationKey.id; contentHostsNutupane.setParams(params); contentHostsNutupane.load(); }); diff --git a/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-associations-content-hosts.html b/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-associations-content-hosts.html index bfc4d68bcfb..c0083465e4e 100644 --- a/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-associations-content-hosts.html +++ b/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-associations-content-hosts.html @@ -26,21 +26,21 @@ - - - {{ contentHost.name }} + + {{ host.name }} - + - {{ contentHost.environment.name }} - {{ contentHost.content_view.name || "" }} - {{ contentHost.service_level }} - {{ contentHost.release_ver }} + {{ host.environment.name }} + {{ host.content_view.name || "" }} + {{ host.service_level }} + {{ host.release_ver }} diff --git a/engines/bastion_katello/test/activation-keys/details/activation-key-associations.controller.test.js b/engines/bastion_katello/test/activation-keys/details/activation-key-associations.controller.test.js index 88bb413d900..2eb97a62b88 100644 --- a/engines/bastion_katello/test/activation-keys/details/activation-key-associations.controller.test.js +++ b/engines/bastion_katello/test/activation-keys/details/activation-key-associations.controller.test.js @@ -2,6 +2,7 @@ describe('Controller: ActivationKeyAssociationsController', function() { var $scope, ActivationKey, CurrentOrganization, + Host, translate; beforeEach(module( @@ -11,7 +12,8 @@ describe('Controller: ActivationKeyAssociationsController', function() { beforeEach(inject(function($injector) { var $controller = $injector.get('$controller'), - ActivationKey = $injector.get('MockResource').$new(); + ActivationKey = $injector.get('MockResource').$new(), + Host = $injector.get('MockResource').$new(); translate = function (message) { return message; @@ -36,6 +38,7 @@ describe('Controller: ActivationKeyAssociationsController', function() { $scope: $scope, translate: translate, ActivationKey: ActivationKey, + Host: Host, ContentHostsHelper: {}, CurrentOrganization: "ACME" }); diff --git a/test/models/host/subscription_facet_test.rb b/test/models/host/subscription_facet_test.rb index 12809268da7..5fd95f1da5c 100644 --- a/test/models/host/subscription_facet_test.rb +++ b/test/models/host/subscription_facet_test.rb @@ -6,6 +6,7 @@ class SubscriptionFacetBase < ActiveSupport::TestCase let(:library) { katello_environments(:library) } let(:dev) { katello_environments(:dev) } let(:view) { katello_content_views(:library_dev_view) } + let(:activation_key) { katello_activation_keys(:simple_key) } let(:empty_host) { ::Host::Managed.create!(:name => 'foobar', :managed => false) } let(:host) do FactoryGirl.create(:host, :with_content, :with_subscription, :content_view => view, @@ -140,5 +141,15 @@ def test_backend_update_needed? subscription_facet.host.content_facet.lifecycle_environment_id = dev.id assert subscription_facet.backend_update_needed? end + + def test_search_by_activation_key_id + host.subscription_facet.activation_keys << activation_key + assert_includes ::Host.search_for("activation_key_id = #{activation_key.id}"), host + end + + def test_search_by_activation_key + host.subscription_facet.activation_keys << activation_key + assert_includes ::Host.search_for("activation_key = \"#{activation_key.name}\""), host + end end end diff --git a/test/models/pool_test.rb b/test/models/pool_test.rb index 3e5595351d7..1dd7af4605c 100644 --- a/test/models/pool_test.rb +++ b/test/models/pool_test.rb @@ -3,6 +3,8 @@ module Katello class PoolTest < ActiveSupport::TestCase def setup + @library = katello_environments(:library) + @view = katello_content_views(:library_dev_view) @pool_one = katello_pools(:pool_one) @pool_two = katello_pools(:pool_two) end @@ -56,6 +58,17 @@ def test_with_identifiers assert_equal Pool.with_identifier("#{@pool_one.id}"), @pool_one end + def test_hosts + active_pool = FactoryGirl.build(:katello_pool, :active) + host_one = FactoryGirl.create(:host, :with_content, :with_subscription, :content_view => @view, + :lifecycle_environment => @library) + cp_id = "foo" + active_pool.cp_id = cp_id + pool_data = [{"pool" => {"id" => cp_id}, "consumer" => {"uuid" => host_one.content_facet.uuid}}] + Resources::Candlepin::Entitlement.expects(:get).returns(pool_data) + assert_equal active_pool.hosts, [host_one] + end + def test_search_consumed subscriptions = Pool.search_for("consumed = \"#{@pool_one.consumed}\"") assert_includes subscriptions, @pool_one