/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2011
//
// Copyright in this software belongs to University of Southampton
// IT Innovation Centre of 2 Venture Road, Chilworth Science Park, Southampton SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
// Created By : Vegard Engen, Stuart E. Middleton
// Created Date : 13-Jan-2011
// Created for Project : IRMOS---BonFIRE
//
/////////////////////////////////////////////////////////////////////////
//
// Dependencies : none
//
/////////////////////////////////////////////////////////////////////////
package uk.ac.soton.itinnovation.pes.optimiser.test;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import uk.ac.soton.itinnovation.pes.data.dao.DAOFactory;
import uk.ac.soton.itinnovation.pes.data.model.AttributeType;
import uk.ac.soton.itinnovation.pes.data.model.BasicType;
import uk.ac.soton.itinnovation.pes.data.model.MetricType;
import uk.ac.soton.itinnovation.pes.data.model.Model;
import uk.ac.soton.itinnovation.pes.data.model.ModelExecutionResult;
import uk.ac.soton.itinnovation.pes.data.model.Resource;
import uk.ac.soton.itinnovation.pes.data.model.ValueType;
import java.util.Map.Entry;
import uk.ac.soton.itinnovation.pes.optimiser.common.AModel;
import uk.ac.soton.itinnovation.pes.optimiser.common.OptimiserUtil;
/**
* A sample model wrapper that simulates that a model is executed and a reliability
* value is calculated as a measurement of the probability of achieving some desired
* QoS defined in an hypothetical ASLA.
*/
public class SampleModel extends AModel
{
private Model model = null;
private DAOFactory daoFactory = null;
private File configFile = null;
private Random rand = new Random(); // used to randomly set the output values
private int executionTime = 1000; // (sleep time) the unit is milliseconds
private OptimiserUtil optUtil = OptimiserUtil.getInstance();
/**
* constructor
*/
public SampleModel()
{
System.out.println("[AppModel] created instance");
rand.setSeed(System.currentTimeMillis());
}
/**
* Initialise and validate the application model wrapper.
* Normally both the Model and DAOFactory objects should be validated
* since these are required by the super class to perform caching of the
* execution results. However, since this sample model wrapper was designed
* to be used within the optimiser without a database to perform caching,
* only the Model object is validated.
* @param m Model object specifying details of the application model.
* @param daoFact DAOFactory object used for DB caching.
* @throws Exception if initialisation fails.
*/
@Override
public void init(Model m, DAOFactory daoFact) throws Exception
{
this.model = m;
this.daoFactory = daoFact;
if (validateModelParams())
System.out.println("[AppModel] initialised");
else
{
System.out.println("[AppModel] failed to initialise");
throw new RuntimeException("Failed to initialise application model. The model parameters could not be validated.");
}
}
/**
* Initialise and validate the application model wrapper.
* Normally both the Model and DAOFactory objects should be validated
* since these are required by the super class to perform caching of the
* execution results. However, since this sample model wrapper was designed
* to be used within the optimiser without a database to perform caching,
* only the Model object is validated.
* @param m Model object specifying details of the application model.
* @param daoFact DAOFactory object used for DB caching.
* @param aslaParams ASLA parameters.
* @throws Exception if initialisation fails.
*/
@Override
public void init(Model m, DAOFactory daoFact, Map<String, List<AttributeType>> aslaParams) throws Exception
{
this.model = m;
this.daoFactory = daoFact;
if (validateModelParams())
System.out.println("[AppModel] initialised");
else
{
System.out.println("[AppModel] failed to initialise");
throw new RuntimeException("Failed to initialise application model. The model parameters could not be validated.");
}
}
/**
* Validates the model execution file path and config file. Parameters are
* extracted from the config file and validated too.
* @return True if all valid, false otherwise.
*/
private boolean validateModelParams()
{
boolean returnVal = true;
Map<String, String> configParams = null;
if (model == null)
{
System.out.println("[AppModel] ERROR: Model object is NULL");
returnVal = false;
}
else
{
System.out.println("[AppModel] model name = " + model.getName());
System.out.println("[AppModel] model version = " + model.getVersion());
// check config file
if ((model.getConfigFilePath() != null) && (model.getConfigFilePath().length() > 0))
{
configFile = new File (model.getConfigFilePath());
if (configFile.exists())
{
System.out.println("[AppModel] configFile = " +configFile.getAbsolutePath());
try
{
configParams = optUtil.readKeyValueFile(configFile);
if (configParams != null)
{
if (validateAndSaveConfigParams(configParams))
{
System.out.println("[AppModel] execution time set to " + executionTime + "ms");
}
else
{
System.out.println("[AppModel] ERROR: unable to find all expected parameters from the config file: " + configFile.getAbsolutePath());
System.out.println("[AppModel] default parameter values will be used");
returnVal = false;
}
}
else
{
System.out.println("[AppModel] ERROR: unable to extract config parameters from the file: " + configFile.getAbsolutePath());
System.out.println("[AppModel] default parameter values will be used");
}
} catch (Exception ex){
System.out.println("[AppModel] ERROR: unable to read config file :: " + ex.getMessage());
}
}
else // config file does not exist
{
System.out.println("[AppModel] model config file path invalid: " + configFile.getAbsolutePath());
System.out.println("[AppModel] default parameter values will be used");
}
}
else // no config file set
{
System.out.println("[AppModel] model config file path not set");
System.out.println("[AppModel] default parameter values will be used");
}
} // end if (model != null)
if (!optUtil.disableDBCaching && daoFactory == null)
{
System.out.println("[AppModel] ERROR: DAOFactory object is NULL");
returnVal = false;
}
return returnVal;
}
/**
* Set the values of configuration parameters according to the data read in
* from the config file (stored in the parameter map passed on to this method).
* @param paramMap A map of the configuration parameters and their values.
* @return True if all expected configuration parameters are found and their values are valid.
*/
private boolean validateAndSaveConfigParams (Map<String, String> paramMap)
{
System.out.println("[AppModel] validating the config parameters");
boolean valid = true;
try
{
if (paramMap.containsKey("executionTime"))
{
executionTime = Integer.parseInt(paramMap.get("executionTime"));
if (executionTime < 0)
{
System.out.println("[AppModel] ERROR: cheeky human! executionTime cannot be set to a negative value");
System.out.println("[AppModel] executionTime set to the default value");
executionTime = 1000;
}
}
}
catch (java.lang.NumberFormatException ex)
{
System.out.println("[AppModel] ERROR: NumberFormatException thrown when trying to save config parameters");
valid = false;
}
return valid;
}
/**
* Return the model instance.
*/
@Override
public Model getModel() {
return model;
}
/**
* Setup the ASLA parameter map.
* OBS: NOT USED always does nothing.
*/
@Override
public void setASLAParameters(Map<String, List<AttributeType>> parameters) throws Exception {}
/**
* Return the ASLA parameter map.
* OBS: NOT USED always returns NULL.
*/
@Override
public Map<String, List<AttributeType>> getASLAParameters() {
return null;
}
/**
* Return if the model instance has been setup correctly.
* @return true if model instance setup correctly.
*/
@Override
public boolean isValid() {
if (model != null)
return true;
else
return false;
}
/**
* This is the execute method that will be called by the objective function,
* passing on a map of the chosen HW specifications.
* @param inputs Hardware specifications given by the objective function.
* @param outoutDir The directory where model outputs could be saved (e.g. log file).
* @return ModelExecutionResult containing the reliability calculated by this wrapper.
*/
@Override
public ModelExecutionResult execute(Map<String, Set<Resource>> inputs, String outputDir) throws Exception
{
Set<AttributeType> outputs = null;
double output;
AttributeType reliability = null;
if (!optUtil.disableDBCaching) // will not check for cached execution results
{
System.out.println(" checking for cached execution result in the DB");
ModelExecutionResult res = super.checkExecutionResult(model, daoFactory, optUtil.convertResourceMapToSet(inputs), null, null);
if (res != null)
{
System.out.println(" found a model execution result, which is returned");
return res;
}
System.out.println(" did not find a matching existing execution result in the DB");
}
System.out.println(" executing " + model.getName());
// Add some sleep time to simulate some wait time if a model had been executed
Thread.sleep(executionTime);
// This would be a point at which an application model would be executed,
// which could be done with the ModelExecuter class in pes-common.
// Since we do not provide an excutable application model, this sample
// wrapper calculates a reliability (probability of achieving some QoS
// constraints) based on a simple hard coded formula that has been invented
// for this simple test.
output = optUtil.roundDouble(getReliability(inputs), 3);
System.out.println(" reliability calculated as " + output);
// create an output set for the ModelExecutionResult object
outputs = new HashSet<AttributeType>();
reliability = new AttributeType();
reliability.setName("reliability");
reliability.setType(BasicType.convert("double"));
reliability.addAValue(new ValueType(String.valueOf(output), MetricType.DEFAULT));
outputs.add(reliability);
if (!optUtil.disableDBCaching)
return super.saveExecutionResult(model, daoFactory, optUtil.convertResourceMapToSet(inputs), null, null, outputs, null);
else
return new ModelExecutionResult(model, optUtil.convertResourceMapToSet(inputs), outputs);
}
/**
* Return the reliability (based on to CPU speed and bandwidth)
* @param inputs The HW spec from the objective function.
* @return A value between 0 to 100, specifying the reliability.
*/
private double getReliability(Map<String, Set<Resource>> inputs) throws Exception
{
String mapKey = null;
Set<Resource> resourceSet = null;
// semi-random generation of reliability according to CPU speed and bandwidth
//double reliability = optUtil.roundDouble(90.0 + (rand.nextDouble()*10), 3);
double reliability = 80;
int cpuSpeed_s1 = 0; // max 3
int cpuSpeed_s2 = 0; // max 3
int cpuSpeed_lb = 0; // max 3
int bw_l1_in = 0; // max 5
int bw_l1_out = 0; // max 5
int bw_l2_in = 0; // max 5
int bw_l2_out = 0; // max 5
//----------------------------
// sum 29
//============================
for (Entry<String, Set<Resource>> entry : inputs.entrySet())
{
mapKey = entry.getKey();
resourceSet = entry.getValue();
for (Resource resource : resourceSet)
{
if (resource.getName().equalsIgnoreCase("CPU"))
{
for (AttributeType attribute : resource.getAttrs())
{
if (attribute.getName().equalsIgnoreCase("cpuSpeed"))
{
if (mapKey.equalsIgnoreCase("Server1"))
cpuSpeed_s1 = Integer.parseInt(attribute.getExactValue().getVal());
else if (mapKey.equalsIgnoreCase("Server2"))
cpuSpeed_s2 = Integer.parseInt(attribute.getExactValue().getVal());
else if (mapKey.equalsIgnoreCase("LoadBalancer"))
cpuSpeed_lb = Integer.parseInt(attribute.getExactValue().getVal());
break;
}
} // end for all attributes
} // end if resource == CPU
else if (resource.getName().equalsIgnoreCase("INBOUND"))
{
for (AttributeType attribute : resource.getAttrs())
{
if (attribute.getName().equalsIgnoreCase("bandwidth"))
{
if (mapKey.equalsIgnoreCase("Link1"))
bw_l1_in = Integer.parseInt(attribute.getExactValue().getVal());
else if (mapKey.equalsIgnoreCase("Link2"))
bw_l2_in = Integer.parseInt(attribute.getExactValue().getVal());
break;
}
} // end for all attributes
} // end if resource == INBOUND
else if(resource.getName().equalsIgnoreCase("OUTBOUND"))
{
for (AttributeType attribute : resource.getAttrs())
{
if (attribute.getName().equalsIgnoreCase("bandwidth"))
{
if (mapKey.equalsIgnoreCase("Link1"))
bw_l2_out = Integer.parseInt(attribute.getExactValue().getVal());
else if (mapKey.equalsIgnoreCase("Link2"))
bw_l2_out = Integer.parseInt(attribute.getExactValue().getVal());
break;
}
} // end for all attributes
} // end if resource == OUTBOUND
} // end for all resources
} // end for all map entries
reliability += getCpuSpeedAcc(cpuSpeed_s1);
reliability += getCpuSpeedAcc(cpuSpeed_s2);
reliability += getCpuSpeedAcc(cpuSpeed_lb);
reliability += getBandwidthAcc(bw_l1_in);
reliability += getBandwidthAcc(bw_l1_out);
reliability += getBandwidthAcc(bw_l2_in);
reliability += getBandwidthAcc(bw_l2_out);
if (reliability > 100)
reliability = 100;
return reliability;
}
/**
* Get the cost of a specific CPU speed.
* @param speed A positive integer in the set {2000, 2200, 2400}, which is
* hard coded according to the test framework.
* @return A cost for the CPU.
*/
private double getCpuSpeedAcc (int speed)
{
switch (speed)
{
case 2000:
{
return 1;
}
case 2200:
{
return 2;
}
case 2400:
{
return 3;
}
default:
return 1;
}
}
/**
* Get the cost of a specific bandwidth.
* @param bw A positive value in the range [1000000, 10000000].
* @return A cost, calculated by scaling the value of the bandwidth within the
* range [1,5]. That is, the minimum bandwidth gives a cost of 1, and
* the maximum bandwidth gives a cost of 5.
*/
private double getBandwidthAcc (int bw)
{
// [1000000, 10000000]
// scale to [1, 5]
return optUtil.scale(bw, 1000000, 10000000, 1, 5);
}
//============================ UNUSED METHODS ============================//
/**
* method unused
*/
@Override
public ModelExecutionResult execute(Set<AttributeType> inputs, String outputDir) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* method unused
*/
@Override
public ModelExecutionResult execute(File[] inputFiles, String outputDir) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* method unused
*/
@Override
public ModelExecutionResult execute(File inputFile, String outputDir) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* method unused
*/
@Override
public ModelExecutionResult execute(String[] inputs, String outputDir) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* method unused
*/
@Override
public ModelExecutionResult execute(String input, String outputDir) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
} // SampleModel