/////////////////////////////////////////////////////////////////////////
//
// © 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 uk.ac.soton.itinnovation.pes.optimiser.common.OptimiserResult;
import uk.ac.soton.itinnovation.pes.optimiser.common.OptimiserUtil;
import uk.ac.soton.itinnovation.pes.optimiser.common.IOptimisationManager;
import uk.ac.soton.itinnovation.pes.optimiser.common.AObjectiveFunction;
import uk.ac.soton.itinnovation.pes.optimiser.common.IPriceCalculator;
import uk.ac.soton.itinnovation.pes.optimiser.common.AModel;
import uk.ac.soton.itinnovation.pes.optimiser.common.IOptimisationAlgorithm;
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.EstimationResult;
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.ObjectiveFunctionConfig;
import uk.ac.soton.itinnovation.pes.data.model.OptimisationAlgConfig;
import uk.ac.soton.itinnovation.pes.data.model.Resource;
import uk.ac.soton.itinnovation.pes.data.model.UnitType;
import uk.ac.soton.itinnovation.pes.data.model.ValueType;
import eu.irmosproject.pes.common.PESClassLoader;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import uk.ac.soton.itinnovation.pes.optimiser.core.OptimisationManager;
/**
* This is a Sample test framework for running the optimiser, which allows a user
* to specify XML files for loading an optimisation algorithm, objective function
* and application model (wrapper).
*
* Since this is a test framework for the optimiser independent of the performance
* estimation service it can be plugged into, there are limitations to what can be
* tested with this class:
*
* 1: Interaction with a database to cache execution results is not possible
* since this relies on information to be put in the database that is
* performed by an Estimation Manager (a part of the PES Service).
* 2: The optimisation space is hard coded.
* 3: The ASLA parameters are hard coded.
*
* The arguments to this test framework are:
* 1: Full path to the XML file describing the optimisation algorithm
* 2: Full path to the XML file describing the objective function
* 3: Full path to the XML file describing the application model (wrapper)
* 4: Full path to the workspace where files can be saved optimiser components
*
* For example:
*
* SampleOptimiserTest C:\OptAlg.xml C:\ObjFunc.xml C:\Model.xml C:\workspace
*
*/
public class SampleOptimiserTest
{
private final static MetricType exactType = MetricType.EXACT;
private OptimiserUtil optUtil = null;
private PESClassLoader classLoader = null;
private final static int optAlgIdx = 0;
private final static int objFuncIdx = 1;
private final static int modelIdx = 2;
private final static int workspaceIdx = 3;
/**
* Main method for test execution framework to run from command line.
* Expecting the following command line arguments:
* 1: Full path to the XML file describing the optimisation algorithm
* 2: Full path to the XML file describing the objective function
* 3: Full path to the XML file describing the application model (wrapper)
* 4: Full path to the workspace where files can be saved optimiser components
*/
public static void main(String[] args)
{
SampleOptimiserTest testOptimiser = new SampleOptimiserTest();
IOptimisationManager optimiser = null;
OptimiserResult optRes = null;
System.out.println("\n\n");
System.out.println("==== OPTIMISER TEST ====\n\n");
if (!testOptimiser.validateArguments(args))
System.exit(1);
try
{
optimiser = testOptimiser.setUp(args);
}
catch (Exception ex)
{
System.out.println("[TestFramework] Failed to set up the Optimiser: " + ex.getMessage());
System.exit(1);
}
try {
optRes = optimiser.getOptimalResources();
if (optRes != null) {
System.out.println("\n[TestFramework] Optimisation successful!");
} else {
System.out.println("\n[TestFramework] Optimisation failed :(");
}
}
catch (Exception ex) {
System.out.println("\n[TestFramework] The optimisation failed, an exception was caught: " + ex.getMessage());
System.exit(1);
}
System.out.println("\n\nSo long, and thanks for all the fish!\n\n");
}
/**
* Validate the command line arguments.
* @param args Command line arguments from the main method.
* @return True if valid.
*/
private boolean validateArguments (String[] args)
{
File optAlgFile, objFuncFile, modelFile, workspace;
if (args == null)
{
System.out.println("[TestFramework] ERROR: no arguments given");
return false;
}
if (args.length < 4)
{
System.out.println("[TestFramework] ERROR: not enough arguments given");
System.out.println("[TestFramework] Expecting: ");
System.out.println("[TestFramework] 1: full path to xml file for the optimisation algorithm");
System.out.println("[TestFramework] 2: full path to xml file for the objective function");
System.out.println("[TestFramework] 3: full path to xml file for the model");
System.out.println("[TestFramework] 4: full path to workspace where file I/O can be done by the optimisation components");
return false;
}
optAlgFile = new File (args[optAlgIdx]);
objFuncFile = new File (args[objFuncIdx]);
modelFile = new File (args[modelIdx]);
workspace = new File (args[workspaceIdx]);
if (!optAlgFile.exists() || !optAlgFile.isFile())
{
System.out.println("[TestFramework] ERROR with argument 1: not a valid path to the xml file for an optimisation algorithm '"+args[optAlgIdx]+"'");
return false;
}
if (!objFuncFile.exists() || !objFuncFile.isFile())
{
System.out.println("[TestFramework] ERROR with argument 2: not a valid path to the xml file for an objective function '"+args[objFuncIdx]+"'");
return false;
}
if (!modelFile.exists() || !modelFile.isFile())
{
System.out.println("[TestFramework] ERROR with argument 3: not a valid path to the xml file for a model '"+args[modelIdx]+"'");
return false;
}
if (!workspace.exists() || !workspace.isDirectory())
{
System.out.println("[TestFramework] ERROR with argument 4: not a valid path to a workspace directory");
return false;
}
return true;
}
/**
* Create and configure an optimisation manager instance according to the
* XML files provided as command line arguments to the main method.
* @param args Command line arguments from main method.
* @return Optimisation manager instance.
*/
private IOptimisationManager setUp (String[] args) throws Exception
{
DAOFactory daoFactory = null;
EstimationResult estimationResult = null;
IPriceCalculator priceCalculator = null;
Map<String, List<AttributeType>> aslaParams = null;
Map<String, Set<Resource>> optimisationSpace = null;
IOptimisationAlgorithm optAlg = null;
AModel appModel = null;
AObjectiveFunction objFunc = null;
IOptimisationManager opt;
boolean valid = false;
//--- Setting up various things required by several components ---//
optUtil = OptimiserUtil.getInstance();
classLoader = new PESClassLoader();
daoFactory = DAOFactory.createInstance(DAOFactory.HIBERNATE);
estimationResult = new EstimationResult();
priceCalculator = new SamplePriceCalculator();
//--- Setting up ASLA parameters and optimisation space ---//
aslaParams = setUpASLAParams();
optimisationSpace = setUpOptimisationSpace();
//--- Setting up optimiser components from XML files ---//
optAlg = setUpOptAlg(args[optAlgIdx]);
appModel = setUpAppModel(args[modelIdx], daoFactory);
objFunc = setUpObjFunc(args[objFuncIdx], appModel, priceCalculator, estimationResult, daoFactory, args[workspaceIdx], aslaParams);
//--- Creating and initialising the Optimiser ---//
opt = new OptimisationManager();
valid = opt.init(optimisationSpace, optAlg, objFunc, priceCalculator, args[workspaceIdx], estimationResult, daoFactory);
if (!valid)
throw new RuntimeException("ERROR: Optimiser could not be initialised");
//--- Disabling caching to allow this to execute stand-alone ---//
optUtil.disableDBCaching = true;
return opt;
}
/**
* Create a new model instance from the XML file provided as a command line
* argument to the main method.
* @param modelXML XML configuration file specified as a command line argument.
* @param daoFactory DAOFactory required for caching execution results.
* @return model wrapper instance.
*/
private AModel setUpAppModel(String modelXML, DAOFactory daoFactory) throws Exception
{
Model m = null;
AModel appModel = null;
System.out.println("[TestFramework] Loading model from XML file");
m = optUtil.loadAppModel(modelXML);
appModel = classLoader.getApplicationModelInstance(m.getWrapper());
System.out.println("[TestFramework] Initialising model wrapper");
appModel.init(m, daoFactory);
System.out.println();
return appModel;
}
/**
* create a new objective function instance from the XML file provided as a command line
* argument to the main method.
* @param xmlFile XML configuration file specified as a command line argument.
* @param appModel Application model wrapper that should be executed when the
* objective function is evaluated.
* @param priceCalculator Price calculator instance.
* @param estimationResult Estimation result object used when caching execution results.
* @param daoFactory DAOFactory required for caching execution results.
* @param workspace The workspace directory where file I/O can be performed.
* @param aslaParams ASLA parameter map.
* @return objective function instance.
*/
private AObjectiveFunction setUpObjFunc(String xmlFile, AModel appModel,
IPriceCalculator priceCalculator, EstimationResult estimationResult,
DAOFactory daoFactory, String workspace, Map<String, List<AttributeType>> aslaParams) throws Exception
{
ObjectiveFunctionConfig objFuncConfig = null;
AObjectiveFunction objFunc = null;
System.out.println("[TestFramework] Loading objective function from XML file");
objFuncConfig = optUtil.loadObjectiveFunction(xmlFile);
objFunc = classLoader.getObjectiveFunctionInstance(objFuncConfig.getImplClazz());
System.out.println("[TestFramework] Initialising objective function");
objFunc.init(objFuncConfig, appModel, aslaParams, workspace, estimationResult, daoFactory);
objFunc.setPriceCalculator(priceCalculator);
System.out.println();
return objFunc;
}
/**
* Create a new optimisation algorithm instance from the XML file provided as a command line
* argument to the main method.
* @param xmlFile XML configuration file specified as a command line argument.
* @return optimisation algorithm instance.
*/
private IOptimisationAlgorithm setUpOptAlg(String xmlFile) throws Exception
{
OptimisationAlgConfig optAlgConfig = null;
IOptimisationAlgorithm optAlg = null;
System.out.println("[TestFramework] Loading optimisation algorithm from XML file");
optAlgConfig = optUtil.loadOptAlg(xmlFile);
optAlg = classLoader.getOptimisationAlgorithmInstance(optAlgConfig.getAlgClassName());
System.out.println();
return optAlg;
}
/**
* Create a new ASLA parameter map.
* @return ASLA parameter map with a hard coded example value for 'reliability'.
*/
private Map<String, List<AttributeType>> setUpASLAParams()
{
Map<String, List<AttributeType>> params = null;
List<AttributeType> attrList1 = null;
params = new HashMap<String, List<AttributeType>>();
attrList1 = new ArrayList<AttributeType>();
attrList1.add(optUtil.newAttribute("reliability", "A hypothetical reliability from an ASLA", BasicType.FLOAT, "0.99"));
params.put("LoadBalancer", attrList1);
return params;
}
/**
* Sets up an optimisation space for a very simple scenario of two servers,
* a load balancer and two network links.
* @return an optimisation space in the form Map<String, Set<Resource>>
*/
private Map<String, Set<Resource>> setUpOptimisationSpace()
{
Map<String, Set<Resource>> optSpace = new HashMap<String, Set<Resource>>();
optSpace.put("Server1", getASCResources());
optSpace.put("Server2", getASCResources());
optSpace.put("LoadBalancer", getASCResources());
optSpace.put("Link1", getLinkResources());
optSpace.put("Link2", getLinkResources());
return optSpace;
}
/**
* Create a new ASC resource set of a CPU, RAM and DISK.
* @return ASC resource set.
*/
private Set<Resource> getASCResources ()
{
Set<Resource> resources = new HashSet<Resource>();
resources.add(getCPUResource());
resources.add(getRAMResource());
resources.add(getDiskResource());
return resources;
}
/**
* Create a new CPU resource, which sets up Model, Speed, computationTime,
* computationDeadline and computationInterval. Only Speed is variable.
* @return CPU resource.
*/
private Resource getCPUResource ()
{
Resource res = new Resource();
res.setName("CPU");
res.addAnAttr(newCpuModelAttrib());
res.addAnAttr(newCpuSpeedAttrib());
res.addAnAttr(newCPUCompAttrib("computationTime", 100, "msec"));
res.addAnAttr(newCPUCompAttrib("computationDeadline", 100, "msec"));
res.addAnAttr(newCPUCompAttrib("computationInterval", 100, "msec"));
return res;
}
/**
* Create a new CPU model attribute with hard coded 'x86_80486' value.
* @return CPU model attribute.
*/
private AttributeType newCpuModelAttrib()
{
AttributeType a = new AttributeType();
a.setName("cpuModel");
a.setType(BasicType.STRING);
a.addAValue(new ValueType("x86_80486", MetricType.EXACT));
return a;
}
/**
* Create a new CPU speed attribute with hard coded allowed values of '2000, 2200, 2400 MHz'.
* @return CPU speed attribute.
*/
private AttributeType newCpuSpeedAttrib()
{
AttributeType a = new AttributeType();
a.setName("cpuSpeed");
a.setType(BasicType.STRING);
a.setUnit(new UnitType("MHz", null, null, null));
a.addAValue(new ValueType("2000", MetricType.VALUESALLOWED));
a.addAValue(new ValueType("2200", MetricType.VALUESALLOWED));
a.addAValue(new ValueType("2400", MetricType.VALUESALLOWED));
return a;
}
/**
* Set up a CPU attribute with a specific name, value (int) and unit type.
* @return AttributeType instance.
*/
private AttributeType newCPUCompAttrib(String name, int val, String unit)
{
AttributeType a = new AttributeType();
a.setName(name);
a.setType(BasicType.INT);
a.setUnit(new UnitType(unit, null, null, null));
a.addAValue(new ValueType(String.valueOf(val), MetricType.EXACT));
return a;
}
/**
* Set up a CPU attribute with a specific name, unit type and a min/max/step value range (int).
* @return AttributeType instance.
*/
private AttributeType newCPUCompAttrib(String name, int min, int max, int step, String unit)
{
AttributeType a = new AttributeType();
a.setName(name);
a.setType(BasicType.INT);
a.setUnit(new UnitType(unit, null, null, null));
a.addAValue(new ValueType(String.valueOf(min), MetricType.MIN));
a.addAValue(new ValueType(String.valueOf(max), MetricType.MAX));
a.addAValue(new ValueType(String.valueOf(step), MetricType.STEP));
return a;
}
/**
* Set up a CPU attribute with a specific name, value (double) and unit type.
* @return AttributeType instance.
*/
private AttributeType newCPUCompAttrib(String name, double val, String unit)
{
AttributeType a = new AttributeType();
a.setName(name);
a.setType(BasicType.DOUBLE);
a.setUnit(new UnitType(unit, null, null, null));
a.addAValue(new ValueType(String.valueOf(val), MetricType.EXACT));
return a;
}
/**
* Set up a CPU attribute with a specific name, unit type and a min/max/step value range (double).
* @return AttributeType instance.
*/
private AttributeType newCPUCompAttrib(String name, double min, double max, double step, String unit)
{
AttributeType a = new AttributeType();
a.setName(name);
a.setType(BasicType.DOUBLE);
a.setUnit(new UnitType(unit, null, null, null));
a.addAValue(new ValueType(String.valueOf(min), MetricType.MIN));
a.addAValue(new ValueType(String.valueOf(max), MetricType.MAX));
a.addAValue(new ValueType(String.valueOf(step), MetricType.STEP));
return a;
}
/**
* Create a new RAM resource with a hard coded value of '2048 Mbytes'
* @return RAM resource instance.
*/
private Resource getRAMResource ()
{
Resource res = new Resource();
AttributeType a = new AttributeType();
res.setName("RAM");
a.setName("ramSize");
a.setType(BasicType.STRING);
a.setUnit(new UnitType("MB", null, null, null));
a.addAValue(new ValueType("2048", MetricType.EXACT));
res.addAnAttr(a);
return res;
}
/**
* Create a new disk resource with a hard coded value of '100 GBytes'
* @return Disk resource object.
*/
private Resource getDiskResource ()
{
Resource res = new Resource();
AttributeType a = new AttributeType();
res.setName("DISK");
a.setName("DISKSPACE");
a.setType(BasicType.STRING);
a.setUnit(new UnitType("GB", null, null, null));
a.addAValue(new ValueType("100", MetricType.EXACT));
res.addAnAttr(a);
return res;
}
/**
* Create a new link resource (for network links etc), which sets up a set of
* an INBOUND and OUTBOUND resource.
* @return Link resource set.
*/
private Set<Resource> getLinkResources ()
{
Set<Resource> resources = new HashSet<Resource>();
resources.add(getLinkResource("INBOUND"));
resources.add(getLinkResource("OUTBOUND"));
return resources;
}
/**
* Create a new link resource (for network links etc).
* @param name name of the link (INBOUND or OUTBOUND allowed values)
* @return link resource instance.
*/
private Resource getLinkResource (String name) // INBOUND or OUTBOUND
{
Resource res = new Resource();
res.setName(name);
res.addAnAttr(newBandwidthAttrib());
res.addAnAttr(newJitterAttrib());
res.addAnAttr(newDelayAttrib());
return res;
}
/**
* Create a bandwidth attribute with hard coded allowed values in the range
* [1000000, 10000000] with steps of 1000000 bps.
* @return Bandwidth attribute instance.
*/
private AttributeType newBandwidthAttrib()
{
AttributeType a = new AttributeType();
a.setName("bandwidth");
a.setType(BasicType.INT);
a.setUnit(new UnitType("bps", null, null, null));
a.addAValue(new ValueType("1000000", MetricType.MIN));
a.addAValue(new ValueType("10000000", MetricType.MAX));
a.addAValue(new ValueType("1000000", MetricType.STEP));
return a;
}
/**
* Create a jitter attribute with a hard coded value of '20 ms'.
* @return Jitter attribute instance.
*/
private AttributeType newJitterAttrib()
{
AttributeType a = new AttributeType();
a.setName("jitter");
a.setType(BasicType.INT);
a.setUnit(new UnitType("msec", null, null, null));
a.addAValue(new ValueType("20", MetricType.EXACT));
return a;
}
/**
* Create a delay attribute with a hard coded value of '20 ms'.
* @return Delay attribute instance.
*/
private AttributeType newDelayAttrib()
{
AttributeType a = new AttributeType();
a.setName("delay");
a.setType(BasicType.INT);
a.setUnit(new UnitType("msec", null, null, null));
a.addAValue(new ValueType("20", MetricType.EXACT));
return a;
}
} // SampleOptimiserTest