/* * 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.ReportDataDAO; import cloudreports.dao.SettingDAO; 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.Vm; /** * Provides methods to generate simulation reports with information about * usage of resources by customers. * * @author Thiago T. Sá * @since 1.0 */ class CustomerReport { /** The name of the customer. */ private String name; /** Number of cloudlets executed per virtual machine. */ private TreeMap<String, Integer> cloudletsPerVm; /** Cloudlets' start and finish time of execution. */ private TreeMap<Integer, List<Double>> cloudletsTimesOfExecution; /** RAM utilization per virtual machine. */ private HashMap<Integer, TreeMap<Double, Double>> vmsUsedRam; /** CPU utilization per virtual machine. */ private HashMap<Integer, TreeMap<Double, Double>> vmsUsedCpu; /** Bandwidth utilization per virtual machine. */ private HashMap<Integer, TreeMap<Double, Double>> vmsUsedBandwidth; /** Overall usage of RAM by the customer. */ private TreeMap<Double, Double> overallUsedRam; /** Overall usage of CPU by the customer. */ private TreeMap<Double, Double> overallUsedCpu; /** Overall usage of bandwidth by the customer. */ private TreeMap<Double, Double> overallUsedBandwidth; /** 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 customer report for a given broker. * * @param broker the customer's broker. * @since 1.0 */ public CustomerReport(DatacenterBroker broker, boolean htmlReportsEnabled, boolean rawDataReportsEnabled) throws IOException, URISyntaxException { this.name = broker.getName(); //Get all virtual machines resource utilization data from the database this.vmsUsedRam = new HashMap<Integer, TreeMap<Double, Double>>(); this.vmsUsedCpu = new HashMap<Integer, TreeMap<Double, Double>>(); this.vmsUsedBandwidth = new HashMap<Integer, TreeMap<Double, Double>>(); cloudletsPerVm = new TreeMap<String, Integer>(); List<Vm> vmsList = broker.getVmList(); ReportDataDAO rdDAO = new ReportDataDAO(); for(Vm vm : vmsList) { int vmId = vm.getId(); vmsUsedRam.put(vmId, rdDAO.getVmUsedResources("RAM", this.name, vmId)); vmsUsedCpu.put(vmId, rdDAO.getVmUsedResources("CPU", this.name, vmId)); vmsUsedBandwidth.put(vmId, rdDAO.getVmUsedResources("BANDWIDTH", this.name, vmId)); cloudletsPerVm.put("VM"+vmId, 0); } //Get the overall resource utilization from this customer this.overallUsedRam = rdDAO.getCustomerOverallData("RAM", this.name); this.overallUsedCpu = rdDAO.getCustomerOverallData("CPU", this.name); this.overallUsedBandwidth = rdDAO.getCustomerOverallData("BANDWIDTH", this.name); cloudletsTimesOfExecution = new TreeMap<Integer, List<Double>>(); List<Cloudlet> cloudletsList = broker.getCloudletSubmittedList(); for(Cloudlet cloudlet : cloudletsList) { List<Double> timesOfExecution = new ArrayList<Double>(); timesOfExecution.add(cloudlet.getExecStartTime()); timesOfExecution.add(cloudlet.getFinishTime()); cloudletsTimesOfExecution.put(cloudlet.getCloudletId(), timesOfExecution); cloudletsPerVm.put("VM"+cloudlet.getVmId(), cloudletsPerVm.get("VM"+cloudlet.getVmId()) + 1); } if(htmlReportsEnabled) generateHtml(); if(rawDataReportsEnabled) generateRawData(); } /** * Gets the name of the customer. * * @return a string that contains the name of the customer. */ 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 customer'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/customer"); StringBuilder vmResUtilizationOptions = new StringBuilder(); StringBuilder vmsResUtilization = new StringBuilder(); List<Integer> vmIds = Arrays.asList(vmsUsedRam.keySet().toArray(new Integer[0])); for(Integer vmId : vmIds) { vmResUtilizationOptions.append("<option value=\"resource_utilization_") .append(this.name).append("_").append("VM") .append(vmId).append("\">VM").append(vmId) .append("</option>\n"); //Create virtual machine resource utilization html String resHtml = FileIO.readStringFromResource("cloudreports/gui/reports/resources/vm_resource_utilization"); resHtml = resHtml.replace("<!--INSERT_VM_NAME-->", "VM"+vmId); resHtml = resHtml.replace("<!--INSERT_RAM_DATA-->", getDataString(vmsUsedRam.get(vmId))); resHtml = resHtml.replace("<!--INSERT_CPU_DATA-->", getDataString(vmsUsedCpu.get(vmId))); resHtml = resHtml.replace("<!--INSERT_BANDWIDTH_DATA-->", getDataString(vmsUsedBandwidth.get(vmId))); vmsResUtilization.append(resHtml); } //Insert virtual machine options html = html.replace("<!--INSERT_VM_RESOURCE_UTILIZATION_OPTIONS-->", vmResUtilizationOptions.toString()); //Insert virtual machines resource utilization html = html.replace("<!--INSERT_VM_RESOURCE_UTILIZATION_LIST-->", vmsResUtilization.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 cloudlets data html = html.replace("<!--INSERT_CLOUDLETS_DATA-->", getDataAndLabelString(cloudletsPerVm)); //Insert execution times data String[] timeData = getCloudletsTimesDataString(cloudletsTimesOfExecution); if(timeData.length > 0) { html = html.replace("<!--INSERT_START_DATA-->", timeData[0]); html = html.replace("<!--INSERT_FINISH_DATA-->", timeData[1]); html = html.replace("<!--INSERT_AVERAGE_START_DATA-->", timeData[2]); html = html.replace("<!--INSERT_AVERAGE_FINISH_DATA-->", timeData[3]); } //Insert customer's name html = html.replace("<!--INSERT_CUSTOMER_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("["); dataStringBuilder.append(time); dataStringBuilder.append(","); dataStringBuilder.append(dataMap.get(time)); dataStringBuilder.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,Integer> dataMap) { StringBuilder dataStringBuilder = new StringBuilder("["); Iterator<String> iterator = dataMap.keySet().iterator(); int i = -1; while (iterator.hasNext()) { i += 2; String datacenter = iterator.next(); dataStringBuilder.append("{ data: [["); dataStringBuilder.append(i); //The x label is not shown here dataStringBuilder.append(","); dataStringBuilder.append(dataMap.get(datacenter)); dataStringBuilder.append("]], label: \""); dataStringBuilder.append(datacenter); dataStringBuilder.append("\"},"); } dataStringBuilder.deleteCharAt(dataStringBuilder.lastIndexOf(",")); dataStringBuilder.append("]"); return dataStringBuilder.toString(); } /** * Converts the map of cloudlets' execution times to an array of formatted * strings that can be parsed by the Flot library. * * @param dataMap the map of cloudlets' execution times. * @return an array of formated strings containing the data of the map. * @see <a href="http://code.google.com/p/flot/">The Flot library</a> * @since 1.0 */ private String[] getCloudletsTimesDataString(TreeMap<Integer, List<Double>> dataMap) { double startSum = 0, finishSum = 0; StringBuilder startStringBuilder = new StringBuilder("["); StringBuilder finishStringBuilder = new StringBuilder("["); List<Integer> cloudletIdsList = Arrays.asList(dataMap.keySet().toArray(new Integer[0])); if(cloudletIdsList.isEmpty()) return new String[0]; for(Integer cloudletId : cloudletIdsList) { List<Double> timesList = dataMap.get(cloudletId); startStringBuilder.append("["); startStringBuilder.append(cloudletId); startStringBuilder.append(","); startStringBuilder.append(timesList.get(0)); startStringBuilder.append("],"); startSum += timesList.get(0); finishStringBuilder.append("["); finishStringBuilder.append(cloudletId); finishStringBuilder.append(","); finishStringBuilder.append(timesList.get(1)); finishStringBuilder.append("],"); finishSum += timesList.get(1); } if(startStringBuilder.lastIndexOf(",") >= 0) startStringBuilder.deleteCharAt(startStringBuilder.lastIndexOf(",")); startStringBuilder.append("]"); if(finishStringBuilder.lastIndexOf(",") >= 0) finishStringBuilder.deleteCharAt(finishStringBuilder.lastIndexOf(",")); finishStringBuilder.append("]"); int numberOfCloudlets = cloudletIdsList.size(); double averageStart = startSum/numberOfCloudlets; String averageStartData = "[[" + cloudletIdsList.get(0) + "," + averageStart + "],[" + cloudletIdsList.get(numberOfCloudlets-1) + "," + averageStart + "]]"; double averageFinish = finishSum/numberOfCloudlets; String averageFinishData = "[[" + cloudletIdsList.get(0) + "," + averageFinish + "],[" + cloudletIdsList.get(numberOfCloudlets-1) + "," + averageFinish + "]]"; return new String[] {startStringBuilder.toString(), finishStringBuilder.toString(), averageStartData, averageFinishData}; } /** * Generates the report's raw data and assigns it to the {@link #rawData} * field. * * @since 1.0 */ private void generateRawData() { 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")); List<Integer> vmIds = Arrays.asList(vmsUsedRam.keySet().toArray(new Integer[0])); for(Integer vmId : vmIds) { rawDataStringBuilder.append(getRawDataString(vmsUsedRam.get(vmId), "Sim" + simulationId + "_" + this.name + "_vm" + vmId + "_ram")); rawDataStringBuilder.append(getRawDataString(vmsUsedCpu.get(vmId), "Sim" + simulationId + "_" + this.name + "_vm" + vmId + "_cpu")); rawDataStringBuilder.append(getRawDataString(vmsUsedBandwidth.get(vmId), "Sim" + simulationId + "_" + this.name + "_vm" + vmId + "_bw")); } 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(); } }