/**
* 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.entitlement;
import org.candlepin.audit.EventFactory;
import org.candlepin.audit.EventSink;
import org.candlepin.common.config.Configuration;
import org.candlepin.config.ConfigProperties;
import org.candlepin.controller.ProductManager;
import org.candlepin.model.Consumer;
import org.candlepin.model.ConsumerCurator;
import org.candlepin.model.OwnerCurator;
import org.candlepin.model.OwnerProductCurator;
import org.candlepin.model.Entitlement;
import org.candlepin.model.Pool;
import org.candlepin.model.PoolCurator;
import org.candlepin.model.PoolQuantity;
import org.candlepin.model.Product;
import org.candlepin.model.ProductCurator;
import org.candlepin.model.ProductShareCurator;
import org.candlepin.policy.ValidationError;
import org.candlepin.policy.ValidationResult;
import org.candlepin.policy.js.JsRunner;
import org.candlepin.policy.js.JsonJsContext;
import org.candlepin.policy.js.RuleExecutionException;
import org.candlepin.policy.js.RulesObjectMapper;
import org.candlepin.util.DateSource;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.inject.Inject;
import org.slf4j.LoggerFactory;
import org.xnap.commons.i18n.I18n;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Enforces entitlement rules for normal (non-manifest) consumers.
*/
public class EntitlementRules extends AbstractEntitlementRules implements Enforcer {
@Inject
public EntitlementRules(DateSource dateSource,
JsRunner jsRules, I18n i18n, Configuration config, ConsumerCurator consumerCurator,
PoolCurator poolCurator, ProductCurator productCurator, RulesObjectMapper mapper,
OwnerCurator ownerCurator, OwnerProductCurator ownerProductCurator,
ProductShareCurator productShareCurator, ProductManager productManager, EventSink eventSink,
EventFactory eventFactory) {
this.jsRules = jsRules;
this.dateSource = dateSource;
this.i18n = i18n;
this.attributesToRules = null;
this.config = config;
this.consumerCurator = consumerCurator;
this.poolCurator = poolCurator;
this.productCurator = productCurator;
this.objectMapper = mapper;
this.ownerCurator = ownerCurator;
this.ownerProductCurator = ownerProductCurator;
this.shareCurator = productShareCurator;
this.productManager = productManager;
this.eventSink = eventSink;
this.eventFactory = eventFactory;
log = LoggerFactory.getLogger(EntitlementRules.class);
jsRules.init("entitlement_name_space");
}
@Override
public ValidationResult preEntitlement(Consumer consumer, Pool entitlementPool,
Integer quantity) {
return preEntitlement(consumer, entitlementPool, quantity, CallerType.UNKNOWN);
}
@Override
public ValidationResult preEntitlement(Consumer consumer, Pool entitlementPool,
Integer quantity, CallerType caller) {
return preEntitlement(consumer, getHost(consumer), entitlementPool, quantity, caller);
}
public ValidationResult preEntitlement(Consumer consumer, Consumer host,
Pool entitlementPool, Integer quantity, CallerType caller) {
List<PoolQuantity> poolQuantities = new ArrayList<PoolQuantity>();
poolQuantities.add(new PoolQuantity(entitlementPool, quantity));
return preEntitlement(consumer, host, poolQuantities, caller).get(entitlementPool.getId());
}
@Override
public Map<String, ValidationResult> preEntitlement(Consumer consumer,
Collection<PoolQuantity> entitlementPoolQuantities,
CallerType caller) {
return preEntitlement(consumer, getHost(consumer), entitlementPoolQuantities, caller);
}
@Override
public Map<String, ValidationResult> preEntitlement(Consumer consumer, Consumer host,
Collection<PoolQuantity> entitlementPoolQuantities, CallerType caller) {
Map<String, ValidationResult> resultMap = new HashMap<String, ValidationResult>();
/* This document describes the java script portion of the pre entitlement rules check:
* http://www.candlepinproject.org/docs/candlepin/pre_entitlement_rules_check.html
* As described in the document, none of the checks are related to share binds, so we
* skip that step for share consumers.
*/
if (!consumer.isShare()) {
JsonJsContext args = new JsonJsContext(objectMapper);
args.put("consumer", consumer);
args.put("hostConsumer", host);
args.put("consumerEntitlements", consumer.getEntitlements());
args.put("standalone", config.getBoolean(ConfigProperties.STANDALONE));
args.put("poolQuantities", entitlementPoolQuantities);
args.put("caller", caller.getLabel());
args.put("log", log, false);
String json = jsRules.runJsFunction(String.class, "validate_pools_batch", args);
TypeReference<Map<String, ValidationResult>> typeref =
new TypeReference<Map<String, ValidationResult>>() {};
try {
resultMap = objectMapper.toObject(json, typeref);
for (PoolQuantity poolQuantity : entitlementPoolQuantities) {
if (!resultMap.containsKey(poolQuantity.getPool().getId())) {
resultMap.put(poolQuantity.getPool().getId(), new ValidationResult());
log.info("no result returned for pool: {}", poolQuantity.getPool());
}
}
}
catch (Exception e) {
throw new RuleExecutionException(e);
}
}
for (PoolQuantity poolQuantity : entitlementPoolQuantities) {
if (consumer.isShare()) {
ValidationResult result = new ValidationResult();
resultMap.put(poolQuantity.getPool().getId(), result);
validatePoolSharingEligibility(result, poolQuantity.getPool());
}
finishValidation(resultMap.get(poolQuantity.getPool().getId()),
poolQuantity.getPool(), poolQuantity.getQuantity());
}
return resultMap;
}
@Override
public List<Pool> filterPools(Consumer consumer, List<Pool> pools, boolean showAll) {
JsonJsContext args = new JsonJsContext(objectMapper);
Map<String, ValidationResult> resultMap = new HashMap<String, ValidationResult>();
if (!consumer.isShare()) {
args.put("consumer", consumer);
args.put("hostConsumer", getHost(consumer));
args.put("consumerEntitlements", consumer.getEntitlements());
args.put("standalone", config.getBoolean(ConfigProperties.STANDALONE));
args.put("pools", pools);
args.put("caller", CallerType.LIST_POOLS.getLabel());
args.put("log", log, false);
String json = jsRules.runJsFunction(String.class, "validate_pools_list", args);
TypeReference<Map<String, ValidationResult>> typeref =
new TypeReference<Map<String, ValidationResult>>() {};
try {
resultMap = objectMapper.toObject(json, typeref);
}
catch (Exception e) {
throw new RuleExecutionException(e);
}
}
List<Pool> filteredPools = new LinkedList<Pool>();
for (Pool pool : pools) {
ValidationResult result;
if (consumer.isShare()) {
result = new ValidationResult();
resultMap.put(pool.getId(), result);
validatePoolSharingEligibility(result, pool);
}
else {
result = resultMap.get(pool.getId());
}
finishValidation(result, pool, 1);
if (result.isSuccessful() && (!result.hasWarnings() || showAll)) {
filteredPools.add(pool);
}
else if (log.isDebugEnabled()) {
log.debug("Omitting pool due to failed rules: " + pool.getId());
if (result.hasErrors()) {
log.debug("\tErrors: " + result.getErrors());
}
if (result.hasWarnings()) {
log.debug("\tWarnings: " + result.getWarnings());
}
}
}
return filteredPools;
}
private Consumer getHost(Consumer consumer) {
Consumer host = consumer.hasFact("virt.uuid") ? consumerCurator.getHost(
consumer.getFact("virt.uuid"), consumer.getOwner()) : null;
return host;
}
private void finishValidation(ValidationResult result, Pool pool, Integer quantity) {
validatePoolQuantity(result, pool, quantity);
if (pool.isExpired(dateSource)) {
result.addError(new ValidationError(i18n.tr("Subscriptions for {0} expired on: {1}",
pool.getProductId(),
pool.getEndDate())));
}
}
@Override
public ValidationResult update(Consumer consumer, Entitlement entitlement, Integer change) {
ValidationResult result = new ValidationResult();
if (!consumer.getType().isManifest()) {
Pool pool = entitlement.getPool();
// multi ent check
if (!"yes".equalsIgnoreCase(pool.getProductAttributeValue(Pool.Attributes.MULTI_ENTITLEMENT)) &&
entitlement.getQuantity() + change > 1) {
result.addError(new ValidationError(
EntitlementRulesTranslator.PoolErrorKeys.MULTI_ENTITLEMENT_UNSUPPORTED));
}
if (!consumer.isGuest()) {
String multiplier = pool.getProductAttributeValue(Product.Attributes.INSTANCE_MULTIPLIER);
if (multiplier != null) {
int instanceMultiplier = Integer.parseInt(multiplier);
// quantity should be divisible by multiplier
if ((entitlement.getQuantity() + change) % instanceMultiplier != 0) {
result.addError(new ValidationError(
EntitlementRulesTranslator.PoolErrorKeys.QUANTITY_MISMATCH
));
}
}
}
}
finishValidation(result, entitlement.getPool(), change);
return result;
}
}