/*
* Title: CloudSim Toolkit
* Description: CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
* Licence: GPL - http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2009-2012, The University of Melbourne, Australia
*/
package org.cloudbus.cloudsim;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.cloudbus.cloudsim.distributions.ContinuousDistribution;
/**
* An implementation of a storage system. It simulates the behaviour of a typical harddrive storage.
* The default values for this storage are those of a Maxtor DiamonMax 10 ATA harddisk with the
* following parameters:
* <ul>
* <li>latency = 4.17 ms
* <li>avg seek time = 9 ms
* <li>max transfer rate = 133 MB/sec
* </ul>
*
* @author Uros Cibej
* @author Anthony Sulistio
* @since CloudSim Toolkit 1.0
*/
public class HarddriveStorage implements Storage {
/** a list storing the names of all the files on the harddrive. */
private List<String> nameList;
/** a list storing all the files stored on the harddrive. */
private List<File> fileList;
/** the name of the harddrive. */
private final String name;
/** a generator required to randomize the seek time. */
private ContinuousDistribution gen;
/** the current size of files on the harddrive. */
private double currentSize;
/** the total capacity of the harddrive in MB. */
private final double capacity;
/** the maximum transfer rate in MB/sec. */
private double maxTransferRate;
/** the latency of the harddrive in seconds. */
private double latency;
/** the average seek time in seconds. */
private double avgSeekTime;
/**
* Creates a new harddrive storage with a given name and capacity.
*
* @param name the name of the new harddrive storage
* @param capacity the capacity in MByte
* @throws ParameterException when the name and the capacity are not valid
*/
public HarddriveStorage(String name, double capacity) throws ParameterException {
if (name == null || name.length() == 0) {
throw new ParameterException("HarddriveStorage(): Error - invalid storage name.");
}
if (capacity <= 0) {
throw new ParameterException("HarddriveStorage(): Error - capacity <= 0.");
}
this.name = name;
this.capacity = capacity;
init();
}
/**
* Creates a new harddrive storage with a given capacity. In this case the name of the storage
* is a default name.
*
* @param capacity the capacity in MByte
* @throws ParameterException when the capacity is not valid
*/
public HarddriveStorage(double capacity) throws ParameterException {
if (capacity <= 0) {
throw new ParameterException("HarddriveStorage(): Error - capacity <= 0.");
}
name = "HarddriveStorage";
this.capacity = capacity;
init();
}
/**
* The initialization of the harddrive is done in this method. The most common parameters, such
* as latency, average seek time and maximum transfer rate are set. The default values are set
* to simulate the Maxtor DiamonMax 10 ATA harddisk. Furthermore, the necessary lists are
* created.
*/
private void init() {
fileList = new ArrayList<File>();
nameList = new ArrayList<String>();
gen = null;
currentSize = 0;
latency = 0.00417; // 4.17 ms in seconds
avgSeekTime = 0.009; // 9 ms
maxTransferRate = 133; // in MB/sec
}
/**
* Gets the available space on this storage in MB.
*
* @return the available space in MB
*/
@Override
public double getAvailableSpace() {
return capacity - currentSize;
}
/**
* Checks if the storage is full or not.
*
* @return <tt>true</tt> if the storage is full, <tt>false</tt> otherwise
*/
@Override
public boolean isFull() {
if (Math.abs(currentSize - capacity) < .0000001) { // currentSize == capacity
return true;
}
return false;
}
/**
* Gets the number of files stored on this storage.
*
* @return the number of stored files
*/
@Override
public int getNumStoredFile() {
return fileList.size();
}
/**
* Makes a reservation of the space on the storage to store a file.
*
* @param fileSize the size to be reserved in MB
* @return <tt>true</tt> if reservation succeeded, <tt>false</tt> otherwise
*/
@Override
public boolean reserveSpace(int fileSize) {
if (fileSize <= 0) {
return false;
}
if (currentSize + fileSize >= capacity) {
return false;
}
currentSize += fileSize;
return true;
}
/**
* Adds a file for which the space has already been reserved. The time taken (in seconds) for
* adding the file can also be found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param file the file to be added
* @return the time (in seconds) required to add the file
*/
@Override
public double addReservedFile(File file) {
if (file == null) {
return 0;
}
currentSize -= file.getSize();
double result = addFile(file);
// if add file fails, then set the current size back to its old value
if (result == 0.0) {
currentSize += file.getSize();
}
return result;
}
/**
* Checks whether there is enough space on the storage for a certain file.
*
* @param fileSize a FileAttribute object to compare to
* @return <tt>true</tt> if enough space available, <tt>false</tt> otherwise
*/
@Override
public boolean hasPotentialAvailableSpace(int fileSize) {
if (fileSize <= 0) {
return false;
}
// check if enough space left
if (getAvailableSpace() > fileSize) {
return true;
}
Iterator<File> it = fileList.iterator();
File file = null;
int deletedFileSize = 0;
// if not enough space, then if want to clear/delete some files
// then check whether it still have space or not
boolean result = false;
while (it.hasNext()) {
file = it.next();
if (!file.isReadOnly()) {
deletedFileSize += file.getSize();
}
if (deletedFileSize > fileSize) {
result = true;
break;
}
}
return result;
}
/**
* Gets the total capacity of the storage in MB.
*
* @return the capacity of the storage in MB
*/
@Override
public double getCapacity() {
return capacity;
}
/**
* Gets the current size of the stored files in MB.
*
* @return the current size of the stored files in MB
*/
@Override
public double getCurrentSize() {
return currentSize;
}
/**
* Gets the name of the storage.
*
* @return the name of this storage
*/
@Override
public String getName() {
return name;
}
/**
* Sets the latency of this harddrive in seconds.
*
* @param latency the new latency in seconds
* @return <tt>true</tt> if the setting succeeded, <tt>false</tt> otherwise
*/
public boolean setLatency(double latency) {
if (latency < 0) {
return false;
}
this.latency = latency;
return true;
}
/**
* Gets the latency of this harddrive in seconds.
*
* @return the latency in seconds
*/
public double getLatency() {
return latency;
}
/**
* Sets the maximum transfer rate of this storage system in MB/sec.
*
* @param rate the maximum transfer rate in MB/sec
* @return <tt>true</tt> if the setting succeeded, <tt>false</tt> otherwise
*/
@Override
public boolean setMaxTransferRate(int rate) {
if (rate <= 0) {
return false;
}
maxTransferRate = rate;
return true;
}
/**
* Gets the maximum transfer rate of the storage in MB/sec.
*
* @return the maximum transfer rate in MB/sec
*/
@Override
public double getMaxTransferRate() {
return maxTransferRate;
}
/**
* Sets the average seek time of the storage in seconds.
*
* @param seekTime the average seek time in seconds
* @return <tt>true</tt> if the setting succeeded, <tt>false</tt> otherwise
*/
public boolean setAvgSeekTime(double seekTime) {
return setAvgSeekTime(seekTime, null);
}
/**
* Sets the average seek time and a new generator of seek times in seconds. The generator
* determines a randomized seek time.
*
* @param seekTime the average seek time in seconds
* @param gen the ContinuousGenerator which generates seek times
* @return <tt>true</tt> if the setting succeeded, <tt>false</tt> otherwise
*/
public boolean setAvgSeekTime(double seekTime, ContinuousDistribution gen) {
if (seekTime <= 0.0) {
return false;
}
avgSeekTime = seekTime;
this.gen = gen;
return true;
}
/**
* Gets the average seek time of the harddrive in seconds.
*
* @return the average seek time in seconds
*/
public double getAvgSeekTime() {
return avgSeekTime;
}
/**
* Gets the file with the specified name. The time taken (in seconds) for getting the file can
* also be found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param fileName the name of the needed file
* @return the file with the specified filename
*/
@Override
public File getFile(String fileName) {
// check first whether file name is valid or not
File obj = null;
if (fileName == null || fileName.length() == 0) {
Log.printLine(name + ".getFile(): Warning - invalid " + "file name.");
return obj;
}
Iterator<File> it = fileList.iterator();
int size = 0;
int index = 0;
boolean found = false;
File tempFile = null;
// find the file in the disk
while (it.hasNext()) {
tempFile = it.next();
size += tempFile.getSize();
if (tempFile.getName().equals(fileName)) {
found = true;
obj = tempFile;
break;
}
index++;
}
// if the file is found, then determine the time taken to get it
if (found) {
obj = fileList.get(index);
double seekTime = getSeekTime(size);
double transferTime = getTransferTime(obj.getSize());
// total time for this operation
obj.setTransactionTime(seekTime + transferTime);
}
return obj;
}
/**
* Gets the list of file names located on this storage.
*
* @return a List of file names
*/
@Override
public List<String> getFileNameList() {
return nameList;
}
/**
* Get the seek time for a file with the defined size. Given a file size in MB, this method
* returns a seek time for the file in seconds.
*
* @param fileSize the size of a file in MB
* @return the seek time in seconds
*/
private double getSeekTime(int fileSize) {
double result = 0;
if (gen != null) {
result += gen.sample();
}
if (fileSize > 0 && capacity != 0) {
result += (fileSize / capacity);
}
return result;
}
/**
* Gets the transfer time of a given file.
*
* @param fileSize the size of the transferred file
* @return the transfer time in seconds
*/
private double getTransferTime(int fileSize) {
double result = 0;
if (fileSize > 0 && capacity != 0) {
result = (fileSize * maxTransferRate) / capacity;
}
return result;
}
/**
* Check if the file is valid or not. This method checks whether the given file or the file name
* of the file is valid. The method name parameter is used for debugging purposes, to output in
* which method an error has occured.
*
* @param file the file to be checked for validity
* @param methodName the name of the method in which we check for validity of the file
* @return <tt>true</tt> if the file is valid, <tt>false</tt> otherwise
*/
private boolean isFileValid(File file, String methodName) {
if (file == null) {
Log.printLine(name + "." + methodName + ": Warning - the given file is null.");
return false;
}
String fileName = file.getName();
if (fileName == null || fileName.length() == 0) {
Log.printLine(name + "." + methodName + ": Warning - invalid file name.");
return false;
}
return true;
}
/**
* Adds a file to the storage. First, the method checks if there is enough space on the storage,
* then it checks if the file with the same name is already taken to avoid duplicate filenames. <br>
* The time taken (in seconds) for adding the file can also be found using
* {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param file the file to be added
* @return the time taken (in seconds) for adding the specified file
*/
@Override
public double addFile(File file) {
double result = 0.0;
// check if the file is valid or not
if (!isFileValid(file, "addFile()")) {
return result;
}
// check the capacity
if (file.getSize() + currentSize > capacity) {
Log.printLine(name + ".addFile(): Warning - not enough space" + " to store " + file.getName());
return result;
}
// check if the same file name is alredy taken
if (!contains(file.getName())) {
double seekTime = getSeekTime(file.getSize());
double transferTime = getTransferTime(file.getSize());
fileList.add(file); // add the file into the HD
nameList.add(file.getName()); // add the name to the name list
currentSize += file.getSize(); // increment the current HD size
result = seekTime + transferTime; // add total time
}
file.setTransactionTime(result);
return result;
}
/**
* Adds a set of files to the storage. Runs through the list of files and save all of them. The
* time taken (in seconds) for adding each file can also be found using
* {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param list the files to be added
* @return the time taken (in seconds) for adding the specified files
*/
@Override
public double addFile(List<File> list) {
double result = 0.0;
if (list == null || list.isEmpty()) {
Log.printLine(name + ".addFile(): Warning - list is empty.");
return result;
}
Iterator<File> it = list.iterator();
File file = null;
while (it.hasNext()) {
file = it.next();
result += addFile(file); // add each file in the list
}
return result;
}
/**
* Removes a file from the storage. The time taken (in seconds) for deleting the file can also
* be found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param fileName the name of the file to be removed
* @return the deleted file
*/
@Override
public File deleteFile(String fileName) {
if (fileName == null || fileName.length() == 0) {
return null;
}
Iterator<File> it = fileList.iterator();
File file = null;
while (it.hasNext()) {
file = it.next();
String name = file.getName();
// if a file is found then delete
if (fileName.equals(name)) {
double result = deleteFile(file);
file.setTransactionTime(result);
break;
} else {
file = null;
}
}
return file;
}
/**
* Removes a file from the storage. The time taken (in seconds) for deleting the file can also
* be found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param fileName the name of the file to be removed
* @param file the file which is removed from the storage is returned through this parameter
* @return the time taken (in seconds) for deleting the specified file
*/
@Override
public double deleteFile(String fileName, File file) {
return deleteFile(file);
}
/**
* Removes a file from the storage. The time taken (in seconds) for deleting the file can also
* be found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param file the file which is removed from the storage is returned through this parameter
* @return the time taken (in seconds) for deleting the specified file
*/
@Override
public double deleteFile(File file) {
double result = 0.0;
// check if the file is valid or not
if (!isFileValid(file, "deleteFile()")) {
return result;
}
double seekTime = getSeekTime(file.getSize());
double transferTime = getTransferTime(file.getSize());
// check if the file is in the storage
if (contains(file)) {
fileList.remove(file); // remove the file HD
nameList.remove(file.getName()); // remove the name from name list
currentSize -= file.getSize(); // decrement the current HD space
result = seekTime + transferTime; // total time
file.setTransactionTime(result);
}
return result;
}
/**
* Checks whether a certain file is on the storage or not.
*
* @param fileName the name of the file we are looking for
* @return <tt>true</tt> if the file is in the storage, <tt>false</tt> otherwise
*/
@Override
public boolean contains(String fileName) {
boolean result = false;
if (fileName == null || fileName.length() == 0) {
Log.printLine(name + ".contains(): Warning - invalid file name");
return result;
}
// check each file in the list
Iterator<String> it = nameList.iterator();
while (it.hasNext()) {
String name = it.next();
if (name.equals(fileName)) {
result = true;
break;
}
}
return result;
}
/**
* Checks whether a certain file is on the storage or not.
*
* @param file the file we are looking for
* @return <tt>true</tt> if the file is in the storage, <tt>false</tt> otherwise
*/
@Override
public boolean contains(File file) {
boolean result = false;
if (!isFileValid(file, "contains()")) {
return result;
}
result = contains(file.getName());
return result;
}
/**
* Renames a file on the storage. The time taken (in seconds) for renaming the file can also be
* found using {@link gridsim.datagrid.File#getTransactionTime()}.
*
* @param file the file we would like to rename
* @param newName the new name of the file
* @return <tt>true</tt> if the renaming succeeded, <tt>false</tt> otherwise
*/
@Override
public boolean renameFile(File file, String newName) {
// check whether the new filename is conflicting with existing ones
// or not
boolean result = false;
if (contains(newName)) {
return result;
}
// replace the file name in the file (physical) list
File obj = getFile(file.getName());
if (obj == null) {
return result;
} else {
obj.setName(newName);
}
// replace the file name in the name list
Iterator<String> it = nameList.iterator();
while (it.hasNext()) {
String name = it.next();
if (name.equals(file.getName())) {
file.setTransactionTime(0);
nameList.remove(name);
nameList.add(newName);
result = true;
break;
}
}
return result;
}
}