/*
* Copyright (c) 2010-2012 Thiago T. Sá
*
* This file is part of CloudReports.
*
* CloudReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CloudReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For more information about your rights as a user of CloudReports,
* refer to the LICENSE file or see <http://www.gnu.org/licenses/>.
*/
package cloudreports.reports;
import cloudreports.business.SettingBusiness;
import cloudreports.dao.MigrationDAO;
import cloudreports.dao.ReportDataDAO;
import cloudreports.dao.SettingDAO;
import cloudreports.extensions.PowerDatacenter;
import cloudreports.models.Migration;
import cloudreports.utils.FileIO;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
import org.cloudbus.cloudsim.Cloudlet;
import org.cloudbus.cloudsim.DatacenterBroker;
import org.cloudbus.cloudsim.power.PowerHost;
/**
* Provides methods to generate simulation reports with information about
* usage of resources by datacenters.
*
* @author Thiago T. Sá
* @since 1.0
*/
class DatacenterReport {
/** The name of the datacenter. */
private String name;
/** Number of executed cloudlets per host. */
private TreeMap<String,Double> executedCloudlets;
/** Number of deployed virtual machines per host. */
private TreeMap<String,Double> deployedVms;
/** Costs generated per customer. */
private TreeMap<String,Double> costs;
/** RAM utilization per host. */
private HashMap<Integer, TreeMap<Double, Double>> hostsUsedRam;
/** CPU utilization per host. */
private HashMap<Integer, TreeMap<Double, Double>> hostsUsedCpu;
/** Bandwidth utilization per host. */
private HashMap<Integer, TreeMap<Double, Double>> hostsUsedBandwidth;
/** Power consumption per host. */
private HashMap<Integer, TreeMap<Double, Double>> hostsUsedPower;
/** Overall usage of RAM by the datacenter. */
private TreeMap<Double, Double> overallUsedRam;
/** Overall usage of CPU by the datacenter. */
private TreeMap<Double, Double> overallUsedCpu;
/** Overall usage of bandwidth by the datacenter. */
private TreeMap<Double, Double> overallUsedBandwidth;
/** Overall power consumption by the datacenter. */
private TreeMap<Double, Double> overallUsedPower;
/** The HTML version of the report. */
private String html;
/** The raw data.
* This is useful for importing simulation data to other softwares, such
* as Octave or MATLAB.
*/
private String rawData;
/**
* Creates a datacenter report.
*
* @param datacenter the datacenter.
* @param brokersList a list of all brokers.
* @since 1.0
*/
public DatacenterReport(PowerDatacenter datacenter, List<DatacenterBroker> brokersList, boolean htmlReportsEnabled,
boolean rawDataReportsEnabled) throws IOException, URISyntaxException {
this.name = datacenter.getName();
// Get all hosts resource utilization data from the database
this.hostsUsedRam = new HashMap<Integer, TreeMap<Double, Double>>();
this.hostsUsedCpu = new HashMap<Integer, TreeMap<Double, Double>>();
this.hostsUsedBandwidth = new HashMap<Integer, TreeMap<Double, Double>>();
this.hostsUsedPower = new HashMap<Integer, TreeMap<Double, Double>>();
List<PowerHost> hostsList = datacenter.getHostList();
ReportDataDAO rdDAO = new ReportDataDAO();
for(PowerHost host : hostsList) {
int hostId = host.getId();
hostsUsedRam.put(hostId, rdDAO.getHostUsedResources("RAM", this.name, hostId));
hostsUsedCpu.put(hostId, rdDAO.getHostUsedResources("CPU", this.name, hostId));
hostsUsedBandwidth.put(hostId, rdDAO.getHostUsedResources("BANDWIDTH", this.name, hostId));
hostsUsedPower.put(hostId, rdDAO.getHostUsedResources("POWER", this.name, hostId));
}
//Get the overall resource utilization data from the database
this.overallUsedRam = rdDAO.getDatacenterOverallData("RAM", this.name);
this.overallUsedCpu = rdDAO.getDatacenterOverallData("CPU", this.name);
this.overallUsedBandwidth = rdDAO.getDatacenterOverallData("BANDWIDTH", this.name);
this.overallUsedPower = rdDAO.getDatacenterOverallData("POWER", this.name);
executedCloudlets = new TreeMap<String, Double>();
deployedVms = new TreeMap<String, Double>();
costs = new TreeMap<String, Double>();
for(DatacenterBroker broker : brokersList) {
//Set of IDs from virtual machines deployed by this datacenter
Set<Integer> vmIds = new HashSet<Integer>();
// Compute all cloudlets executed in this datacenter
executedCloudlets.put(broker.getName(), 0D);
List<Cloudlet> cloudletsList = broker.getCloudletSubmittedList();
for(Cloudlet cloudlet : cloudletsList) {
if(cloudlet.getResourceId() == datacenter.getId()) {
executedCloudlets.put(broker.getName(), executedCloudlets.get(broker.getName()) + 1);
vmIds.add(cloudlet.getVmId());
}
}
//Set the number of virtual machines from this customer deployed on this datacenter
deployedVms.put(broker.getName(), Double.valueOf(String.valueOf(vmIds.size())));
//Get the customer's debt on this datacenter
costs.put(broker.getName(), datacenter.getDebts().get(broker.getId()));
}
if(htmlReportsEnabled) generateHtml();
if(rawDataReportsEnabled) generateRawData();
}
/**
* Gets the name of the datacenter.
*
* @return a string that contains the name of the datacenter.
*/
public String getName() {
return name;
}
/**
* Gets the HTML version of the report.
*
* @return a string that contains the HTML version of the report.
*/
public String getHtml() {
return html;
}
/**
* Gets the raw data.
*
* @return a string containing the report's raw data.
*/
public String getRawData() {
return rawData;
}
/**
* Generates the HTML version of the datacenter's report.
* It reads the template files and replaces the existing tags with real
* simulation data. The resulting string is assigned to the {@link #html}
* field.
*
* @throws IOException If the template files could not be loaded
* correctly.
* @throws URISyntaxException If the path to the template files could not
* be parsed successfully.
* @since 1.0
*/
private void generateHtml() throws IOException, URISyntaxException {
this.html = FileIO.readStringFromResource("cloudreports/gui/reports/resources/datacenter");
StringBuilder hostResUtilizationOptions = new StringBuilder();
StringBuilder hostsResUtilization = new StringBuilder();
StringBuilder hostPowerConsumptionOptions = new StringBuilder();
StringBuilder hostsPowerConsumption = new StringBuilder();
List<Integer> hostIds = Arrays.asList(hostsUsedRam.keySet().toArray(new Integer[0]));
for(Integer hostId : hostIds) {
hostResUtilizationOptions.append("<option value=\"resource_utilization_")
.append(this.name).append("_").append("Host").append(hostId)
.append("\">Host").append(hostId).append("</option>\n");
hostPowerConsumptionOptions.append("<option value=\"power_consumption_")
.append(this.name).append("_").append("Host").append(hostId)
.append("\">Host").append(hostId).append("</option>\n");
//Create host resource utilization html
String resHtml = FileIO.readStringFromResource("cloudreports/gui/reports/resources/host_resource_utilization");
resHtml = resHtml.replace("<!--INSERT_HOST_NAME-->", "Host"+hostId);
resHtml = resHtml.replace("<!--INSERT_RAM_DATA-->", getDataString(hostsUsedRam.get(hostId)));
resHtml = resHtml.replace("<!--INSERT_CPU_DATA-->", getDataString(hostsUsedCpu.get(hostId)));
resHtml = resHtml.replace("<!--INSERT_BANDWIDTH_DATA-->", getDataString(hostsUsedBandwidth.get(hostId)));
hostsResUtilization.append(resHtml);
//Create host power consumption html
String powerHtml = FileIO.readStringFromResource("cloudreports/gui/reports/resources/host_power_consumption");
powerHtml = powerHtml.replace("<!--INSERT_HOST_NAME-->", "Host"+hostId);
powerHtml = powerHtml.replace("<!--INSERT_POWER_CONSUMPTION_DATA-->", getDataString(hostsUsedPower.get(hostId)));
hostsPowerConsumption.append(powerHtml);
}
//Insert host resource utilization options
html = html.replace("<!--INSERT_HOST_RESOURCE_UTILIZATION_OPTIONS-->", hostResUtilizationOptions.toString());
//Insert hosts resource utilization
html = html.replace("<!--INSERT_HOST_RESOURCE_UTILIZATION_LIST-->", hostsResUtilization.toString()) ;
//Insert host power consumption options
html = html.replace("<!--INSERT_HOST_POWER_CONSUMPTION_OPTIONS-->", hostPowerConsumptionOptions.toString());
//Insert hosts power consumption
html = html.replace("<!--INSERT_HOST_POWER_CONSUMPTION_LIST-->", hostsPowerConsumption.toString());
//Insert overall resource utilization data
html = html.replace("<!--INSERT_OVERALL_RAM_DATA-->", getDataString(overallUsedRam));
html = html.replace("<!--INSERT_OVERALL_CPU_DATA-->", getDataString(overallUsedCpu));
html = html.replace("<!--INSERT_OVERALL_BANDWIDTH_DATA-->", getDataString(overallUsedBandwidth));
//Insert overall power consumption data
html = html.replace("<!--INSERT_OVERALL_POWER_CONSUMPTION_DATA-->", getDataString(overallUsedPower));
//Insert virtual machines data
html = html.replace("<!--INSERT_VIRTUAL_MACHINES_DATA-->", getDataAndLabelString(deployedVms));
//Insert cloudlets data
html = html.replace("<!--INSERT_CLOUDLETS_DATA-->", getDataAndLabelString(executedCloudlets));
//Insert costs data
html = html.replace("<!--INSERT_COSTS_DATA-->", getDataAndLabelString(costs));
//Insert migrations data
html = html.replace("<!--INSERT_MIGRATIONS_DATA-->", getMigrationsString());
//Insert datacenter's name
html = html.replace("<!--INSERT_DATACENTER_NAME-->", this.name);
}
/**
* Converts a time-value map of generic data to a string format that can be
* parsed by the Flot library.
*
* @param dataMap a map of generic report data.
* @return a formated string containing the data of the map.
* @see <a href="http://code.google.com/p/flot/">The Flot library</a>
* @since 1.0
*/
private String getDataString(TreeMap<Double, Double> dataMap) {
StringBuilder dataStringBuilder = new StringBuilder("[");
Iterator<Double> iterator = dataMap.keySet().iterator();
while (iterator.hasNext()) {
double time = iterator.next();
dataStringBuilder.append("[").append(time).append(",")
.append(dataMap.get(time)).append("],");
}
dataStringBuilder.deleteCharAt(dataStringBuilder.lastIndexOf(","));
dataStringBuilder.append("]");
return dataStringBuilder.toString();
}
/**
* Converts a label-value map of generic data to a string format that can
* be parsed by the Flot library.
*
* @param dataMap a map of generic report data.
* @return a formated string containing the data of the map.
* @see <a href="http://code.google.com/p/flot/">The Flot library</a>
* @since 1.0
*/
private String getDataAndLabelString(TreeMap<String,Double> dataMap) {
StringBuilder dataStringBuilder = new StringBuilder("[");
Iterator<String> iterator = dataMap.keySet().iterator();
int i = -1;
while (iterator.hasNext()) {
i += 2;
String customer = iterator.next();
dataStringBuilder.append("{ data: [[")
.append(i) //The x label is not shown here
.append(",").append(dataMap.get(customer))
.append("]], label: \"").append(customer)
.append("\"},");
}
dataStringBuilder.deleteCharAt(dataStringBuilder.lastIndexOf(","));
dataStringBuilder.append("]");
return dataStringBuilder.toString();
}
/**
* Generates the report's raw data and assigns it to the {@link #rawData}
* field.
*
* @since 1.0
*/
private void generateRawData() throws IOException, URISyntaxException {
StringBuilder rawDataStringBuilder = new StringBuilder();
int simulationId = SettingBusiness.getCurrentSimulation();
rawDataStringBuilder.append(getRawDataString(overallUsedRam, "Sim" + simulationId + "_" + this.name + "_overall_ram"));
rawDataStringBuilder.append(getRawDataString(overallUsedCpu, "Sim" + simulationId + "_" + this.name + "_overall_cpu"));
rawDataStringBuilder.append(getRawDataString(overallUsedBandwidth, "Sim" + simulationId + "_" + this.name + "_overall_bw"));
rawDataStringBuilder.append(getRawDataString(overallUsedPower, "Sim" + simulationId + "_" + this.name + "_overall_power"));
List<Integer> hostIds = Arrays.asList(hostsUsedRam.keySet().toArray(new Integer[0]));
for (Integer hostId : hostIds) {
rawDataStringBuilder.append(getRawDataString(hostsUsedRam.get(hostId), "Sim" + simulationId + "_" + this.name + "_host" + hostId + "_ram"));
rawDataStringBuilder.append(getRawDataString(hostsUsedCpu.get(hostId), "Sim" + simulationId + "_" + this.name + "_host" + hostId + "_cpu"));
rawDataStringBuilder.append(getRawDataString(hostsUsedBandwidth.get(hostId), "Sim" + simulationId + "_" + this.name + "_host" + hostId + "_bw"));
rawDataStringBuilder.append(getRawDataString(hostsUsedPower.get(hostId), "Sim" + simulationId + "_" + this.name + "_host" + hostId + "_power"));
}
this.rawData = rawDataStringBuilder.toString();
}
/**
* Converts a time-value map to a string that can be easily parsed by a
* third party software.
*
* @since 1.0
*/
private String getRawDataString(TreeMap<Double, Double> dataMap, String label) {
StringBuilder timeStringBuilder = new StringBuilder("\n" + label + "_time\t");
StringBuilder valuesStringBuilder = new StringBuilder("\n" + label + "_values\t");
Iterator<Double> iterator = dataMap.keySet().iterator();
while (iterator.hasNext()) {
double time = iterator.next();
timeStringBuilder.append(time).append("\t");
valuesStringBuilder.append(dataMap.get(time)).append("\t");
}
return timeStringBuilder.toString() + valuesStringBuilder.toString();
}
/**
* Gets a string that describes all migrations performed by this datacenter.
*
* @return a string containing a description of all performed migrations.
* @since 1.0
*/
private String getMigrationsString() {
MigrationDAO mDAO = new MigrationDAO();
List<Migration> migrationList = mDAO.getMigrationList(this.name);
StringBuilder migrationStringBuilder = new StringBuilder("<br/>Number of migrations: ");
migrationStringBuilder.append(migrationList.size()).append("<br/>");
for(Migration migration : migrationList) {
migrationStringBuilder.append("<br/><hr>")
.append("<strong>Migration ")
.append(migrationList.indexOf(migration))
.append(":</strong><br/>")
.append("Description: ").append(migration.getDescription())
.append("<br/><strong>").append(migration.getVmLabel())
.append("</strong> from <strong>").append(migration.getSourceHostLabel())
.append("</strong> to <strong>").append(migration.getTargetHostLabel())
.append("</strong> at <strong>").append(migration.getTime()/60)
.append("</strong> minutes.<br/>")
.append("Source host was consuming ").append(migration.getSourceHostCpuUtilization()*100)
.append("% of CPU, ").append(migration.getSourceHostRamUtilization()*100)
.append("% of RAM and ").append(migration.getSourceHostPowerConsumption()*100)
.append("% of power.<br/>")
.append("Target host was consuming ").append(migration.getTargetHostCpuUtilization()*100)
.append("% of CPU, ").append(migration.getTargetHostRamUtilization()*100)
.append("% of RAM and ").append(migration.getTargetHostPowerConsumption()*100)
.append("% of power.<br/>");
}
migrationStringBuilder.append("<br/><br/>");
return migrationStringBuilder.toString();
}
}