use of org.candlepin.model.Entitlement in project candlepin by candlepin.
the class CandlepinPoolManager method getBestPoolsForHost.
/**
* Here we pick uncovered products from the guest where no virt-only
* subscriptions exist, and have the host bind non-zero virt_limit
* subscriptions in order to generate pools for the guest to bind later.
*
* @param guest whose products we want to provide
* @param host to bind entitlements to
* @param entitleDate
* @param owner
* @param serviceLevelOverride
* @return PoolQuantity list to attempt to attach
* @throws EntitlementRefusedException if unable to bind
*/
@Override
@SuppressWarnings("checkstyle:methodlength")
public List<PoolQuantity> getBestPoolsForHost(Consumer guest, Consumer host, Date entitleDate, String ownerId, String serviceLevelOverride, Collection<String> fromPools) throws EntitlementRefusedException {
Map<String, ValidationResult> failedResults = new HashMap<>();
log.debug("Looking up best pools for host: {}", host);
boolean tempLevel = false;
if (StringUtils.isEmpty(host.getServiceLevel())) {
host.setServiceLevel(guest.getServiceLevel());
tempLevel = true;
}
Date activePoolDate = entitleDate;
if (entitleDate == null) {
activePoolDate = new Date();
}
PoolFilterBuilder poolFilter = new PoolFilterBuilder();
poolFilter.addIdFilters(fromPools);
List<Pool> allOwnerPools = this.listAvailableEntitlementPools(host, null, ownerId, null, null, activePoolDate, false, poolFilter, null, false, false, null).getPageData();
log.debug("Found {} total pools in org.", allOwnerPools.size());
logPools(allOwnerPools);
List<Pool> allOwnerPoolsForGuest = this.listAvailableEntitlementPools(guest, null, ownerId, null, null, activePoolDate, false, poolFilter, null, false, false, null).getPageData();
log.debug("Found {} total pools already available for guest", allOwnerPoolsForGuest.size());
logPools(allOwnerPoolsForGuest);
for (Entitlement ent : host.getEntitlements()) {
// filter out pools that are attached, there is no need to
// complete partial stacks, as they are already granting
// virtual pools
log.debug("Removing pool host is already entitled to: {}", ent.getPool());
allOwnerPools.remove(ent.getPool());
}
List<Pool> filteredPools = new LinkedList<>();
ComplianceStatus guestCompliance = complianceRules.getStatus(guest, entitleDate, false);
Set<String> tmpSet = new HashSet<>();
// we only want to heal red products, not yellow
tmpSet.addAll(guestCompliance.getNonCompliantProducts());
log.debug("Guest's non-compliant products: {}", Util.collectionToString(tmpSet));
/*Do not attempt to create subscriptions for products that
already have virt_only pools available to the guest */
Set<String> productsToRemove = getProductsToRemove(allOwnerPoolsForGuest, tmpSet);
log.debug("Guest already will have virt-only pools to cover: {}", Util.collectionToString(productsToRemove));
tmpSet.removeAll(productsToRemove);
String[] productIds = tmpSet.toArray(new String[] {});
if (log.isDebugEnabled()) {
log.debug("Attempting host autobind for guest products: {}", Util.collectionToString(tmpSet));
}
// Bulk fetch our provided and derived provided product IDs so we're not hitting the DB
// several times for this lookup.
Map<String, Set<String>> providedProductIds = this.poolCurator.getProvidedProductIds(allOwnerPools);
Map<String, Set<String>> derivedProvidedProductIds = this.poolCurator.getDerivedProvidedProductIds(allOwnerPools);
for (Pool pool : allOwnerPools) {
boolean providesProduct = false;
// and we only need to check that it's non-zero
if (pool.getProduct().hasAttribute(Product.Attributes.VIRT_LIMIT) && !pool.getProduct().getAttributeValue(Product.Attributes.VIRT_LIMIT).equals("0")) {
Map<String, Set<String>> providedProductMap;
String baseProductId;
// Determine which set of provided products we should use...
if (pool.getDerivedProduct() != null) {
providedProductMap = derivedProvidedProductIds;
baseProductId = pool.getDerivedProduct().getId();
} else {
providedProductMap = providedProductIds;
baseProductId = pool.getProduct().getId();
}
// Add the base product to the list of derived provided products...
Set<String> poolProvidedProductIds = providedProductMap.get(pool.getId());
if (baseProductId != null) {
if (poolProvidedProductIds != null) {
poolProvidedProductIds.add(baseProductId);
} else {
poolProvidedProductIds = Collections.<String>singleton(baseProductId);
}
}
// Check if the pool provides any of the specified products
if (poolProvidedProductIds != null) {
for (String productId : productIds) {
// provides anything for the guest, otherwise we use the parent.
if (poolProvidedProductIds.contains(productId)) {
log.debug("Found virt_limit pool providing product {}: {}", productId, pool);
providesProduct = true;
break;
}
}
}
}
if (providesProduct) {
ValidationResult result = enforcer.preEntitlement(host, pool, 1, CallerType.BEST_POOLS);
if (result.hasErrors() || result.hasWarnings()) {
// Just keep the last one around, if we need it
failedResults.put(pool.getId(), result);
if (log.isDebugEnabled()) {
log.debug("Pool filtered from candidates due to failed rule(s): {}", pool);
log.debug(" warnings: {}", Util.collectionToString(result.getWarnings()));
log.debug(" errors: {}", Util.collectionToString(result.getErrors()));
}
} else {
filteredPools.add(pool);
}
}
}
// Only throw refused exception if we actually hit the rules:
if (filteredPools.size() == 0 && !failedResults.isEmpty()) {
throw new EntitlementRefusedException(failedResults);
}
ComplianceStatus hostCompliance = complianceRules.getStatus(host, entitleDate, false);
log.debug("Host pools being sent to rules: {}", filteredPools.size());
logPools(filteredPools);
List<PoolQuantity> enforced = autobindRules.selectBestPools(host, productIds, filteredPools, hostCompliance, serviceLevelOverride, poolCurator.retrieveServiceLevelsForOwner(ownerId, true), true);
if (log.isDebugEnabled()) {
log.debug("Host selectBestPools returned {} pools: ", enforced.size());
for (PoolQuantity poolQuantity : enforced) {
log.debug(" " + poolQuantity.getPool());
}
}
if (tempLevel) {
host.setServiceLevel("");
// complianceRules.getStatus may have persisted the host with the temp service level,
// so we need to be certain we undo that.
consumerCurator.update(host);
}
return enforced;
}
use of org.candlepin.model.Entitlement in project candlepin by candlepin.
the class CandlepinPoolManager method regenerateDirtyEntitlements.
@Override
public void regenerateDirtyEntitlements(Iterable<Entitlement> entitlements) {
if (entitlements != null) {
for (Entitlement entitlement : entitlements) {
if (entitlement.isDirty()) {
log.info("Found dirty entitlement to regenerate: {}", entitlement);
this.ecGenerator.regenerateCertificatesOf(entitlement, false);
}
}
}
}
use of org.candlepin.model.Entitlement in project candlepin by candlepin.
the class CandlepinPoolManager method entitleByProductsForHost.
/**
* Request an entitlement by product for a host system in
* a host-guest relationship. Allows getBestPoolsForHost
* to choose products to bind.
*
* @param guest consumer requesting to have host entitled
* @param host host consumer to entitle
* @param entitleDate specific date to entitle by.
* @return Entitlement
* @throws EntitlementRefusedException if entitlement is refused
*/
//
// NOTE: after calling this method both entitlement pool and consumer
// parameters will most certainly be stale. beware!
@Override
@Transactional
public List<Entitlement> entitleByProductsForHost(Consumer guest, Consumer host, Date entitleDate, Collection<String> possiblePools) throws EntitlementRefusedException {
host = consumerCurator.lockAndLoad(host);
List<Entitlement> entitlements = new LinkedList<>();
if (!host.getOwnerId().equals(guest.getOwnerId())) {
log.debug("Host {} and guest {} have different owners", host.getUuid(), guest.getUuid());
return entitlements;
}
// Use the current date if one wasn't provided:
if (entitleDate == null) {
entitleDate = new Date();
}
List<PoolQuantity> bestPools = getBestPoolsForHost(guest, host, entitleDate, host.getOwnerId(), null, possiblePools);
if (bestPools == null) {
log.info("No entitlements for host: {}", host.getUuid());
return null;
}
// now make the entitlements
return entitleByPools(host, convertToMap(bestPools));
}
use of org.candlepin.model.Entitlement in project candlepin by candlepin.
the class CandlepinPoolManager method revokeEntitlements.
/**
* Revokes the given set of entitlements.
*
* @param entsToRevoke entitlements to revoke
* @param alreadyDeletedPools pools to skip deletion as they have already been deleted
* @param regenCertsAndStatuses if this revocation should also trigger regeneration of certificates
* and recomputation of statuses. For performance reasons some callers might
* choose to set this to false.
*/
@Transactional
@Traceable
public void revokeEntitlements(List<Entitlement> entsToRevoke, Set<String> alreadyDeletedPools, boolean regenCertsAndStatuses) {
if (CollectionUtils.isEmpty(entsToRevoke)) {
return;
}
log.debug("Starting batch revoke of {} entitlements", entsToRevoke.size());
if (log.isTraceEnabled()) {
log.trace("Entitlements IDs: {}", getEntIds(entsToRevoke));
}
Set<Pool> poolsToDelete = this.poolCurator.listBySourceEntitlements(entsToRevoke);
log.debug("Found {} additional pools to delete from source entitlements", poolsToDelete.size());
if (log.isTraceEnabled()) {
log.trace("Additional pool IDs: {}", getPoolIds(poolsToDelete));
}
List<Pool> poolsToLock = new ArrayList<>();
poolsToLock.addAll(poolsToDelete);
for (Entitlement ent : entsToRevoke) {
poolsToLock.add(ent.getPool());
// associated pool as well.
if (ent.getPool() != null && ent.getPool().isDevelopmentPool()) {
poolsToDelete.add(ent.getPool());
}
}
poolCurator.lockAndLoad(poolsToLock);
log.info("Batch revoking {} entitlements", entsToRevoke.size());
entsToRevoke = new ArrayList<>(entsToRevoke);
for (Pool pool : poolsToDelete) {
for (Entitlement ent : pool.getEntitlements()) {
ent.setDeletedFromPool(true);
entsToRevoke.add(ent);
}
}
log.debug("Adjusting consumed quantities on pools");
List<Pool> poolsToSave = new ArrayList<>();
Set<String> entIdsToRevoke = new HashSet<>();
for (Entitlement ent : entsToRevoke) {
// or just continue silently ignoring them?
if (ent == null || ent.getId() == null) {
continue;
}
// Collect the entitlement IDs to revoke seeing as we are iterating over them anyway.
entIdsToRevoke.add(ent.getId());
// We need to trigger lazy load of provided products
// to have access to those products later in this method.
Pool pool = ent.getPool();
int entQuantity = ent.getQuantity() != null ? ent.getQuantity() : 0;
pool.setConsumed(pool.getConsumed() - entQuantity);
Consumer consumer = ent.getConsumer();
ConsumerType ctype = this.consumerTypeCurator.getConsumerType(consumer);
if (ctype != null) {
if (ctype.isManifest()) {
pool.setExported(pool.getExported() - entQuantity);
} else if (ctype.isType(ConsumerTypeEnum.SHARE)) {
pool.setShared(pool.getShared() - entQuantity);
}
}
consumer.setEntitlementCount(consumer.getEntitlementCount() - entQuantity);
consumerCurator.update(consumer);
poolsToSave.add(pool);
}
poolCurator.updateAll(poolsToSave, false, false);
/*
* Before deleting the entitlements, we need to find out if there are any
* modifier entitlements that need to have their certificates regenerated
*/
if (regenCertsAndStatuses) {
log.debug("Marking dependent entitlements as dirty...");
int update = this.entitlementCurator.markDependentEntitlementsDirty(entIdsToRevoke);
log.debug("{} dependent entitlements marked dirty.", update);
}
log.info("Starting batch delete of pools");
poolCurator.batchDelete(poolsToDelete, alreadyDeletedPools);
log.info("Starting batch delete of entitlements");
entitlementCurator.batchDelete(entsToRevoke);
log.info("Starting delete flush");
entitlementCurator.flush();
log.info("All deletes flushed successfully");
Map<Consumer, List<Entitlement>> consumerSortedEntitlements = entitlementCurator.getDistinctConsumers(entsToRevoke);
filterAndUpdateStackingEntitlements(consumerSortedEntitlements, alreadyDeletedPools);
// post unbind actions
for (Entitlement ent : entsToRevoke) {
enforcer.postUnbind(ent.getConsumer(), this, ent);
}
if (!regenCertsAndStatuses) {
log.info("Regeneration and status computation was not requested finishing batch revoke");
sendDeletedEvents(entsToRevoke);
return;
}
log.info("Recomputing status for {} consumers.", consumerSortedEntitlements.size());
int i = 1;
for (Consumer consumer : consumerSortedEntitlements.keySet()) {
if (i++ % 1000 == 0) {
consumerCurator.flush();
}
complianceRules.getStatus(consumer);
}
consumerCurator.flush();
log.info("All statuses recomputed.");
sendDeletedEvents(entsToRevoke);
}
use of org.candlepin.model.Entitlement in project candlepin by candlepin.
the class CandlepinPoolManager method deletePools.
@Override
@Transactional
@Traceable
@SuppressWarnings("checkstyle:methodlength")
public void deletePools(Collection<Pool> pools, Collection<String> alreadyDeletedPoolIds) {
if (pools == null || pools.isEmpty()) {
return;
}
log.info("Attempting to delete {} pools...", pools.size());
// than they need to be and is resulting in running slow calculations multiple times.
if (alreadyDeletedPoolIds == null) {
alreadyDeletedPoolIds = new HashSet<>();
}
Set<String> poolIds = new HashSet<>();
Set<String> entitlementIds = new HashSet<>();
Owner owner = null;
// Convert pools to pool IDs.
log.info("Fetching related pools and entitlements...");
for (Pool pool : pools) {
if (owner == null) {
owner = pool.getOwner();
}
poolIds.add(pool.getId());
}
// Fetch pools which are derived from the pools we're going to delete...
poolIds.addAll(this.poolCurator.getDerivedPoolIdsForPools(poolIds));
// Fetch related pools and entitlements (recursively)
Collection<String> pids = poolIds;
int cachedSize;
do {
// Fetch entitlement IDs for our set of pools
Collection<String> eids = this.poolCurator.getEntitlementIdsForPools(pids);
// Fetch pools which are derived from these entitlements...
pids = this.poolCurator.getPoolIdsForSourceEntitlements(eids);
// Fetch stack derived pools which will be unentitled when we revoke entitlements
// Impl note: This may occassionally miss stack derived pools in cases where our
// entitlement count exceeds the IN block limitations. In those cases, we'll end
// up doing a recursive call into this method, which sucks, but will still work.
pids.addAll(this.poolCurator.getUnentitledStackDerivedPoolIds(eids));
// Fetch pools which are derived from the pools we're going to delete...
pids.addAll(this.poolCurator.getDerivedPoolIdsForPools(pids));
// Add the new entitlement and pool IDs to our list of things to delete
cachedSize = poolIds.size();
entitlementIds.addAll(eids);
poolIds.addAll(pids);
} while (poolIds.size() != cachedSize);
// TODO: Remove this and stop recursively calling into this method.
if (alreadyDeletedPoolIds != null) {
poolIds.removeAll(alreadyDeletedPoolIds);
}
// Lock pools we're going to delete (also, fetch them for event generation/slow deletes)
pools = this.poolCurator.lockAndLoadByIds(poolIds);
if (!pools.isEmpty()) {
log.info("Locked {} pools for deletion...", pools.size());
// Impl note:
// There is a fair bit of duplicated work between the actions below this block and
// methods like revokeEntitlements. However, the decision was made to decouple these
// methods explicitly to avoid situations such as fetching collections of pools, getting
// entitlements from them (a slow process in itself) and then passing it off to another
// standalone method which repeats the process of fetching pools and related entitlements.
//
// More work can be done in revokeEntitlements to optimize that method and maybe make it
// slightly more generic so that this work can be offloaded to it again. Though, at the time
// of writing, that's no small undertaking. Even changing this method has far-reaching
// consequences when trying to remove direct uses of entities as far as interoperability is
// concerned. Going forward we need to be more aware of the amount of duplication we're
// adding to our code when writing standlone/generic utility methods and linking them
// together, and perhaps take steps to avoid getting into situations like these two methods.
// Fetch the list of pools which are related to the entitlements but are *not* being
// deleted. We'll need to update the quantities on these.
Collection<String> affectedPoolIds = this.poolCurator.getPoolIdsForEntitlements(entitlementIds);
affectedPoolIds.removeAll(poolIds);
// Fetch entitlements (uggh).
// TODO: Stop doing this. Update the bits below to not use the entities directly and
// do the updates via queries.
Collection<Entitlement> entitlements = !entitlementIds.isEmpty() ? this.entitlementCurator.listAllByIds(entitlementIds).list() : Collections.<Entitlement>emptySet();
// Mark remaining dependent entitlements dirty for this consumer
this.entitlementCurator.markDependentEntitlementsDirty(entitlementIds);
// Unlink the pools and entitlements we're about to delete so we don't error out while
// trying to delete entitlements.
this.poolCurator.clearPoolSourceEntitlementRefs(poolIds);
// Revoke/delete entitlements
if (!entitlements.isEmpty()) {
log.info("Revoking {} entitlements...", entitlements.size());
this.entitlementCurator.batchDelete(entitlements);
this.entitlementCurator.flush();
log.info("Entitlements successfully revoked");
} else {
log.info("Skipping entitlement revocation; no entitlements to revoke");
}
// Delete pools
log.info("Deleting {} pools...", pools.size());
this.poolCurator.batchDelete(pools, alreadyDeletedPoolIds);
this.poolCurator.flush();
log.info("Pools successfully deleted");
if (!entitlements.isEmpty()) {
// Update entitlement counts on affected, non-deleted pools
log.info("Updating entitlement counts on remaining, affected pools...");
Map<Consumer, List<Entitlement>> consumerStackedEnts = new HashMap<>();
List<Pool> poolsToSave = new LinkedList<>();
Set<String> stackIds = new HashSet<>();
for (Entitlement entitlement : entitlements) {
// Since we're sifting through these already, let's also sort them into consumer lists
// for some of the other methods we'll be calling later
Consumer consumer = entitlement.getConsumer();
Pool pool = entitlement.getPool();
List<Entitlement> stackedEntitlements = consumerStackedEnts.get(consumer);
if (stackedEntitlements == null) {
stackedEntitlements = new LinkedList<>();
consumerStackedEnts.put(consumer, stackedEntitlements);
}
if (!"true".equals(pool.getAttributeValue(Pool.Attributes.DERIVED_POOL)) && pool.hasProductAttribute(Product.Attributes.STACKING_ID)) {
stackedEntitlements.add(entitlement);
stackIds.add(entitlement.getPool().getStackId());
}
// Update quantities if the entitlement quantity is non-zero
int quantity = entitlement.getQuantity() != null ? entitlement.getQuantity() : 0;
if (quantity != 0) {
// Update the pool quantities if we didn't delete it
if (affectedPoolIds.contains(pool.getId())) {
pool.setConsumed(pool.getConsumed() - quantity);
poolsToSave.add(pool);
}
// Update entitlement counts for affected consumers...
consumer.setEntitlementCount(consumer.getEntitlementCount() - quantity);
// Set the number exported if we're working with a manifest distributor
ConsumerType ctype = this.consumerTypeCurator.getConsumerType(consumer);
if (ctype != null && ctype.isManifest()) {
pool.setExported(pool.getExported() - quantity);
}
}
}
this.poolCurator.updateAll(poolsToSave, false, false);
this.consumerCurator.updateAll(consumerStackedEnts.keySet(), false, false);
this.consumerCurator.flush();
log.info("Entitlement counts successfully updated for {} pools and {} consumers", poolsToSave.size(), consumerStackedEnts.size());
// Update stacked entitlements for affected consumers(???)
if (!stackIds.isEmpty()) {
// Get consumer + pool tuples for stack ids
Map<String, Set<String>> consumerStackDerivedPoolIds = this.poolCurator.getConsumerStackDerivedPoolIdMap(stackIds);
if (!consumerStackDerivedPoolIds.isEmpty()) {
log.info("Updating stacked entitlements for {} consumers...", consumerStackDerivedPoolIds.size());
for (Consumer consumer : consumerStackedEnts.keySet()) {
Set<String> subPoolIds = consumerStackDerivedPoolIds.get(consumer.getId());
if (subPoolIds != null && !subPoolIds.isEmpty()) {
// Resolve pool IDs...
Collection<Pool> subPools = this.poolCurator.listAllByIds(subPoolIds).list();
// Invoke the rules engine to update the affected pools
if (subPools != null && !subPools.isEmpty()) {
log.debug("Updating {} stacking pools for consumer: {}", subPools.size(), consumer);
this.poolRules.updatePoolsFromStack(consumer, subPools, alreadyDeletedPoolIds, true);
}
}
}
}
}
this.consumerCurator.flush();
// Hydrate remaining consumer pools so we can skip some extra work during serialization
Set<Pool> poolsToHydrate = new HashSet<>();
for (Consumer consumer : consumerStackedEnts.keySet()) {
for (Entitlement entitlement : consumer.getEntitlements()) {
poolsToHydrate.add(entitlement.getPool());
}
}
this.productCurator.hydratePoolProvidedProducts(poolsToHydrate);
// Fire post-unbind events for revoked entitlements
log.info("Firing post-unbind events for {} entitlements...", entitlements.size());
for (Entitlement entitlement : entitlements) {
this.enforcer.postUnbind(entitlement.getConsumer(), this, entitlement);
}
// Recalculate status for affected consumers
log.info("Recomputing status for {} consumers", consumerStackedEnts.size());
int i = 0;
for (Consumer consumer : consumerStackedEnts.keySet()) {
this.complianceRules.getStatus(consumer);
if (++i % 1000 == 0) {
this.consumerCurator.flush();
}
}
this.consumerCurator.flush();
log.info("All statuses recomputed");
}
// Impl note:
// We don't need to fire entitlement revocation events, since they're all being revoked as
// a consequence of the pools being deleted.
// Fire pool deletion events
// This part hurts so much. Because we output the whole entity, we have to fetch the bloody
// things before we delete them.
log.info("Firing pool deletion events for {} pools...", pools.size());
for (Pool pool : pools) {
this.sink.queueEvent(this.eventFactory.poolDeleted(pool));
}
} else {
log.info("Skipping pool deletion; no pools to delete");
}
}
Aggregations