/**
* Copyright 2013 Technische Universitat Wien (TUW), Distributed SystemsGroup
* E184. * This work was partially supported by the European Commission in terms
* of the CELAR FP7 project (FP7-ICT-2011-8 #317790).
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* Author : Georgiana Copil - e.copil@dsg.tuwien.ac.at
*/
package at.ac.tuwien.dsg.rSybl.planningEngine.resourcesLevelControl;
import at.ac.tuwien.dsg.csdg.DependencyGraph;
import at.ac.tuwien.dsg.csdg.Node;
import at.ac.tuwien.dsg.csdg.Relationship;
import at.ac.tuwien.dsg.csdg.elasticityInformation.ElasticityRequirement;
import at.ac.tuwien.dsg.csdg.elasticityInformation.elasticityRequirements.BinaryRestriction;
import at.ac.tuwien.dsg.csdg.elasticityInformation.elasticityRequirements.Constraint;
import at.ac.tuwien.dsg.csdg.elasticityInformation.elasticityRequirements.SYBLSpecification;
import at.ac.tuwien.dsg.csdg.inputProcessing.multiLevelModel.abstractModelXML.SYBLDirectiveMappingFromXML;
import at.ac.tuwien.dsg.rSybl.cloudInteractionUnit.api.EnforcementAPIInterface;
import at.ac.tuwien.dsg.rSybl.cloudInteractionUnit.utils.Configuration;
import at.ac.tuwien.dsg.rSybl.cloudInteractionUnit.utils.RuntimeLogger;
import at.ac.tuwien.dsg.rSybl.dataProcessingUnit.api.MonitoringAPIInterface;
import at.ac.tuwien.dsg.rSybl.planningEngine.utils.PlanningLogger;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
/**
*
* @author Georgiana
*/
public class ResourcesLevelControl implements Runnable {
private Timer t;
private int REFRESH_PERIOD = 350000;
private DependencyGraph dependencyGraph;
private EnforcementAPIInterface enforcementAPI;
private MonitoringAPIInterface monitoringAPI;
private double stdDevThreshold = 23.0;
private HashMap<String, HashMap<Double, HashMap<String, Double>>> expectedEffects = new HashMap<String, HashMap<Double, HashMap<String, Double>>>();
private HashMap<String, HashMap<String, Double>> solution = new HashMap<String, HashMap<String, Double>>();
public ResourcesLevelControl(EnforcementAPIInterface enforcementAPIInterface, MonitoringAPIInterface monitoringAPIInterface, DependencyGraph dependencyGraph) {
enforcementAPI = enforcementAPIInterface;
monitoringAPI = monitoringAPIInterface;
this.dependencyGraph = dependencyGraph;
REFRESH_PERIOD = Configuration.getRefreshPeriod();
readResourceActionEffects();
}
public void start() {
run();
}
public void stop() {
boolean ok = false;
while (!ok) {
if (enforcementAPI.getPluginsExecutingActions().size() > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(RuntimeLogger.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
ok = true;
}
}
t.purge();
t.cancel();
}
public void replaceDependencyGraph(DependencyGraph dependencyGraph) {
this.dependencyGraph = dependencyGraph;
}
public void checkForOverusedResourcesAndScale() {
List<Node> serviceUnits = dependencyGraph.getAllServiceUnits();
for (Node serviceUnit : serviceUnits) {
List<String> metrics = monitoringAPI.getAvailableMetrics(serviceUnit);
double remainingCost = this.remainingCost(serviceUnit);
for (String metric : metrics) {
if (metric.toLowerCase().contains("usage") || metric.toLowerCase().contains("usedpercent")) {
try {
List<Node> associatedVMs = serviceUnit.getAllRelatedNodesOfType(Relationship.RelationshipType.HOSTED_ON_RELATIONSHIP, Node.NodeType.VIRTUAL_MACHINE);
double max = 0;
Node maxVm = null;
boolean foundSmaller = false;
HashMap<String, Double> currentValues = new HashMap<>();
double sum = 0;
for (Node vm : associatedVMs) {
try {
double vmMetric = monitoringAPI.getMetricValue(metric, vm);
sum += vmMetric;
if (vmMetric > max) {
max = vmMetric;
maxVm = vm;
}
if (vmMetric < 50) {
foundSmaller = true;
}
currentValues.put(vm.getId(), vmMetric);
} catch (Exception ex) {
PlanningLogger.logger.info("Error in resources control, when converting values for metrics "+ ex);
}
}
double val = sum/associatedVMs.size();
if (maxVm != null && max > 90 && foundSmaller) {
searchAction(serviceUnit, maxVm, metric, val, remainingCost,currentValues);
} else {
double stdDev = computeStdDeviation(currentValues);
if (stdDev > stdDevThreshold) {
double maxDiff = -2;
Node vmToFix = null;
for (Node vm : associatedVMs) {
double vmValue = currentValues.get(vm.getId());
if (maxDiff < Math.abs(vmValue - val)) {
maxDiff = Math.abs(vmValue - val);
vmToFix = vm;
}
}
if (vmToFix != null) {
searchAction(serviceUnit, vmToFix, metric, stdDev, remainingCost,currentValues);
}
}
}
} catch (Exception ex) {
// ex.printStackTrace();
PlanningLogger.logger.info("Could not get metric " + metric + " for node " + serviceUnit + " ex " + ex.getLocalizedMessage());
}
}
}
executeGeneratedSolution(remainingCost);
}
}
private void executeGeneratedSolution(double remainingCost) {
for (String vmID : solution.keySet()) {
if (solution.size() != 0) {
Object[] parameters = new Object[2 * solution.get(vmID).size() + 1];
int i = 0;
for (String type : this.solution.get(vmID).keySet()) {
parameters[i] = type;
parameters[i + 1] = solution.get(vmID).get(type);
i += 2;
}
parameters[i] = remainingCost;
enforcementAPI.enforceAction("scaleVertically", dependencyGraph.getNodeWithID(vmID), parameters);
}
}
solution = new HashMap<>();
}
public double remainingCost(Node serviceUnit) {
List<ElasticityRequirement> requirements = new ArrayList<>();
Node serviceTopology = dependencyGraph.findParentNode(serviceUnit.getId());
Node cloudService = dependencyGraph.findParentNode(serviceTopology.getId());
requirements.addAll(serviceUnit.getElasticityRequirements());
requirements.addAll(serviceTopology.getElasticityRequirements());
requirements.addAll(cloudService.getElasticityRequirements());
double minCost = 1000000;
for (ElasticityRequirement elasticityRequirement : requirements) {
Node unit = dependencyGraph.getNodeWithID(elasticityRequirement.getAnnotation().getEntityID());
SYBLSpecification syblSpecification = SYBLDirectiveMappingFromXML.mapFromSYBLAnnotation(elasticityRequirement.getAnnotation());
for (Constraint c : syblSpecification.getConstraint()) {
BinaryRestriction restriction = c.getToEnforce().getBinaryRestriction().get(0).getBinaryRestrictions().get(0);
if (restriction.getLeftHandSide().getMetric() != null && restriction.getLeftHandSide().getMetric().equalsIgnoreCase("cost")) {
try {
double currentCost = monitoringAPI.getMetricValue(restriction.getLeftHandSide().getMetric(), unit);
if (minCost > (Double.parseDouble(restriction.getRightHandSide().getNumber()) - currentCost)) {
minCost = Double.parseDouble(restriction.getRightHandSide().getNumber()) - currentCost;
}
} catch (Exception ex) {
RuntimeLogger.logger.info("Could not get metric " + restriction.getLeftHandSide().getMetric() + " for node " + unit + " ex " + ex.getLocalizedMessage());
}
}
if (restriction.getRightHandSide().getMetric() != null && restriction.getRightHandSide().getMetric().equalsIgnoreCase("cost")) {
try {
double currentCost = monitoringAPI.getMetricValue(restriction.getRightHandSide().getMetric(), unit);
if (minCost > (Double.parseDouble(restriction.getLeftHandSide().getNumber()) - currentCost)) {
minCost = Double.parseDouble(restriction.getLeftHandSide().getNumber()) - currentCost;
}
} catch (Exception ex) {
RuntimeLogger.logger.info("Could not get metric " + restriction.getRightHandSide().getMetric() + " for node " + unit + " ex " + ex.getLocalizedMessage());
}
}
}
}
return minCost;
}
public boolean searchAction(Node serviceUnit, Node vm, String metric, double initialStdDev, double remainingCost,HashMap<String,Double> currentValues) {
boolean ok = true;
double minStdDev = initialStdDev;
String resTypeMinStdDev = "";
double resSizeMinStdDev = 10000;
for (String resourceType : this.expectedEffects.keySet()) {
for (double resourceSize : expectedEffects.get(resourceType).keySet()) {
double valBefore = currentValues.get(vm.getId());
if (expectedEffects.get(resourceType).get(resourceSize).get(metric) != null) {
currentValues.put(vm.getId(), valBefore + expectedEffects.get(resourceType).get(resourceSize).get(metric));
double newStdDev = computeStdDeviation(currentValues);
if (newStdDev < minStdDev) {
minStdDev = newStdDev;
resTypeMinStdDev = resourceType;
resSizeMinStdDev = resourceSize;
}
currentValues.put(vm.getId(), valBefore);
}
}
}
if (!resTypeMinStdDev.equalsIgnoreCase("")) {
if (!solution.containsKey(vm.getId())) {
solution.put(vm.getId(), new HashMap<String, Double>());
}
this.solution.get(vm.getId()).put(resTypeMinStdDev, resSizeMinStdDev);
}
return ok;
}
public void readResourceActionEffects() {
JSONParser parser = new JSONParser();
try {
InputStream inputStream = Configuration.class.getClassLoader().getResourceAsStream(Configuration.getResourcesEffectsPath());
Object obj = parser.parse(new InputStreamReader(inputStream));
JSONObject jsonObject = (JSONObject) obj;
for (Object resourceType : jsonObject.keySet()) {
expectedEffects.put((String) resourceType, new HashMap<Double, HashMap<String, Double>>());
JSONObject object = (JSONObject) jsonObject.get(resourceType);
for (Object resourceSize : object.keySet()) {
expectedEffects.get((String) resourceType).put(Double.parseDouble((String) resourceSize), new HashMap<String, Double>());
JSONObject metrics = (JSONObject) object.get(resourceSize);
for (Object metric : metrics.keySet()) {
double expectedEffect = (double) metrics.get(metric);
expectedEffects.get((String) resourceType).get(Double.parseDouble((String) resourceSize)).put((String) metric, expectedEffect);
}
}
}
} catch (Exception e) {
RuntimeLogger.logger.error("Error while reading resources effects");
}
}
public double computeStdDeviation(HashMap<String, Double> currentValues) {
double sum = 0;
for (Double val : currentValues.values()) {
sum += val;
}
double average = sum / currentValues.size();
double sumOfSquares = 0;
for (Double val : currentValues.values()) {
sumOfSquares += Math.pow((val - average), 2);
}
sumOfSquares /= currentValues.size();
return Math.sqrt(sumOfSquares);
}
@Override
public void run() {
t = new Timer();
try {
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (dependencyGraph.isInControlState()) {
// try {
// Thread.sleep(REFRESH_PERIOD);
// } catch (InterruptedException e) {
// RuntimeLogger.logger.error(e.toString());
// }
Node cloudService = monitoringAPI.getControlledService();
dependencyGraph.setCloudService(cloudService);
PlanningLogger.logger.info("Planning resource level control ");
checkForOverusedResourcesAndScale();
}
}
}, 2 * REFRESH_PERIOD, 2 * REFRESH_PERIOD);
} catch (Exception e) {
PlanningLogger.logger.info("Found error " + e.getMessage());
}
}
}