/*******************************************************************************
* Copyright (c) 2014 Imperial College London
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Raul Castro Fernandez - initial API and implementation
******************************************************************************/
package uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.evaluate;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections4.map.MultiValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.PolicyRule;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.PolicyRules;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.operator.AllOperators;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.operator.OneOperator;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.operator.Operator;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.InfrastructureAdaptor;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.MetricReadingProvider;
import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.util.SystemTimeReference;
/**
* Evaluator for multiple policy rules. The class relies on an infrastructure adaptor
* to inform the platform the new size of a given operator and query it to determine
* its current size, when needed. Using an adaptor instigates decoupling from the
* rest of the platform and facilitates testing.
*
* @author mrouaux
*/
public class PolicyRulesEvaluator
extends AbstractEvaluator<PolicyRules, InfrastructureAdaptor, MetricReadingProvider>{
private static final Logger logger =
LoggerFactory.getLogger(PolicyRulesEvaluator.class);
private MultiValueMap<Integer, PolicyRuleEvaluator> evaluators;
private List<PolicyRuleEvaluator> allEvaluators;
/**
* Convenience constructor
* @param rules scaling policy rules to evaluate
* @param adaptor infrastructure adaptor (allows evaluators to delegate resizing
* of a particular operator as a consequence of an action triggered by some
* of the rules being evaluated). An adaptor is used to decouple policy
* evaluation from execution.
*/
public PolicyRulesEvaluator(final PolicyRules rules,
final InfrastructureAdaptor adaptor) {
super(rules, adaptor);
initializeEvaluators();
}
/**
*
* @param provider
*/
@Override
public synchronized void evaluate(MetricReadingProvider provider) {
routeReadingToEvaluators(provider);
}
/**
* Initializes the data structure that allows for the mapping of metric readings
* to the correct evaluators given the operator identifier in the reading and
* the operator(s) referenced by each scaling rule.
*/
protected void initializeEvaluators() {
logger.info("Initialising evaluators for scaling policy rules...");
this.evaluators = new MultiValueMap<Integer, PolicyRuleEvaluator>();
this.allEvaluators = new ArrayList<PolicyRuleEvaluator>();
// Rules that reference concrete operators are processed first
PolicyRules rules = getEvalSubject();
if(rules != null) {
for(PolicyRule rule : rules) {
Operator op = rule.getOperator();
if(op instanceof OneOperator) {
Integer operatorId =
Integer.valueOf(((OneOperator)op).getId());
logger.debug("Creating evaluator for operator [" + operatorId + "]");
logger.debug("Binding to rule " + rule.toString());
evaluators.put(operatorId,
new PolicyRuleEvaluator(
rule, getEvalAdaptor(),
new SystemTimeReference()));
}
}
// Now process wildcard rules that apply to all operators
for(PolicyRule rule : rules) {
Operator op = rule.getOperator();
if(op instanceof AllOperators) {
logger.debug("Creating evaluator for all operators");
logger.debug("Binding to rule " + rule.toString());
allEvaluators.add(
new PolicyRuleEvaluator(
rule, getEvalAdaptor(),
new SystemTimeReference()));
}
}
// Finally, the evaluators that apply to all operators should be referenced
// by all operator identifiers in the map for OneOperator instances. This
// facilitates the routing to the correct evaluators for a metric reading
for(Integer operatorId : evaluators.keySet()) {
for(PolicyRuleEvaluator allEvaluator : allEvaluators) {
evaluators.put(operatorId, allEvaluator);
}
}
}
logger.info("Done initialising evaluators for scaling policy rules");
}
/**
* Routes metric reading to the appropriate evaluator given the operator
* identifier in the reading and the operator(s) referenced by each scaling
* rule.
* @param provider Metric reading provider
*/
protected void routeReadingToEvaluators(MetricReadingProvider provider) {
if((evaluators != null) && (provider != null)) {
Integer operatorId = Integer.valueOf(provider.getOperatorId());
logger.debug("Received metric reading for operator ["
+ operatorId.intValue() + "]");
// If there is at least one operator for the identifier, then forward
// to these evaluators (which will also include any rules with wildcards)
if(evaluators.containsKey(operatorId)) {
for(PolicyRuleEvaluator e : evaluators.getCollection(operatorId)) {
logger.debug("Routing to evaluator " + e.toString());
routeReadingToEvaluator(e, provider);
}
} else {
// No specific rules for the operator, only forward to evaluators
// not associated to any concrete operator
for(PolicyRuleEvaluator allEvaluator : allEvaluators) {
logger.debug("Routing to evaluator " + allEvaluator.toString());
routeReadingToEvaluator(allEvaluator, provider);
}
}
}
}
protected void routeReadingToEvaluator(
final PolicyRuleEvaluator evaluator, final MetricReadingProvider provider) {
evaluator.evaluate(provider);
}
}