use of org.candlepin.model.Pool 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");
}
}
use of org.candlepin.model.Pool in project candlepin by candlepin.
the class CandlepinPoolManager method createAndEnrichPools.
@Override
public Pool createAndEnrichPools(Pool pool, List<Pool> existingPools) {
List<Pool> pools = poolRules.createAndEnrichPools(pool, existingPools);
log.debug("Creating {} pools: ", pools.size());
for (Pool p : pools) {
createPool(p);
}
return pool;
}
use of org.candlepin.model.Pool in project candlepin by candlepin.
the class Entitler method bindByProducts.
/**
* Force option is used to heal entire org
*
* @param data AutobindData encapsulating data required for an autobind request
* @param force heal host even if it has autoheal disabled
* @return List of Entitlements
* @throws AutobindDisabledForOwnerException when an autobind attempt is made and the owner
* has it disabled.
*/
@Transactional
public List<Entitlement> bindByProducts(AutobindData data, boolean force) throws AutobindDisabledForOwnerException {
Consumer consumer = data.getConsumer();
Owner owner = data.getOwner();
if (!consumer.isDev() && owner.isAutobindDisabled()) {
log.info("Skipping auto-attach for consumer '{}'. Auto-attach is disabled for owner {}.", consumer, owner.getKey());
throw new AutobindDisabledForOwnerException(i18n.tr("Auto-attach is disabled for owner \"{0}\".", owner.getKey()));
}
// entitlements based on the planned design of the subscriptions
if (consumer.hasFact("virt.uuid") && !consumer.isDev()) {
String guestUuid = consumer.getFact("virt.uuid");
// Remove any expired unmapped guest entitlements
revokeUnmappedGuestEntitlements(consumer);
// Scoped to the consumer's organization. Even in the event of sharing, a guest in one
// organization should not be able to compel a heal in an another organization
Consumer host = consumerCurator.getHost(consumer, consumer.getOwnerId());
if (host != null && (force || host.isAutoheal())) {
log.info("Attempting to heal host machine with UUID \"{}\" for guest with UUID \"{}\"", host.getUuid(), consumer.getUuid());
if (!StringUtils.equals(host.getServiceLevel(), consumer.getServiceLevel())) {
log.warn("Host with UUID \"{}\" has a service level \"{}\" that does not match" + " that of the guest with UUID \"{}\" and service level \"{}\"", host.getUuid(), host.getServiceLevel(), consumer.getUuid(), consumer.getServiceLevel());
}
try {
List<Entitlement> hostEntitlements = poolManager.entitleByProductsForHost(consumer, host, data.getOnDate(), data.getPossiblePools());
log.debug("Granted host {} entitlements", hostEntitlements.size());
sendEvents(hostEntitlements);
} catch (Exception e) {
// log and continue, this should NEVER block
log.debug("Healing failed for host UUID {} with message: {}", host.getUuid(), e.getMessage());
}
/* Consumer is stale at this point. Note that we use find() instead of
* findByUuid() or getConsumer() since the latter two methods are secured
* to a specific host principal and bindByProducts can get called when
* a guest is switching hosts */
consumer = consumerCurator.find(consumer.getId());
data.setConsumer(consumer);
}
}
if (consumer.isDev()) {
if (config.getBoolean(ConfigProperties.STANDALONE) || !poolCurator.hasActiveEntitlementPools(consumer.getOwnerId(), null)) {
throw new ForbiddenException(i18n.tr("Development units may only be used on hosted servers" + " and with orgs that have active subscriptions."));
}
// Look up the dev pool for this consumer, and if not found
// create one. If a dev pool already exists, remove it and
// create a new one.
String sku = consumer.getFact("dev_sku");
Pool devPool = poolCurator.findDevPool(consumer);
if (devPool != null) {
poolManager.deletePool(devPool);
}
devPool = poolManager.createPool(assembleDevPool(consumer, owner, sku));
data.setPossiblePools(Arrays.asList(devPool.getId()));
data.setProductIds(new String[] { sku });
}
// Attempt to create entitlements:
try {
// the pools are only used to bind the guest
List<Entitlement> entitlements = poolManager.entitleByProducts(data);
log.debug("Created entitlements: {}", entitlements);
return entitlements;
} catch (EntitlementRefusedException e) {
// TODO: Could be multiple errors, but we'll just report the first one for now
String productId = "Unknown Product";
if (data.getProductIds().length > 0) {
productId = data.getProductIds()[0];
}
throw new ForbiddenException(messageTranslator.productErrorToMessage(productId, e.getResults().values().iterator().next().getErrors().get(0)));
}
}
use of org.candlepin.model.Pool in project candlepin by candlepin.
the class Refresher method run.
public void run() {
// If products were specified on the refresher, lookup any subscriptions
// using them, regardless of organization, and trigger a refresh for those
// specific subscriptions.
Set<Subscription> subscriptions = new HashSet<>();
for (Product product : products) {
// TODO: This adapter call is not implemented in prod, and cannot be. We plan
// to fix this whole code path in near future by looking for pools using the
// given products to be refreshed.
List<Subscription> subs = subAdapter.getSubscriptions(product.toDTO());
log.debug("Will refresh {} subscriptions in all orgs using product: ", subs.size(), product.getId());
if (log.isDebugEnabled()) {
for (Subscription s : subs) {
Owner so = s.getOwner();
if (so == null || so.getKey() == null) {
log.debug(" Received a subscription without a well-defined owner: {}", s.getId());
continue;
}
if (!this.owners.containsKey(so.getKey())) {
log.debug(" {}", s);
}
}
}
subscriptions.addAll(subs);
}
for (Subscription subscription : subscriptions) {
// drop any subs for owners in our owners list. we'll get them with the full
// refreshPools call.
Owner so = subscription.getOwner();
// This probably shouldn't ever happen, but let's make sure it doesn't anyway.
if (so == null || so.getKey() == null) {
log.error("Received a subscription without a well-defined owner: {}", subscription.getId());
continue;
}
if (this.owners.containsKey(so.getKey())) {
log.debug("Skipping subscription \"{}\" for owner: {}", subscription.getId(), so);
continue;
}
/*
* on the off chance that this is actually a new subscription, make
* the required pools. this shouldn't happen; we should really get a
* refresh pools by owner call for it, but why not handle it, just
* in case!
*
* Regenerate certificates here, that way if it fails, the whole
* thing rolls back. We don't want to refresh without marking ents
* dirty, they will never get regenerated
*/
Pool masterPool = poolManager.convertToMasterPool(subscription);
poolManager.refreshPoolsForMasterPool(masterPool, true, lazy, Collections.<String, Product>emptyMap());
}
for (Owner owner : this.owners.values()) {
poolManager.refreshPoolsWithRegeneration(this.subAdapter, owner, this.lazy);
poolManager.recalculatePoolQuantitiesForOwner(owner);
ownerManager.refreshContentAccessMode(this.ownerAdapter, owner);
ownerManager.updateRefreshDate(owner);
}
}
use of org.candlepin.model.Pool in project candlepin by candlepin.
the class RevocationOp method execute.
@Transactional
public void execute(PoolManager poolManager) {
Collection<Pool> overflowing = new ArrayList<>();
for (Pool pool : pools) {
if (pool.isOverflowing()) {
overflowing.add(pool);
}
}
if (overflowing.isEmpty()) {
return;
}
overflowing = poolCurator.lockAndLoad(overflowing);
for (Pool pool : overflowing) {
poolNewConsumed.put(pool, pool.getConsumed());
List<Pool> shared = poolCurator.listSharedPoolsOf(pool);
if (!shared.isEmpty()) {
sharedPools.put(pool, shared);
// first determine shared pool counts where allotted units are not in use
reduceSharedPools(pool);
}
// we then start revoking the existing entitlements
determineExcessEntitlements(pool);
}
// revoke the entitlements amassed above
poolManager.revokeEntitlements(new ArrayList<>(entitlementsToRevoke));
// We have to wait until we get here so that share pool entitlements we want revoked are gone
for (Entitlement entitlement : shareEntitlementsToAdjust.keySet()) {
try {
poolManager.adjustEntitlementQuantity(entitlement.getConsumer(), entitlement, shareEntitlementsToAdjust.get(entitlement).intValue());
} catch (EntitlementRefusedException e) {
// TODO: Could be multiple errors, but we'll just report the first one for now:
throw new ForbiddenException(e.getResults().values().iterator().next().getErrors().get(0).toString());
}
}
}
Aggregations