/**
* Copyright (c) 2009 - 2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package org.candlepin.policy.js.pool;
import org.candlepin.controller.PoolManager;
import org.candlepin.model.Branding;
import org.candlepin.model.Consumer;
import org.candlepin.model.Entitlement;
import org.candlepin.model.Owner;
import org.candlepin.model.OwnerProductCurator;
import org.candlepin.model.Pool;
import org.candlepin.model.Product;
import org.candlepin.model.ProductCurator;
import org.candlepin.model.SourceStack;
import org.candlepin.model.SourceSubscription;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Post Entitlement Helper, and some attribute utility methods.
*/
public class PoolHelper {
private PoolHelper() {
}
/**
* Both products and pools can carry attributes, we need to
* trigger rules for each. In this map, pool attributes will
* override product attributes, should the same key be set
* for both.
*
* @param pool Pool can be null.
* @return Map of all attribute names and values. Pool attributes
* have priority.
*/
public static Map<String, String> getFlattenedAttributes(Pool pool) {
Map<String, String> attributes = new HashMap<String, String>();
if (pool != null) {
attributes.putAll(pool.getProductAttributes());
attributes.putAll(pool.getAttributes());
}
return attributes;
}
/**
* Create a pool only for virt guests of a particular host consumer.
*
* @param pools Pools these host restricted pools are being derived from.
* @return pools the created pools
*/
public static List<Pool> createHostRestrictedPools(PoolManager poolManager, Consumer consumer,
List<Pool> pools, Map<String, Entitlement> sourceEntitlements,
Map<String, Map<String, String>> attributeMaps, ProductCurator productCurator) {
List<Pool> poolsToCreate = new ArrayList<Pool>();
List<Pool> poolsToUpdateFromStack = new ArrayList<Pool>();
for (Pool pool : pools) {
Product product = pool.getProduct();
Pool consumerSpecificPool = null;
Map<String, String> attributes = attributeMaps.get(pool.getId());
String quantity = attributes.get("virt_limit");
if (pool.getDerivedProduct() == null) {
consumerSpecificPool = createPool(
product,
pool.getOwner(),
quantity,
pool.getStartDate(),
pool.getEndDate(),
pool.getContractNumber(),
pool.getAccountNumber(),
pool.getOrderNumber(),
productCurator.getPoolProvidedProductsCached(pool),
sourceEntitlements.get(pool.getId()));
}
else {
// If a derived product is on the pool, we want to define the
// derived pool
// with the derived product data that was defined on the parent
// pool,
// allowing the derived pool to have different attributes than
// the parent.
consumerSpecificPool = createPool(
pool.getDerivedProduct(),
pool.getOwner(),
quantity,
pool.getStartDate(),
pool.getEndDate(),
pool.getContractNumber(),
pool.getAccountNumber(),
pool.getOrderNumber(),
productCurator.getPoolDerivedProvidedProductsCached(pool),
sourceEntitlements.get(pool.getId()));
}
consumerSpecificPool.setAttribute(Pool.Attributes.REQUIRES_HOST, consumer.getUuid());
consumerSpecificPool.setAttribute(Pool.Attributes.DERIVED_POOL, "true");
consumerSpecificPool.setAttribute(Pool.Attributes.VIRT_ONLY, "true");
consumerSpecificPool.setAttribute(Pool.Attributes.PHYSICAL_ONLY, "false");
// If the originating pool is stacked, we want to create the derived
// pool based on the entitlements in the stack, instead of just the
// parent pool.
if (pool.isStacked()) {
poolsToUpdateFromStack.add(consumerSpecificPool);
}
else {
// attribute per 795431, useful for rolling up pool info in headpin
consumerSpecificPool.setAttribute(Pool.Attributes.SOURCE_POOL_ID, pool.getId());
consumerSpecificPool.setSourceSubscription(new SourceSubscription(pool.getSubscriptionId(),
sourceEntitlements.get(pool.getId()).getId()));
}
poolsToCreate.add(consumerSpecificPool);
}
if (CollectionUtils.isNotEmpty(poolsToUpdateFromStack)) {
poolManager.updatePoolsFromStack(consumer, poolsToUpdateFromStack);
}
return poolManager.createPools(poolsToCreate);
}
/**
* Copies the provided products from a source pool to a bonus pool. The
* logic is not completely straightforward.
*
* The source has so called 'main product' (source.getProduct()) and also
* derived product source.getDerivedProduct(). It also has 'main provided products'
* source.getProvidedProducts().
*
* During the copy, the following takes place.
*
* If the source has derived product, then the destination will receive source's
* derived product as main product (destination.getProduct). Also, it will receive
* source's derived provided products as destination main provided products.
* In other words:
*
* IF source.getDerivedProduct is null
*
* source.product >>> destination.product
* source.providedProducts >>> destionation.providedProducts
*
* IF source.getDerivedProduct is not null
*
* source.derivedProduct >>> destination.product
* source.derivedProvidedProducts >>> destination.providedProducts
*
* @param source subscription
* @param destination bonus pool
*/
private static void copyProvidedProducts(Pool source, Pool destination,
OwnerProductCurator curator, ProductCurator productCurator) {
Set<Product> products;
// If the source pool has id filled, we assume that it is stored in the
// database and also assume that the provided Products or
// derived provided Products are linked with the Pool in the database!
if (source.getId() != null) {
if (source.getDerivedProduct() != null) {
products = productCurator.getPoolDerivedProvidedProductsCached(source);
}
else {
products = productCurator.getPoolProvidedProductsCached(source);
}
}
// Otherwise we just use the products attached directly on the entity
else {
if (source.getDerivedProduct() != null) {
products = source.getDerivedProvidedProducts();
}
else {
products = source.getProvidedProducts();
}
}
for (Product product : products) {
// If no result is returned here, the product has not been correctly imported
// into the organization, indicating a problem somewhere in the sync or refresh code:
Product destprod = curator.getProductById(destination.getOwner(), product.getId());
if (destprod == null) {
throw new RuntimeException("Product " + product.getId() +
" has not been imported into org " + destination.getOwner().getKey());
}
destination.addProvidedProduct(destprod);
}
}
public static Pool clonePool(Pool sourcePool, Product product, String quantity,
Map<String, String> attributes, String subKey, OwnerProductCurator curator,
Entitlement sourceEntitlement, ProductCurator productCurator) {
Pool pool = createPool(product, sourcePool.getOwner(), quantity,
sourcePool.getStartDate(), sourcePool.getEndDate(),
sourcePool.getContractNumber(), sourcePool.getAccountNumber(),
sourcePool.getOrderNumber(), new HashSet<Product>(), sourceEntitlement);
pool.setSourceSubscription(new SourceSubscription(sourcePool.getSubscriptionId(), subKey));
copyProvidedProducts(sourcePool, pool, curator, productCurator);
// Add in the new attributes
for (Entry<String, String> entry : attributes.entrySet()) {
pool.setAttribute(entry.getKey(), entry.getValue());
}
for (Branding b : sourcePool.getBranding()) {
pool.getBranding().add(new Branding(b.getProductId(), b.getType(), b.getName()));
}
return pool;
}
private static Pool createPool(Product product, Owner owner, String quantity, Date startDate,
Date endDate, String contractNumber, String accountNumber, String orderNumber,
Set<Product> providedProducts, Entitlement sourceEntitlement) {
Long q = Pool.parseQuantity(quantity);
Pool pool = new Pool(
owner,
product,
new HashSet<Product>(),
q,
startDate,
endDate,
contractNumber,
accountNumber,
orderNumber
);
// Must be sure to copy the provided products, not try to re-use them directly:
pool.setProvidedProducts(providedProducts);
if (sourceEntitlement != null && sourceEntitlement.getPool() != null) {
if (sourceEntitlement.getPool().isStacked()) {
pool.setSourceStack(new SourceStack(sourceEntitlement.getConsumer(),
sourceEntitlement.getPool().getStackId()));
}
else {
pool.setSourceEntitlement(sourceEntitlement);
}
}
// temp - we need a way to specify this on the product
pool.setAttribute(Pool.Attributes.REQUIRES_CONSUMER_TYPE, "system");
return pool;
}
public static boolean checkForOrderChanges(Pool existingPool, Pool pool) {
return (!StringUtils.equals(existingPool.getOrderNumber(), pool.getOrderNumber()) ||
!StringUtils.equals(existingPool.getAccountNumber(), pool.getAccountNumber()) ||
!StringUtils.equals(existingPool.getContractNumber(), pool.getContractNumber()));
}
}